From e39bf5bcdef58b8c0b1614c90e2f87e6b9ff7989 Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:46:57 -0700 Subject: [PATCH 01/11] merge with master, cleanup, colorize Reinforce Button --- .github/ISSUE_TEMPLATE/bug_report.yml | 189 +- .../request_for_enhancement.yml | 117 +- .github/dependabot.yml | 9 + .github/workflows/ci.yml | 4 +- .github/workflows/code-coverage.yml | 4 +- .github/workflows/dependency-scan.yml | 2 +- .github/workflows/javadoc.yml | 2 +- .github/workflows/nightly-ci.yml | 2 +- .gitignore | 2 +- LICENSE.txt | 675 ++ MekHQ/build.gradle | 4 +- .../Filtvelt Citizens Militia.jpg | Bin .../Thumpers.jpg | Bin .../data/images/hexes/minimap/cyberpunk.theme | 25 + MekHQ/data/images/hexes/minimap/darkula.theme | 24 + MekHQ/data/images/hexes/minimap/default.theme | 26 + .../images/hexes/minimap/defaultminimap.theme | 26 + .../images/hexes/minimap/dreamy colors.theme | 25 + .../hexes/minimap/earth and grass.theme | 25 + .../data/images/hexes/minimap/gbc green.theme | 25 + MekHQ/data/images/hexes/minimap/noctis.theme | 25 + .../images/hexes/minimap/non-boolean.theme | 24 + .../images/hexes/minimap/phosphor green.theme | 25 + .../images/hexes/minimap/pink lemonade.theme | 25 + .../images/hexes/minimap/pink world.theme | 25 + MekHQ/data/images/hexes/minimap/rebirth.theme | 24 + .../images/hexes/minimap/red amaranth.theme | 25 + .../hexes/minimap/shades of green.theme | 24 + .../data/images/hexes/minimap/solarized.theme | 25 + .../data/images/hexes/minimap/synthwave.theme | 25 + .../images/hexes/minimap/wood cabin.theme | 24 + .../images/hexes/minimap/zone of battle.theme | 25 + MekHQ/data/images/misc/MekHQ AutoResolve.png | Bin 0 -> 344997 bytes MekHQ/data/images/misc/MekHQ.png | Bin 0 -> 32580 bytes MekHQ/data/images/misc/acar_splash_hd.png | Bin 0 -> 849900 bytes .../images/misc/challenge_estimate_full.png | Bin 2042 -> 21511 bytes .../images/misc/challenge_estimate_half.png | Bin 852 -> 14313 bytes .../stratcon/Stratcon_Cold_Facility.png | Bin 0 -> 20432 bytes .../stratcon/Stratcon_Frozen_Facility.png | Bin 0 -> 20676 bytes .../images/stratcon/Stratcon_Hot_Facility.png | Bin 0 -> 21038 bytes .../stratcon/Stratcon_Temperate_Facility.png | Bin 0 -> 20393 bytes .../universe/factions/logo_clan_wolf-2.png | Bin 7122 -> 0 bytes ...t Infantry (Elephant)(Laser Rifle_MRR).blk | 2 +- MekHQ/data/names/callsigns.csv | 1320 +- MekHQ/data/names/surnames.csv | 4 +- .../data/scenariomodifiers/AlliedASFAce01.xml | 4 +- .../scenariomodifiers/AlliedAirSupport.xml | 4 +- .../AlliedAirSupportBombers.xml | 7 +- .../scenariomodifiers/AlliedArtyGarrison.xml | 4 +- .../scenariomodifiers/AlliedArtySupport.xml | 4 +- .../data/scenariomodifiers/AlliedCavLance.xml | 4 +- .../data/scenariomodifiers/AlliedHorseCav.xml | 4 +- .../scenariomodifiers/AlliedMekAceCRD.xml | 4 +- .../scenariomodifiers/AlliedMekAceGLT.xml | 4 +- .../scenariomodifiers/AlliedMekAceGOL.xml | 4 +- .../scenariomodifiers/AlliedMekAceOTT.xml | 4 +- .../scenariomodifiers/AlliedMekAceTBT.xml | 4 +- .../scenariomodifiers/AlliedMekGarrison.xml | 2 +- .../scenariomodifiers/AlliedOfficerMek.xml | 7 +- .../scenariomodifiers/AlliedTraineesAir.xml | 4 +- .../AlliedTraineesGround.xml | 4 +- .../data/scenariomodifiers/AlliedTurrets.xml | 4 +- .../EnemyAirSupportBombers.xml | 3 + .../scenariomodifiers/EnemyCommanderMek.xml | 3 + .../scenariomodifiers/EnemyOfficerMek.xml | 3 + .../scenariomodifiers/FacilityAlliedEvac.xml | 4 +- .../scenariomodifiers/HouseOfficerAir.xml | 3 + .../scenariomodifiers/HouseOfficerGround.xml | 3 + MekHQ/data/scenariomodifiers/LiaisonAir.xml | 5 +- .../data/scenariomodifiers/LiaisonGround.xml | 5 +- .../LocalGarrisonInfantry.xml | 2 +- .../scenariomodifiers/PrimaryAlliesAir.xml | 4 +- .../scenariomodifiers/PrimaryAlliesGround.xml | 4 +- .../scenariomodifiers/airBattleModifiers.xml | 8 +- .../groundBattleModifiers.xml | 13 +- .../scenariomodifiers/modifiermanifest.xml | 10 +- .../data/scenariotemplates/Assassination.xml | 4 + .../scenariotemplates/Chasing a Rumor.xml | 146 + .../scenariotemplates/Close Air Support.xml | 2 +- .../data/scenariotemplates/Convoy Escort.xml | 14 +- .../scenariotemplates/Convoy Interdiction.xml | 6 +- MekHQ/data/scenariotemplates/Convoy Raid.xml | 3 +- .../Critical Convoy Escort.xml | 14 +- .../scenariotemplates/Deep Raid Defense.xml | 15 +- MekHQ/data/scenariotemplates/Deep Raid.xml | 2 +- .../data/scenariotemplates/DropShip Raid.xml | 2 +- ...nvoy Defense - Player - Low-Atmosphere.xml | 139 + ...ergency Convoy Defense - Player - VTOL.xml | 172 + .../Emergency Convoy Defense - Player.xml | 143 + .../Emergency Convoy Defense.xml | 192 + .../Frontline VIP Capture.xml | 112 +- .../scenariotemplates/Heavy Recon Evasion.xml | 4 +- .../Intercept Engagement.xml | 4 + .../Interception Defense.xml | 4 +- .../Irregular Force Assault.xml | 4 + .../Isolated DropShip Defense.xml | 15 +- .../Low-Atmosphere DropShip Assault.xml | 54 +- .../Low-Atmosphere DropShip Escort.xml | 3 + ...-Atmosphere Reinforcements Intercepted.xml | 3 + .../Picket Line Breakthrough.xml | 4 + .../data/scenariotemplates/Recon Evasion.xml | 9 +- .../Reconnaissance Interdiction.xml | 7 +- .../Reinforcements Intercepted.xml | 5 + .../scenariotemplates/Remote VIP Capture.xml | 112 +- .../scenariotemplates/ScenarioManifest.xml | 56 +- .../scenariotemplates/Space Blockade Run.xml | 7 +- .../Space DropShip Intercept.xml | 9 +- .../Space Reinforcements Intercepted.xml | 3 + .../{VIP Capture.xml => VIP Ambush.xml} | 124 +- MekHQ/data/scenariotemplates/VIP Defense.xml | 8 +- .../StratconBiomeManifest.xml | 24 +- .../ExtractionRaid.xml | 4 +- .../GuerillaWarfare.xml | 2 +- .../stratconcontractdefinitions/ReconRaid.xml | 2 +- .../stratconfacilities/AlliedDataCenter.xml | 4 +- .../stratconfacilities/AlliedSpacePort.xml | 9 + .../stratconfacilities/AlliedSupplyDepot.xml | 9 - .../stratconfacilities/HostileSpacePort.xml | 11 + .../stratconfacilities/HostileSupplyDepot.xml | 11 - .../stratconfacilities/facilitymanifest.xml | 4 +- .../universe/academies/Unit Education.xml | 62 +- MekHQ/data/universe/systems.xml | 13 +- .../Against_the_Bot_Starter_Guide_v4.pdf | Bin .../Against_the_bot.txt | 0 .../AtB documentation.19_8 (WIP).txt | 0 MekHQ/docs/Asset Licenses/.gitkeep | 0 ...Q.md => DistributedMekHQ (Depreciated).md} | 0 .../docs/Personnel Modules/Awards Module.pdf | Bin 1116638 -> 1144348 bytes .../Personnel Modules/Education Module.pdf | Bin 511259 -> 529972 bytes .../Prisoners, Defection & Dependents.pdf | Bin 76962 -> 82433 bytes .../Random Personalities.pdf | Bin 76611 -> 77629 bytes .../Turnover & Retention Module.pdf | Bin 698801 -> 713402 bytes ...ract Combat Auto-Resolve documentation.pdf | Bin 0 -> 192368 bytes ...eams, Roles, Training & Reinforcements.pdf | Bin 0 -> 129309 bytes .../MekHQ Morale.pdf | Bin 65063 -> 72395 bytes .../Resupply & Convoys.pdf | Bin 0 -> 103429 bytes .../Unit Markets.pdf | Bin 51180 -> 61868 bytes MekHQ/docs/history.txt | 309 +- ... Operations.xml => CampaignOperations.xml} | 516 +- ...on).xml => CampaignOperationsStratCon.xml} | 478 +- ... Pilot Program.xml => NewPilotProgram.xml} | 1158 +- MekHQ/mmconf/campaignPresets/Tabula Rasa.xml | 1540 --- ...perience.xml => TheCompleteExperience.xml} | 1161 +- MekHQ/mmconf/log4j2.xml | 8 +- .../AssignForceToTransport.properties | 47 + .../AtBDynamicScenarioFactory.properties | 2 +- .../mekhq/resources/AtBStratCon.properties | 89 +- ...esolveBehaviorSettingsDialog_en.properties | 4 + .../mekhq/resources/Campaign.properties | 30 +- .../mekhq/resources/CampaignGUI.properties | 12 +- .../CampaignHasProblemOnLoad.properties | 36 + .../CampaignOptionsDialog.properties | 3113 +++-- .../resources/ContractAutomation.properties | 6 +- .../resources/ContractMarketDialog.properties | 17 +- .../resources/ContractViewPanel.properties | 5 +- .../mekhq/resources/DateChooser.properties | 12 +- .../mekhq/resources/Education.properties | 6 + .../resources/mekhq/resources/GUI.properties | 311 +- .../resources/mekhq/resources/Loot.properties | 3 + .../mekhq/resources/MekLabTab.properties | 3 + .../mekhq/resources/Mission.properties | 41 +- .../NEWCampaignOptionsDialog.properties | 11 - .../mekhq/resources/NewsDialog.properties | 27 + .../resources/PartsInUseTableModel.properties | 1 + .../resources/PartsReportDialog.properties | 3 + .../resources/PartsStoreDialog.properties | 1 - .../mekhq/resources/Resupply.properties | 3511 ++++++ .../resources/ScenarioTableModel.properties | 2 +- ...VocationalExperienceAwardDialog.properties | 10 + .../mekhq/resources/messages.properties | 19 + MekHQ/src/mekhq/AtBGameThread.java | 239 +- .../mekhq/{campaign => }/CampaignPreset.java | 19 +- MekHQ/src/mekhq/GameThread.java | 35 +- MekHQ/src/mekhq/IconPackage.java | 9 +- MekHQ/src/mekhq/MekHQ.java | 281 +- MekHQ/src/mekhq/Utilities.java | 201 +- MekHQ/src/mekhq/campaign/Campaign.java | 1892 +-- MekHQ/src/mekhq/campaign/CampaignFactory.java | 92 +- MekHQ/src/mekhq/campaign/CampaignOptions.java | 362 +- MekHQ/src/mekhq/campaign/CampaignSummary.java | 47 +- .../campaign/CampaignTransporterMap.java | 188 + .../campaign/ResolveScenarioTracker.java | 26 +- .../campaign/autoresolve/AtBSetupForces.java | 505 + .../autoresolve/AutoResolveMethod.java | 65 + .../campaign/autoresolve/OrderFactory.java | 234 + .../campaign/enums/CampaignTransportType.java | 103 + .../event/PersonCrewAssignmentEvent.java | 41 +- .../mekhq/campaign/finances/Accountant.java | 27 + .../src/mekhq/campaign/finances/Finances.java | 160 +- .../finances/enums/FinancialTerm.java | 4 +- ...trategicFormation.java => CombatTeam.java} | 206 +- MekHQ/src/mekhq/campaign/force/Force.java | 151 +- .../handler/PostScenarioDialogHandler.java | 138 + ...onalStatus.java => OperationalStatus.java} | 25 +- .../mekhq/campaign/io/CampaignXmlParser.java | 170 +- .../src/mekhq/campaign/market/PartsStore.java | 14 +- .../campaign/market/PersonnelMarket.java | 155 +- .../AbstractContractMarket.java | 379 +- .../AtbMonthlyContractMarket.java | 110 +- .../contractMarket/CamOpsContractMarket.java | 2 +- .../contractMarket/ContractAutomation.java | 178 +- .../market/procurement/Procurement.java | 364 + .../market/unitMarket/AbstractUnitMarket.java | 14 +- .../unitMarket/AtBMonthlyUnitMarket.java | 19 +- .../mekhq/campaign/mission/AtBContract.java | 538 +- .../campaign/mission/AtBDynamicScenario.java | 10 +- .../mission/AtBDynamicScenarioFactory.java | 787 +- .../mekhq/campaign/mission/AtBScenario.java | 185 +- .../mission/CommonObjectiveFactory.java | 11 +- .../src/mekhq/campaign/mission/Contract.java | 21 + MekHQ/src/mekhq/campaign/mission/Loot.java | 190 +- .../campaign/mission/ObjectiveEffect.java | 6 +- .../src/mekhq/campaign/mission/Scenario.java | 104 +- .../mission/ScenarioObjectiveProcessor.java | 90 +- .../campaign/mission/ScenarioTemplate.java | 93 +- .../mission/atb/AtBScenarioFactory.java | 104 +- .../mission/atb/AtBScenarioModifier.java | 21 +- .../atb/AtBScenarioModifierApplicator.java | 25 +- .../scenario/BaseAttackBuiltInScenario.java | 21 +- .../scenario/BreakthroughBuiltInScenario.java | 12 +- .../atb/scenario/ChaseBuiltInScenario.java | 9 +- .../scenario/ExtractionBuiltInScenario.java | 7 +- .../scenario/HideAndSeekBuiltInScenario.java | 12 +- .../scenario/HoldTheLineBuiltInScenario.java | 8 +- .../atb/scenario/ProbeBuiltInScenario.java | 6 +- .../scenario/ReconRaidBuiltInScenario.java | 8 +- .../atb/scenario/StandUpBuiltInScenario.java | 7 +- .../mission/enums/AtBContractType.java | 268 +- .../campaign/mission/enums/AtBLanceRole.java | 115 - .../campaign/mission/enums/CombatRole.java | 139 + .../mission/enums/ScenarioStatus.java | 13 +- .../campaign/mission/enums/ScenarioType.java | 100 + .../GenerateResupplyContents.java | 265 + .../resupplyAndCaches/PerformResupply.java | 583 + .../mission/resupplyAndCaches/Resupply.java | 952 ++ .../resupplyAndCaches/ResupplyUtilities.java | 229 + .../resupplyAndCaches/StarLeagueCache.java | 640 + .../src/mekhq/campaign/parts/AeroSensor.java | 8 +- .../campaign/parts/MissingAeroSensor.java | 22 +- MekHQ/src/mekhq/campaign/parts/Part.java | 18 +- MekHQ/src/mekhq/campaign/parts/PartInUse.java | 34 + MekHQ/src/mekhq/campaign/parts/Refit.java | 74 +- .../campaign/parts/equipment/HeatSink.java | 27 + .../src/mekhq/campaign/personnel/Person.java | 273 +- .../mekhq/campaign/personnel/SkillType.java | 34 +- .../personnel/autoAwards/MiscAwards.java | 2 +- .../autoAwards/TheatreOfWarAwards.java | 12 +- .../personnel/death/AbstractDeath.java | 2 +- .../personnel/divorce/AbstractDivorce.java | 66 +- .../campaign/personnel/education/Academy.java | 8 +- .../education/EducationController.java | 260 +- .../education/TrainingCombatTeams.java | 287 + .../generator/AbstractPersonnelGenerator.java | 32 +- .../generator/AbstractSkillGenerator.java | 2 +- .../AbstractSpecialAbilityGenerator.java | 2 +- .../generator/DefaultPersonnelGenerator.java | 12 +- .../personnel/marriage/AbstractMarriage.java | 36 +- .../procreation/AbstractProcreation.java | 107 +- .../enums/personalities/Aggression.java | 60 +- .../enums/personalities/Ambition.java | 60 +- .../enums/personalities/Greed.java | 60 +- .../enums/personalities/Intelligence.java | 52 +- .../enums/personalities/PersonalityQuirk.java | 129 +- .../enums/personalities/Social.java | 60 +- .../AverageExperienceRating.java | 4 + .../ReputationController.java | 3 +- .../TransportationRating.java | 23 +- .../storypoint/CreateCharacterStoryPoint.java | 47 +- .../stratcon/StratconCampaignState.java | 56 +- .../stratcon/StratconContractInitializer.java | 65 +- .../campaign/stratcon/StratconFacility.java | 13 +- .../stratcon/StratconRulesManager.java | 2001 ++- .../campaign/stratcon/StratconScenario.java | 275 +- .../campaign/stratcon/StratconTrackState.java | 33 + .../stratcon/SupportPointNegotiation.java | 277 + .../unit/AbstractTransportedUnitsSummary.java | 260 + .../mekhq/campaign/unit/CargoStatistics.java | 11 +- .../campaign/unit/ITransportAssignment.java | 85 + .../unit/ITransportedUnitsSummary.java | 137 + .../src/mekhq/campaign/unit/MothballInfo.java | 21 +- .../unit/ShipTransportedUnitsSummary.java | 191 + .../unit/TacticalTransportedUnitsSummary.java | 214 + .../campaign/unit/TransportAssignment.java | 199 + .../unit/TransportShipAssignment.java | 44 +- MekHQ/src/mekhq/campaign/unit/Unit.java | 1229 +- MekHQ/src/mekhq/campaign/unit/UnitOrder.java | 15 +- .../unit/actions/HirePersonnelUnitAction.java | 4 +- .../unit/actions/RestoreUnitAction.java | 14 +- .../unit/cleanup/EquipmentUnscrambler.java | 2 +- .../campaign/unit/cleanup/UnscrambleStep.java | 2 +- .../campaign/unit/enums/TransporterType.java | 97 + .../campaign/universe/IUnitGenerator.java | 22 +- .../src/mekhq/campaign/universe/NewsItem.java | 5 +- .../universe/SocioIndustrialData.java | 2 +- .../src/mekhq/campaign/universe/StarUtil.java | 75 +- .../src/mekhq/campaign/universe/Systems.java | 54 +- .../AbstractBattleMekQualityGenerator.java | 2 +- ...AbstractBattleMekWeightClassGenerator.java | 2 +- .../AbstractCompanyGenerator.java | 27 +- .../partGenerators/AbstractPartGenerator.java | 2 +- .../AbstractFactionSelector.java | 2 +- .../AbstractPlanetSelector.java | 4 +- .../utilities/CampaignTransportUtilities.java | 258 + MekHQ/src/mekhq/gui/BriefingTab.java | 249 +- MekHQ/src/mekhq/gui/CampaignGUI.java | 241 +- MekHQ/src/mekhq/gui/FileDialogs.java | 4 +- MekHQ/src/mekhq/gui/FinancesTab.java | 10 + MekHQ/src/mekhq/gui/ForceRenderer.java | 33 +- MekHQ/src/mekhq/gui/InterstellarMapPanel.java | 40 +- MekHQ/src/mekhq/gui/MekLabTab.java | 46 +- .../mekhq/gui/ReportHyperlinkListener.java | 37 +- MekHQ/src/mekhq/gui/SpecialAbilityPanel.java | 156 - MekHQ/src/mekhq/gui/StratconPanel.java | 177 +- MekHQ/src/mekhq/gui/StratconTab.java | 78 +- MekHQ/src/mekhq/gui/TOETab.java | 8 +- .../gui/adapter/LoanTableMouseAdapter.java | 17 +- .../adapter/PersonnelTableMouseAdapter.java | 84 +- .../mekhq/gui/adapter/TOEMouseAdapter.java | 533 +- .../gui/adapter/UnitTableMouseAdapter.java | 20 +- ...ialog.java => AbstractMHQDialogBasic.java} | 142 +- .../baseComponents/AbstractMHQNagDialog.java | 237 +- .../baseComponents/MHQDialogImmersive.java | 489 + .../CampaignOptionsAbilityInfo.java | 143 + .../CampaignOptionsDialog.java | 259 + .../campaignOptions/CampaignOptionsPane.java | 480 + .../CampaignOptionsUtilities.java | 211 + .../CreateCampaignPreset.java} | 51 +- .../campaignOptions/SelectPresetDialog.java | 105 +- .../components/CampaignOptionsButton.java | 96 + .../components/CampaignOptionsCheckBox.java | 100 + .../CampaignOptionsGridBagConstraints.java | 91 + .../CampaignOptionsHeaderPanel.java | 125 + .../components/CampaignOptionsLabel.java | 98 + .../components/CampaignOptionsSpinner.java | 168 + .../CampaignOptionsStandardPanel.java | 111 + .../components/CampaignOptionsTextField.java | 88 + .../contents/AbilitiesTab.java | 500 + .../contents/AdvancementTab.java | 1027 ++ .../contents/BiographyTab.java | 1478 +++ .../contents/EquipmentAndSuppliesTab.java | 1197 ++ .../campaignOptions/contents/FinancesTab.java | 854 ++ .../campaignOptions/contents/GeneralTab.java | 558 + .../campaignOptions/contents/MarketsTab.java | 816 ++ .../contents/PersonnelTab.java | 1580 +++ .../contents/RelationshipsTab.java | 866 ++ .../contents/RepairAndMaintenanceTab.java | 460 + .../campaignOptions/contents/RulesetsTab.java | 1176 ++ .../campaignOptions/contents/SkillsTab.java | 700 ++ .../contents/TurnoverAndRetentionTab.java | 901 ++ .../mekhq/gui/dialog/AcquisitionsDialog.java | 81 +- .../mekhq/gui/dialog/AdvanceDaysDialog.java | 36 +- .../mekhq/gui/dialog/AutoAwardsDialog.java | 2 +- .../AutoResolveBehaviorSettingsDialog.java | 5 +- .../gui/dialog/AutoResolveChanceDialog.java | 0 MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java | 44 +- .../gui/dialog/CampaignExportWizard.java | 11 +- .../gui/dialog/CampaignHasProblemOnLoad.java | 138 + .../gui/dialog/CampaignOptionsDialog.java | 219 - .../dialog/CampaignPresetSelectionDialog.java | 0 .../gui/dialog/ContractAutomationDialog.java | 161 + .../gui/dialog/ContractMarketDialog.java | 20 + .../dialog/CustomizeAtBContractDialog.java | 4 +- .../mekhq/gui/dialog/DataLoadingDialog.java | 132 +- MekHQ/src/mekhq/gui/dialog/DateChooser.java | 48 +- .../dialog/ForceTemplateAssignmentDialog.java | 8 +- MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java | 6 +- .../gui/dialog/MekHQUnitSelectorDialog.java | 19 + .../gui/dialog/NewAtBContractDialog.java | 26 +- .../dialog/NewCampaignConfirmationDialog.java | 11 +- MekHQ/src/mekhq/gui/dialog/NewsDialog.java | 229 + .../mekhq/gui/dialog/PartsReportDialog.java | 169 +- .../mekhq/gui/dialog/PartsStoreDialog.java | 84 +- .../dialog/ResolveScenarioWizardDialog.java | 191 +- .../VocationalExperienceAwardDialog.java | 193 + ...AutoResolveBehaviorSettingsHelpDialog.java | 9 +- .../DeploymentShortfallNagDialog.java | 78 + .../nagDialogs/EndContractNagDialog.java | 83 +- .../InsufficientAstechTimeNagDialog.java | 106 +- .../InsufficientAstechsNagDialog.java | 86 +- .../InsufficientMedicsNagDialog.java | 87 +- .../nagDialogs/InvalidFactionNagDialog.java | 114 +- .../gui/dialog/nagDialogs/NagController.java | 175 + .../nagDialogs/NoCommanderNagDialog.java | 71 +- .../OutstandingScenariosNagDialog.java | 90 +- .../PregnantCombatantNagDialog.java | 86 +- .../dialog/nagDialogs/PrisonersNagDialog.java | 67 +- .../nagDialogs/ShortDeploymentNagDialog.java | 88 - .../UnableToAffordExpensesNagDialog.java | 97 +- .../UnableToAffordJumpNagDialog.java | 108 +- .../UnableToAffordLoanPaymentNagDialog.java | 104 +- .../UnmaintainedUnitsNagDialog.java | 71 +- .../UnresolvedStratConContactsNagDialog.java | 115 +- .../UntreatedPersonnelNagDialog.java | 58 +- .../nagLogic/DeploymentShortfallNagLogic.java | 43 + .../nagLogic/EndContractNagLogic.java | 35 + .../InsufficientAstechTimeNagLogic.java | 60 + .../nagLogic/InsufficientAstechsNagLogic.java | 25 + .../nagLogic/InsufficientMedicsNagLogic.java | 23 + .../nagLogic/InvalidFactionNagLogic.java | 27 + .../nagLogic/NoCommanderNagLogic.java | 21 + .../OutstandingScenariosNagLogic.java | 101 + .../nagLogic/PregnantCombatantNagLogic.java | 48 + .../nagLogic/PrisonersNagLogic.java | 24 + .../UnableToAffordExpensesNagLogic.java | 42 + .../nagLogic/UnableToAffordJumpNagLogic.java | 56 + .../UnableToAffordLoanPaymentNag.java | 57 + .../nagLogic/UnmaintainedUnitsNagLogic.java | 29 + .../UnresolvedStratConContactsNagLogic.java | 65 + .../nagLogic/UntreatedPersonnelNagLogic.java | 32 + .../reportDialogs/AbstractReportDialog.java | 6 +- .../DialogAbandonedConvoy.java | 168 + .../DialogContractStart.java | 259 + .../resupplyAndCaches/DialogInterception.java | 212 + .../resupplyAndCaches/DialogItinerary.java | 354 + .../DialogPlayerConvoyOption.java | 225 + .../DialogResupplyFocus.java | 216 + .../DialogRoleplayEvent.java | 190 + .../resupplyAndCaches/DialogSwindled.java | 189 + .../ResupplyDialogUtilities.java | 149 + .../menus/AssignForceToShipTransportMenu.java | 111 + .../AssignForceToTacticalTransportMenu.java | 110 + .../gui/menus/AssignForceToTransportMenu.java | 165 + .../gui/menus/AssignPersonToUnitMenu.java | 4 +- .../gui/menus/AssignUnitToPersonMenu.java | 9 +- .../mekhq/gui/model/AutoAwardsTableModel.java | 4 +- .../mekhq/gui/model/FinanceTableModel.java | 3 + .../mekhq/gui/model/PartsInUseTableModel.java | 69 +- .../mekhq/gui/model/ScenarioTableModel.java | 45 +- .../mekhq/gui/panels/CampaignPresetPanel.java | 8 +- .../mekhq/gui/panels/StartupScreenPanel.java | 85 +- .../mekhq/gui/panes/CampaignOptionsPane.java | 10121 ---------------- .../src/mekhq/gui/panes/RankSystemsPane.java | 41 +- .../gui/renderers/CampaignPresetRenderer.java | 66 - .../stratcon/CampaignManagementDialog.java | 104 +- .../stratcon/ScenarioWizardLanceModel.java | 13 +- .../stratcon/ScenarioWizardLanceRenderer.java | 42 +- .../stratcon/ScenarioWizardUnitRenderer.java | 36 +- .../gui/stratcon/StratconScenarioWizard.java | 759 +- .../gui/stratcon/TrackForceAssignmentUI.java | 36 +- .../mekhq/gui/view/AtBScenarioViewPanel.java | 39 +- .../mekhq/gui/view/ContractSummaryPanel.java | 27 +- MekHQ/src/mekhq/gui/view/ForceViewPanel.java | 52 +- .../mekhq/gui/view/LanceAssignmentView.java | 82 +- .../src/mekhq/gui/view/MissionViewPanel.java | 63 +- MekHQ/src/mekhq/module/api/MekHQModule.java | 2 +- .../src/mekhq/utilities/EntityUtilities.java | 53 + MekHQ/src/mekhq/utilities/ImageUtilities.java | 65 + .../utilities/MHQInternationalization.java | 118 + .../utilities/PotentialTransportsMap.java | 153 + .../mekhq/campaign/CampaignTest.java | 71 +- .../campaign/autoresolve/ResolverTest.java | 407 + .../mekhq/campaign/parts/RefitTest.java | 3 + .../marriage/AbstractMarriageTest.java | 352 +- .../procreation/AbstractProcreationTest.java | 704 +- .../unit/LegacyUnitShipTransportTest.java | 232 + .../mekhq/campaign/unit/UnitPersonTest.java | 3 +- .../campaign/unit/UnitTransportTest.java | 153 +- .../unit/actions/RestoreUnitActionTest.java | 10 +- .../InsufficientAstechTimeNagDialogTest.java | 291 - ...resolvedStratConContactsNagDialogTest.java | 150 - .../EndContractNagLogicTest.java} | 12 +- .../InsufficientAstechsNagLogicTest.java} | 25 +- .../InsufficientMedicsNagLogicTest.java} | 20 +- .../InvalidFactionNagLogicTest.java} | 56 +- .../NoCommanderNagLogicTest.java} | 13 +- .../OutstandingScenariosNagLogicTest.java} | 90 +- .../PregnantCombatantNagLogicTest.java} | 20 +- .../PrisonersNagLogicTest.java} | 12 +- .../ShortDeploymentNagLogicTest.java} | 25 +- .../UnableToAffordExpensesNagLogicTest.java} | 16 +- .../UnableToAffordJumpNagLogicTest.java} | 33 +- ...nableToAffordLoanPaymentNagLogicTest.java} | 56 +- .../UnmaintainedUnitsNagLogicTest.java} | 30 +- ...nresolvedStratConContactsNagLogicTest.java | 109 + .../UntreatedPersonnelNagLogicTest.java} | 21 +- .../mekhq/gui/model/UnitTableModelTest.java | 13 +- MekHQ/userdata/data/universe/ranks.xml | 2 +- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 43504 -> 43583 bytes 479 files changed, 51740 insertions(+), 23250 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 LICENSE.txt rename MekHQ/data/images/camo/{Filtvelt Coaltion => Filtvelt Coalition}/Filtvelt Citizens Militia.jpg (100%) rename MekHQ/data/images/camo/{Filtvelt Coaltion => Filtvelt Coalition}/Thumpers.jpg (100%) create mode 100644 MekHQ/data/images/hexes/minimap/cyberpunk.theme create mode 100644 MekHQ/data/images/hexes/minimap/darkula.theme create mode 100644 MekHQ/data/images/hexes/minimap/default.theme create mode 100644 MekHQ/data/images/hexes/minimap/defaultminimap.theme create mode 100644 MekHQ/data/images/hexes/minimap/dreamy colors.theme create mode 100644 MekHQ/data/images/hexes/minimap/earth and grass.theme create mode 100644 MekHQ/data/images/hexes/minimap/gbc green.theme create mode 100644 MekHQ/data/images/hexes/minimap/noctis.theme create mode 100644 MekHQ/data/images/hexes/minimap/non-boolean.theme create mode 100644 MekHQ/data/images/hexes/minimap/phosphor green.theme create mode 100644 MekHQ/data/images/hexes/minimap/pink lemonade.theme create mode 100644 MekHQ/data/images/hexes/minimap/pink world.theme create mode 100644 MekHQ/data/images/hexes/minimap/rebirth.theme create mode 100644 MekHQ/data/images/hexes/minimap/red amaranth.theme create mode 100644 MekHQ/data/images/hexes/minimap/shades of green.theme create mode 100644 MekHQ/data/images/hexes/minimap/solarized.theme create mode 100644 MekHQ/data/images/hexes/minimap/synthwave.theme create mode 100644 MekHQ/data/images/hexes/minimap/wood cabin.theme create mode 100644 MekHQ/data/images/hexes/minimap/zone of battle.theme create mode 100644 MekHQ/data/images/misc/MekHQ AutoResolve.png create mode 100644 MekHQ/data/images/misc/MekHQ.png create mode 100644 MekHQ/data/images/misc/acar_splash_hd.png create mode 100644 MekHQ/data/images/stratcon/Stratcon_Cold_Facility.png create mode 100644 MekHQ/data/images/stratcon/Stratcon_Frozen_Facility.png create mode 100644 MekHQ/data/images/stratcon/Stratcon_Hot_Facility.png create mode 100644 MekHQ/data/images/stratcon/Stratcon_Temperate_Facility.png delete mode 100644 MekHQ/data/images/universe/factions/logo_clan_wolf-2.png create mode 100644 MekHQ/data/scenariotemplates/Chasing a Rumor.xml create mode 100644 MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player - Low-Atmosphere.xml create mode 100644 MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player - VTOL.xml create mode 100644 MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player.xml create mode 100644 MekHQ/data/scenariotemplates/Emergency Convoy Defense.xml rename MekHQ/data/scenariotemplates/{VIP Capture.xml => VIP Ambush.xml} (67%) create mode 100644 MekHQ/data/stratconfacilities/AlliedSpacePort.xml delete mode 100644 MekHQ/data/stratconfacilities/AlliedSupplyDepot.xml create mode 100644 MekHQ/data/stratconfacilities/HostileSpacePort.xml delete mode 100644 MekHQ/data/stratconfacilities/HostileSupplyDepot.xml rename MekHQ/docs/{Stratcon and Against the Bot => Archive Stuff}/Against_the_Bot_Starter_Guide_v4.pdf (100%) rename MekHQ/docs/{Stratcon and Against the Bot => Archive Stuff}/Against_the_bot.txt (100%) rename MekHQ/docs/{Stratcon and Against the Bot => Archive Stuff}/AtB documentation.19_8 (WIP).txt (100%) create mode 100644 MekHQ/docs/Asset Licenses/.gitkeep rename MekHQ/docs/{DistributedMekHQ.md => DistributedMekHQ (Depreciated).md} (100%) create mode 100644 MekHQ/docs/Stratcon and Against the Bot/ACAR-Abstract Combat Auto-Resolve documentation.pdf create mode 100644 MekHQ/docs/Stratcon and Against the Bot/Combat Teams, Roles, Training & Reinforcements.pdf create mode 100644 MekHQ/docs/Stratcon and Against the Bot/Resupply & Convoys.pdf rename MekHQ/mmconf/campaignPresets/{Campaign Operations.xml => CampaignOperations.xml} (95%) rename MekHQ/mmconf/campaignPresets/{Campaign Operations (StratCon).xml => CampaignOperationsStratCon.xml} (94%) rename MekHQ/mmconf/campaignPresets/{New Pilot Program.xml => NewPilotProgram.xml} (81%) delete mode 100644 MekHQ/mmconf/campaignPresets/Tabula Rasa.xml rename MekHQ/mmconf/campaignPresets/{Complete Experience.xml => TheCompleteExperience.xml} (82%) create mode 100644 MekHQ/resources/mekhq/resources/AssignForceToTransport.properties create mode 100644 MekHQ/resources/mekhq/resources/AutoResolveBehaviorSettingsDialog_en.properties create mode 100644 MekHQ/resources/mekhq/resources/CampaignHasProblemOnLoad.properties create mode 100644 MekHQ/resources/mekhq/resources/Loot.properties create mode 100644 MekHQ/resources/mekhq/resources/MekLabTab.properties delete mode 100644 MekHQ/resources/mekhq/resources/NEWCampaignOptionsDialog.properties create mode 100644 MekHQ/resources/mekhq/resources/NewsDialog.properties create mode 100644 MekHQ/resources/mekhq/resources/Resupply.properties create mode 100644 MekHQ/resources/mekhq/resources/VocationalExperienceAwardDialog.properties create mode 100644 MekHQ/resources/mekhq/resources/messages.properties rename MekHQ/src/mekhq/{campaign => }/CampaignPreset.java (97%) create mode 100644 MekHQ/src/mekhq/campaign/CampaignTransporterMap.java create mode 100644 MekHQ/src/mekhq/campaign/autoresolve/AtBSetupForces.java create mode 100644 MekHQ/src/mekhq/campaign/autoresolve/AutoResolveMethod.java create mode 100644 MekHQ/src/mekhq/campaign/autoresolve/OrderFactory.java create mode 100644 MekHQ/src/mekhq/campaign/enums/CampaignTransportType.java rename MekHQ/src/mekhq/campaign/force/{StrategicFormation.java => CombatTeam.java} (82%) create mode 100644 MekHQ/src/mekhq/campaign/handler/PostScenarioDialogHandler.java rename MekHQ/src/mekhq/campaign/icons/enums/{LayeredForceIconOperationalStatus.java => OperationalStatus.java} (76%) create mode 100644 MekHQ/src/mekhq/campaign/market/procurement/Procurement.java delete mode 100644 MekHQ/src/mekhq/campaign/mission/enums/AtBLanceRole.java create mode 100644 MekHQ/src/mekhq/campaign/mission/enums/CombatRole.java create mode 100644 MekHQ/src/mekhq/campaign/mission/enums/ScenarioType.java create mode 100644 MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/GenerateResupplyContents.java create mode 100644 MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/PerformResupply.java create mode 100644 MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/Resupply.java create mode 100644 MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/ResupplyUtilities.java create mode 100644 MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/StarLeagueCache.java create mode 100644 MekHQ/src/mekhq/campaign/personnel/education/TrainingCombatTeams.java create mode 100644 MekHQ/src/mekhq/campaign/stratcon/SupportPointNegotiation.java create mode 100644 MekHQ/src/mekhq/campaign/unit/AbstractTransportedUnitsSummary.java create mode 100644 MekHQ/src/mekhq/campaign/unit/ITransportAssignment.java create mode 100644 MekHQ/src/mekhq/campaign/unit/ITransportedUnitsSummary.java create mode 100644 MekHQ/src/mekhq/campaign/unit/ShipTransportedUnitsSummary.java create mode 100644 MekHQ/src/mekhq/campaign/unit/TacticalTransportedUnitsSummary.java create mode 100644 MekHQ/src/mekhq/campaign/unit/TransportAssignment.java create mode 100644 MekHQ/src/mekhq/campaign/unit/enums/TransporterType.java create mode 100644 MekHQ/src/mekhq/campaign/utilities/CampaignTransportUtilities.java delete mode 100644 MekHQ/src/mekhq/gui/SpecialAbilityPanel.java rename MekHQ/src/mekhq/gui/baseComponents/{AbstractMHQDialog.java => AbstractMHQDialogBasic.java} (79%) create mode 100644 MekHQ/src/mekhq/gui/baseComponents/MHQDialogImmersive.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsAbilityInfo.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsDialog.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsPane.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsUtilities.java rename MekHQ/src/mekhq/gui/{dialog/CreateCampaignPresetDialog.java => campaignOptions/CreateCampaignPreset.java} (95%) rename MekHQ/src/mekhq/gui/{panes => }/campaignOptions/SelectPresetDialog.java (61%) create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsButton.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsCheckBox.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsGridBagConstraints.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsHeaderPanel.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsLabel.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsSpinner.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsStandardPanel.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsTextField.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/AbilitiesTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/BiographyTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/EquipmentAndSuppliesTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/FinancesTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/GeneralTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/MarketsTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/PersonnelTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/RelationshipsTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/RepairAndMaintenanceTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/RulesetsTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/SkillsTab.java create mode 100644 MekHQ/src/mekhq/gui/campaignOptions/contents/TurnoverAndRetentionTab.java create mode 100644 MekHQ/src/mekhq/gui/dialog/AutoResolveChanceDialog.java create mode 100644 MekHQ/src/mekhq/gui/dialog/CampaignHasProblemOnLoad.java delete mode 100644 MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java create mode 100644 MekHQ/src/mekhq/gui/dialog/CampaignPresetSelectionDialog.java create mode 100644 MekHQ/src/mekhq/gui/dialog/ContractAutomationDialog.java create mode 100644 MekHQ/src/mekhq/gui/dialog/NewsDialog.java create mode 100644 MekHQ/src/mekhq/gui/dialog/VocationalExperienceAwardDialog.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/DeploymentShortfallNagDialog.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/NagController.java delete mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialog.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/DeploymentShortfallNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/EndContractNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechTimeNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechsNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientMedicsNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InvalidFactionNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/NoCommanderNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/OutstandingScenariosNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/PregnantCombatantNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/PrisonersNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordExpensesNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordJumpNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordLoanPaymentNag.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnmaintainedUnitsNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnresolvedStratConContactsNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UntreatedPersonnelNagLogic.java create mode 100644 MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogAbandonedConvoy.java create mode 100644 MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogContractStart.java create mode 100644 MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogInterception.java create mode 100644 MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogItinerary.java create mode 100644 MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogPlayerConvoyOption.java create mode 100644 MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogResupplyFocus.java create mode 100644 MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogRoleplayEvent.java create mode 100644 MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogSwindled.java create mode 100644 MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/ResupplyDialogUtilities.java create mode 100644 MekHQ/src/mekhq/gui/menus/AssignForceToShipTransportMenu.java create mode 100644 MekHQ/src/mekhq/gui/menus/AssignForceToTacticalTransportMenu.java create mode 100644 MekHQ/src/mekhq/gui/menus/AssignForceToTransportMenu.java delete mode 100644 MekHQ/src/mekhq/gui/renderers/CampaignPresetRenderer.java create mode 100644 MekHQ/src/mekhq/utilities/EntityUtilities.java create mode 100644 MekHQ/src/mekhq/utilities/ImageUtilities.java create mode 100644 MekHQ/src/mekhq/utilities/MHQInternationalization.java create mode 100644 MekHQ/src/mekhq/utilities/PotentialTransportsMap.java create mode 100644 MekHQ/unittests/mekhq/campaign/autoresolve/ResolverTest.java create mode 100644 MekHQ/unittests/mekhq/campaign/unit/LegacyUnitShipTransportTest.java delete mode 100644 MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialogTest.java delete mode 100644 MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialogTest.java rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{EndContractNagDialogTest.java => nagLogic/EndContractNagLogicTest.java} (91%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{InsufficientAstechsNagDialogTest.java => nagLogic/InsufficientAstechsNagLogicTest.java} (74%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{InsufficientMedicsNagDialogTest.java => nagLogic/InsufficientMedicsNagLogicTest.java} (77%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{InvalidFactionNagDialogTest.java => nagLogic/InvalidFactionNagLogicTest.java} (58%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{NoCommanderNagDialogTest.java => nagLogic/NoCommanderNagLogicTest.java} (86%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{OutstandingScenariosNagDialogTest.java => nagLogic/OutstandingScenariosNagLogicTest.java} (57%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{PregnantCombatantNagDialogTest.java => nagLogic/PregnantCombatantNagLogicTest.java} (87%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{PrisonersNagDialogTest.java => nagLogic/PrisonersNagLogicTest.java} (89%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{ShortDeploymentNagDialogTest.java => nagLogic/ShortDeploymentNagLogicTest.java} (81%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{UnableToAffordExpensesNagDialogTest.java => nagLogic/UnableToAffordExpensesNagLogicTest.java} (87%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{UnableToAffordJumpNagDialogTest.java => nagLogic/UnableToAffordJumpNagLogicTest.java} (76%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{UnableToAffordLoanPaymentNagDialogTest.java => nagLogic/UnableToAffordLoanPaymentNagLogicTest.java} (67%) rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{UnmaintainedUnitsNagDialogTest.java => nagLogic/UnmaintainedUnitsNagLogicTest.java} (81%) create mode 100644 MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnresolvedStratConContactsNagLogicTest.java rename MekHQ/unittests/mekhq/gui/dialog/nagDialogs/{UntreatedPersonnelNagDialogTest.java => nagLogic/UntreatedPersonnelNagLogicTest.java} (84%) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e65c54a1478..834919fd9a6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,169 +1,110 @@ -name: Bug Report -description: Report an issue with MekHQ -title: "Brief Description" -labels: [bug] -body: - - type: markdown - attributes: - value: | - **Fields marked with a red asterisk (*) are mandatory to complete.** - In this form, you’ll provide a description of the issue, steps to reproduce it, your system details, and any files that may help us resolve the problem. - - - type: checkboxes - id: prerequisites-and-pre-issue-checks - attributes: - label: Prerequisites and Pre-Issue Checklist - description: Please confirm you have completed the following steps before submitting an issue. - options: - - label: | - I'm reporting the issue to the correct repository: - - [MegaMek](https://github.com/MegaMek/megamek/issues) - - [MegaMekLab](https://github.com/MegaMek/megameklab/issues) - - [MekHQ](https://github.com/MegaMek/mekhq/issues) - required: true - - label: "I've tested the issue against at least the latest MILESTONE version" - required: true - - label: "I've asked on the MegaMek Discord about the error" - required: true - - label: "I've reviewed the BattleTech rules and MekHQ documentation, and I've confirmed that something isn't working as intended." - required: true - - label: "I've searched the Github tracker and haven't found the issue listed" - required: true - - - type: markdown - attributes: - value: | - ![How to Search the GitHub Tracker](https://i.imgur.com/QMJY7pll.png) - - - type: dropdown - id: severity - attributes: - label: Severity * - description: Choose the severity of the bug. - options: - - "Critical (Game-breaking/Crash): The game crashes or a core feature (like saving, loading, or network connection) is completely unusable." - - "High (Major Disruption): A major feature is broken or incorrect, but a workaround exists." - - "Medium (Gameplay Limitation): Non-core functionality is impaired, providing a suboptimal but playable experience." - - "Low (Minor/Nuisance): Minor glitches or cosmetic issues that don’t affect gameplay and occur rarely." - validations: - required: true - - - type: markdown - attributes: - value: | - ## Brief Description * - - What happened? Please explain the issue in one or two sentences and how it stopped you from using the program. You can also upload a picture of the issue if possible. +name: "Report an Issue" +description: > + Please make sure you're opening the issue on the correct repo; + you've checked to make sure this isn't a duplicate, and checked + on the Discord that there is an issue and not working as intended. +title: "[Issue]" +labels: + - bug +body: - type: textarea id: brief-description attributes: - label: Brief Description * + label: "Brief Description *" description: | - Please describe the issue in detail: + Please describe the issue in detail. + + For more detailed instructions, check out + [our wiki](https://github.com/MegaMek/megamek/wiki/Creating-an-Issue-(Bug-Report%2C-Request-for-Enhancement%2C-Errata)). + 1. Provide in-game screenshots if possible. 2. If it's a rules-related issue: - Specify the rulebook edition - Include the page number - Quote the relevant text - 3. If applicable, list the steps to reproduce the issue in the saved game. placeholder: "Describe the issue here..." validations: required: true - - type: markdown - attributes: - value: | - ## Steps to Reproduce - - How did the problem happen? Please list the steps you followed before the issue appeared. - - type: textarea id: steps-to-reproduce attributes: - label: Steps to Reproduce - description: Provide a detailed numbered list of steps. + label: "3. Steps to Reproduce" + description: "Provide a detailed numbered list of steps." placeholder: "1. Step one\n2. Step two\n3. Step three" - - type: markdown + - type: textarea + id: attached-files attributes: - value: | - ## User Environment - - For the next sections go to the logs folder inside MekHQ (usually called 'logs'). Find the MekHQ.log file, open it with a text editor. It will look like the image below. Fill the information you find there into the following sections: - - ![Example Screenshot](https://i.imgur.com/KD8cnvf.png) + label: "Attach Files" + description: "Please provide all the logs from the logs folder (zipped), Custom Units, and you CPNX file." + placeholder: "Drag and drop or click to upload relevant files." - type: dropdown - id: operating-system + id: severity attributes: - label: Operating System * - description: What kind of computer are you using? Select your operating system (like Windows or Mac). + label: "Severity *" + description: "Choose the severity of the bug." options: - - "Windows 11" - - "Windows 10" - - "Older Windows" - - "Mac" - - "Linux" + - "Critical (Game-breaking/Crash): The game crashes or a core feature (like saving, loading, or network connection) is completely unusable." + - "High (Major Disruption): A major feature is broken or incorrect, but a workaround exists." + - "Medium (Gameplay Limitation): Non-core functionality is impaired, providing a suboptimal but playable experience." + - "Low (Minor/Nuisance): Minor glitches or cosmetic issues that don't affect gameplay and occur rarely." validations: required: true - - type: input - id: java-version + - type: markdown attributes: - label: Java Version * - description: What version of Java is on your computer? You can find this information at the top of your MekHQ.log file. - validations: - required: true + value: | + ## User Environment + For the next sections, go to the "logs" folder. Find the MekHQ.log file and open it with a text editor. The information in the header will be needed. - - type: dropdown - id: megamek-version + - type: markdown attributes: - label: MekHQ Suite Version * - description: Which version of MekHQ are you using? Pick your version from the list. If you select "Free Text," please enter your version in the box below. - options: - - "v0.50.0" - - "v0.49.19.1" - - "v0.49.19" - - "v0.49.18" - - "v0.49.17" - - "v0.49.16" - - "v0.49.15" - - "Free Text (type manually)" + value: | + ![Alt text](https://i.imgur.com/KD8cnvf.png) - type: input id: custom-megamek-version attributes: - label: Custom MekHQ Version - description: If you selected "Free Text" in the version dropdown, enter your MegaMek version here. - placeholder: "Enter your MegaMek version here if it's not listed above." + label: "MekHQ Suite Version *" + description: "Enter your MekHQ version here" + placeholder: "Example: 0.50.02" + validations: + required: true - - type: markdown + - type: input + id: operating-system attributes: - value: | - ## Files - - Please zip (compress) your logs folder and upload the file here. If you're not sure how to zip the folder, ask for help. It would also be helpful to screenshot the log header (like above) and paste it here. + label: "Operating System *" + description: "Select your operating system" + placeholder: "Please be specific with the OS, e.g. Windows 11, macOS 15 Sequoia, Linux (Ubuntu)" + validations: + required: true - - type: textarea - id: attached-files + - type: input + id: java-version attributes: - label: Attach Files - description: Provide any relevant files by attaching them to the issue. - placeholder: "Drag and drop or click to upload relevant files." + label: "Java Version *" + description: "Enter the Java version from the .log file" + placeholder: "Example: Java Vendor: Eclipse Adoptium Java Version: 17.0.11" + validations: + required: true - type: checkboxes id: final-checks attributes: - label: Final Checklist - description: Before submitting, confirm the following steps + label: "Final Verification" + description: "Please confirm the following before submitting" options: - - label: "I've checked to make sure that this issue has not already been filed" + - label: "I confirm this is a single, unique issue that hasn't been reported before" required: true - - label: "I'm reporting only one issue in this ticket for clarity and focus" + - label: "I have filled and provided all necessary information above" + required: true + - label: "I have included any and all logs, custom units, and CPNX (save) files" + required: true + - label: "I have asked on MegaMek Discord about this issue" + required: true + - label: "I have confirmed this issue is being opened on the correct repository: MegaMek, MegaMekLab, or MekHQ" required: true - - - type: markdown - attributes: - value: | - **You will not be able to submit this report unless all fields marked with a red asterisk (*) are complete and filled out before submitting your report.** diff --git a/.github/ISSUE_TEMPLATE/request_for_enhancement.yml b/.github/ISSUE_TEMPLATE/request_for_enhancement.yml index ffa0280a149..4e205658a1d 100644 --- a/.github/ISSUE_TEMPLATE/request_for_enhancement.yml +++ b/.github/ISSUE_TEMPLATE/request_for_enhancement.yml @@ -1,97 +1,74 @@ -name: Request for Enhancement -description: Use this to submit a Request for Enhancement (RFE)—asking for a new feature, extending an existing feature, or implementing missing rules or errata. -title: "[RFE] Brief Description" -labels: [(RFE) Enhancement] +name: "Request for Enhancement" +description: > + Request an enhancement for MekHQ. Be sure you've confirmed it's not a duplicate + and that you've checked on the Discord if your request is already known. +title: "[RFE]" +labels: + - (RFE) Enhancement + body: - - type: markdown - attributes: - value: | - **This form is to submit a Request for Enhancement (RFE)—asking for a new feature, extending an existing feature, or implementing missing rules or errata. Submitting an RFE is no guarantee of its prioritization by the development team.** - - **Fields marked with a red asterisk (*) are mandatory to complete.** - - Before submitting an RFE, please review our [Content Policy](https://github.com/MegaMek/megamek/wiki/Unofficial-or-Non%E2%80%90Canon-Content-Policy). - - - type: checkboxes - id: prerequisites-and-pre-issue-checks - attributes: - label: Prerequisites and Pre-Issue Checklist - description: Please confirm you have completed the following steps before submitting an RFE. - options: - - label: | - I'm reporting the RFE to the correct repository: - - [MegaMek](https://github.com/MegaMek/megamek/issues) - - [MegaMekLab](https://github.com/MegaMek/megameklab/issues) - - [MekHQ](https://github.com/MegaMek/mekhq/issues) - required: true - - label: "Does this conform with our Content Policy linked above?" - required: true - - label: "I've searched the GitHub tracker and haven't found a similar feature request listed" - required: true - - - type: markdown - attributes: - value: "![How to Search the GitHub Tracker](https://i.imgur.com/QMJY7pll.png)" - - type: dropdown - id: rfe + id: enhancement-type attributes: - label: RFE Details * - description: Please select the type of RFE you're submitting. + label: "Enhancement Type *" + description: "Select the category of enhancement you are requesting." options: - - "This is a new feature" - - "This is an improvement to an existing feature" - - "This is an implementation of a missing official rule" - - "This is a request to implement Errata" + - "New Feature" + - "Improvement to Existing Feature" + - "Implementation of Missing Official Rule" + - "Implementation of Errata" validations: required: true - - type: markdown - attributes: - value: | - For new features or improvements to existing features, please describe them in detail. - For rule implementations, provide the book, page, and edition. - For errata, reference the official documents: [Errata](https://bg.battletech.com/errata/) - - type: textarea id: brief-description attributes: - label: Brief Description * + label: "Brief Description of Enhancement *" description: | - ## Brief Description of the Feature * - placeholder: | - Provide a detailed description of the requested feature, including why it's needed, any relevant use cases, and screenshots/mock-ups if applicable. + Please describe the feature or improvement you’d like to see. + If it's based on any official rules or errata, mention that here. + placeholder: "Describe your enhancement here..." validations: required: true - - type: input - id: megamek-version + - type: textarea + id: proposed-use-cases attributes: - label: MekHQ Suite Version * - description: Which version of MekHQ are you using? All RFEs must be checked against the most current development release. - placeholder: "e.g., v0.50.0, v0.49.19.1, or development version" - validations: - required: true + label: "Use Cases or Rationale" + description: | + Elaborate on why this enhancement is needed or valuable: + - What problem does it solve? + - How does it improve gameplay or user experience? + - Are there any related rulebook references or official errata? + placeholder: "1. ...\n2. ...\n3. ..." - type: textarea id: attached-files attributes: - label: Attach Files - description: Provide any relevant files by attaching them to the request here. + label: "Attach Files" + description: | + Provide any relevant files, images, or logs that illustrate your idea or + help explain how it should work. placeholder: "Drag and drop or click to upload relevant files." + - type: input + id: custom-megamek-version + attributes: + label: "MekHQ Suite Version *" + description: "Which version of MekHQ (or related tool) are you currently using?" + placeholder: "Example 0.50.02" + validations: + required: true + - type: checkboxes id: final-checks attributes: - label: Final Checklist - description: Before submitting, confirm the following steps- + label: "Final Verification" + description: "Before submitting, please confirm the following:" options: - - label: "I've checked to make sure that this RFE has not already been filed" + - label: "I confirm this request hasn't already been submitted (checked the tracker)" required: true - - label: "I am requesting implementation of only one relevant set of features in this ticket for clarity and focus " + - label: "I've discussed or asked about this enhancement on MegaMek Discord" + required: true + - label: "I’m opening this on the correct repo (MegaMek, MegaMekLab, or MekHQ)" required: true - - - type: markdown - attributes: - value: | - **You will not be able to submit this report unless all fields marked with a red asterisk (*) are complete and filled out before submitting your report.** diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..27f2601fbba --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/keeping-your-actions-up-to-date-with-dependabot + +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9fd5cd7dd71..f09b7a5798a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,7 +81,7 @@ jobs: java-version: ${{ matrix.java-version }} - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 with: build-scan-publish: true build-scan-terms-of-use-url: "https://gradle.com/terms-of-service" @@ -103,7 +103,7 @@ jobs: path: ./mekhq/MekHQ/build/reports/ - name: CodeCov.io Coverage Report - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: directory: ./mekhq/MekHQ/build/reports/jacoco/test fail_ci_if_error: false diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index e17d2fde271..b2d3e3cf22a 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -41,7 +41,7 @@ jobs: java-version: ${{ matrix.java-version }} - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 - name: Build with Gradle working-directory: mekhq @@ -55,7 +55,7 @@ jobs: path: ./mekhq/MekHQ/build/reports/ - name: CodeCov.io Coverage Report - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: directory: ./mekhq/MekHQ/build/reports/jacoco/test fail_ci_if_error: false diff --git a/.github/workflows/dependency-scan.yml b/.github/workflows/dependency-scan.yml index cd65e6c692a..6fc31110f05 100644 --- a/.github/workflows/dependency-scan.yml +++ b/.github/workflows/dependency-scan.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v4 - name: Generate and submit dependency graph - uses: gradle/actions/dependency-submission@v3 + uses: gradle/actions/dependency-submission@v4 with: build-scan-publish: true build-scan-terms-of-use-url: "https://gradle.com/terms-of-service" diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml index 270a6d07a72..19bc61b792d 100644 --- a/.github/workflows/javadoc.yml +++ b/.github/workflows/javadoc.yml @@ -81,7 +81,7 @@ jobs: java-version: ${{ matrix.java-version }} - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 - name: Build with Gradle working-directory: mekhq diff --git a/.github/workflows/nightly-ci.yml b/.github/workflows/nightly-ci.yml index 538f9be54b7..e9234dd4d53 100644 --- a/.github/workflows/nightly-ci.yml +++ b/.github/workflows/nightly-ci.yml @@ -44,7 +44,7 @@ jobs: java-version: ${{ matrix.java-version }} - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 with: build-scan-publish: true build-scan-terms-of-use-url: "https://gradle.com/terms-of-service" diff --git a/.gitignore b/.gitignore index 1ed5e66a482..3967c9f3742 100644 --- a/.gitignore +++ b/.gitignore @@ -59,7 +59,7 @@ /settings_local.gradle /*.sav.gz .DS_Store - +.vs MegaMek.l4j.ini MegaMekLab.l4j.ini MekHQ.l4j.ini diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000000..53d1f3d0186 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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. + + This program 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 this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/MekHQ/build.gradle b/MekHQ/build.gradle index c548235e235..e35da51306c 100644 --- a/MekHQ/build.gradle +++ b/MekHQ/build.gradle @@ -1,5 +1,3 @@ -import edu.sc.seis.launch4j.tasks.Launch4jLibraryTask - import java.time.LocalDateTime plugins { @@ -92,6 +90,8 @@ dependencies { implementation 'org.commonmark:commonmark:0.22.0' implementation 'org.jfree:jfreechart:1.5.5' implementation 'org.joda:joda-money:1.0.4' + implementation 'com.fasterxml.jackson.core:jackson-core:2.17.2' + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.2' runtimeOnly 'org.glassfish.jaxb:jaxb-runtime:4.0.5' // Required for mml printing scaled vector graphics (SVG) - Eclipse IDE Compatability. diff --git a/MekHQ/data/images/camo/Filtvelt Coaltion/Filtvelt Citizens Militia.jpg b/MekHQ/data/images/camo/Filtvelt Coalition/Filtvelt Citizens Militia.jpg similarity index 100% rename from MekHQ/data/images/camo/Filtvelt Coaltion/Filtvelt Citizens Militia.jpg rename to MekHQ/data/images/camo/Filtvelt Coalition/Filtvelt Citizens Militia.jpg diff --git a/MekHQ/data/images/camo/Filtvelt Coaltion/Thumpers.jpg b/MekHQ/data/images/camo/Filtvelt Coalition/Thumpers.jpg similarity index 100% rename from MekHQ/data/images/camo/Filtvelt Coaltion/Thumpers.jpg rename to MekHQ/data/images/camo/Filtvelt Coalition/Thumpers.jpg diff --git a/MekHQ/data/images/hexes/minimap/cyberpunk.theme b/MekHQ/data/images/hexes/minimap/cyberpunk.theme new file mode 100644 index 00000000000..6bcf855d8d0 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/cyberpunk.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +# Pal 10 cyber punk +background 10 10 30 +none 255 20 147 +sinkhole 138 43 226 +woods 0 255 127 +heavywoods 34 139 34 +ultraheavywoods 0 100 0 +rough 75 0 130 +rubble 147 112 219 +water 0 191 255 +pavement 128 128 128 +road 105 105 105 +fire 255 69 0 +smoke 112 128 144 +smokeandfire 255 140 0 +swamp 0 250 154 +building 70 130 180 +bridge 169 169 169 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/darkula.theme b/MekHQ/data/images/hexes/minimap/darkula.theme new file mode 100644 index 00000000000..3d6e8854106 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/darkula.theme @@ -0,0 +1,24 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +background 39 40 34 +none 249 38 114 +sinkhole 230 219 116 +woods 166 226 46 +heavywoods 102 217 239 +ultraheavywoods 166 226 46 +rough 230 219 116 +rubble 102 217 239 +water 102 217 239 +pavement 166 226 46 +road 249 38 114 +fire 253 151 31 +smoke 166 226 46 +smokeandfire 253 151 31 +swamp 102 217 239 +building 230 219 116 +bridge 117 113 94 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/default.theme b/MekHQ/data/images/hexes/minimap/default.theme new file mode 100644 index 00000000000..5f8422e9dd7 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/default.theme @@ -0,0 +1,26 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments +# + +# terrain colours (red,green,blue) +background 0 0 0 +none 215 211 156 +sinkhole 210 180 150 +woods 180 230 130 +heavywoods 160 200 100 +ultraheavywoods 0 100 0 +rough 186 191 160 +rubble 200 200 200 +water 200 247 253 +pavement 204 204 204 +road 71 79 107 +fire 255 0 0 +smoke 204 204 204 +smokeandfire 153 0 0 +swamp 49 136 74 +building 204 204 204 +bridge 109 55 25 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/defaultminimap.theme b/MekHQ/data/images/hexes/minimap/defaultminimap.theme new file mode 100644 index 00000000000..5f8422e9dd7 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/defaultminimap.theme @@ -0,0 +1,26 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments +# + +# terrain colours (red,green,blue) +background 0 0 0 +none 215 211 156 +sinkhole 210 180 150 +woods 180 230 130 +heavywoods 160 200 100 +ultraheavywoods 0 100 0 +rough 186 191 160 +rubble 200 200 200 +water 200 247 253 +pavement 204 204 204 +road 71 79 107 +fire 255 0 0 +smoke 204 204 204 +smokeandfire 153 0 0 +swamp 49 136 74 +building 204 204 204 +bridge 109 55 25 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/dreamy colors.theme b/MekHQ/data/images/hexes/minimap/dreamy colors.theme new file mode 100644 index 00000000000..66135c328bf --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/dreamy colors.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +# PAL 4 light blue pink +background 0 0 0 +none 212 190 161 +sinkhole 165 176 160 +woods 178 218 224 +heavywoods 165 176 160 +ultraheavywoods 141 159 170 +rough 212 190 161 +rubble 246 224 177 +water 178 218 224 +pavement 165 176 160 +road 141 159 170 +fire 255 165 0 +smoke 165 176 160 +smokeandfire 212 190 161 +swamp 178 218 224 +building 165 176 160 +bridge 141 159 170 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/earth and grass.theme b/MekHQ/data/images/hexes/minimap/earth and grass.theme new file mode 100644 index 00000000000..eba9d2dde47 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/earth and grass.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +# PAL 2 Brown Green +background 0 0 0 +none 80 100 70 +sinkhole 90 70 50 +woods 100 180 100 +heavywoods 70 150 70 +ultraheavywoods 40 120 40 +rough 90 110 80 +rubble 110 120 110 +water 0 130 130 +pavement 90 90 80 +road 60 60 50 +fire 255 120 0 +smoke 100 100 90 +smokeandfire 200 80 0 +swamp 60 100 60 +building 100 100 90 +bridge 80 80 70 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/gbc green.theme b/MekHQ/data/images/hexes/minimap/gbc green.theme new file mode 100644 index 00000000000..3fd8f72ee02 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/gbc green.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +# Pal 8 GBC +background 15 56 15 +none 48 98 48 +sinkhole 63 127 63 +woods 79 159 79 +heavywoods 63 127 63 +ultraheavywoods 15 56 15 +rough 48 98 48 +rubble 79 159 79 +water 48 98 48 +pavement 63 127 63 +road 48 98 48 +fire 255 102 0 +smoke 63 127 63 +smokeandfire 79 159 79 +swamp 48 98 48 +building 63 127 63 +bridge 48 98 48 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/noctis.theme b/MekHQ/data/images/hexes/minimap/noctis.theme new file mode 100644 index 00000000000..ea93fc674a1 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/noctis.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +#pal 11 noctis +background 20 20 30 +none 121 133 153 +sinkhole 99 122 142 +woods 76 144 114 +heavywoods 60 113 90 +ultraheavywoods 43 85 70 +rough 121 133 153 +rubble 140 140 150 +water 72 130 180 +pavement 99 108 120 +road 79 90 100 +fire 230 85 55 +smoke 99 108 120 +smokeandfire 230 120 80 +swamp 76 144 114 +building 99 108 120 +bridge 79 90 100 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/non-boolean.theme b/MekHQ/data/images/hexes/minimap/non-boolean.theme new file mode 100644 index 00000000000..d8fad3641da --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/non-boolean.theme @@ -0,0 +1,24 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +background 0 0 0 +none 255 255 255 +sinkhole 240 230 140 +woods 255 255 0 +heavywoods 200 200 0 +ultraheavywoods 150 150 0 +rough 211 211 211 +rubble 128 128 128 +water 148 0 211 +pavement 105 105 105 +road 80 80 80 +fire 255 85 0 +smoke 128 128 128 +smokeandfire 200 75 150 +swamp 218 165 32 +building 169 169 169 +bridge 80 80 80 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/phosphor green.theme b/MekHQ/data/images/hexes/minimap/phosphor green.theme new file mode 100644 index 00000000000..01ee2b4cc87 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/phosphor green.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +# Pal 7 phosphor green +background 0 0 0 +none 227 255 128 +sinkhole 214 247 105 +woods 200 233 93 +heavywoods 182 215 91 +ultraheavywoods 169 214 92 +rough 200 233 93 +rubble 214 247 105 +water 169 214 92 +pavement 200 233 93 +road 182 215 91 +fire 255 165 0 +smoke 182 215 91 +smokeandfire 214 247 105 +swamp 200 233 93 +building 182 215 91 +bridge 169 214 92 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/pink lemonade.theme b/MekHQ/data/images/hexes/minimap/pink lemonade.theme new file mode 100644 index 00000000000..bfd7731a4a1 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/pink lemonade.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +# PAL 5 Pink Lemonade +background 0 0 0 +none 220 237 191 +sinkhole 255 212 184 +woods 168 230 206 +heavywoods 220 237 191 +ultraheavywoods 168 230 206 +rough 255 173 173 +rubble 220 237 191 +water 168 230 206 +pavement 255 212 184 +road 255 173 173 +fire 255 165 0 +smoke 212 168 255 +smokeandfire 255 173 173 +swamp 168 230 206 +building 212 168 255 +bridge 220 237 191 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/pink world.theme b/MekHQ/data/images/hexes/minimap/pink world.theme new file mode 100644 index 00000000000..e504e6f09b7 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/pink world.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +# Pal 6 full pink world +background 0 0 0 +none 244 195 211 +sinkhole 246 162 183 +woods 246 121 159 +heavywoods 241 80 123 +ultraheavywoods 230 55 79 +rough 246 162 183 +rubble 244 195 211 +water 246 121 159 +pavement 246 162 183 +road 241 80 123 +fire 255 165 0 +smoke 241 80 123 +smokeandfire 246 162 183 +swamp 246 121 159 +building 241 80 123 +bridge 230 55 79 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/rebirth.theme b/MekHQ/data/images/hexes/minimap/rebirth.theme new file mode 100644 index 00000000000..0e741370148 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/rebirth.theme @@ -0,0 +1,24 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +background 15 15 30 +none 255 255 255 +sinkhole 250 200 210 +woods 135 206 235 +heavywoods 85 170 215 +ultraheavywoods 50 115 190 +rough 245 235 240 +rubble 220 220 225 +water 135 206 235 +pavement 180 180 190 +road 140 140 150 +fire 255 85 85 +smoke 200 200 210 +smokeandfire 245 145 170 +swamp 210 185 210 +building 220 220 225 +bridge 180 180 190 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/red amaranth.theme b/MekHQ/data/images/hexes/minimap/red amaranth.theme new file mode 100644 index 00000000000..d000dece609 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/red amaranth.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +#pal 12 Red Amaranth +background 15 15 15 +none 229 43 80 +sinkhole 180 34 65 +woods 110 20 40 +heavywoods 85 15 30 +ultraheavywoods 50 10 20 +rough 140 30 50 +rubble 200 50 70 +water 20 20 20 +pavement 50 50 50 +road 30 30 30 +fire 255 69 0 +smoke 50 50 50 +smokeandfire 180 30 50 +swamp 110 20 40 +building 100 100 100 +bridge 40 40 40 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/shades of green.theme b/MekHQ/data/images/hexes/minimap/shades of green.theme new file mode 100644 index 00000000000..ec9402b6b15 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/shades of green.theme @@ -0,0 +1,24 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +background 0 0 0 +none 209 248 175 +sinkhole 166 231 186 +woods 126 211 195 +heavywoods 77 185 209 +ultraheavywoods 29 160 221 +rough 166 231 186 +rubble 209 248 175 +water 29 160 221 +pavement 166 231 186 +road 126 211 195 +fire 255 165 0 +smoke 77 185 209 +smokeandfire 166 231 186 +swamp 126 211 195 +building 77 185 209 +bridge 29 160 221 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/solarized.theme b/MekHQ/data/images/hexes/minimap/solarized.theme new file mode 100644 index 00000000000..bd31bcf83fa --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/solarized.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments +# +# solarized +background 0 43 54 +none 131 148 150 +sinkhole 108 113 196 +woods 133 153 0 +heavywoods 88 110 117 +ultraheavywoods 38 139 210 +rough 101 123 131 +rubble 147 161 161 +water 42 161 152 +pavement 88 110 117 +road 101 123 131 +fire 220 50 47 +smoke 88 110 117 +smokeandfire 203 75 22 +swamp 133 153 0 +building 147 161 161 +bridge 88 110 117 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/synthwave.theme b/MekHQ/data/images/hexes/minimap/synthwave.theme new file mode 100644 index 00000000000..b8c782d9900 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/synthwave.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +# Pal 3 Sinthwave +background 10 10 45 +none 252 127 237 +sinkhole 141 76 206 +woods 0 255 204 +heavywoods 0 186 148 +ultraheavywoods 0 139 111 +rough 127 0 255 +rubble 204 102 255 +water 0 204 255 +pavement 102 102 153 +road 77 77 128 +fire 255 71 71 +smoke 153 102 204 +smokeandfire 255 112 112 +swamp 0 255 204 +building 102 102 153 +bridge 77 77 128 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/wood cabin.theme b/MekHQ/data/images/hexes/minimap/wood cabin.theme new file mode 100644 index 00000000000..9ab98a99857 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/wood cabin.theme @@ -0,0 +1,24 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +background 30 10 10 +none 255 245 245 +sinkhole 255 210 200 +woods 255 140 105 +heavywoods 255 110 80 +ultraheavywoods 220 70 50 +rough 255 190 170 +rubble 235 150 140 +water 255 140 180 +pavement 200 130 120 +road 180 100 90 +fire 255 70 20 +smoke 200 130 120 +smokeandfire 255 100 60 +swamp 255 160 130 +building 235 150 140 +bridge 200 130 120 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/hexes/minimap/zone of battle.theme b/MekHQ/data/images/hexes/minimap/zone of battle.theme new file mode 100644 index 00000000000..f341eca17b9 --- /dev/null +++ b/MekHQ/data/images/hexes/minimap/zone of battle.theme @@ -0,0 +1,25 @@ +# +# Minimap configuration file +# Lines starting with "#" (like this one) are comments + +# Pal 9 Battlezone +background 2 56 33 +none 196 245 186 +sinkhole 33 207 33 +woods 2 56 33 +heavywoods 33 207 33 +ultraheavywoods 2 56 33 +rough 196 245 186 +rubble 33 207 33 +water 1 37 51 +pavement 196 245 186 +road 33 207 33 +fire 236 62 11 +smoke 33 207 33 +smokeandfire 236 62 11 +swamp 33 207 33 +building 196 245 186 +bridge 1 37 51 + +# unit size: defines the size of the triangle for unit representation +unitsize 6 diff --git a/MekHQ/data/images/misc/MekHQ AutoResolve.png b/MekHQ/data/images/misc/MekHQ AutoResolve.png new file mode 100644 index 0000000000000000000000000000000000000000..e73fb89c60ea63a8a66e64e68fe631b0bb7f5fe7 GIT binary patch literal 344997 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

(Bv zNI%?5;~;G|ctUN2Ydc@p)U8WhMMhB=0o;43LVXh{3d@VPu5-0m@!|asIiFqe;)~Do z;>-8Qi;PE)j|KkRCL;=?DKmq$8JU($A%2#;&_z8dgzxx*d?|=9PO$h8B?DNHgitk@8&=Ia&WZf#J z84OWa1Lr>Sq=iC7f)k6DkGcnub^cCIWT-WzR;=;Z==uE1H~6KmeUkY~^Y8wbze?3K z9PLV1xHp<$Gr1A2s)mPGHM@hHJrnu*D>wP(2UlQ<{wCC+V}nbV8t}yEq?1c$leOSt zz=R|O>o5#nR82s(UB$Oq{&+RvL{YFR@BbTd& zNoL543>90JF5rS#O_hrLLH`(IxLirv*UjBA#%dm(UQxAy<9Wk>@~hwF7r*)vUwYw? zi&ZOm63Qg}t@_yJ{9^}s|J}+(By58yf~NFx`NXj31`08TK8#(!DxP&4$xF>}I_1?D zZ?jy~EY>yi*@DCC2aL;{WmPdOaw*i6aC<}RsFEP8<8m9XIJ=m0Wflzbg3)M@ASQ;8 zI03pB0B)5jNst+Xb693f z$^suFI#vE6N|z8EnF>fy4$5M_^68hqk=DbauuKf}gXxG><2b)sFeo%T(;eB_IiFr! z(gB`2X=N~0n6x^RPJOS)sAEi6!{nRmg*aVyQG#E@2tlf!v)P=gYFMr-u2yRnYasy1 zg{8=hO+QFvG{`CQg4t}wXgp*(p0c|$CC12Ny(0R+?s&k?c)%bxOveMVET^gJTiA%oJ&ghHF-mz9xLrZt(C zi_B)0L1CpzD>BBz0h93nt?4t@%Od0MokPmZ_MMb6n&<<|#T;uiJChN^qF^)-@2n4j zr>7S@I)2LIr)SKU649!vok8+dM{STu?c*KjQpwR;_<^XtF|9aO|QC1}(Lmu+C2Szf-g&+88^_>2Fm ze~tN*_jvuoCphn!4hNFRrD0qaJn-MmVyeNyCY z;$32u?2G#fJ&nTUFUp=u#AI?!)^$xAgP=c0j>sG#gS!U!qrlhX4hudbTe?$duh*ZuqTh{CjbEd-r7a~pL@E+cLctKF`$=iEa zEAL^U6uR%nKUlLp-L6dseBtgs>&98OB3S%>#zjK||yMFZVeIR;A)6}f$29+jc!@*EGWTVBY;yhQ& z1*_Fs=$UzrvS#Z*k%mR6lcFlmwZdeUYQ3gj*Ie715QFDpzG8noz$imm7>2`)y~&8C zbyyu`oJwH0JK{Uvf1f)yj&Ni+~YUiUh?-}KgW|}Q<>YgEiW8qvyBi= zNfvhMk|kBLTiL3h&{0ETORVdPw)Lsd(F7A^#hVfWRqtn4w9av~JLcMr>yjm3tvNY9 zX14ngU4q)J(=#Sa5MRXMVNDwT{KI;cB^LFUrOYyhWl8i-3T_S7kQ#_|j~e zrW$k-^3*PHv^VBz+3@y+@`BNzAkT6jGAJ`{9Uf56=eR*h zp5?r7_cqSed~p8}dy@f^=_HNHJg%qc;}N@ER_xmY@s z7ObPq^CUtFl2zO`spE@iC88j$EjmW&#tB$svFQLb28vFKWFo^NXK$*BK_1S_#fnuD z+YAS}s8&OF5J{J!jlrmhDJa_>2A-nvGX7kulj zbAIQ|6JEZx%a48Si~JM6{8PO7d%w$n{h$4JeDA#{39F@sFtx_WJ~SZfA&){O#qz_m6<_$$%Us(#<^JOf+NNQ-T66RI z0eNOvR1*2De57jDT+UY#wqhg#I?JpG6l28u$laUQDT^HE9B;h)0mqjYtm>MhYx`tn zDd^r9L|@Z68vt(l55S0D*2ng^9a1-@L-Q3Q?xA!7pKr9oqoLr2jDj|&38v6=0sYZC6{B^^$QvrHR>kB;0F z*vuyDq7<*zSh6DTdGOuO6G=9=7%^Y@%*)^Cr14!CkxmB&N--YfENaKa<$`OI5yh|s zmjD{6^w&z0%HmoSMbAx8_CAu~dnQd%As z+L)em-M<+fc;6%n5ej9ct2fq)XeS*$-nGgZrT|)u<&k85miWI zyqMCVERl$dB3eNyf(p2GBL{tvLn;Dd2&foneV}d|TIZABJJLjjBM|WD5D=pK=CIwg z#qxiJ)f6_D2Sk+gBoV;rE~q)ZhvIejAxKx=6~8gXf2ME|5hKYVANmaDPA#vt(?>t{ zy~t&#XuKeHhnaY9eg8d~t%btJck3uv*DcG|b7z0T&Y<9Q+0c4e)sAnz^@N?#fG@td z&wuk<@Au&rA3Mwwuf6*I+v>8|H6){9^ z9A2X+GA^!WESGDB!vd@G-W4D*n!>TyN|^3#)8d??EJ|Lvdz-eccyfM8p65)8g2~u& zbTDCEHL{{eO6v8x=HzP4#e9K|fy;Tt2alexSgeU5G8qpzIN0Ujp>I$PZ zlkt?vsAN!z*`T_Fxe2TJjxpOr+cYjmI=Z2L1brG zqR`A%a*!68WwmZt*Phxrn%1#y9E-Z;V!7sWS#hy$xLU7RRt>Xt!@R0x<&X)sTP41| z56OYktwl?PcDf&p=UQ{`-b+LUd1i@jh4GQYoiTf(AxF~@*LTNU-yLx{9dm6w;b1!A zU?RVFZEwWk{)D}~0fQo^Z3Uw{9$21x?udiEF?Cf_)lOCzMu}xDO$=oHoDO+>lA0-| zdt=7qg1pEmikv*l1^R=3Hm10HpMor@Y`4nV=EbWn889V}-MlvC(dmqL9zNzX_wG=X z1#anRV_<)0!rs9FCbyhEJ!QQRv%b=Dfwn3#&J{x!se^PYMrUb6;0QjDhfH43^v&Gb zpE4MaDGI}KRg>o#FTQw}=WgHPVs^>t=>?bb1#K%Mr&U#nNKb35Sx}6o$-_S4@Y(_A zXD7)EBt)G6m{jEpS63>ob+yJPQ3(MCMM17C%7_qSHeXUXiHkLvp$(2AdWyk7Y;Z)nW(Bm5S*z*vuc8;-woKA{6 zzxp5kH2>GX_W$H>{K~fpP0h5lOhe!_h=+Zsq4uzf^8eOEc8ww{41f5=T_yv?`?JXD zx~4Fi&{o`;j<`7akdW}Gvh@ixF)|q#j`l`;@brS?izR>f3x^EyA&)K|rZJfa*pyP( zXuzjp(#D=zB0(i!V+5C$IbHj%W4~ig?>;d)0lhdcgs&KgN>j&x@e!9;-lRh{Buh@H z(}8N`=TKVFrV4Sp7gZlobVt7d#}M4+oDD&&Cn*}WB@)`$bB;oYs3>Fg?fre1)FdLh z(6_X)s{%g)YkBre-iXwy*5iX=kZbZxvH-kyJz?ybUcK}5CJX9v)$rX1C)_!>##ir+ zxSF3ct1YVr{`x<7o!1`;RD`Wb-ZL?(A2F|W;yks*{@aAh#AgZ;9n<>|HzLHs8s7i# zj4%DrD`ZA-ezoMKyEoBFMwV6^t`>7+%CUA#R%8UPuvYc&+o;TV?r4YmPcQiP_unTt(M4pr#joo=s&%$pa(*#S zK4>FwC98X8zCeT|aj;riYV2m3o*-{0YlcOI}NV%{HCZm~0G))^9RgM>P)0&bAS8GY z)UF|TC&iT2+dHX0}oQBWTm_(^aAhYNarlgn>PJbiv~%SFBsda=qro zPu$`DqjS+sWYUDmFemm1cGD5$y37Nj9Q+YNXw&{Z_62q~)z&KO3uUDZMjLGG^B&b! z?y*KIk!yq~qwwc$?(@$3k9lx%#+}1MMx!x@J4439p^U@`+_`&;x~e%pJ*Qf)h$y^Q zl#>zHX!-D>#^z#UFe;+G!5N7a)X6ItV?;Sv&F1(JneHD@*Nr@6tm4|W0}ihpP**kI z`~JHumrJtDKv0t3+pKx(o%i_6r(Qv67!HQajd0z!gyJ&3?5dX-S=Tjp&r~LtI|Y`Mt&4 zl$oI@3tqT;7p>v1|LtGr3$MJ)zw*ESm-(Om>4*FW|EIso-}$ZYG0ZGOCF*mf6+;yn zsetoJl+)Hy3`#Nszx}vk(L`eOeE#N`f${8)b3S;y=qD5|IG#V;V=^o-T624M%=6cG z`R;p9d31cve_JJwcgi$ZDHvFVkY1HYbcXJz2uW-f0lO8is)7)0<8<9=UE~u-zZK8j(kFLyJN)i^iF%V-s-TZs^~oOO13lQqs7Mo&04$3A ztV?E!Y1iB7gu+(3pv)co=AQL`fG6xDjXH_C6}RT_P3!SKGAa#28(6mv7fj#z_9;;$ zDqniYYNM&0VYYnArw<%I@j}VB-@jnq1g;Hp#^r~Y8=CJvMhJN+NT#blHX_2fRqXUG zovoio6xT}2o~28M?X+l}b@qscw<)f}7UJa_Yu4<0_{_~evZcWz=7@ILUC~2CSllhVt4)^)Q?HRxE+Cv_nFZj%h*Hd(6O7Yg9lXdoRXGm@gSIaf4 zs^!7ioEY2QVHiCOivnX6r7_g4?82qYvw)0m>$My#Rntn^pHa_FMV#b6D5E#;iw+Qt z#J;9SoAb_x$GrdG1d~Fyy02H{ISU`bTH4@n zPQW*+g9w_OyF=@hJt&Gl1zDw}%*85MmoUO?+ zO|*KmGf$OdXgjt}#28DxZgB0ItQ>%oS8lMIqk=(Ol_GFMA7~{0Hl4(OB2Yu6=Lm-D z*LTrI@!mU6dGz3%8+Q)4etU+G?z^ ztQIo>ru)~Z>l)`Jr#!TgJj;3M`P+Q=jkjr930cufGssGoizOd^_<$Gh-D5bNvR7A} zoSX=~%_uq%pk&Hx)=kB_T9ao5!$PP=B(JKm7PMI3jFxWAXwAS1ydZihcw%~bo9*32 zM;uq8{O`QVnK3D`Cyn3}cz8KqBxa~Q_>~H&%a^!V6%%t^YsyRxo2CuS{gT{T21P+( zRG;kA2`^9qr807BSg%)DtI2GR_KKb9gz328op;}3es#$wKlwc0`tJAn{<{zOv9ErG zfAfF;Z}ZQ+{eAv-f91d6gYSKtQbk-$Ia28+*&kU}XngdXt{p~cM%L4KLy_1*+87EG zab8l-G9`cws}!Go?ixpv5pC-jek$WDxA*z?{?;4G*WTejWg*;l`&<-Sc@gk!je@-f zg6{Wm-8!i|AyiVTnvPJWh5g>GTwcAFbnh+hYlb8sHA;wL`&rG)(-{K{XUoXnf9;al%2QWz{=`IQ~M_FT^Y z{hN+;qx#98-?qqf4uMX+t~*`CGYKw91gF#MmNXLy>Z6i)E;EA492(8@&mGY^$!m@t zPA?Y>MkB88@8W~pn^B3iX?Hk)QQ7f6XuOw$B0}_*JZkdNPRFG?_`FV4YT!$p#tI_#gnGf}$JJ&!ub9FwqV7?R8|$CKT8W+&`&&yJ#Qu;Qf$J=*hDcyEDERCrUqo5UbUdbN zYMQp9agNcT;NIZ@Rx8;x29F}}{SQ9m`|o_n^`k@n>_7dBeD0Gk^3J;tcyN9xncf(3 zt+=&6B)5jsmE&lfb2P~~7;4roP=&z3Kr`6c=eg%@qFl?n4^F|i?2JoBdB*OjL@C8P zr!#hkhBuEZ-g-JG>+-~v+5jTb1r{*)7|xl0i9 z$Qb%-uO5jDq`Ur2VxBA2kBhYko_Tz=W-u!HgJ^lVWU*TD+|j-i1<57T?OHpBKpw$w>v`$E)u6X0%CycUY;h~=yVyWj6Jv$>lfUp2-?+G1 z&^RZ4$llyYFAR!a9woa6*Vx;caB_BzHHMv0fh`7%hNI1{D=mY&adJ0S2*HzUMXhp% zQ@J3Qi#hmKMu}~TiP^pL_2)IA92BX0_FR_B&F)lOOAn8wy^GqgV;Z{!u9PI7kLts9ib2eL2JIA;z zM4jtA)oLMCZH&Bq{~>R_|A4zMzsf)N=l&TE#s%Mc?H$h7Ek;Lfj4fGiShkWzcQDG> z$rY!K6kyjz7E_K``;xaGUGlwm9&q>iu1skHG(nirSL>E9+}`DHzV?JRNVlT4xDs(Y zZ1+QStP>Jts>iRvBcZ9A}WnCnN<9}HBLTn)7DhZ$(X!9x3|P+ z;p3?Cur*4~jG-tjO$fLk3f<^pO2yk!u5JChPdo7qWY(tw;n{XOek?=n2OZ>Uqblz{ zN!*Sv-Tc1u;!n>qi$ShQw=vBTf@V@S+}m%NW-u)xclI>@_3xZ9TSsy$;x3(r2k)OW zy#9E}OFJ#oVaCFY*SV6 z)UKs<0q@}WYD+LCeI9)R|_($8J2?{ z4XYAd#-#8KlZtDT<$Us$Pm|e-C&x1`mK8t#<(F8_tJF!#WF`>ODo)}~8!x!(WmRLe z;oxAG7-UWO==hXvWvI;xA-<)TH@Jwg846FeZsb}D3CN?36oS*A%FE|_uIHY(|E~3(2=qzdG7W#?w>AM*Db`rWH6*C3W9e+ zHVh44dU?NZw7Nsni1!GL08?UOu&V?BQ0YEQ`--Gll1}^h{7OP)jJSsc)}l74X=(MT z6u7qK?wuR#O!o=iqm{+Gg{*<;I`jmy=}2&CMIsHF2y68gJx(bwxn*{>LWRI|XUJ%j zbK}-NC#P4mO)Dbsw2_NUsYuY8Zrf;!g1jgaSV8rBvyhaz zJJT_zXBYiA&M1v*TK4w$uu9>grz}e{Yx-U0>BSWa#bh)>rNpDEX^Bd3rFGpBfLsYQ zVVDX1M}i3CJ}L*fqyoiFctD@ph6Iar4;z(skXytjV1IAQe7WNM zd`9Cv!<6Eb=Oxa0*2_7i5ghLOAAHF9)qekg_f3fCXvD4()TZlAAds-dHi=5%+Dc*xZJq>+-Ki}j$JVt} z2_CUoNTq7Qf3)L_A3JpO%#d3{?PYHngYW5D+e_{~jq0^;f56WC zSti>?Gw{(iUOm$*D?FV?S!|Iyjk9YfcEJ5X&D~wct_>Uv0-rdrymj92E8m-wXNgPD zO$!v{7MdpV_LG{Et2sx5nwVuYA$w+A*daW3CsV4rA_Sj0NY9|O{Mcu1;hNBYUZr&} zn%anYHKW3CysBt}$3#uNtT?;4;>O_)d2ajkEVbo{@860ql zQus}Xk=6$;mMivlcPYz~=sZ>3^62CopE^~oG{eClC1T1C=)Cyb+t&Bl+O79OdGitP zLjt%c;htyW(6LrvGL>t%x>{m0OY0(S>%}uY9HC4ms9Y`gYUmo8C{sd+w5ugo%a-%2 z3Kb)}I}--EFbq7ArfvDki~B-G)SK0sR=NlFC}mJF$|Odc1XGe{txoZ%9Yx7n%lYMu zg$wd5MB+*XpC(=a7yy$ZY9+9f>O2N5GZ5VX2M!6 zwA00+VpXl!-y1U?S&FhCD>6lFes&SH^QS9RMLjbQx-XUyHk#j&mfKP8|S$=zu?C88@RS5#K>g2!(zGS;nf*~ zGG~+*w9x}XT5=(fSwrxV$~6p&0j(3ufyqpgz3If~R@l7Qyx|?BKRs}jl8by}(-D8E z(^7#NMIYKC4z%t#x5D|2o|E$nX3Hgq`};h9?=GvRVSYKMZd$Z4*rK2)hJ?0B+~


(6I|Q&mXUg0Q5Geid-Wxr9-r~x;S=7fYo5Px4XqSKo}+WY z|jYy94CeVcJUwEE^b% zgrL{7E#G|mjJm3M@wsav3{y~f&+2N)s&0AyaKwN6`^O0!(M5<_Ibf9(Z=y+wMBQCh z&nlulBAoMRG%Y3ycp{{UiC5CS2MtPlv`Pi!R??YCu~_|>S#Oxoo5_0Ych{X^I(`ht z!ZYiZen*~0s{P5Ge(%{4w2U5O2tDyl{h%Fv3UKI(la3SjF4 zWNceGvQ${_4h>(t?zuUN)GjhGp4$^k$Sfb6&ie^Szt2Nl5CHb~g#`Ji-xx&YPEXFTzNPHiR~1>VD|RM3K)@)&$_v}Trcqkw zk?+Kh`kBksr17*y>kXqln7Hw)Yb8IEY9#qADojZCr|#cM5mj38l~?sTU4tSdls$sHt6l99knXm<5t9K7R_jwF}pIft;cG4j!lLG@v>){JhzO8Lk@4;V|{hZ z`0gh#`B;jfEpnsSH!JAx^zDE2Gk3eq@|>b@?`e zSetP^UvhkTB~f~VAz5Bh_y*@?9}-*=dIWh_lop)oq%>TtC2YbFFy1ja*hQ5E^ZA_h zasevhoQ$#e4h}F`j&p75U{WMtzf+Y0AkVY(Q0UNzk;~bF8#hAAn3kBltSH#unUE=H z+Lq|LF;5!FhDhZdnaPqs3Q8+uEEDAhk1!IN05x4HEuw zaXDi=8d6w=>u{m2A=ieRx306lx63>4-skNHPq=Zo$NqT8WHcfll&P~-#L#m7=mG!L zfA*j7Yj^MRlRy2l{Coe-U*etDe}}*PfBuho^x!E6W5ukAoK}&d(BwAb$&-p|HRt6U zdug04_Pce{;zHmPH~0CS_s_Uod&*2k9BwNJMRpW&xPl~X#UEgG+F(~hg12ch9VCz} zg;lszBnB5HqAcY(%Z#$c2YG^M4KB3Og>>&>H|F2+P{%(2ZW=#wVEPz|!S{l+t~uSP z&Y$U}d)>{ZLlBH@Z&&l45Il7dtC`7j3Y%~8)VDsPPg$;pO(yg+V#1G(@O!6POwp^6 z&)+iq)Mu~ZoM&~lpmJ*#TCyA7J8k;UW76OENVv_IuL0)>Hn` zJC{5@zd#o)rZ{*OZTC#lYWG?sV3SUVM(jq%iNg?$hZ0!`{5~C!}Wt5thLZONL|0=S;zt8T*{x<2Adgzi&$G? zV^<96e*Dz7E!xVO(BX+LqP3w^n%eu+0VZ3Q7Hxz!61Qq?#8@k-VcsSES0seMYBMcMH*$0lU8=;$i0G(=s@N5@DiFO0q}Quu8eI#~ z)D{!k^%+sggr{4ZDbu~QvbIu*_$G$(gw##9mbEaN3@|5GEY=l43oYH|I<2uhu3j_P zKcsyAbL4J`HUqIftIhIkbC~nYGp=9iKO-Dfn4kUnm%fo_8RxS(P2E6LjHXj`mWlsO zb!oDs^fdWIWu9|(ae+~q{qYE!W!S7h8$(-5WMx<7MnZZF#Uz+hTvejrj!L6~6CStD z8H@*vCsRhFiR|ZhcPIxXu9ba3_c;>LCW$~4#%QXl=KOLl2{}mtc0s~v~cV9c^@x_XD>$rPZQajD>J*`p72)43&W~YlWFEVaVO8)-ar(|Uz zcF8TFo9POn7`Eobo4u9lX1r?SNsqn%RHy4j)-E!bmjf;&F_b3K`i7uIEgWOODY^Mu zFNuA@hZKt&HecXl1XJ|`5A>kCbh%n^lPbo(G4IBA-F4Fmvyw)%=Z421mHNfijI-GV zi`9a~ddad{v05!zFBhzTA0}=R4Y$?n+XcWjGByR6#0XzF@*M3= zxpn6`GOMVnmPM4^^Z)l-Px`4#_xYWV+}O$ZpZ}p3c{&fcXvvB(&+U)+271VvppR87tG{axy|;e5Vke>x<3(WC?)XuaorzUE+m zPt?&q&_>Ubiy2lM;ok=tB}-6JmmC?$Y(bW1vaZw$lUvGCgjQC`e%Ts>$udgQ5zdlP zM`^0IP1s}2b2qMmR_qK*maB%QZ75a1dC_3iP0i`k6Jls(Y_Bav2JfC#5F%w+Fda=( zp%kc_z^9&@GAX4S?v?&gW0XPn0?vrG-7u_UhtNMuyJxR4nyPL%o7aix7JDRgxB68| zERb4@MrAx0aP!XX^cgkLHw3qmbUc#|bImj2TV*#@uTj$Nn=YuwMhTbkk*nDXrQ!PF z4%XyEB9HE$V$lpH8GCzs46lC*V+IK|V%#$+^2ow|^)8Jwl= zCeL7Cq4w#pvj$qZ!5Sy3Ws_+p`9R2SH0TUmt7*C#D+bAyjsX<{5yzm+(FuX6qvFod z0V;UTPo83n5^J+msasr;qisf9Rq2J%*bICtma5!JQBasnsBIrn3VSD_5v4Z=jp~mP zwPh{R{Ww&qjZeoKjcWt0m1L#uVXjge(T3nWQ)kjPoS&Vux3@>*tRyc@h75)!i^V!s zzf!5jWDAiLP3bD~EEjRjvckEX>9E8k;7yF4#z`@f&RSv^G8~qP zqioojP6>_Y@uSE5^}q4|;*+m@l3)CVU*aeK(2ww!{>p!zvLxqx>%olqN--%cP3!xg zKco@aIMe){*FGf6C;b$wS7CR^)--1}K@n~0Dq~u0u*pa6y+cV)V36^=4>5sA6kBhD z1QMlPz3$MgDT6%Hb+Vf19IcBO9sa=7Fx6N70;(~p>yAB*6FI93A2pRd`-xo2m+o-3 zAxJ0Q#>iqZXLWIb8IU-pCmWMa(~52cuC>f50%j9kn!fk4%y3>h zI$yQaizW4{VtnHOv~Ue#jLFqw2`UH+9EFZP*#re7(Bb(-;TQXawf;e>W#_LGH@y=L zcGE$nyI5KYVj+6!bxlN(6_%aJ5H%^edHpuuefM38+;V4smy>11)5|&gX&;V)+**uM zJbHS}?dwN0xRmgwamx4icvR3jPt&@TjghBgN`${@mX0Zq5@RGNgRBb7R%VpI z(Ao-P@+3@gTfBaq4)bRvrp9lyQs|^~_W@(Ad~PCnCO&x*kV|PTscHjhmUT?gyQyi5 zzD+UWLt^ylFgKebLT$7$+w;inrb6F;a*DQ!+c%D|BHyE0tw=zx9#Rs4Hf~qnj_XI) zm@O)f&n^khQLooxkS|M6mZ;U1#x`!rZeW;eQTIjq4%QGUGAOb@sR9Ov(IP<85v=M5 z21zWXQU?plSY}t330&a_p+yHz@GWjtvA&!!J2~d^^aSS|MNu*yjTsGxf(Uf3FLb=7 zCy0sgMuZqvZ}Fv-5(KM}>43E+@rG3rJcs_YQ6CGJQnan3sn+Rc+B`;e^5XU)E&|$s zu?lOY)1J@gxVEKrp3AE_Avkt-MocCXjLo0jqeVlE27)F=mpqV~wsl-B*HmqbF^VkD zQl>CeZA+GCynOQ-lR-x1S{mn=&lb#Q3%m{}ErJ@AS$4+*_9kQAeg9qlAOGop#e?IT zfBRqmm-v&v_+vb1CfuL-J}t{R-*Fn!{>clcx)QL0ESI|sGu7|TI>LnSs=~0@orfTb zapmbfUxF6yp1r!~-}gPLikq%JwPP{iREhG?4=xbfM9_*RX(Nn4CzO$CN)q0A4|VHV z*AkLZw~kfgsGTCD-&-}FRUKJWo<-$Ybsy{WQ8^kH@h;Lju@J>>x=>ft%LVylL^&)4 z6RZu^bn(M!M-U_P)2Cd_&ctC6K9ceJj{(kn^f2FE(|zHVVkZ|g=+*p+_Z~ms#7FiI zcUUZZirn1Z*~`QD^S8&WY7r!b5Li}A9-W?0=upi z^phcyLb6VuRY*5-uQq_N9OCA%6T$ll7(22=a!4}E2^f(7)e7bl8-(4_h?#= zYdyovG8qjR4F^ny19EFc9Hwn5h%~Nll882EP?ih_V@xI^_cn#1H?2pToWuP+2BQIs z#fq}XDF-Fxzz~9r@U1nNT+9C26ulMMD4BxfxfH8?m#akgfYHnw2sV-K(h+9+wb_=E z-b-;LMO{ef)Z`S8y1V@4wMvw~LWveDCLy&|5v!9^D(PaH;0Ufw>42#Xi$2YMT;Bn! zR6!_}o)r#O9S&?J1JSn>4RvxXWv||hm$`bD# z4<4U!@6I)OV~x;kTvOql?@Rwaac1kLT(z;cc&KD?GkRm+=nlIW({8H^)Z_L@sC#RqM!iOcnbvQj8mBx!B+W zO>Rf;ZB)eTiE+JxL%j5dr%18hhX zGQaU;6J>wr8jrSepy|{hX#+)>(==;dd-E+mxPQz`FTKK__#zE!T|7 zM5_Yo+LK$&gUgEHXeV_v+ryB!c^!Iix5S^y`>+vrMZM|im9Z(0AZ3KdM0D%-O^M*= z*I$EDToLB*?wxw>R;Bj6G7h$5yhYL;$VjtzY zRq7uHbo&80XYzfYU!7yDA~SvCxKXtgSrG zf|d?ySQ=K&@%GyvfR{Mvt5wadqiYzY#cIW70@PtVx6#+J8AQbimWc4|wqOlIM^1Ik@==UjP1ULX^8)^YnC1 zSs3!nGA&C2fyP18dahPWGMh0RmEawZPp?>4&Bk6AVOUyhmiJB|QN3@DjqaLSt=A&< zHG)fxF>^5qt{=W5WwK2KY9nyz&jRL+fruEghAtGgLoO+StgFFfgr7ADvv{ zP3*0Ss^fmxuDDOIWu*wM=kD!WBD{_frxp0M_}@3i$PG6IQQV{S#t~sVi!%NFsf=a4 zcC4$GoymalcnT4&E@!M)4M*4a*uS-pncS9XQ8BJLT0+ynsfyz4f}5T@49xkRI$I23jv8u&QZ!?$!~@rsczj$Do9VjuL-% zXSaC<<(QtqMr*YF=nydU9SI+iIH{=n54;eAbgF`lGI%^qov?x(s3~lY(coL@nzdBS zi>pfrj=>;HKJJ`+Fr+Al_~`q$F@%lDSgQ?KptFLhC=izUYR%)5GafvA%=yJ7#%$pG z&x}}AO#b|^1@eg-sv8?c=d&qPJgZVhv0klENHUR%P**LhYQ><)I6B;=7z}!%Sx8yl zU85G03f5>%aGu#>$=TJMs&4vNP`Sy~*2>s{@>FfZ{Ax~JSAFLlyzJr2Vae`zz~#j; z|IL5@xA`A`^;>-Di=X8`{>%S7qx}P(&Ye7flh8_8i!H_yf$6uF(qa4D30s|H%GVC0 zeEF>zFQAl_IAtI3UhIjn->=Do&KfzKqShFbqqQMKL})m>;_~v6Rkg%9Pu(`0T+X;! z%vjeIjcaMVqj4=Ru>sf`+NkZd)-&#~KyV(N1Z#S$+WzRDedg~gMcvdrZul9nP=Y>b zm3E+7ZuDRJ`e*r%{`LO}|NdY6IsWp$@RJLw<`ahj?^JqzNTI!H47Q;h^1K)On&RU46v!k) z!6@=9rHiGVd2csTJ)wrw?4_v9%^|2$M;N!XFe>&T=o`a9Ctg#XT~+nLCjIvt6*kk! zzBR_Vz+%;)RluN_j3(T^endGO@x6CX_~z>$^3LO9s=CD*#ld7iSrp6`E7>m_(>DX1 zP_7*0WV!8i9?JAjK)Ifb6u-)fKfZQO1bsmon#MPT=-J!fWsstTy$fRL0%|X=i_?o4 z)@qv8aeOuBe7@vzzM}Dt(i(<&f$G%dQ86uTvWNOKZRz4x6(?6Unbw>4v*XD3_TPlk zMYJp-MGWiytBM!vW1K#mEos?xNT zh>|@)%3tuC>l(S}RTA-P717F5Xnyy^7MrHYQ@g(9>z#eeJ4Qa(twoe>{*c_ z7d7eS)slO+4kgVevnXw-s%2t}chH%%ztr-%)@=}OHX4FUHHo~4nK6`QNs(nlG)?d_ zW{(l8B5hy`sU@jqK6qyHg(%>SVKN>hp11gCBN3xF`xjR=qC7_9+6w2r*chrNF}R(K z)U0@MBk?0zYT-vuMCgkKoV)3bAa|BVlM{?2Xw;y?S-y!*iue&@}n zI4rw66P~U&(;xNGqor#bRs0AXWm5-rhhFzuc9O5o!)dI=yN1iflGSR#e7@xT>Vl_d zCpmc+@07*KFiF*)I*Ps49tx7T_<4lzx7=|VNDIWr>^*oIk4Vl*cA+FPPBGYNtzp{1E^ApZakIWl64c#-oCt|JqBu z`TiM?&*y0zoS6UJ>m4@0oh}NtgTnmmONLv!hB`>MX$?3eRjlxy|N6Hdv1$VIwY)~p z?~k}P$yhtb&wX|uV+vwQ5;NVZEP}R%$spr$x#s`&x8LEz)0#)KmfwAU!M8q~bFpsu zL(c^&Rq}KJnO6MFm+thpe6MU)x+m!=4Mmo5xma>`xh6Q_UM#DIz3CWht?(K?G9L-C zPen?MgY>$Fk33WRdrG5@rsE;Umvg><{|V>w3S$k2lL0XXE|(3H$%wjjEb4}< z_>2(GnmP|C0~9^pd3JC8D|7i;ZK zN+y#rdpkp#y5;zCPSZG+b(1&M|rgpHigRutJ%Hgb3Vn!;d=)i~$+MssI6#O66oRdI1K zBhM_?4t6oS*U@Gy3=Ka++o^QnN^K5_ROxiMTVR@^+?BR7_dWyNS` zr{A4-=Z@BT3kRoCRV?8K$7fgEIGi#Xj>U(kEp@$4nZX-;-X=R;e6F<@dx(k-YhY(G zksGCreDLUu2Tv|2hXqAeNOWRStNYkNGLjG_1ojW69G_gujy%tpj)r(A8hhQF^93oZ zLQ2VUj>a{Bkt#GoTQ~CWN&+bIw8QGtf9wzGpZnswC@ zVn_nK4e^My(vZw|S*O&sV=-TmWkyDC9pEKyMjtxeq`?K=ynoF3Vu{T()4_nY@_6SZ zPPfRo^}_SK|8&k|G~v(vi67_IRP*2e);nCS-DW+(M+6_;glSv0z6Ep$DMLau6y}-J zP$@;z)HJSQHlOqM+i!DudcxJ^CG)EbR*O09dQE6*mh&0Y>5hyFLwA^O;bY|X8?O?HKeU3l#%RkKd>5{r`@jlQtf(`xIuYZEedCgn*Ph=X>5kL}=Z{q+IAzbU=69Ul4dzLFd;%9#71@N(t-_$+%LH7l) zHJY>eg3D!vZ-tUrH;&EFGj4PqFjgT?BA8s%x z$+H}zHBBR2i-Y|gd=T8NQ8MWmmKmeLkmqjfQ>~ZG7fmW$G?U4go$(Z17K{c3u4>4# z9IFg(KAofROon5MJWo{2i1RI9{p3D4p~uB8yd?<{dn@kNh`nn8G(6K786ymUr>8R( z&IybLO_rvdE+KwR(og6Z2;Q?hop5+`ljv)7jCj9JR<<@)Wyu|5jjrXzd4zstAZc%Rqa{D3#!z0c$0OJ08d4rQ66 zHCU68*$hl3A{IdcP}rPRwdV9{hVRBL2^TA)V^O3VZCEy*RVB*!x~|iHNYg}#t886M zh@N3lB*DX$wy;x6YlSgM1m|NCm}r(&gNcgqbWHGDajI#Kq_oCn8TG1?sf1R~+Ap^3 z7^>@#JGh13w#P-(ZO~Me;BS>-Z+FUSDfYp=>4d=`r>bl5t#`P@xQ#$pNgtue zGQ+xVSgtDaOsqJaZPBI-do4m%L0f7#y;yO2wWM;6doMmmQ4T4F1EvQD6oV39uXy+2 z87>%p>c_v#PkreYfAiN~XI-}#^U?5-tp_L{r>SjhwrY#$v~}Uf$ZB2T+Q5Sc_wjAB zVLq!A$Jp%@6&P#D$|8}X!ZTA75`l}fe(G#gT}o2y>-LRiqRWSO+dqrZVzCUOdN*pT zaQSFK*W*kBt7<9q>SSl>CMF@RzhaDJ#_;EV;Yav0|HKb*akdog%9a?@y8zDf^)J4_ z-e|zL-h7<$gH+F5ke~>;rlU;N`7>WCD9em>IzW|H>1U7Zm65;w`%k%l(j+TqvR8Vz zn791!OFMk}-Vp_XwU0Djy83PuF~}5;PcHZy-#nIB+zzBGf*#e6N-dh4B}kZH|$Ji@!g@zd%Vk)Xte%1sd^+jq04 z{fNV*E5wzCtNDtmZ5U5Sl!XWtwF0Yb5?&cem(*fYeeUK>a%%`m^V%Elb9{2ncr+x$ z$ekP4nJwllR!hc%0n1gz{_Y-o(mY|yGc1RkDVVvJ7RgW>q>f~drw9kuLgjJ7H7STeLje<*D2zV(L6(d{(XRgDLB zrc-%PMz!TZmyRm485dV`&Swi&O@mR2JjT7h z4V7zIR1Jld8)A|~g>Aj#;_`y^eBO`Vwce`oL9WwcfcI?@#hH)M(7Qw<=N(FG z2E`y<56>1KI=z0K>%yzeil#$~>TcQ*Ny1AhjzSD+x4ZSDDn(T-X}wDws7gh|Mm*WE z7Jl)EKgExH?k69_U?7=zHk#)C+7q@iFWBT$n2pj+y6g)Gacn>MY1pl%vgtBR&=CEz6;AEQyp z$>}*~7nihkP1V#a=1UF_CQQdu?%unLZyYba@Eq0{>bk+$42`0x*6a)m-aTEgt{lVR zfKgG>1cy?vUN?O8)oWyW<3!Pk6VQpjx?(&tk^`WT#!Ng&x}R)nBHCIm=L?BZwH6za z2rh=?Uk@ZIS>g&ric)j;&I|p$uDaFnXy0{_JD|d!6LzF6>RnzoY1NsMQZTb%VC%*pWZ*=yd zw)O1okIAg%@yWSFhi-KdN_Tto_>7D`vqH%|yPEO*?L!&cSg~?c)jH9OHVdh~lEJWQ zrOU7yzV*g?>`#XL!jFDha>uRa-mQJE=1ac!)`y&3%y@Eq&eO{oPfyP|IlJJ|@hSJ8 z9P{+#l#|m-&aUPZdC7P%z(%PaTPL3N7$Vjh-g)$tHy=DDHzOZDtw9bwfp1R@7}JD4O~(Ym{+HZ!sDctnwB%$IAN3ycRv zuY%st7&r8{t{8fTDV(ZR*ECd3(^JO|_YY)Epd>+Sy>2OrjNQpZRN1xbe_w5_0@PL! zF&YfeSw_<|%$F-3o}Hx~d!TI_)~(}WwPIP-Nkk%Ksne4Su3wun8BdeWLJszBpLTY3 z&dHVI?(LiW*fy>UY5&CWbVKKuMle({Gt!Kw)y?TjQ~LW@#cCO;jm zP?YCdMU2v{8_mUHN!_Hihte1;Q_nlQia+z|f~A)tmQ3Nj0w<)q;Xv{FqdC9+#vH5F zGii3w!zXVI`MFp3IbAx=*Da%>V1Hy;*A=VUg9_N__-}sanAyrFT}cNUlCCC%$k$%l z<%Odm7t5Nafkox{+uwW*;{sp(%BN`?Co8?q1rg;yzL+nmnn2q+s@kDcWLV^kM%tD4q3j28Rc!o5M|fDTrtQEk7o^+ z^NQhMz+^ncw-FZuRqgn~-F?P|OuzJ&uv8Er5v4cRbLccix+_FCV|b+$CeKg-9-l2F zSuTpLOzx%FH!y(?cT%65lTgU#jG3D+aro5G|;uT8e=|GRq!en})z#sgN4K84;2BQf+ zaCtH3=-Qs32fGVDb}2H+e}_tpU4d^n7Z?vqe1MaabEe}7KDe|CQO{B))95(_MTjjL zN8=;GicfTBTGBM0W$pTh;g>)4Jl}ik0l)Y9TS;xWDVRg@$!}9Ktu_={PMI5utYADC zkmZ&#FIYB?r{{BycE)I{cyw~XXqcxRYUJtJ71Qy6JRRP~7($S-jLATI`kZYg=6RIg z=<~GIOd9zJ=>b5ew`xE@Ltn6$NA-)>9FL+!2wUXV4c#Nwm%;P zLa<2(>KPU}gCghtlM_bc9c~`&GAc_hW(!=~FkdcvzjPZs8IlqItAFn|`P3^fG8zpM zt;%rY_7Q`!M5&6ClgIq6-0{Y1nLCAzy>b_#4HzUR}6%4n7BhNJkkGAl$ zFXyz@;=POw*G}RqWAtD=!z|;s-n{CkB3f;sVl{mIg(-_x2|F9dql+sJc19eIN(?Km zD$g5d6%Q{PnO~;wZ99DCOLr&y`Jeg(gPkd_zkkecz5R&$r)y5<4gcx?_#6D*`;YiH z|H7ZZddqrVC#ticl)YDYO$wG#RBc4JEjq$@G~nWD(GPq&p(m=>1IqoJ?4lZ(wkRJ3 zDyBXjidpD+XD^ z=Ra|sN5>Z|7Bfj8Gn%TdnJ*VoK&HKZSr*(nI+Xm4OvHazizPa-t z|4=F0qk9zT)SVktnsjKqPZn#fFoh*9#hE13q%P2Gn3{ntYpLQ7gjs!ThyK+sedZft zu@dT(RfdDZT~1CfaL)C+sjiw8YSL!6q?CZ@O=h^bn&F+}&dnp~BHEU^Y2<&2kZ9df={QMW#QuZqE-AjXKvGTKHS z2qx1#L|m)wZaw8Grw7EUZj)cx(KyFqv0|}U(6)}zsFV?NI;5>N%;!s5AIOZR%(K3h zVoT}XSBPgtq&>3B$?A_nR*rBy@)#gmhBo*bX>(G=|I$DE)4ch{d%XSO37P4JXd+%zDJxnf;f+=uOd=4X zBLs(zj*OO^C9A6wVsO-nN*6y0R!P@OQIzaYr~K)k`zk;Fqn~Aecgzd>BYyLZN4>pJ zMMak7ePu3bbv4$O!UJgQwAa=j$-P(4bnY7$q9XbrkdG`?;X#BsqIuJ_ZPTmgyP^Df$vKBRIe+rUUgWe6oG%= z_B(Nv5+MbE)0w!#mRPWjo;+8SdB)|{T0%FhWilLMOv-LZ`&!c}vpYzsRvhdUT)(!< z>G3hY{hfE2FPAjVb7OzX?fo6D&d+)2)*%PG6Tb4<=ec+N2B29iSIn1Ha&-pQRmI(# zM~p{9e)sh^DXgWc8_*h?6=di{CCl($ESxs!REj*usx7OeR%8h}<9wiQ8mzW`ml+jk zV`&-*ytuYIp{iTChjPWugI(5(3Y1|u9gBxN!D~VkUB=PTAy3cGS+7?-zH}71WoJC4 z^)10ms@&cEf;$I8LfG0{r(7JJ+&FaBv#2+|`@Wm+)bR3*aKMw(OF5skCTJsrZ>y-UheJ&&Y|)zxWj7ex@6riaUAOWS&i!ZMx= z(F$glE7psO@i=30Z5KO!K3UgXdgf<*B<5{Hqx{Ep=sjjbL7qIXp%t$8y7kG<5+%DE zO;qxblW0dd)M}g@>eh0xSkg2#g^}G%3_Zw_j_Dwnd_#;%>{2OMWp8K98*hEUt1ldJ z^Jt%C)iTHpuf6ktRb8_)o?^73Zd+DWjZ#n+IWflmYlQ?6@iAaSm%`+zYO!Oinu-|3 zrgylv!>wz(9G_h>9u66nIZrNT6q%uNnz*VM=9VnCgrcCSYqH#6PB4S(V321_ z1_KtWHC5H{!TtNmGBM@3=kB1ClCTP+xpB0|!$)WMAhx_dU?xgG9D?jeor(z@p_pAQ zXkEkM{tn~ukWajHm!@f1ELT#^dg;=e*ihOWV{GyP81b920SE#rs3_-X#=&wqv0e1&(OFMRqQUwP#N ze(gK&p@JvN%T3;P5`>~;A6d2aMin1_pj>^ELGIIinO^7^0=ZSFd_YyNB^9e_(W>qL zu8u#r9(Pe}Q9t#S7g?^HJOtB|CMuoy*C{MAb?u#XR3X;05F)F}(YEmOUw?sVsrk=- z^}FQOqBBi_X3==+ATc>gi^HT)ir;wug1U|}w%sy)L=Ugr7*Zj+fbQx=DGCE;s})aI z4WGI`VYP~srJ@_eZIAH2d~ctfjceTAzs_4vj~S0NKlYg+^Hs~Q z{QBR;y9KYj@;U1DYQs-YqD^h2t8QDFP!(m#?r6mE`5bFH3!q_L3!JGe<(icpYV7&q zt#=Fx%kH@3!O117y2V;erY$DZABpEl7?79__H?%nEVy>f8}B^e@yP}EZte5@;Xb3H zOnEQz%vi2hXfE+KFb7&5}}P!fx)1_s>s!>p>ZuX%Tr0196}~?FZW3v1gKVNyh%WXpd@W2 zY=s(y#6eG!IT_t=h>c1`q0MDESuTfsXg$%lC~L^l$y-;BSg*D?2L&-UTR&L*|Fbtw z2l_LA&}wS$XxpamYT_0>$tSqVCL}Z_Xul!^#~{<3U$v~(6dH;U7+<)7#Y2*PMz(ag>@LE*eM6hs!F`a-4UG>n5(AZ{fCbUh(dW(4w$c2 zjLL#RZX~Z+ZPe6p%i+|I`Zup=f6yxhO^6)qjAb{qZc=_bTwTq`@)K?x9f+7`vF31p zhr??-9G_l6Oc#HbrJO!*AWCVgBU)RE!mygR9LE9SEFRM@defyk?`Bn%`fMc&2<%g8RoZ1D7OmY9JrtzBVJ3|zXvstrs zwaR&VG_qt?arJQFW z3(vrUdLml_@yNC6Fj-Df6x0pi+kPjnl}`EY^o20cPo{qK)fag2-fiA|>#4-@Cg!Sf9Ll_p_>ZrEJ|mK2JZmp`Of{tGZKMr zXk?Azho7I)`uI%ppjNQ-k=h3eTsF-k=JytImz4 zXK1dnpwvp*Sf<8aDb?>%9*Y$=Vw$0$fy zqqf#7LLhS(iX?!IL~?DhT61xF&NsjNK7+z?_h=8R4bFQBj8VcqP)g%`WS||ZdO?UV z9cVsz=ZISe6Yd|M^YFw5lMDP1ZBXWz7j}S#LM1!lrhNMG4(=|?;^f)AREfK^&V$~Gpxd#YEg$mGi0GA(kh zDoOtINk0>TEJDy=l0X$DK`~8Jp`tXbTFZMcBbo@_^;wgFOD=LtQ=l_%T27<%snN!;tQ_;DBpGiQBHXHXL0hZ^vQpL|LUZj7J5T(ySY|C1Bf#E5v>jshftK;eegtK*BvzAc#`W zD$t)vZtIjhN^L@4wjQEoQBXAvA#9#EMk@{u4)ER)QLNT$4t94Kj|#4?<|*mznJC^Z z{Osmlvr4mGH?*zC7-Z}?9uiTEh9wbLtk$$`E21K=7>){VUfW|(6kOY#ULj;y#4qbZ3m1;Q|iX^_-utuP?e~qyR%urje`T0 zPP1qf$McqxSU*MyF7`3sF#*a)^qCy)>3=S89MD;4wj~wa8c=4Iw zlo%tW&G^Qj|9Q&7ay4J`^m5H~m~-P`B9^r+YMoAF%Pz#qW}5SP&1)ZA@`V@nF-G-% z^`pZPpSpLGYIe!0Y0xo%c4P@(sw14tDt_g)t90FMepc@zw|6ss>B~o4RFY~J5gTM3 zV9`o0+Wy$`Yu`KJ%_mEW+)(D4>ArEZ3BI#yHPu z8pm(Gb;56b|Ab}bXqzRvKH=7Lx2e{F)8#7JveLxPLyVrP@mwvJT;JOv&vTaRirHe# z{$#*pR1%3)&I@;3X^JA}rP~J_?M`{^orj!XiNGsHMV48LqU;|A@_LK6%BJzXRwAGq zmWG`{!8;F*`QAGZ`SeRS8I17h?UI){ zrYwLHmTqPDtG43Fuz=9-`_eM9w*`<`sq2Or|4rR4E z<%Wa(0rucUv?{hbeV%1U_OaOdW52b1xZAGL=$PS3DO%^TR(~uKH1s20g$_|FcOxSI z42=&C7b8vMSXb-h|L*dh2;o}qD{NNFb<%+|-J)XOBlN$#|ELUr; zX6KAXCE8eSUcbq7XHRm7ivrNxfAArT<%02eoOVC5?;H%|hwm&#M~63f>z%h)H;$+0 zXWYDY#N*?KJUx2~O0zQ=^WpiTe{GhvP~?it;1x73^qxg)Ehc(=^yv*0Dp*WXU2$M( zqW7$7m+Cb2jKFJifWY2xz|MF`<2?AzYaV-uPM6dc8yjO*trO>KY#v|3AjgEXK0!yz9TU)}GFMYU=9l>h3%GUSD4iacswq zhX7GP2}#5lKuCyy1cU@4fslBI7Z4zU$SW@h2`S_O1!H6r1wsZVjt61;&i8iTp1Zn+ zGoL-JfrqvBKBxM&ElMi2x~ot3u2XxjwZ8BB|9}4nkI%Sq^%9pZjX6G^5JmBYpOb5Y z$9)SP3sEHLsFc;Z3^R5gd)Y}d>RR*i3(xZ0^((w{>n@Y&f}&Wkzdz!}r5!5mqjZJX z#axF{*E~F(a(4R;X}7~yCg=S0pZFZV^vnO0{NMc>zV-UM2v_s+(TI2MpEF;Yw#(%N zym9Z8SFaB+#!*>^Fxn?u35#%+Zl);eaOYZIF`cb6yMqz0zWNeXnSX!)Sgb0Zd+vy% z{ULANJ0X$o!l1(no`NXe?Btt{%Esb(A)3_rs3}8&Lg9?2w7CZv$qk?0ljttC^R0FK zxi5W$tCtU0E!V*^wh2I$3Y_ut+ZH7p>^$uNuvPVJuFN_|k=LYALi*f~U~YY%)%-D0 z>Su>}p&3Sk*X}LR+DC3~CyP1wUpwK`?>ypX zzVIc|+F`6~zg?juLQ9g!v0N3r``{5b4)^JIGG@sd8L5_2K3f&+3_E=6g=_RW9gfeY zeEoaxu&y+{B=-Ba5~SS@B8qXwcaB`ZZ?@r#t=d$PDM4i{??0H(O%q1_4(nAx8pl3I z!b|LNQjo})T8G3r>$v*NbspTgN1m^7PEgjCQ8(qqtNT<&bG|G%oh*IauCRVB;$kRN zb6Y(vQ@dG@wD#k|QNPPdVj0_ng zBH+B)q+YskuVtMCv&xA>6V*zCP`(psjKc^^tOUwH5!4Sfdzu3k^)PzY@O-^OUWqnt zw}lcl+$-lh!clWXvc7X2D}QaqIxpaj@Nu?2-;27gNnLp%@xz6(_XmeFKm8A6v?WS$ zL6lPDg-3X9V8>f2VuXbACb!Dx@CpL=+ju2rSygClaoSUgtf38Esg2(lRFZP<_>Awo zd6UU(&cV)zYe$#aAMb%wbb1}G9X-Pf&ppr8%SXgnCy3$Za!y$n^t(M?df_Di7Rv>zvS3+O+`9LeB+b}Ayv%B~ zY~Lqs97XLzIGlymy6`hp>sV`pN<)NY8xiXTQ%VZT$}m~3Ii4>mwf+DRtkJ;=2ZK&V zH%qCFxv@ggh^Jd^%&XTw<+d*y~bTi%b$e`H5Hf*hgOE z>cN;;SdP!C-X};xV*MHKO7JE#lP@b*7M~C!)t@@ zT&gz6r!^(nZ7|A~{l#tRDz_t5wZ<94<>x=a?xh=8Z72#aUXsG{-Fr(KH>cR>*hmw> z$FC1LU+b2Zr=4J41)T|wSPJeh$f;=y9){vx0n_x!B zy>MY*WicqB!Pb;j$z-u$cig8oL6;&zHH{7SUjOsbc}-`Ka_Pz;X`FCybVQt`-Xf)Z z1=vdyMp?q;euvBZL!!iUCv>ep;81SIzfTDb5L?xHT~s8Uj(6i|+Xet-S%*PyM3ihHBsRPU=LF8yZ7bTkAYfFRgftlmq_C7C1bDkJ`!eOif79;%|iu-Ht7eL!=Zc@CS6Of@%V?yr@nazk>>V)UZIHwV^5v&aSpr z47nYy{B$g~d*U-J(9$7%#St#e0C`!rK0PV61z6&O|C#_DFrf-I){}<>f}*S`%aWq5 zeL|NpXrrmE!CH%oV;(&?<#+z@|FT@I84mi4hr=-NljOOFq>M&mYVF9<4!ZXLW>r8<=VAt?2botv&`Eq%AAw4V{YAfkJsORowskk&7;Q;skLS{U$R&( zc;&^HxpsJks;)UXn^0>_q(r*_XzMri%2@KU_5u&3u$5uH%!#rNoxza)XoypS5^XKN!+7$~x?i`;7W2Zfiqki|*w%(GQzytNpe8%GPTw1Y@BEA|c3< zsNEZ+RA3$NzV|-Ik4}l>m^6*3Yt6&sa}M@)7>@?+roPF^c3UAN5+nTeDdE$Yigm&9 z<0&VT8BRzBU60(IFG_m7KEr+nz~3oUgjv6N1R6J#U*s@Dps3Ctl};Tq-OSn`caJ_`o>l_}L$S zi6n}sjcyzBg*F!hUF60p+W4QFtOt4FVEFWFx5i-VlHIE>vw!p|>q-y_$GayvC-cDd zW}~rj)^Tkdb7dTphilOS7Pax!claN>sp74>i;MoWmRaw(anPaEn*CJKkG7af(zWx? zG!lI0_8CTp4wdm^$>wW9!ut;v{EJ`zADk@b^pqmj*2iPV3K?bI_9O+7@}jX(w~KL> z=^|&I=ftsKz9{(CTX#4OSk+;_iGwc+9MDYNrAs!g~I9S`BC0vDmE zeA3uEcTdRknxp+4V&TJ48l*5nU?X2SDk0f9+@rsHnf_=@RcR!GtIu3#U1*H92&ph3 zC`%~C^H&e~^s6uP%5&EPOv|)@`#4BUiI>m4c*g>->M;yngU(g!&2Hi(tKSDjPfj89FBBt{#mvhs8E)@`Jo zSRb}`ZSzB;86$*3VS?|^r`k9dGHycvmXIz81zej{moib-qF= z?^)M%&B5*^_V*4j+ESK95aW1^fHsCAUsDuo))d4Rn|2aZzIB5N*pUG=?!~eHHU}$#A!^K=bWBS+1(v^J6EsEr6>-wl?}QyPE*$F zHIGit*iHwHBSoSVb=d8t*qptAzCHy$RqOiSLSUa)W(pouqc&B6xu$ z6p+1-j*nTbS6sh-#9+{)C@N0QrrfxCh*A;L*^EfVVN~zEnIZsDglT4O)~hwUJ}3E+ zaYz+$FzPetcA2eKI3#hDlIJ-X!}+9QJQ@)t2@yVV&tecJ^3}3Z^tv4u=M#SOw|U$V+eKKJ20zWm1z{6ZGS8z=LcJEsdid}W7YXD`S|aEd`MVOi#_ zy3AV0vWQ5Q3{_&AcW&+XG~p%u}kEaHb3ue{4{EFlxPB#Y$J5sFwjNIgtt#WkXTDy<_vZY zdF4}=xc&M!xb^rPYrVSNxeKISuI^-%8vqxJ%28T~rGLMp2-dmb&HJklb_SfI(~(@+ z&-nVCGY*FXt_?dZYr~|pEsrZvg5|p4`|nRKWZP_@L4xNFGj@9k?>t=apT72WUbwQy z7e4VZBI!3m8~HAV31cE@1-)+MBXhOJX`joWQh7m$*zI((N5wIH5}&!ol=yVD2aT4mG&uK#wk=d`Wb5(j7Ow{0Z!*0 zd?W>?Hbk*RB@xFDA9H1I7h@Gd_#z|8IwVT*{4>|Nab)<`ci&>MDu@%+j%39Xhf3l1 zmX-qDv|Ws`BuRp->HwjMkwOLGqheiDoSdI?X=lXkPDY-WeEWO1Nn*)-wZu3}ER-*P zoOgsoNyKD2W0WasV7*$=8LN8LBiVqLP^%fJ{+Gcyk?eP=xk$2hl%u63T^DMB8B z3@u<6MwVGO4W+(+z9U52JrP^Xb!S0Eip8=dU#;o%M_%YAd{@Ug+akMB;qaiHuM8py zQe#_QzYYV~u%0`wT~bkuu4}B-IGYefK5n+smdb#umfrU0o`%-CFvt7hDt8%xG5$2- zFIt9nmMI-@tP%l<=jZbYr;n%Hy!((e zQ4D%ru3x#zPkiBv#BoLxDQ|(B&HyO#HS^_)Uauc4IHm=`RJz)L`lX_-D^x5!$32Rv zqlh%?k#z=m(XjURn=GTQYr0vN{qZih?%gF{Ec{IP39N~+mgRJT6pr3vO|O@cq$zn> za&|srZ+A!>C3L$HF0_BzABuziV8D$i=HBsfTUu9DO_s!-+$9BNZG6Cqh}anq==Zyu zS-T|!aQI%4iegmwUUgZbRMc)5QivAoYON*D%MdHMt%LCJhs9#ao!fU9jfNCu&1|vY zY&zqa%ZDta#92d{budQz_)6QxqWGVKb=1ZY$C6&B%lUjk)|rwh$#Br4lcwZlMbt@= zbsgeA{cLS_G=y4XbnW{P)+0G(pt6~=VliLxUw-2&{O!N-3;gQe{X6{YfBS3f_hX(r z=<>$>WuRS!D*eukSC4iAwvpCC;)I}^D56OEu}1Jkdp~;sRs_{r+tvydvNk@cZ-Y8^ zf%5i&2))hAw)SxQlqiJ@y)G9v-u4%X71|$uKN!-734|{vHVJb=3X0NfW!Y>*It?79 zP2R$UAe$}pX#+0*0UxHWNz;VK$qi2Q8IEatFljrtQt;91Ju2<5^Q3YV+HdYAya^p8 zx%qI#Vr@U*J$Dw4b~8Fz%zABjuvkzS%`@Wx`@nhW3yw5W+&Wn^UFbF(qd8C*?YME+ z~@{`*Z%=@BKcX|M;hn!nd`JuoQVo*6$)>iLO0uE{+qT2(Y^C zz8GLQ=&?H-aOY$~UixqpWBn#G>h~EAI-E~d%qB~QqaIPLyge$G{}dp>Wo4 zdOqWYXRjcY;^^`oMOpLcbb_@|)fHuBLQC6F*P1wyBvHgk)DPCVpb4_gh)S}oB>Vk< z<~1T5zI}I0%B{n%G|1LrWP&wKZf*wa)vK|PD5gq z=HA0^T(B5@AgSFt7Sk1R6w~WuAO+n{#yT%JJD;+#zbFe24=G zjP+5Hy)@zR$&4(GQL&<%Cf)*>blJJoX(LV3EM>CJx%vKm21(2_S1yqzvA1MduaH)e zq^G*9%bYKN<*WRqKmW7*wZHgh`St($-+ATwn46DR=-{=L(yOmeXY0^D^O{r*Xz37E z(v1YOasi5@NOjBB z8E%uXWNF*|X&SZHAVWPoUsvJp%K#D(?cyc?rv#h1k87K-LWwXl{2?D96OIjPb5b8cDs9etMHkteJW!~71UO6zRIx< zZj1)(CytX!QyRmY_ox2NP#3S!8Mv~SvRs$UYlq5WKJwxouYPEUhbL=pJ$##;@ezg$ z;T*aySz1fC-zQ2_Y*h!FpabQ7$U;e0vo&Isv&t(b^PD7Bq=}!G#@!SZDm9Ypj@xXLv-@-*@WdP;qu;)(V!DrLVj~A%ZemULaWAL ztw5k~rbb(M<%Jvk!M9(-+9f-Kj8a=xdBJ<{-{raM*Ev6*uw1P$A)OEF4|1+)Ww@<- zUdjtqpiq8OH^vb~39E922#gOY6i!%_faS6vjS{*^!eUjhUYDGnFW4CkdEwdTnJ?DN zW(#)5J7}v}WP67f>)72s5V8TvwBz_N z2Qt>rHe{G%G?lt-5t5QPO+rh-aXMRsT;L29DJHWC_wPOMW{Kd1w#K(_ma`SFzjv1? zN;uf{Ud->m`7Wd3fW5svALtz^D`yu4?%GxJ!C^j(m0~13yiRgdtIi}1(U@b zAyq5F^Q!iUvM5+gC(KUHIX^o?=$axgcz8TvynMx@~JHY`GW^@4nb{v#r2PW z@#p!e&wk_%&&k=e#$W zlWRkt8c`M8I$l53vLh0~Gne{Ib05m$oMoA7=1a@oxX0%|dWq)_3!d4n$T}Hy4fQ%_ zyCw_wU_qNq52#0&Ie~u!TW}M6)oC6K!6A|C&@`mM^`B zCsC0HUI{P4^s$QqZG8v87(<$LaKhV=PtR6NXA7K=499zn#(Ny>9WWg3^0^leNt1|L z*IsBEs&xdMPxDrKZNqo$#grC_K}laoh-MV*ETCzXE`j{!+u|A}6{(Wo3^t^v2^=cm zWd#mlHBkt7Q34;{<3#9W`EH!2)Y%Y^x~(iUZ2=HB%wTIhP^J;UgrYJ_qws9IfRYvT zdmWr}o=Cf?M+=184HQLIA^uSwXGQEAc}pvqFF$FN?Ar6 zOICA@RF>n%=OG*2ivl7QQB@Vk4S&0OqOd7#$)0%#o@U3=mFpV z-uL*z7k`5OWQsMKq9{;NL{-(KY07-D@M&Vk&srP*eKQqt;bs+5QtLV#Mk10l4iv6t z(~Pj%cqop5vaERZm6v$;y>~8TQVSsvks`?whTSgXeutf5pCpMWD?_e*7bEGU)Md%} z`JBDc&>wVyi`cqNU8I_}P-s_|&MV+m5OeGdhpdZ|>dl_+>(d%YJiK5D@7NJ^Inoc+4@X`T$ zdj~u|KH<)-JG_180cjfX%;i1C!Ot$7qu)((-|0%@m_e`S;X)QqWeZ)Hx^_fK z%8$OZ%boiVx&3I3FctmrfKh+IaydmRK_p{F-IPH$W4_8MEKKqeeY)geZ$zB-$Sdgf zyjt9fjgu&7w5Y0*{r!Dj!SUeul<9Pdl9E&@Z^yEAyTsdYI&8#}p(5I#yu&?golhTZ z??~x$Lfh9gf;yo>7t9|#p1tC^Af=Yvyz>wd33kR~@?wPm_Qzv{QY;r!le@ zc2k3Ds&yIK?=~1$1uV}dxa&t=n;oK=ovHlVt30xK6Vfg=y)+4mAWCSI_|~)d5x!gC z-3dW)FCvEH9`4R5MWL~)HO`ed5p7ebf=JeF!7Mi9fDI*7wChI{MLrN-Na=aUQli5~ z)oR~E=!g*hf$XLkaimzR*I275OA}CtF}*DFEp`Nvl9WZk@%?-39vm-CB>OIfUQoOATLT}}`o@j$ ztRRZg)Wpgk7CK>G?pMHOVK-=JNu)n54*NU&Dd&0UXkp|*4p)NI(`I6n8q2CmQ3(AwGvXxKFMMW#R+1nX$u)pU4 z5=BjvD2#0uFV%u;nwh9C<2?P&bE*UAAmmzG2g80pEc$}Fu0S|+WvE3?mSpVi>~MU1 zjEt05*#g6Uhheu%QEQNr{VQYNQd_Qwq6kx4^7WcZSHx0cL-z&;+Bv#ehjpd7bLRp5 zG^Udr_*hSAs7>u#Qbto%lC$X)U285~K4NcgM7dt`_3ymFH($TSaFFo)l`95yYH~GcZt9HSN=N3zxMALb|)-K+bWD#h2fD{G4AbPwC4nP3yp2ZYfa>+{(xpp zToj;Ssjb<}?buGWmEq5yEO|G{W3nNyz=Zmd&7vEcQwvhRMz3>@+H6dI6n$9 z)-&HzCAqXSBpVDkIi0X93QiUUfBfbh4)*srIvj%yERDdFca0W8;G;M9_v!QpoIE~Z zHeFKI6}9&BWq&jhSh<-{Yzm(3=V5~@aZLv^2yNpiL8>*CE(732UFcrcl}5%9N!BAF z1La3*qv4R#vooJCHX6}MGRC7Zov!3yH^S=Q_wXKF`J34I!|BPa49^8#{q90r+xi>1 z!P#0H1Z^hdX~+hQZ4u_8AUL*#t!d>ko~B2i;B9SleO_BUVSWV+z_qboQBtDcUZ4AS{uJ97|VQKcrlH$IAO`slux~MjX@{jjeDmomIV$+w;xm2nzAV9 z^}0mT&pT!0!wQ@=bdrQv37XMWZ9VGoV0RDcEU8jtgUmO0pY#=N3z33Kx24B5sY6Zh zLes8q0wYvF7SaSAIQDl(tQIrxw+sOS76yX>Do*+Qk9~ss4~}{N?gPH} z-aYR260Tm_XQvyZ>yrQZJ74FgzVsP>?x()Qdk^Ql{b1@vnvHEEiW&DaR2*TI$L=cO z0T-2VtV+kQlQHb2p~bb8F4C+W$}hZW6p_Y0@Mc@47OR+U7Gq6>A_?!gZ(&;Z!FG0= z2DhDgt`x!YvDF##r~hp`T(cEpEQOH%THMA?ssfK(HB1atcxh>0g=SfLRBogsuRgO&5k`6q zV?;?voM$_gg6A)CF!r*AyZnmk$|r6u%qc3vbULH1_bGM4Dh~&eM%zIHV6iw;lf;T=Z(MR35*rc_>h0Jk92OxK6CfB99~TiN8x$)Z7#$T6 zBN-GsG%P6{6*n&?JU1>hEhQxy7CSmMKRGZmDkALf*)J(0G9w#AJTf;ZA2%^6KRPon zAsB#wfD8!;FDxf1BOY;TU=R)tA{r7_P(?H)9h#AbWnWcvZDIog0!2SHK|M8IS4&w` zOE)wvCLkK1oti{NL^?4kD<&a8HY_4yT{tWvR82vho0(BcKct|Y zT2n`2T1|{AVNKjZrOjB=V zSafr7dU|*#CL^Sqk2W|tP(m|qY-np~W1*g%IX^;pb8J~lJ4;nwv#FgvKtNz#SEZhm zw6U;3M@>aFC_O|-tDux=U{tiNqL7ApOifH~Z*EOmXj4>Dyt1f7Dj^gR4U&z8O)(|H zxUFJ9FlA$d>C}RAV32ZI;pEYqig<-(O3%^2w6>>lOE-I3L2P+|lz?l%pL)^6wv(Wz z*381KjcuTOT3c{~e2taQu!-EjoAKSRskp(n#moKq)9CB(R;T}30000ObW%=J0QH3F z#044+#0}U#Dr}rqvf($ojbWJGHqfdWrh&c2jgXV-By&f6r;dfk#ZPF!Ne-I68CY8E9* zq9~D~*g+BmK@vpoz+f=*yLUi}l;R>OpO^Rh!$%T;84PCb_nvc4z4r>>Gy04^qtECw z`iwrK&*(Gyj6S2!=rj6^KBND?5pn*BizZrf{+YiEahdbaTrB;K(B*Oo1kOMCcOWeQ z%d>wQ`o9gjT>WR$&u*rMEj{`rfdzlrOHJ4(Hi52t=~{`tQHeGmTF=k&_r=byg_`dhdzCqQ5M^l0@j z?mz#V|NB$~4}KlYDe2!J@_!50&D&~=pDsV9>^uL9zX#O;daOqiKL7lM6LtQH3nzxL zMsKyg?DU@h#ovXz__3-unLYoD3nudX6BkaJ+u#d5KU1E6`tM3@-1%oNoGu-@AsgN# z?3VsNk@mCHCsUn&>VoN#q0{6Y+SaGK&OiP4CJjlSf9gW%QlZo8vhbD%AN+sJbVPCH z{4*Cym#X3Airc`#yy+AV>f*>o&W$?B{D<>s!HWJGzPaf99Em@|zm?>Ee)jUex*LAf zVxy8uUZR1!RJ*P!4d1FM+LJqo&(JJxq=33o0y)oGkx(SiD?k#T#$7DQ^)r2URE@4? z2_&icFaxZ3nxK*l`iQO~2o~W-?RpFz1pGeVFBu>hUc5JF6W?;weEbrjU!uS>T*Zjh zb#Jco>n6oUBO*y5gODo}_#koi=IC9J%Q-GX5E+4GF>*v1mUTgXK0yG1;3h%5i9ix! zFCuynzjK(x%Q@6VEFt!uWU_AV&X_WnDDsym@TObCckMhmH`2>p;v0fR0*_$wh~Fgs zfIcCnqi=Z3ek=r8Rsxv2=pOk@10w)Tiie=}d{p@Ix%(a#POKzm) zXf`Jc7fYAMb!6CXGZJ&i>qb#y2#kc_MZjc=oWCuCE0Mt2rX{ckh2?@38K1!ff`G4+ zGq{)JSY9L&EatgO5N>>s8b`nfTb8|`OF;L!B~!TfyDIrF6% zxJzxA@g}5sYTmdzm&%s3>d7F?z4CXjdo8|h1fZR_QlA@5H z3p`I?SwMn=ugc;px%o~+g7nxGdq@%;kPJl$z>z=*FW47(?*Y}NXu3<`x}0CGJ@wUj z9rvUHEj*m(PSOmH21F4Dae(Ab=iV`4RS##4 z+#`=6_0MHEcydAH>o%pOXAdr%E=A3mODn#8YQxua(CrhV3KOjuCV>`>9J;Z9`<2f< z0RtDmMrP}=2Spe(WbjC!MYYEQDKd@_$H8L{35)Exu6W*xGw=w0!@n1xctO+E!SU$X zgA1lhfNl`39{J6gCv#7(8x*xSCK38P!wCEl!$%(lVjj6f^#jDX80I09yAmPSP9mOV zkWCY>!n$3&AV;_-7*S!s9Q$DoJ6XNp*VJ{8v9kvkN|zw;wxXoD&ru8Yc+( zBjO*Cghd&ezSi7GxhT4Lt|O-3mj9r&Zvfbhlb0j*LfLwUOJOV!up<}EM=DKno`Xg9V;IM{aqQKQI68R8+w!1EjE>_@m<%S0i zEq_1f&1j-hB&z5hfjQNlrGTQc5-fT-bLP*aJ+q1*RTzP7=DVpo_|5aifkuAXkLDG0g5i*g&X0Fb2I$pAK*YQbNP<^ zHhAY+NR{#1wc#LasDr*1b<}norpcXDDEos)n!7M-5kefcVu_!_j?~{{KY(0NBXV6G zTCC|J7ir)w5;}_59{%9fxwvJ=FAk{Z2PI}SCB9L>V)sN*DNz3k*_Wk|?tADAIKiGy zbp_K~yMmQN*G@~g)EWB&yGMs_II{c%EC;3|@b&Pt8`3ag91Q(Tyyb)V1;fml3@q|k z599_v(gzH(3s#4CBvM^x@}i1x7Y7|1wx`SI44d7Ln4FZeB4Cjdxr->ob*Ay=Ix72b zjfGR7nF1(V_HVa@%ZR`=uMWiyG^qVT``}G4P*V_+ zhF^RYe2tYeN!6PLLADj{5m9~p-~S2h&%7XG6_1N{4$1D-P*h$zN_5`2amU0sM`0^cjTQSa|Xu7ew9?VlG0H#n zK|9iFuzj0h>U3jTS~!*NT`fTA&Z)LeFvPy)Spm3O2yWejGgraU#|-eGUobhIVB}n{`D^W|@ZL(+Upwk>xupAYAl_59WtG>T&{mIbs$Ar#;odT$2!)OQz4JNIsP=Sm0@O zMa6AT|6<;Gr)RQ=)w%Qx%nf+1@++`!Vi|0=mbhI=B5!}e9BD|F!Jfv{w7jMg3=w!A zR;3Dd0yNfHJcR3T)0K~@C-0{RD)w#6-Dfiols3%7?>E_x{HhcnaFb{L5s;I1oZX+N zT%sUlT@d-YeToY!)Gc@G_7dq4LDaEr{BmsxvslM-ZuXBRTIY}j1V_?l>|bok|VK=|h!Ag!GbXw>u}oLSvB&xz`x8feOgqsEPn8ubG3@mmKtTfEBg>~ zFpmbIZu@0AWEA#SPC;SJ91x1-%oK5@X0%zmGrDnIo-`Oz5+lKX*mvC@0{*IPGKJaT zKt_4>`bgCTl(MhfdC+w7CR;~-ysDlUW)&GRoX2@hjz%gqOCrY88T1Er#JQTz>>@6Z z?H3ANe|!3y+uxPv9`XDu;v30w98L0VHkXn6*(OKU79AIngU?_H_LUce5w$_O#FtH& zA_hr^sjPGM+PwvQw*BzYLQjgFD3J4abyxiGRe#IAyj8Q7#B~jS9pDHcpX41PK9D6{o+jgL z%o)7QE|sdKo#|}mn6H?&(_Nm>eMf`gY`2NDOs@vX;H|?ae%KEG^M~L8I@G%9om)#} z3@~<{W+eN{cIra3G-SWg$0K#I{Uy5AVH%}=j0|jHg%bZHZq_4?nE*wWPX9(7XbyDYZ>mwj2OMEW* z^ch>Cr9K$G@5rqubcM%kz?HWQ>x&(KfXKbaE@x~Fe330~QDxpK31zI~TY6{QON*-1 z>3CQn<3h}gL<|o(hRRT)KSCkiavX`hN}79d!N9@Faq2=e-338MhV6m)d{#>FoEB!X zb2UTM?RPzPQV%fFYyYwA*pk`SY#wS66_Pj%4ydz7rTb+QzWP0mk!5BH+gwrXwJWUj z@g9@*1R&^+a{8@M^2XIi9H0Ab&h*G7OMCUh8_EPXYnDl*)+EGnZumqX3H z4hupKg-9l>wa|loE;=KHtfL(4gK9`GZ(ODdXR~jSW;MJvLH-y}B%THwnH-V9Zn$bQ z@rQ%Ae=ZEZvID@!#w0~h$%dfi@}2yaY5141p@agVWz?^SYc@kNnJa1#*ng%Ov4TTU z!2RH>Q@>eMcJ9kwNZI*9xURfC{htS3m@{k&Hk>Vr&Q+5U^8a!$M2ZhX#g7Z9B1=?Y zvIO*+6JRtrwoPO$FP5FL#^U43{I%~jg)??xc+=G8{iI4!GYw6etaP)dDRi{jd%Q|} zeDyP*m)kP&e<;^iP1wk)lEOhY<==X0(5?fXrD*4#{4-vjCTOHrDcov?qHs=>5cnUF z&c>b{%0-$^8oB@jcL7|-^9bUO`FR9b>So000dn-G^qYa5;pozx8k-v+wZhJF2ekRAtx%WY%w}Z~e+yoetStt%|0v`0;m*H%flAS2AK(1=N?E3d?|!3YPdF4#L@)c`7G1Es`|vI0NN+Y&D6aN9hWksM)u5QJQN>kZ zYF088pJ=UTQc+0BvQzr))~O@{U)?kg3>{@zA*m?+7n(S+*Rz{I%I>*~%vF6lbTKP6 z@4VZ8i?ov)hp1APldk#^?KS(9S&FBAIc3OS(t{+Uw%_2Zj#4_Ex?o~p)T;^?NJFY{ zUfw~VeRZlvuXNbuga@iWyvDs_)w?Ha?4BOF>}@$5?A^M*JhsODgFi4V z83g4fiseaMj54eD-&Z;uzPvP|onkp0L=F^GVzTPWE7niI0ROoWa%8_qR z%p0~bdomV=6f4D9_cg(l5tGl9fs9jjL|V*SX4(d?Z@=DnbKYNWwCxzP`?qzdvrVC( zOxK_~(R}5RXxp$kag}fF5LCYT1xIcF>NU%DCp5Fhk+EcQ!&XIO)?ZR$vAndm{eOCd zPtR7ZJy;mhBYh-Oso!BG%6dk1dZ=p1pq(Z`bJepQ@g^=7be0T1A3UI}exc=0Tg~1B z2Kk3K|5|AvNwzao0O@^K^HC`&eSKLsxbxknuobt%Mbnki+e_uY8*@lFo{Z(ZH&VKY z?Xpj;Lwy=_cpm}~xAR?{&0S{?)X41_auNctuWUpUuQKyRWWux9= z{YSMODR?WfJbn@(&2B|D~h)CPmYP}CLU z8uH}wU+TTj8NBe8!P#4bxso_Q+vRc|4Mb%@xwu1+-fvE zy@q$Yg3nKS&2-RQ?BD)<6y_Z$k(*AAzh$r4KWR?hZH&U7mhGA_@A0jzzlBXwFA9_kSyb-FO@Dwi`|{4BRoXpfo;sfvkan`sED z_7laD7C)UbNyjTz|8JW|P+Wze8yq{{GnYKOSu&;@*U6Mk4T&*+-IPMD0~ytMlcT&f zqWjAS-`>rPRnwXOxGwOIyI}&S>UltRj7qe65tQ*4t&2oHB>m(%)F(qfr`%Ha^OtBQ ziu?(zO{`x?`Mql5K>uq#?&fF9)8?7LKsN88^5%+iiQ;>sH3JPg^owYU@YcKRdn-#* z4J?yYtKtD>eeB+eXm@d1tNZP>?h01@jOH1t_dKxYCy&2W{FpyK;S#cD-SMpoq8nc62biv|vp(~e5!eLV zaUDXnOuZb^vFY@fX=-W;dQ+z?ufbnv2-EHzcO7_q)xiX`;H8Rlv_uURw;&A#uv8ZU zXL=>LEib`1EdP3WtQPFYOjo?D_~V0Hw#E0g=jZ1`K7Pr#GWq#<`WftFAnID=(^)=w z;_xRy&nWMJU;pR)>Xx{Cu6$k$ZEZH-u~Pw=3fKZ81?7c3LK8ZClv>xLP~Xi|h!qoX zI;(e?Pfnp&3X5f0!`Uedq<^#FR;Lz*RWE-1>z-Ae3E6WKcWh7`I+W^$>4S;CfG;1k zx7>GX57a!NB0T`Nq5Hf;YIpTD1^4`H)C)h|a920u7F*&%e{osD<;t+wtwpD(#WP4- z=Msk>O`jz2=apMz_}bi5^rJ7uITiBVU7i6>tL=3aFf-ukYDh803d<4QlL>v@Pb3`f z|8SnKfXkk!OBY5p>EI2cl{%fq_FBUPd28|p&7n$A6~HgoWKMFu4l}EnI@W`AcANFNAB2_<0)V{x;R9= zRk|?R&7W9|`2^_am0RA%uf={B!Y=%$B9`%|W#M#UmbDt3E;pV5WrRtoeODNcFVREd zWM-TAvphdo878A+{av$pqt-Y2`Ff0kcc;?E^bwLT4U*=#dm1X6scNkid zP6?^-PR3=FNxXrCX7`~gpJ(98akFMDVlHxAr?Kqao+oUeI;*B{+>M9o;@T0#(orWG zT!QVart^9OvX3`#9}oT9a?7t?LszmCi=e~UcW4<;`x4r8n&01K>$V?~tPdbNYDjB{ zf``qkANLp33cOreMpGF*lG6ntm2V{}z2oc-LXsdVv+4ohsuI-T_m zhk}GyX@h_DR|bjO1h(#Ax?$Yr2rA5`l%_7U>6FYX-x4jD45zE#7+sT=?!KNoiFFNu znlwM{qKHGPjsmG-X0a=u{PmHd0OJwiY38k2UPXL5Zs2bZ+FdD8=N}uOCkwVbOX}K??D9WW`TXC&Q!O>3ff7bSIQmL5b1^(ZtYLm+QmP4cP)SmC;Je zkF0PYEt>^uUcR+q3PWG-%g7fcEy8%h%SY44LO-|MGG>oa;>E0V_J5IVSgDZ4EKEdw z`ZBp92$LCOAJJdaJCi?U`EZCFF4F`dh_qwfc58{>V+Ca3{St{Z6L*HgHTlU%S*rfv z$$oV+=i9#*95r%5-g_dDx?@yYZB<2jllfW&~bgI-?oe@@FKZ3V+@#vYFMp>mkg~6Ni-H8>iKQkvrD@n9qlN> z*dbza!+ta^w?CKVrm12|0XcXWV#OrZ1yaqx(pA&?CiJQct?r=SN01=rWI``1Kv4R^Fe^>>` z+Lx5KzX(t*o~X&;;h>wb(lp%5Ef8rnguLRLZBHoN$+9DQuA z>SLhO#2wnEr;g>2!_Ng4@;6<6ImqSKpmK{1v^M)|$-=_dn{;`|x`)suXKoa)oZv|q zdZYKyPV{*jl&8n1LEBlNK$;F1(79@)YdrawQ}dl(p*eC6H-fakW{u*#&cce>>*{?Lqm@3FLH z0?)X%;S5crC%SY~M z2B_JiTSp9@nmmI|KJ|sFqe!>EFEbbnPEulGt4?q&_H17p0@-K^>0%~%6ox92R!Kpn z!IO*(tr;!P#AjFSNPf;eQW~OL9D8eNdN`E$x~bgo`*ntO9{?$fZ`2t8rlL&PP)HXV zs$|59gQ4&@gU2{NLm_(sG{tN298qN1*xEQJE1RJfmz`IRe{`YkoIu%;^ z-c>sqcNIv3$_y8vYr0QmY=M=>udR|KN8C$G-(}0ED8&%MUOx5V((bkHJy)ybkoFe; z+!inwanrSDtUV3o(Y=TEZc%k=WUlatoGY7fCwL$Uz3L@ z^f+Y*w5%$c?73&Uwu586$NhbMDV3?cP6$r0Ax-93o>#J0F~-LaGx}u{a-b34H6zJR z=SKI)*OiGZv4pS<+%}l6>W|b;8sHh3(VISF*k3+NaHH~~4_49RcmfXt43#x=n6Fu! ztwAW2NE_?&7ufFo=v>uDLO-Y6qKSbGNxKV4mgMr4UWV61WgrhA@jw?5Hl%?5FCSXy)Pg7s`W5pipyF}B8 z>VHgI;r^v>Qyq8gUFUGzS1$D7ipHhBT?{yLBymXRSUI4O<{ngd;fwoe8u@DgyyS zz#wA?Wg$h;bnQ_&WW2W>r5<`>Bm^&S?`p z5GNVD@IpEviL$Jeq9|4nfTs&g!%0xf@;)V3_1DnPEw}vY

|q`pj@jAMS6g+Miho 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�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

+Wy426IK2gprY3i%0DogL2v9?$& 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%0A2)b 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⪻Z ziTuQv9ip3+EJ+=J|3&Nn%$R@HA3U2SzqY=9C5p;3k$mLy3yyG=&R=p1Fmu1(cPVhH zjlAss7caHDWuDl2PBZI^hsYl_E8nx+6Mq|GaW;YDF{C)|kU$$V8I6p|!N*UqSQJ!* zjYfQR`^x(te3-Kd6#@{0h&2do#j;?C$N+>IAhbk)l7Kt|b#-U=QIaQVk`QLo40 z^6JZXc6YB!ZbG7~Ya4+I({#GMww5Y2_{oRIC&vdDXaDkt@7%g}_1fm9G4}f(eLS2@ zGGj5%zyBxyAF9~e6oAIl{Kuc|KR7tOdFxh|Wt(doli}d_;OW)f8-%pJ-iyMxe=)jy z_vQY@@a*j5jnPZ5T-*9|d+qksjkQKNm<~}yPvzmmq0%rKkF9XI3<^O;Dn55a3kF6v zs5#FQwmWt~1lDvMnyJY%4WLLV6onKLIk4pJWZFJIZ8gFKbvT*sXg!YO?W~^tZnCt$VM%#>`r&D2`vfdpC-r zAN=444kezMou7G@WoeS+dA{JuwQ@yQM4@@9=0|khWFO1vsI>PDteUUqGN~e9K_lXf zH%JJ!$Z{7ET0sL$ONWGZwY$aPAg|fSORxaD6?4hnN6*nnc|ex;Bgj1W(jqY`|Evy# zz~VNqOl@&gUTW%3H8#ES+pt&^dwdd{p6X6lUAdxncI;rl=Vx{_B1Eo?0D|+rF6I;o z!!TOkzPh%3rMo1WL8r?MA1wzP|qHr=Lbqq_rN6 zMi&Co4UvP5HGJv(l7g8nExp@Kz5LLwuuau?P&qy&fOI)*?eL^e%DnwAk zSQ{wXXv6_gZt{-~k3T*-J{pf*Ca`gqWjRlcby+-o5;&)W6w8I(pNfb$9~xTlD*|3B zt_?CGh&X2)Kp;Ru3=ktCO%yBDByw_NtSBVXNG@ik^gTu|Mv}|mA`&=4E!FM<6vp17 zqqg@7vL!o1mI6v?FNqk2O za0MU+n0b+`)W{U8(OPqKbe5)#8#nSiN5p2cIh{^lTv|pC_Df!8R63yI(rqpul|%nh ze&uK0+oA*nE_CnSt(v@#y;PWk>MkqG7Zk;Q_EJ5#7gi%wKp(2DS{~Ap%Sk=;kfo_x-h-M-e z0DZ)P$QZ!Exn~|5oGLCJAwY!atnQKDBMHEaC==-cG#tRYAMLE7Eh@FHu1+JeXYGZ^ z8%jl20L*-PeDdet{qAHk8I49?dgF~&vk8dZZui#h+i@KK>9@aqcz9UamI1&RGaiqZ zMP;b8)5|d{O!{+4)BPy`?tCvDV2f{ytcwi5E?sW1p@0y`p$H*pKt<$iAp)3#08oIk zplj1u@DPQ|fp^mf0q_Ywr9SAD!E%Eq1w-SaqK$|K5Jthd?YHouMqGdytfslBC5x(` zm)`%r^pGwG1QzZuK9~9uT$ow}fRg?!3bwVsYW8^W6Cy37olpg?j}qy}>cZ zks{}1q5+NHAUSh_7$C+()m2bbU8x%v{;1rEu<8r{!HR5dZth&ae&_b>1|m%+kDojl zfA(3&7&Vzlo`-SV+}<|LW_<0MW^Qh5#M|4AYuDOqYd2nc>Fn%GDW#Nhx$j1!(T{)p z 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�G>jACH>VKdjcO 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


JQ7nq$ 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 z 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 literal 0 HcmV?d00001 diff --git a/MekHQ/data/images/misc/challenge_estimate_full.png b/MekHQ/data/images/misc/challenge_estimate_full.png index 53802ffd076cc881e6d406dbe9590014eeb5e0fc..c74f7ef090b9c8d144a3cdaad4fcb5d3ec7b46dd 100644 GIT binary patch literal 21511 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 Yzxz5ExzkhvM*si-07*qoM6N<$f*Z`L;{X5v diff --git a/MekHQ/data/images/misc/challenge_estimate_half.png b/MekHQ/data/images/misc/challenge_estimate_half.png index 49b49c6113778fad217b2c7a97a19602e5d18b66..0dc090125ad03dfd2fb6b9dbd7a31aa0e09d2318 100644 GIT binary patch literal 14313 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 Ef_a^M2LJ#7 diff --git a/MekHQ/data/images/stratcon/Stratcon_Cold_Facility.png b/MekHQ/data/images/stratcon/Stratcon_Cold_Facility.png new file mode 100644 index 0000000000000000000000000000000000000000..b869fea670da64615fe31734829c0adb29bcfcde GIT binary patch literal 20432 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 z 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

e(Pv z@a6qJgnMvx7q#fE$KKkTB=mnI-oVwTJd$~W(WG!#2%{p@RM(h;?O}&G>-N9Wbw6vH?S{<-HAU(y39r!Hq z(+V^vpW;Rx_gB z-CKf=_D~u^i7`yjcd9t@M88W?e*Un(Q0d5~T32#v+|z>FAv@L+DHs$~FNM%3(ofy- z2&EPd;aL0A{rG!S#i$V->F+#p)SB^u6$!4c*)%JWMFBt_)nU^mOO_jHt&@i7K+BKw za3L;hw}L)E?fSTr7DT$uc{TkgA@I~uFaKwUAT<9>z0HLkxdt`QW3m8`brdh_l3nDj z{?N$3aKwhCkZB59n@j$+ifCI+4|zw!rn1b1)4tsHDlmu>sQc16f~Y;l))gn{jk}E< zHjgd2V6pE&_ih5+`qH6D;Q1)dVH&Sw#1!rCbfbz*a<{~@0|H=adS^MEP$X!!n(~M$ z8HI89^X=EATbM>1`;1lM_5VROuKn>lS3?;(3NtFFBzK&~T@r<^B4ZZ&qn3{60pPwgjKI)Lo==@`w>XLUyLf4|_dY@emZr?h(+L@S#**D_=0;~5;Lh#B7 zhjWGIT%oUtFG#s$l#6)BCj=~EnQR0FjBlpOV0!b?5t|h9G2z9-2}2*Oi2}mL$Dr9_ zE^c}H+W5|OON?-t1AaF)tW?tpEG#uzd>_pgMB}JPS@IaQ;z~=3mAZz2lul~lVDzE@ z)iKk!6mrqSlxhB(t3>-iV1a?oUZD}oyFhyY`KBT80+ic%sErf$z|Ai!z}b4B{oxkD zFFR1}o$A7tTig+(`^ey4>oD9nw6v$a)3?rWH2Yim>d;+rLRhi&!<}qxX2^q5kwYz6 zv`KLoSvpr4TWxLnAxI{7=lrZNMmAL@_ZO9H`l)83Wz3N`N@%5PiyO@qJvD0I$r1Q2 zIVuaW1*o*{ZmiLVK?D4CpLOhlR}~t;!HY-`YV<-Ry*76(`g2OU%0Xr93PLQbp_j-2 z)y7vq9Sk`0TNzA`pPBs!TBB$Ko+@g} z6JRNhZI1HotYtjrW4q)cuWsB9&RuVulX+M!V{but% zG-G90d~1=!cAb6QKhpFZOzlp$)eawifczo_eqFvSGl+1pMxhiXYS^K{z)ZRF;dmZ2 zKjV;tmHxuEhqi+#yU$u6}q& z4IupQQ2H{M$-(Ec>qgNk|3OXJ+$k}5%p-SbA{waS@xHWh%YVSUIHA0--|^e5Q|gn8z1oS4 z4_uQxk2A=AtD}lqS3F;Dql}Vhgd>@8>M+IGBUtPMxzMl7Z$Y1ien@3%Dm6=e4x6r0 zmysh_d+-led`awk2UT-s&#Kkab6V#<1QO`t&ZGuyM}x<#f;ijEILlRgUs${1NqBvR z*O51PZILR*=TIQptSLW2?YLjEVfES8H{L?&y^U7FyBQ-pldu__$8!z?8PPlHgkVw= zlciz>>>iUfS-~rW$_ZOE%(~mBW1Ob+G~eaav~vcktdOp6ll7=fybhgy>o|_&bpVtA zeQ5mtQ3=fdMY|b8K@EF(U? zD?HUg5pXI`zEO^^EO-7jWnQp}xmmL40u5|5Qv4Wm{&pzo^$*fe!e(dd#x@Hvc7b z%CjHMw`z}Xvokymi|XP_GJGSix!OkAHWkZ~HM?Ad3Y2S|vR9R7$IopXG=lsDXhyjv z#?Af8k_q=$;}hw1x^fdcNu6wmm!y7$GaHbh9ON7zZrTbJGQi^SRwVlKP=V&N{yiM8 zz-ii$=oL)7aaUR|2MsSMvxMtDb*l&;Jtv6@3FWs6BbC_~!OnaL9YjzBEcq(p(}Rof%wDo@!=K&OFv zrFMOp4{+KbEb4Vmn^lQxJgqA4deA_KQNtxi+b7r^H5o_N>C0uN<&|Y8Y^F)Y*Kh`x z2$87Qt)1NCxhNZO2%=A`^9|jk+xz1XwFV6h(OXkdHX`Sx=heQg@i>W=9HUmnc-%v+ z9@eARfv9GqTCEWmdS-~9*KGfOTq$K)N*wxi(Fx2>eeuLoEl&LBk@KqJgx5EN#L${6 zX{e*P%8Q>lGUNB$_K+hD%hZxSq5{6eHtja%eYN0N)|&z70*&6+J7~5>0_mFy!E_$- z^r%y2|4`jF;Z%rSeBW3AHBV`mphdU(?J=(y}UB0FF}k zlE-~|Xv!Q3i_WuWvZ>p*TX$!TH_^5Efl|db*&<@STA~~3Wev= zZC4^9on~gN9x0*LM4wq%U-UEzXM7E1Xdh-;_sV`-uJC$JOeb_wN42(pj`+1B!>j9v z%GIU=>lbp~@(iBIT(e~bmh$F}V1T9kfPT7eznWezHi=R#_MU#O1)?JWp2l$r`|$HxBm&l4xg%< zWuM7t({u1&Mv!_@z%J>=C-S)=4A#VMTp1W1y8DmV=YPcdRbctGlj~VIWOF|RUFAA{ z9*Mclr$O-2?9Zn4K@vZ%=>%_$ycB^tnc4?zw$V0K>3&>T+Kkqke(up0%CSGRo55!~ zTNsLYXN%|iY>zFJN^%vc7(B!(BxY*+XQ)A)KG0bAEK>krlyBc*;Hj`^v%@Bk_trms zzZip=*V~;rU@D?7N!1~aD!VHpLms29Dp3eH4&!kCW@i7(3%!eLyM{h)P3L$z?j&F{ z110IP)tHW>)`mKaj#}69Js4DWs%#Upt?4>j-b4&E>ov~ia2-#?yL3;NEr7_YTR6H7 zXT87ipdnsHF{a|-cU>(6FY(DH9{6=8=S1X7&o1MXk8%d3IF$YTX20$XCzv~~{hF8f zIF<4!pf7T6R@^a(5v7AKK;jt`rLZ%XiQAoB)+&7%%9m^;y21J_pnWrss5M~$Q$GLV5Op^c6P;|CN(rlM< zndRv`msVcNk*0fU5^T5x3^C8Tx`ylIY12Y zJ11<2-eES|j$H5gk%H{-x5025JfCw@)i(l6WO(a4ZW=!vqxeGYpBkh;ONuw18O-QI zq86~Kfim{6qJ&`T4F{FUmx<>0>GpwZ1!FOvQkD*nsHSf_o#@4lbP$_M83f$9c(V32 z`i#Z+BUZ2o>*b1?^{S~@RaiSJozlsQCL*O-Ts1rq)mp85q@c3H6D^tP-!#%V?)b+G z<3<(UK8|mUUF>w=oiC|j%Y&Q2l^&*ogb0&Y3|J!TeF77Ei73ACy&N~HUZa8<7!HfA zhp>Ry*i|p>XV|66aOsrDdoLuTY#iPjH@j6c>cmW$U|`UaG24R#5Y?^e!XfPjqL^3f zp7JTlFu>JClOibuoHxM_jjN9MZdH zvrLEGi-+Z9r~v`O#zYQ{WE`Ue|>!zzFFM8foLQAGiy%$2^(beFHm1Pf#v8B^56Y?O#Y*U zXlN`AMmP;6p1|I{%+Xg;T;2s@8ZJLXWS zPI{(ouY-QGO8Dm=Zp((CUK%+=+N*a%110Qh2mwU{h6?<=TU;6zJEJ9gqmndL`B>Eh ze$cvW>L4UkVyG0ty<1U5zM2FJ@t*(R-od((@o|w7tPqavyC4+PQd&U;3*UcE+`r() z#RAhT#b*%FW&FG)&k8A(aqN5V-?}K~&42MkyePz2>gapGoe4u>WM0mk*L7V{$a{}u zUj+>}VlXDgH<1u(33U!=pwak^`%gw*X@R0>8`_avTOcq0xX6(!NNM1E38fK{A1M

yzi@*kQ?tCAF&#?t{@&wpw<`c`2%)9X($cv4dP@as+YHxHH*)m z3O)!{y`X@4s?e1793<>N(jdg4>8Q_Lm{jMAki4ZVXQ~T_`Rj!zVjauVyConHi3Ae# zoXc|;9wY$#QQ8eWES)NF45lCQzN5|6T_hSsp~IYLi;Wl+`EFze$@+FkwU5AKhA;8P zzBfKY%1VN7?;OH?-@pG&7Ib6ZzI=MXR$u4?*h-;$4;l4X{RMOOgYd8FR}R94$b34# z%?sbAvXJ}Z5mpM$d7LaUQXs_tgbd127J(91t)hEIKE(x(6q}7rf9ub#9fxVnKU9EL zEL}jW?TqdqRXx$hvagvrL5@ygWHhD6bQPK!^cq4?Fjv;2*9BWX!N{~>#)97)9?@z< z9ZQrTd%U9pf>Yn^LZDz~R2<67<&Ar+jw?b7o~G}YpdXpsAf#j8H(m(4`h!l29(;A?Juf#W~+wm3A}yGbdS4QA3W?Rh_HY4Y_0cI(X1W z$r$+Nc4#KILJlE0j4~*visg|eQ~{3mGmS+evTu^OYMui_3+(4p3)n9KYbw>K3i;x= zt_UqDO)I&Sx9SR#1e1=IlAw4OMA<3qbV&+13aX%%Hdg4ma>dg8=Pi5D_$r?1p4>TK;OX|flHaX&GGq_H#7`01Z7cJu}U z(Hy*CYEwsOWQjcGICfSLwed^TK(pxn<{c~%=OAdgX@NUL-XgRpYS2n^v9}ygkQB+n z4>#tsmj0J9*mV(B7X{m?Bxr_t8Cj&7H@*gW*+2ipgrP0YSjjXXMvIz6So!&knM{}0 z?089Jvi{n}_T<2mi6|1!d-xKE@5JTj+Y;jQeeKGSamo%u-#ORKqO0sh`F}e*MHLY$ zQ*SzZ&2c)o4`RIidIwoXLGW#WM~nkwqsk!n)6r85Mt#)=xkKb95%H#;C~D03pH%(| zm`r{Rem74?TuB#+rYU~c?GlS+UtOq{auG*6l0IyfAj|P0a>QSMIT(L0@?=c1i6rub zODJG6_%a0vD?fAIG?0zeB(64^RK#Y~G>4G!MhW*Efaeed*MUeFlq6gshvYRyZN(aF z<|4s3EoWN{HACSUjC?N0X?DcC03)W(OVJ0+?yXNww*` z9)r%C7pyrBd<89~IE&h|g66RbIx zjY#I>uy#7~A2v(Bo#^jbf_#{0Nj`9)aocn*)ro|%|L-;s6iYEo#7Gy@#B*u+&dRZB z5T3SA4H}TPp$*C*IS?W~oF--)b#Cxz<0+A)B^w6DDuJ#@-&=SiT^Z;?JRD{!sSVk? zZHHoMy$Sa8_{&d@ko$V>9OV%FOq%M$?2+6$&bvHihQkGdtt~Q6S$rrPTzKSBe4R}k7cUk*FKT_0Q^WsG|k526rO_07&6883e zTi`T#W`nftZ&sSF6HxGL%@$J$k7Vv1p?x#Ur>O!)B(kd4_}PZv1MYyd*5vOpTBqfX z9$3o@4Dbroo_l2k#51iWKxJcP)-SEQE}b0x$Icn4#!P4NV zpz{&-Ki#rd9hx-gq_pd09%juMOperzulc3`ba&S?iu^tPqA){}FSksq$YRc>%o9F7 z^4%4w?bO!|HC;Isy_XcV&1arVXz6gjm^aJ{ZG7P!*A-ZIEhDd*BU^{Wh5(qFK*deU z85F^hqe7`XjOIA9(wc6_FUldksF3wGkkriWd~jwC&YQi(Xs{SI&AP!bWBaI+A%|f? zck2c0S#)0*(!>Fsef&kj(gng|y9dOH3Knj1gPrCowhd15^-^qXKxZa2l^(8&^rrV) zo%oGvOe(Y1IH%C(*3aow*~0gz$Z5tY;BpIcll;6Cq*44}5wM?siA#A@gpNlV#PRxt zeZh_Qiw`Vh`svo4uv5&edq`VggRi+IRxD1*yEHZFjlGWvsLXhx4CYyKL|@K_UjFRs z14;MJ+W1ptxZP9i7+(b+j&w}|t&$(+ltn21uq`I;VTGRPB2i&iF5}8&geKB+Xw+Q<@l@Cps?Vba`Ae=}b?)6jONt^mbH>@B+y=I55r znI*4a%PKQ&t0<`X^K{h&Lq#rFO)S4PPzJ}BgqHs3mx<-6A@sAUnsov_sN~yX$An5F zC6H&mrk>!S7SHeANh7phS_x=jEy_g=pJ@!QTRNlGg2)SS*EvAQpO{UnV#r>+743r3 zUE1d56@XpA4zX8@upQCcaeJiEAjpQ@a(YZ(u3Q;wWB79)E?lWF0*!5go$-aEM1S@l zw*dahUzMuv+oSc`JYGZu>542@91*0&3Z&G7v$5-TGKM!s0wEE|?D4e_EfdPcm9MPc z2qGGZm-ru2&}Ojr?8b%1;IP@OI*(veVzlG!z5e0g#i_FwI>;7hEixmVx@hR7)|IM# zEkI$1a0dfnwrw}0N}Ipkx#{oNtm!DKQ-4ysX9Y=MuIkmf^kAlgsEZw7YT)ka*(2xk zib)HuJ8GC5(8Thgi1S5JHaR1sKIC=#_5Sev{;rZV8Lsob+&0er60BwZt{2ZxkI5v) z>ExN>aJ}mE?Mr5`5N)7jxLyjiedfDgC%3-UBsV(xksX! z1b_ZhXjhPn#i}yCw!fh`v+P1UsyI0Qmm6nxt#e#itD&Tz&pG?GS?p^s#(}#DOTW(i zDC>HPThHi$Vz(|>!i90zh{->9#?SuB{bY8~hv#y!vym$;hfsfjsZHPuCR6;cSU4!$8rG9`m+#&kUjn!*jxmV5*PJgd~XJ4z#^!y=J2LWY06u;pS|D+A( z0Fk?RTGQ4Un~tqtZ&jeN04deDQ(!ID>sAY${FA5Rt6bTs-ge|YNQ~=gtSA~PQbPMh z(XH3B!1#v;YcR{e-|#0`lbn(U{hEfj z!9_l2_7lD9V-_C2MuIQXt^&u0edjV>e=_2(%=6{hQ~Dw+=PUzE%ZhYsiNL#VaA859 zuq8v`FP;dUpb3uS5i-!IbH^&83llaTqJqKVeoRzdLV+HWESO?7$; zu5IS%)UahZ#&$FgT%mk@6Gh>bz_&{uoLYRNUVGF+5qmu!aL43pBmJ#~c@?(;A;ctN z(_moIWphQTSFC6i7A+^id{bj_c1<=2fN#;_Y7>>f(kbTMTK&gD;syTt>HImZx>8@{ z2LOCk z!1bdB8vw5^exg-wZk1w{*DxVQ!McoWP||MK{16-7$%f0>hY(ZClF$;_<0 zlH}hq)JCP7vH#p$(L+d1kpgf@^hThd-ZY%exibFnF$b2I7^X?sbi*@wx~uT%k|qzF zdz3j34(S;(c|hw0z{(TQ&_7dncg9|3mNR#I!K${0QK&nJughg8R^Wjw%8puR9bY$e zX=RQvb~o(z9vd5dKeKQ{T1eBv?K46-ze!=1+7&pNKNGP{QzWO)l@x8`IAzQ`Eh)DPcJ9{=h{l9z(oI8#63YsO2 zW&Ho6NU{ITi@yXPYbM&^EJ>z}MfW&NMp*CN^^6V)w=_#tq-r|mE#ZuS$lUp%kcb5xTjz#E$& zE1hTf5c1RgA8E$a%sbkDO|op>dzEW)R*4E*H5DEW?saS0}Y5U88v; z-*TU`y?&LB)=VXDiQ3!RAN-(dGUJleM`tikFEkgFW4oin-zEdU53C_e#U}Cz6nOH8 zmC|w2MCru82-b9R4z2t=!V)_KpVdlbI3gm=x>`BS-QiLSM_yl~VaQaexjrRrTXv`i z#fubLLLkymvVPhJLV~9F8R-qKX@59Y4H0U5nJ(1%?7IgY zl>wvZxrR6w+3*_Qv3Jqy*29BbzgmykN1DE>)s^}@_;jTOMx*Y{#8^l76jlE#YW@f% z@#nYSbDPW(!-W;>TR{fN22o$n$efKOa*rsvSb%H6 z1!B7*M=`w8pp6~h^{MW*OA;XOUz{hq7Rg(9DrC;iE}k-(6Zn9Rl+(l?|wx{TOR!Zv;Kk0Gwy5A;xn(pABhf_~2%NBUruqSx%XlS3Nd zw}4$|rq|f*uuQcYqM$}{-`@wS!R<`u7P6s84__1sDKF-(3iop>%hQ~fnvgq~ue`T~ zli=P7#eAqSm8yV}c!wy_HqwcFz*erG+;`EGMg=+Gk;dXtztz|0FoHb!>|b6I(LMrU z^ZAssO7Uq-wju;2k8;S~2SL#!jIj*b^Wt&|Lf*U+jfT6-Pm7O1mA*7`yoL%}-~-sW zh&izq2XYR*8hWHXlmdRR5t3lTyq20LIaS7}C(D^{K~5kRHHMXAmXOBj0)(Z(bXVUv z>Zr))R4t6W@%j+C-_-S}&XRxL5cV8@eGi#YXTbR#i0!i#;n5!{nK1JpO%xw)FksbN zH3}pR&-zyoXF-%byI?v@_GgyyWCl-`gq%2s=ey+W@C22)1R`z8sqiY%s8+!rVyZw= zH1tnLIy!)~7w;F|a5&9O`m1H}KF3sj-67Ig@wm$>F#RG%7gOZoIjJ-wowg+NGc(k{ zYc7TWL1mQB@8%k9H-wWJ?>ag5wLvsKcFB_$l8mrv_K=FK$zoylv8G815~)!^w?8i} zs(nqa!vOHOdQH+rkf*ifijixS1AOYgoAD8seiMQatJ-p)5SgG|BF*{kF%t2n>BG|^ z>b7Q(PPaX%U|L+~%RA&HykaM%FVt!azF__;F&`O2W-$W>$Yssl4N>Hvk@~{eIKPi4 zMMP7j&%>i-DGiaab*HaySKZOB#Tn5*w~40t5T7ef&3#Ok=WB9scN(wk{J{M@7@y)hWidnZ$(Y)OJexbDxDlZ&Z2`<=g)K8?HQP zVRni6pXgfoROLhB2D8e*&8rFoKBHAhgX#8kBny3KKM+hXx0!Jz&GoZJ@@F#H#iZFU zUw6tynB#QD(<;n86T?Tj%V()Odq!A=8dGdgP8p#zb?L8NL(BC+z&Yb}U)DMUge6j{ z8}cL5=*CYSF3p5!nPJ=<&+}}(!>^B!9z@Jxb0(Z;PE}nz-9S%Bo|AmcM{a@gBM>h+ z!I>YQ+F}Yd1z#;d36e4wkr$E}XTb2R??2;f|8R!Ne^iDe@Q1?7h3(p|Wn+w~C%7VL ze!zZ-pcdj^C-?XYd0sYar<$9{*47|(c8XO3hAGvPGn>s9qsaxK(kQl zdihc_f)D1mT&?k#$LCxO6+tEIKjE%{@o(wces(HR1+!qoxDS4R@w9K zMzr_*29e8Nh0UPqNI9YhQ3-~e7ojZ^3KP?yG!M38OkcBfipv9&<{v~1Q;Em9c$ur3 zf{@AgiQNuOo3Z|(qTx{HyM``Oj>wS37h-XLRMiVBSq!kr4?|vU6f%&NsaD=Vd`0TX za3fiqzXsQtu&G2?l&i$?II+e9E?py>OE+B$MjC3011xjK#x|M_t7Ew%yF}Gw!8YWG zjP*oK&UP=>4%3>6p1jV7HIGjrUN3Amd!TN>Y1H`R2T|}+793}zEiP-MVmm_XAE^x# z2vF*)-geCnr(S;QHajFoXxO|hfX`9@U zf8L@JPHBX3LOpu?KrX&zlq-wYSc?nF=HkaQ^%b=xuwH=cnQ5iQ_DvE(=#`4E?$p}z zTNUUrCD;Wf@DQ7ZJ&ZW#n)-a-@UVD$jeBOmjv>PxMW^#$VL?w*A(V#A_?8kh<5HyU zA@P&x=Z=7Bi0)Dp&8n>}blT>+pG~II^9q@S%L$yy zDz6c&ai!(GFHmb{gqq0Am)m~NChc@$BZV=2jhui1rejxbgpU=}gqt@ZFhA~6SKt~st$yf8X#_K8_R+_ zxLjzQwXHUVvt;b(tGe4)0E6>Kxk76dNBU+5s^MrDOSS{uzWKCM%h>)}DcJ))-@iwI znPsy#31GwOgUMgQ62QgPm^GY8CBDqXhNe!@5q!d9pPCJFhpO!tOW{+M(K$&3FS)x2 zAfZ*D&sqGCf86?UoeQNHuTb}j6lQlxxk7nXgFWjdgOx`)Uu3P zr`Dl6`dte{caBYBt6dTGba19|PU0*de5Gqg#diw@ne&kmi}g6n3$L#%i(q)Br7OQ` z18S0pOo$dJ8e6Vqr5rGks;I4LOq|7Q(b!=9Q|VVwvK_Z6CYShWMQi~tYLoucsHR8` z>ON3DTWVu_Cvumx#%Dsq^Jnbl&sBp%G`J6Ws&+4tvRNmlgDWKz@ggipBRCtlyY0N8*lgkW;+dNQ}rd#Zfp5U6Kf+^d1z*Pz@Q$}{PLf- z?xykssFxu=9O7p5`K`8V65=zhc{%V1eeonX2?}<;cM~_w5#u#HjIb z)|pAzfU`L-pXpEHq_PwjUhdwIf-#V5ToW-iz|f&xVPjt^Kd-M@Cl=WTf9+0#-Ez^% z{!bJr*|%tHq2YzQ!{`0+UXZ~CYnPg54Y|Ogo~;FgvpxT9yDm-vl;!|$wZv{GW{Q?W zwJn|xo=q^Y(pdH@oD7u5TA39!qROsO1kdR9EtML*M;~mzrI~L)nIaKm8glQ0Uw}go zY0&VKE(tK6~uO{0ZERk(L5 z1@F?CvUg+T>?PwtfB5>AL{Z)IKE7#DdpYkwU7x>JhvoonR$C69>7!u))l+fUK3AE; zZnx{Sh2AY|ELpv0d(~ZDFfgdio&)?#)VSt;qZOaAe&zeZC4NL+7BE+XBAI;iBZD49 z=208}K7^5+Mt^eFOeQQCJT^pIE;iz6EkV)nX+gN!_b zVtu=UUW^tT!KNy7s+LNAi=q5Lo9-{DLBE{HBGSE~LmW@DVHDm&kKC=qWke52_y)rF zWDv2-*2$ov-@z2vA%vfpD|#@iO!v4)X{-uF@XIYOGk%Ez$51MTJZ{|_s@OfN**VJ@ zU+3#S`297uYX0cu`{LYJw0b7>hfTZEd$7sstGAU_cTJ&z5d@qpC` zP?#O9hKPfeCMkg;5nr_sI8dVs-4`{R^v95&v#B=L1^8!pP0YB%`~|xU+|(7EQMxr` z$_^Ad!nm;VC+xMBGqB`M3OQR~-0FmDF;n6A?OJV|(alA-+A&W{Qw)kBOHdMpH!z>akKnDM7njzf54B7NV>pl}tUfeTl4 z`tqEbm*$#2&d7&0a>hq{?X{O`1oq@FKGsC`Y^0Tv8c66|+u|oh80&yd;(i z6~Qax*ph$sXga_@JD}O6R5v7krl|g`=tUsi58{$XcIm57a_SJ?wOf6da3up^U*upN zh41j2MbD-U7ifuSvLxU~R0t476{=L+u(pKz>;qJ&C4w4Ld&Bwkec(2M6dRh-@}4Ff zd%&vQuWiQ!3EXgUb`+2-ael;-$+r2fo=s!L_iB8-8pGxoZS}8gD&xvaaRuY8Oug~` z=OmMQuG8R91>@^#r z3HsK(>yFmA71`;!$nF$xFG#87Z)TCd#%z?S}9TgvO4I^6F+HzE2; zWln2RuL%6)SH258+j4E0-LgH(@X;OhwH(@ESI(1VX#>;c|_)0oo=u1Ywj8jY(W zy2j`v`Mb{hI%NhxLyvN;G5-@`K}Hx*@p?F;9uiqe!;?Qr8faLq)RwX8E4tIc56T-6 zo|6o5c+R-6Y&inUE2*`&nMbJ zItXpAWORVnJr}D;8+W3m%W)fi+E(sDW>7m{VGBKM)KU8xajyZDudWZ)qK}N)CYA#l z3BU1^M+i7&*8S2O*oJ>R%qioBx^4Ad{eD!LrIo*O<zA+kvw0!svW2L7bJ&q5rKb z+u)rSW(o2eun?sxOYUvc-OupWbFMxE-+N;9{=382>vbVUMdy#ge$C`*uIyPdiKDbw z09*E$FuS;w8B>B%T2sv;LNuN()mgAemYdt3jj_2Xy;$v`^1`59n6XvC(FyT4D#0R| z>{CXNva|zzX~e*;Am8Nb!9XX)+pp0KylgvXf&~;mIeMJU*fbPb-kcS?j@b0)6>`uB zL-sX5_mJ@>MY8KQXVH%36|H)q(CurjOmkz1qL?0PJ^TGZ$wBGSjL%$R0IIN6!%?Y8 zrf}YB#HRBjV))6V>w z$`cgiSn*THLZ~Gbw;|^X_D8NsvFQ_?H8vbrVKI?#0Nv$sJz|}S$#&M01)|k>HOiw^ zn1=^(4^WvIdVc=?Em)ml?h}U~G%Im7DcJ}a5#dJAr@L(CMh7E*ugGF$GnA8p__lCMGk%3`na?PG^h8Xkl^(F`a3b@3Z9Dz!>g-V;)zubH|EOMFtM z8MtmwvByC~R_dOEpoVQ6gy`iC9z=?;n>_1Wsunm*MUbD3?KA~^+iWW>>jc!O!9p@! zNuwLusHE!or`Z)ZCtjo|3rNi7Od^WHzBLu@7KeNv*0-8jN`?h(P+o*+%$;}*pMi4Q z92AvtZ+khpX=1ee%DsGA78zy8w}UI6e&e0;48|mmJdkpcRqXqj0eV5$Q{s$(h>!^iq*5V+*>Z#2aH5-$XEcXY|Wu#@2O;@7+n ztho}KQC3@V8r=qSI*e=Hy$+UR_=-XrdRu49*eJUFN3uqcm7I`o@XwtM9PE@W|LOSS- zrZko-XvDNCY&1eBHcnRdw7qR;GPsPJ`zT13#P&4!9cW?jfHdF^G$Di#f11?}G#7|i zXnB`(zBoN{-cPw>x0kj9&GkhocMAm0bKQ$Z6NMp?*?ULg{7=FIk0NRAK8o~{IWhWd z`0*Y-pGANSEW?yL033A@GcnOD`5jTN-PZeqx70TXt?IeIJ8$K5UiHaECvR$U4@!D1 z`@es<7Ar9z^Rs*Q24lNE7lnGi?;k<4yW610M^MlA{$lIDFYni^+wYIfZCt~v9U;Fq z(^HDa_XiTx&xf~9R9<8ODxl@3qk^f9va4evx4${3HQbGm91S6hcjzL@T!?f0Vz!Rh zWhWt(LW*jActF1QGAR^~JBg;NLN;MUQJ#+LFDD(2lg1>{a{@Bj1b7WemN?MT5#eXl zpddJpq_Rn9Dc@RVwM)}v#&GU+lij>MV&bB+oi129k_qwa5KmeaD^YkuD-adw&aECnh^SNC9Psy z4UZ^PF+;^wI4Cq&wP7*gn)IH?%!GiZ@vGMDCZdB!?PMfefRsExf|%1-|@fDDi<-1iS#Bb>b0ZHjTie zXeJy45}AZ4=J2fJDb2q?Z;a%j$%3$_axAlZlqa#6uchr>%b$jgrNBzZKVu*A&D|*i zTZi3$sh{vCp)JYJ#vCsx(_}ot4L-Wv>pT*e~6BYE%4lVt73&MMWxa4MF!CW{NLE8+*N4?VpWFn zr?;`Q$`?__E2hRa15s&2#AkeQH~_Jw$ZQx>g$KNOv1f0g@x&6p>_70*U{jAO^;Q`!)=z$8rt}2Lc+jHTi1>ls~;KQPwd(Fpd=%jo&YxxRquQsTg10CQCTlm** zX63$2e`tmcIn-#kbcXB z{iAyK*_==BfrI0IDMRaTY8rBGyxq=&X&+CV!&i~qYLqh?x-&M-RLQ{Xc@NhnXE{=d zNXz;fXKROr-k20#^Z>N5x&ZKuUIa!9@)znti4-Ke&g>Pwc~G=r9?$y87B|UymroHp zv)jxz=N_A%@}Y68pf8Qru(lj$rj<{jn=`8f5>b<*q>p`;4O;;Yc}KR5&k-I*X?!qg za}HxxXhT`wpij=Yi$$r2| zIZlXyEjon^&wHD&3$eMT(qHEXW%pY79|B}fY{eGB0-9<2qOJCJ4sLB+ z<*h`c4sXbh;p#Y^%)gx*Qbjfz&LnCOag1Kv!h|_mico}*>o@byl^QLwedFP7Sq0-t zguw+TDJ;dQM4xQJ+uSGC)VKO3TRVPf@y|;BO{?~dj^IKbBc_FMOpO&5t_p|@IjOSE zJaifm;~+E4u?)#>6Lm@_g7^LD?H1^u^DEX~R@BbbJOl%H+6FZhaGoiznIy$>is(ZQ zXe@-xB2p!ItL|Jmx~B>QGGGfOEBFL z-xCBG855!P^|7QZ3GlG3_gNOd4I5ySi@qYtuoQ1a(sBoe;wNeB$Xu zMy*)<2c##7lx~-oIhO!4U&={tDOq3`JQJmZyqussJ7FrbrJwipRK_*|w^Km|b2sh3 z9PZs@$wP_A?o)XnfWd@7-UApv9bS3P5R?On|t+gZjINKS3$(-;HVj)C@kKyY| zMQ1Mr*41QRr3uD4Z}Vk)a5%_sJJ{*+`IQmn6$WyV#opEN-wo%b)|Ya~UZ!ur(Sdkp zCU9Hg{%IC8s_k%lVZJ28w~UxdalR2L?g)`R*me@^KRaFx8c%=J@4YYhJv0W5f}eeEkyux6u;r~oaT0fjJ4hgN)gt$griZUgYw^`neQGr~xc z0CX2esjz8uxtSaJo`()&!HsLx8IT7dJU^vXTsP;cs`n~%rr8(g4`Xm50POnyzbG5E zRF$zM$va-{fwXvD{99lFEu*;gAgh>wuSoKj( zDNUUkLimS0?Ubp&?Z>?0_M_nl#j*0PeU|RKX!Eyi!D_94cU`|1Z*#JU$3f=m=J^^!p3c;0f2{6YhN@OT6wN5I!_Xp_9bNpe}o#KSgt z>yFD&`gy+pi>q@E?j!#9b(6-n-PpF#ps}4awyllT82!d+Y}>YNqp{7~bIzULopb-& z+1c5doyE+4-mlm5+;+E&SLXjoFS`QR)?^kR-MLRe*c|k>9vW6`)Q;qA{SA69h*Gw) zzW;QX^y;qth->wXNWUA#67T)L(T{qc%MK~i=_yypI<}b=S=B+8Q{XWTGx>zV3C%ID zR8mm|C)6v^P-#cANW1X#R0dr#u2OFp`4&ioN5|;TJFfD!6;eZQbf?g*c)$xi$Ag<3`j$9Bllfg_=t4T$+SxE#R z!npmeJTA0;9<^#gT=EzCX>J3!;yhO^hA1wI@~l8cy?L-hhN!YQrz6Dhr(#|YYY7@x zeYs|o&NmDy*uhGMPue+Ym#XsMBrly;5Cq&ENgCE!RJ9K}l*XNMsN48ZJU8NT5n{EL z7Ozu%-&>uzr2`V}w&eH@8s>-V3a7Iel}uy78X4}=@h|emD--^;g37%dcSeD6)H(UpsT%d^WKf)z z15CM~YRfF1MQMjRg6@21je((U!|;62EVCxBq2cQFu%tWrHyDGT4IXx6aNQq09$x*s zHf>a_AgV%{c1Jc^c#(Oa-C!TGI*c=V`6pAlWp26c0hW?^d@Exxp4FqOHm1EXI3ia+ zkgr8jW<(wRdv%;|^Q7rq=u(1nje~-kYm0hdSzsGmv-j`@D@nK9751X7S_Zt>Wr?us z!|Y!=Zec>ODDrmRhQv(yC!O8ag%D;2+%K7zX?R_k#`RwOOE`PwcXx5+u**j4A*Nqf=`}1Y*K}hPA18q6s7Zb!IB0KtbRYIY&|L#g3!a(8Y zy+-|a+7`oygC9&gvozJcRQ29F9l_oSsxBHjU$;I=XWh*438R`t53H-m3kV2b6)hK9 zUSyQ18P0ktJq*weeUb8-Zlkz!;EB$y0=ZDP(3Nj-i+I zQ)@y=85ks^oFnVWa zSL=(@=FpuV8Y|H+^0r}{x!hBN4FwZ*>>Iw5+qt4{qt)N~Gau_NH?Y#5YyNnt8QdHNrw4$7^j>eoe1RfaA-X zFijZKUvnNK9nJPvww{X}V%{S^O^H9X@bs@%u?ZGn|0*$-!tV~3x%rmkeUD+##>Wn; zL@0m3KB1fazh?dSUma*@`hQqLB%q&XkpHO`b)7*%FM-~Y;HUu>HqQUuDw3|kA42Wy zGUzczBdNI1_5y?Ws^Fy{c(5(*@616kCWXQmvG?WqxrEYum}TU|G_;SR)^zv=KYl|+ zZ%o6wEjrnt%l$@2s`~jMtOyWD?Ow}{`AjBG>yP7{+xmPuIJ^rJ^8LI!{ITxyd0F%Z zv>d$+YCWW9cY43v-7U;;wWWMcY#|c$n!R{`TqF_`b-r#46>$;YVB>VXo8Ml~CGxe0 zZTP(H(0o2$y)mUXEqZ+p9PVvp5#UV`eckVB^WBAMR9J34g4J6U+Wvd~l~qvm3-8DC z>ClZn3L>c{{=9%z(;7OC18mT=mG164Fm@49E@#8A^VILRe7?woZc7H={wW)|M%BZO zpF8j_nQ{9XD_s9^MKxgB|O4R#Uz?vf#MeSJu|Ak z2pZTHni|z<0~HqJanN`*tvOpBpToti|6BAa#LQ?24LVcR+j)K`5!2^JAefJ>zdj%c zALlk_DYf<{ph#CHkAVhN4M0%=0nI^VqJ6pM+@X6*;i1xtM(^Xs4xL>H9PD+Yj)p$c~C7Xh?!1d6Tlv5vMZI(y$P@4 z@W{<& zzflz_J%L}7SJ;=L$5LsA*H2U+ucjd+wpROOgEs{~#`^xvvrj*ED%XrfKDI$AVcCrB zJ0uYSeP}WCCC>m<2WPv4t0l%FSz#aw9XO}hl799WkJeY5_R#}p`awGd`;WURdq1Do zhl$%Jq0hhRUBKH#myo?5aC=roGJYp)vPN3xl8kFV*T7S^-stoQskfF4Q|C6Rj%m+>OaJKa4&NwMX$A zUjJggxUwBMm=qyA;Qm`$hf7#=xpK80IgTJH1 zZTOqiZ2vVNf>2%H_dtK!c3iKpW?kFMPl>v!Qj;*g@v?wyBWldYJ5Kv9u(QenLmMNB zrsUN0#}Kn!azz|Y9{RGuUpZ{4$)PIuyLde5QK=j8tuS$i1#r-6q@>WM1a0n5{3RDo z4vF-qwR5;rS75Z;KKKn12|-qO7^kMmjBu0^N0_iBH{Tt*Qukg~yL3Q2vcIeluUw}3 zSJws5HbM8`AHE?((oHsIeVaX65`Ce@pnRBHr9kN`VBgz~kz=RZFKqk3lwbLVh9l$X zpu+jQI5FJ*22hF(8vi4i2kUkuD=QXwbT%6_F5y_bj4m%aFS6sK=2{G)`mKr$9A#q@ zMiHO&%k|6wW`ca_Kpd_qWT?)*uJC-rkK(;RTq7lcc3Q#un}V5Bk$7#4Kdt`glyYL* z6*l{^KLKFa$nuNmS$u?1o2$ucwn@cb2<@H;G*GE)G+-8&uA71pnTAJ8HduYD{LpMc6t`%3@5|0tm|!7>3TjivskV5kYRw?T z3a=0``L>#+_sN14XHUdLAKKs#wfJ(S#tQ_9n&IQg46sqoKO)Y0>)2P|mEx|F_vP@F z1)(yn=)i#U`jVH;0YWqOW!5__CO>4)MH6Fma|%Ryt%aCCdj}rT4K7Z28LODNzK~FI z0_y!mIpd9$p{XEvu{V~|wv$Zp+Av3LTZfa2jjr}g$`~FI&9T?P%kiJHoF@#I;9~Sf z;=iQ^4cbo7f!irKnp~wnocTyz*c@+KNsfOjHUO}HpUX)7}-_Ye3==1~|=O7A$ZNH__5Wo=zqE;7{ zWem(fTy1I|^7O0>IPQ>JsFKV?Sz!x^me!he(bx?TBT&Ncdzq7K>v*sS+T~mh|0JjL zpd}Y*;LclZ<_d4>X5lmM+V|hf-7H97tpdDw@^%q0%J={py*yJsE#ECFDl9~@mE}0K zb2GX7jh|JyCZ}TKA1CZeDYOYW5)@TqsiS>1g(uL-GL^at!}Wm_IX-ZTvP@R#=a-`; z5|p$9w?!s2SW`>1Hf8k@?F%l5O3}aY-TxwzVMKWoy89JU5E9WVYx zk^F2R$?wKCj&Hv((9=)O{_d~s9S(%c+9fG-T#;!B1eNyuV={9v-$$+D-3Q3qBgRht ze&+SU%4Dom%29BG$q*M2?=48odJvN|pwOc0b_=%7cCNmZbtOvj;Q1Pe1wF$f_hz_< zFc@5m5>r3)Cb%+%vlW^q&vHCeM44v|*?(S={=N;iBgB_#omW=hQT9dfNg6P%8B}J# zd0i)se;Pz~lo5>K*ILAjn)+1+InxvADcRn5*)JLk`!*d z?E$W=P-grW`{dDGE2D^)cMaec+g>ygf)OEV;p{l&Q+kWz+}bLQOWc3 z-z#})+D%R0<8}G68)HzvNgnp1q+7pXyu$6azKr&hAo-ngbz%Wt*(UA;_H z8I*r;-D3}0hyoFN{inOBteon$2&%ZGdtf)OXaYB()>d_xZ0rJ!sa0*txo|QxQ+oJ1 zh)iQJi-li$3!y5VNO4@m^6Q6nf8y*T-x$}gv*H~@GJI3hL;yT>@sDr0wLPh)cu0>7sp&HNnITL4vuy%T>R)Hi!y|0tUZYj$ zon>mw@-2wLc?CeLa*s2?vZ4Ru5GCj1mT<;^XR=5tT!wlRSm*$;6sxUy>1!AD5F%4o zu)7br4pYq_uGA(#*D>EE*q_6)aeGZC#t8|V4KEg8x7BEiUA;|4S=}FzCAQHL0&C8<(u6w=i{InJ!gr?U5xN2QkC+GG2XXFCzu9wdA*v67b@${jA-+ z8g$GT23zyxJjhtcN=f1yaHZFzmEPv})5l?{aHJ|2%Sfxsv|L}Ek%$xf3G(pnjS1M3 z-q}e#B?7|clBp&Sg~v^rjh-L}>V0dNYrbIYzkFQ0fJ$flX=^U6{Y*$+EUY3> z8yxnk-yvfBR&3myA)UaK8IPjFAA*!S?2#KtcuLHxR0XaiD_p`?vJ&11PLs~Wd)z&p5(U?YHP`d6Aom8VbL5(GCXd+*{y|E+e&JzC1|+x zESuWuI$3^sVIx&Y?6Gv1MKCyLsLHiM-pc6BTmx=oO#D55r@C3* zP6e)oAVG_;+qv&j01UgQG7+@Q3<%iTx#_pR^@7>i( zqUH?dw1`6ra2$UM41e`{`70ev;0{_)g{k5wtT1QrVH7Z`+6t$b$L*j&7CyTe*Ug|g zwwLXd5UC@@H0;B4{#>w8>RecJ9^;Se_5=jq5@H}hVAoq6eYq_@GpdB?bYQKoh!yoj+8DQcfn@t=fTegj!lT{| zuLISOj@E|y!+7lPR=mUP%#vKJA9N*v;Ax#fr`^}npo_-(sP0eDMcOVw;bV6)@eSa4 z?b%D{9#KMvV@7Nrhq<}7yA1&g{_!4lHMP4IHykKUidmXWY~<%?H;QWd2SmuoctKH< z4EDp&i-YnnzXxYhrA4!!i72}SW1V&C-O{>#Jj>DWhL*s_BD=2VDY}qQv zb0?gUJE369*4pH>8Xffu}p~^ne(n5XR6aKtAgI<}m`ME`S ze#Q3?rO8-XBe?R}4o-=+Rjo^8+8_>UM4NYu-))+hM&UE+g?>P(_a=qQ{u~gw(=Nbw zzYhr!4Elj2rynWL3h~cw?efQW#j)ir<>#mscL zz*OEyngj2DGvT83FCUUv_S9H*aeg+02Qm!k3ivQF4thzEg@O ztqbx072F)KMaT`$U@%Qr^Rxj>4!-1`6vuI-W+U7V!YT$yhqyNh=~p_#S~zo@*`DeW z-9u%LQT55^Im9{y#1zBz;qTVB$tqgZI7X%R`Mq2mpKjUuC3SsWbMX^g+eq9n@9$ia z6zhCuyE!}{uASFV`1~qQ;zl?;$-^M_P@65jjZ6_O`@$O!)hE+zq) z%|rcXv7vt+>azgkg#^P2y7z+q&u$fOXx9MHU=a*81UoxB2k7n%nhZEQTP-3;ppVlH z>gPn;KPDIU^NUn&A}>=TZ_}w*kZtEH2RwuSM=#!5fdCUbIsb;e1yF?m|AVcN=q6jGG80KF4kE9|@h8 zS3}2ymqG#^uW4?=jHJJT+J8r&N4*#1cL^mIb#cFT6T2IDkv~~I9#1I{3oU>e-=S%~ z5(}PzcHg1tFuic}$iD2)9c39ja4oZ_$U3#}bPa;YKA?NShCl}&&^C0pv0I<#V@z8D zpF3Nh&+i6;zOP%rrQgFwwqNJ#N!i9LGLr_@{vPwME+3H3Cp0ZsEU4%cS`ZL$<;WNy zrG;)vZ|pJsC87{Pau>l({i8rbEX1JPM5*gyHx+BHN7^s zaJX^WAZjhW7*CZ(bx_-ma`3qZ&V!Vd47<}Hc$n?h z;B#>>rq9`OKQ`S@_eBWC4Pjubt$ZxrepCxjb@NQs3_91TP-0x}=|s(t#-wz51z(aq zpAXSRXZ6$@I7sky@ISIuiD;p97iXVPs&>MOSizEWgI|Q0ytFC04^@jr-%xKtmz6`; zL6?Oz4bz$~9GCnP$kY7$wOe<5hC%_kfkFcNTYwx9r9B&DqUpZCPAWjRon1S-MY#;L z{H)))a41|>^-fhrZFw<`YRDuu)WBQHmN};#>SV&6-$hUHU*2)#_S$Ja7%0*^6dx&!+=_${yqG zKstYQY(}dFvk4jBb^{#m#|2O~HQdFoKw!h{3Nt|;hwqiC@*^1)lFmL!E^hP6SLE30 z9iE-*X+hQ(y0$ItR+1s8LaxSOMLc>F+x9o%GAQ3nmi5{oPi{Bd3gjH3dk_RGquoY$ z9oAtxQ|RjtQj&XSQ4}y?ZvVC}8d_&;4d=Quo_aJHe`FnR!T?;-n8-kC5y{WY8;Tu= zYBRX8ox3*LHintAW<|T?-7k~$jY?WbWr)R~pucsLHjxlwkm z^*)}dorPX;pQT(w0VoHLgx52a7SJ zu8=#rX`0^GJOL_&aoFrNU$E>BWsx@NZly4d@3i54gcU2XZ!;UKw0l~%sf%<{xQ;4(qf_qUZV7TpXSsqF zufY>a<6%}C9@4l#uUUhSVj}_vB#Iz0ttt>GC16) zi(7wvkf*Z~4vVlg9S)`EyuIAJj(j127^ckg4NeM^UZdQqwwYty6 z9TgxjdZalum1+$9+2|X{ri9&B1e;+Qq(QsgfJW81IF2T^Sq9ely-ADQ1_SaZCVYZv zVm4y!b@5fP>Vm?Ax`DV7>OU7WLiM`h{27MvjwmM5a*niqz@V1tn0h>ZtO=#E?q4ls zcABmw*`&O@IA6%%kFL}RVO4z^-Wu4Q4>Qm@1dKS?R1+yAj2{?6s4_~vB6x0<$&har zsO+DJj{!EPq-@e6QWT3+#@XzJ z1hwE0nSoNi{4I@9c$b&i0$F-;{Nu`vUKVHR>!hsVj-_HHSnOyS2N9wR>AY5`E?3h= z(XA>G$gc*YaptAKm(>Ot977klRWXXPhk!S;5E7?QnU&QA8O)OmD%qAjcCdvwnk=er zVz(bIA`6G%8iwPh<)0Rgw5E1W;Zf(3pe9uURyaTvS3!aQ}D2aZ5vd zJ8TiQ*YZw6V^ycCbQn@CtlV`O9i&AYA}L{O|5GRX5h=9JoYnMgcNwfYlYCJxkP#Cu zGWi_v9XcC@h2$*rZA3&q0zZ_gU3p`p)85R2Ez8s+ca9OWBUW(aV{CpYMk-W{Do?J3 zKlgflU(v6_1nIjjiCr^>A$}d1=DVATY1ITojkO}sRGHI97KqP?b0z+LdLLUsUtNu?4IUv2n>5b`o)R$oil$E-$sjOK*Q{ zo_j*)n8Hd&((eAtF!|-boS5}#9`v4vx6HUo9WX)qy zqKOw2gg$Eb$St1}&$9P{Z#x#5S2VyGtV%Ew!1-I6-~M9eIVSlT`j>mrA-CR#iFslN zl9H;w%eFS^*2J#I4-Qbdib}+`&qCpqQ?P?-jn%K`2_FiW5E0{p;tI$Hf(wVYesI|q zJRA(FMhI;^7&_q+8aYf`z;WN+CC<6mx<+b4qC+El6U$Y}pDqO5F7;svt`g9(NhBpN z7*wnwC>MAMSFM7MiwqgS5V(cyPrF5`+{omzr1SsfBQrqz+bB>@b$63iKjXK?wKkr7 zUKw=xp97Pa%ZibM8CY_y1TM?Ep@Ld-d8JBy3^YtQ(nbZFXP}BbgrcK?D&LIdQ4~z$ zt%fswYuq_ZYl+;M;o^crDh22csfTIO$y2ufw_G!D@jOT_tC0KKt-eV2WP^Yqsrjlt zvf=Q7r9rMlt+9B(vadV>?LtO4c`lkrudbyb4Y$M)qY`5~bLvFhKPY6- zKV{5wq#^vRN#pDokNdj=Bz{$OmfD-dE$q(k^pYj|{YRsAr`EwW*+pPcf{BkroqW)( zra>Aur5G)&hVbO8Tf#EZ)^oYrd?@W$59HLEhkKivmR9*cF5(w^#ToC1ksnnE`JBj( zX#}-?QsFB)9wGi8Bxs(dE?o_5$Ygpw~O<^WXpvuaEO&P{$;Hy^m zC@H+{39(;B_8t<4DguDoztDLTu^+Dp$F%w)8s{i~5Pe-*anS0rvo|}opXSwnA4f&? z-y?CE?&CW2%#hn3bf-8)Hp11ytu~r<%gh-lB6YG2vyyj2uht+dD#RLQ=0TB8k!}--X#{`@R0!*t$#XyYJgZ<{AU zOgmu2KxIfU|HAo%R_~-lIEyCZ#A2&rs$X(81LhrzoiXT&&1M_NF-~SK+Ua`Au zc6)d*qwhVx_`VScy}Sv{A^X03-0k2KB9z2^UhTY2DVSxjOT0aL`@J^RF}=O+K--SI zc|3h&D;RWvfF4Oq9^Tqr@Ssa zT10r?aYxmTXY%JA?p$|P8~2)=BVJ!*Dd63wZHa9f85cVcEmpUnuj0c~bTUn2R51eS zc!Uv_kdVKFKlz~X5!p_3uGO}hmZIvw_1FB7C(f!C_-o4x z?He0DZ}m@;sJ9yt*IL-#P~W><6NPUc(7fU8>r`ZHTZL2EVN-~b=OCB5bxljuMW6w- zC8*zIwnzHc^ky_)rNEJKTB7*-13Oj42)Y8n%CHBc#lIxX&ETS@w=~i2*XyD{Y!A(S zuZY<@F1}hKHl9l_nrM1DSw5^~qm0By3^IkXZO#ITeKrOvCCiaDmZ&(4e*Nkm`p4sg z47?DM`pC$vX#OI*XS(eED;2c^JD|NbluS>NDjd8tUXMS1*DyhrFkSLWy5LVoODARU zd@v46$a^Po2ns@+Ts>=tz5=mo(TOywcpLqLggh#wNL~!`4BfW=H>3{z`T0<@y(C2a zE&rGtV@!5!S_EG;Q9)xqrQT9^mP~Oq5|tHLqfi{`6?Q^#oXxAj{O4R#Sz!7*0S)zE zSEqFa5LqD7)H1AzBuzr>9P6D1SoGMF08)`Hp;DUS0>W`7kLuAhetKW~D#7msbx5rY zFy~p3sL&fDm-p*62hq^>u++M5LX0zQ^;8e!Vb$ct2kB=Eo-#oa*`)h=V_EMB`N-h!h;oODUZ@?+7?3G{LXpuC! zYa?%$e;x+XFNwcRhktoQ8bZE22?n{59i}gaA*QzMzrKI34eKzITHL_VX7OG*b(iyO z@|iw+@*n6W9ffMba45FA=bfFVZPVRHEC+ilO)Yn9UGb%jsSb=6zqDwQ$G!`6wUt8r z&J8m?EG1XytMmuS3k=X^5@DK_wTjip02v_>sXtmW6%0W&XNr=xlSY%Em*c27N5{Yt z3k%CSeW~v8_LwB7Rtm{H3&7m;iO6`tbYBQtpOSFhj@2M!{zZ=f<%kYu?0Cmk!FDu! zg0e~Tud#h&rt5wW0#BkfZrxDU`tDXsEa5{&q(aB-zpkE^KmcqeelxC=i_k<4GQWGw zg1*^pCC2mP?n_->WPT#-LH;sCP>!S)bIbyV$Hgp4qUeN}6ABFh&D_Gu+mKt;j;E++ ztE1v?X|79J*cC`&&8GvI&g)!v9TFbFUE=qv%T>9bhmTT^6>Zh3gZb}S3iajC>39t6 zer@-4EGojm*%N>aiQ^zNqZlgaM1y%ZV>d~C)*6Xg)C!NE*>`~BK&-f#jqI_TboK!G zRQ^L2msEyv%Fn>!+l+}lKY4|1ygyx?6M|)kL@SB?i#k^jtUBSXkOsNV#(Y;ZW{~z( zyLouu>8+33_UZOhV1(R6S*W8KdkB<;E9BNA8VQyF904$2xNgV5s2z15x%%nUYlmRx zWvDOA#diMm4M&898k`*ya^8x((UmS^Cq(bb_& z6VV`GKkf7~*CuJzfp}0&@=SK>hrLG4+#h)cm-v{=c|(RO_f~LUVz&uZts{`u@yuFB zRZQ&W7ZP?BQ#{TFfq^Wh$Mwk0KQ`D4Wj84RNnXD%^<4`jyK|yIYT(%QQ|CL}={U*n z1H_Vv242z5BtyBKn=!blSOT>&s_3kmuyQk?A(mr)icUwM6)A86m(E~68?raUOe&%k zsuFhtzHr*hXB++QvY%cfyn$nDv=)|`q$SW?x^R5MMtv`DR<(IpFYgLzh4f?oPfTgJ zZXy|hLr(wfcl+oD2-*hg(Zl(6j(Y-i7?;0rkInlg{@-v*?2x%*SSD{V*RX-52qD_wmv*JJy>8MH8Jma+H$GRw z%abD|pRV6nQPQ=J%Lcxofq6i?=&kUDl`fFRen3lI;QY%Xk~S3UtLH$J zAl8}%OMPX00mpYz>MbExU1YH{_oaS<%d}`C1-=vEmvPNec2zRxBl3BKM|zV`E> ze`U3eha;kYOAkkUu{3%BgCrG5ZP2B^XE`>GFJoNbpvN#w@J$UovqyX&bXG{&H^Q&7 znF@lV>6cQGOH$FK4f(^1^92+zG6??jU`FDA1a9QR$eIdU zZu}8!a-qcuY0&s66yO9z%fT%xn@?NR5_y(m6_uQppOR76L@C3yrZm2UEKy8?1QLtq zQ+q7e7SnbXC*fusP#)ENNcjG=D(ta;uf_=~gsx~nC2ZUh{vGb~d{KeJxL8ew4fl{> zqj;AN{jMc%bg@tpo!jt)HZi?L*g`?@S9IGcCrff;-TgC2_h||cw#VjG{CAl@3fHac zrfIM}?wxi9B?Y>;eo|z7HEIzYD#YKh_Xlkug&SD^1fK+bMW_nF2qle)Tz6u>m&2}5 zxE5);(?`9lY@*bXhVy{`yBnz6ch(RWxZk1~u-;mwweh7wg>{r%uJ#1PVJ5(oyTUP- z{kuK@L;vBAy+jK@B$?`xgI!Xr^iuX|Ke>8GQ`)b7(WoP|e1J8&L}!xNjv08EoS-Y> zHuSyfEE$R7RARGajAIKcafp!?bM%5wQcjNB{=9>2PvUG}4u`s(K9Xq#Wb9qo5`q^x z8egW~(RLh~PgaaYg!W@fxt9hNhN@HdI59}3TKiU>UWg-5v>Ry1mD%jY66bMwS_+*Y*}|GU-rvQb7$x`UsXCM4jrvNS8oBP)WVgm%l7h} zK>O2gfHfqQNIT}_@t}Sts$0IC(~kmo0Z#IB&ya?sT|1Ca8ynhRKPTvFXIh?b22Gyz zh{r@eARjU=nnw3c26LZbd4-$3Q8%9MwISS8!+_77v-M~68SLb?9fkB;sSNG^w zShe)Lb(4+v5jS06}lxi7k(cO@cdbe9(_!j^EugYqlSo$QaYeZoxBE2 znZh^F&QJEC)raNDhqSgKAv0Vuv3Sv&v?Wd-YlNMacLit6Y1=@l@T}Awnk_sAq z21W&N-1MF~rZujvzh$>O*30-65hKByY1pU<+eYi_R+nm<5WXtlnT00!5Fv)ZfYdHi zQscFc?0uf88}-^#0L;f>lR^3E<#y4A8uIg0!7ofPEhQE59hgeYv~O71a4*ZtwiZ-O z5o%!bsb}0bPM`W(I}A%q!Xi6q+~dP&jY0yMdB3f*FOEVUhWrvkf^*t_7yM|eEg`dNvo2R*zlK8sNFGT%USjG+Wn!1bU zrp-0;S-mQC+DjS&QTFWgx|oR9&Cd@eliiy>>>HNP;oFN~?0Z5s?kT`xec;&J`5q2< z3101yW)e*QJ+zEhU&A^2BQ8H7| zHkx#Xtf?tsaJ_@)XM%ycKb!WMQ-sYvER9>pkvGHHuFR=VS~llAl5lmgL<+r`mM&7v z?5FlBucdUK&A_yIXVs1f=-!+wIC}x!CNosHJHVF-%?Pzz<|66u8o*X_R|CH%qj!VP z^p`LnrSN%UKoT6*Wld%*?~I5FW|CgoDKng<5hX$?vwIySzn+R1^0XNoj5VC&IV3t| ze3Lvk+3JK0I;030@ZY7%VP4iZe20l1myY@pev72tl0<^DHo_IwtJ?<_%|Z&a_ogMJ zR-U#?z~#VvckG=z{fqn*1O|Km}O|vgg;jaF*?c2ZD&Mc)ypIZ&Z8 zZpX*-wNy(KLUHAs!CAY56uC*W?!{r)Mkhu+V9@FvZlS=}mHQ#XLEB=qR%eLlSL{#7 zYdE@^F825{rr$D#23@_i(JdSZ*sW9*7CS4xH1=W@lq`#_wYVu`$#2R4E&CcbD%D>+ z8@nH9wia~s$w5&wk~}NTt0N5pLS`XNU3@j}a>g~Zauv28SUFD}*EgQ-O<&^oMA~W` zLyK87Ju#U?^448>eozdBRF1u~mM&sc@|O5~?ZYu%$<^G?=#FOc{5qd3*dR6t^#VxbxGpK8JYk7FB6uQ~)=X&l>z za*YzMm1kQSS}BMXmWYd6rqbfe4c{kz7_MsyEgYJiGC$eP^zis2RugHx=~o9if7%Wl zwobD&ePyuOR;Nh#^{X}_oe>G)+hr9vr-*{_6STsqWyI*A0vmM z!+mXP4|j~zh)UEN!JS`2ItgCWak)0c3dj9UPM77m3%`7G#CuIABKqPHwsP&ucRlco zA0bb%NAmPeWE_jKy`)$dDaQj^y%n?>iCcx&b!5D*gy*0IDrY1bgib4o`vLluVMP(z zb5#RZHW~QT>hj;{HRfO_MLbt7Xsj0Jm8Kr8nXvTV&D%mZ1qXMXOMJ_7cIdUi43Z&E zC8`k0L8OK5rYKe%F*Ovak8lBXyhifNR1Dv8D>$ zk*W~bnU*|(;?;y!qTy|0+J0lcM(LrbcKEU*^Pvyz&xsuSJc4vl!&}nv;G}o18b!C& zHb;na5t0p_H6miR{4N_L8%&~2QC?iy3~>N|7yfYS*G_91sj@o>-awM<*S2yAE}oE) zQB2Vb-m=b3TGZD-%yr@CFcU}e&TaPOVVh`*b#5BKtwl5@<7-_IKl+z{_*AZSz%v)U zcr(}p>U^A%OuEhe7f-}!4^DnL_CZFIFy6HsF2V1qgg5FnL?AfK|m4WKsfQylL# zi;&u!a^Xm#F{w=ZyVjR6@SQ-pP`p&+^zhiQyG8ipodqp-I=FwG??y`5pnc)KHgwbe z*=3+uG$$6nS&=`Vv(7DddJ|dV*+LaUY(w(K5%ZQO9L=T&j(J0hFu-sO@>>&jOD8(T zK8af=CE3`@t7EQ1CvOh{yc+pMUxX79n|Fne3hdR7+{2B0nehgltI~0L5Z8B^67OW4 z<7cjlI;RGh7v53)Ke}XX;f;(zce&xMKL51l3q2m_EcQ(9{Kb@BYVWt$UD(EtVQC+1GfRig2^Wcgc5QlIYrLD8f+L-0Se1V)C(`CW>FII3741Q!VeZ z?zgsP6}{E`2>EuNtz1T&-^5h&vKdG}ca8#M6$QZk^fIb^*5%Eb_aiaTKbVHCn< zI$)Y+I$-(PK?y~0%pidq*#E#4GH+lFazQ*NaQ|)nAp&?<{&zh`C)Q-h)-T&&CRY9n zNR#{&3I<%=`&xwvjmfYJuhE2DDnqjo?DH*7z_(RruCF!I*~?6w4va3xTOqr(`k(Y$ z^0$!x+p9GbkRKTKkLucIK&%TlqyYLH4XcEp4?7k5+~W#cLk3=M`?$Jt`m%w~hd)!M z#K=UV{BL)CeX+=&ANwF*L6nv=mLvG>r6L$7d4WK5uA%3StziFy+RTE#mBE#gZU03 zbnU$FfqPwoA9a~aS1WXO;Er_~iBKOLk8+x$ns^8f7|K-8D4lO01sKc!4B6JKV*`%w zgsoSjW&&^sUaOV4<$yqA1d6bU{m`$?-TjB@U4Q4+on%qI3T@2lG^~{5aF6JJTXF6j zHWiKk0chFlBy-JM&iQybCD|MYDHQSiGQ<3_RfA|-yF{MC-*2v)j29qH!hB~Z%H@H z#TZ*${M7<6<^WSYTkcuzB`!X&Y9CaY9XiP;j-8)mR6?B;;#NG46Ql!@@aG1x$3?k( z;4_?xg&ejmD^0cbAF5Km)u4eiESLw_MlM&M`nhY!IWT)M3zQjAkAM`(Kf4J-Hw8P7 zlpn#{Qc&jdYY#5O$SEEIxzPt#dg2wqrqvnPOy_^iIe@rwDR~9ZRkN~Y<=ycyhPfmQ zylUZuI78w7KKaTmXORX==x^K=#gIEAtsV-eG+Oz^w8T4v;ppA$;>R}VQC~!y`iAYm zaw$c_#`EFyhrJkXP;JH}=h+F;y*p!tLeGIrxw&RSI#qLo$PNyy9AVb(q8l7dWF2&k zG4TaT8DJa335^T2D=yoJeGo{{j&RF(a~?sAjIOd}6-SVJq;UE=tI76jj4tqat!o}X zJd}Jhu8O>T5=pK*g-nlp&Sm@`Mw+CSTl@7gUeim`-{x}0PEv>8SyC2Ljw@@36?N$u z)(m*>etj&O@*M?_T{LmxOSatJFV1Jq*|w!&25Ky;_~NA3?WEic$Ain~&6U1+faWZx z5e@8A4eF@*4ihGWS|d{gf;J+z^E1hkx`{#3Pb9{@>E#FK;Ayu+SgVDg?pjESy#w<5 z?>YQdyy@4Pe(b(&?0-*8tmez( zk=l?%|B_=skFMyx6Plc!1*H|TWd(8nkzV}B+BD#&vsNvoO0m$JZymr-m9S?(yVCS? zQbK~4zUWUh$je#v?V^4c_3-_IM=qdQK!4*tnx@Bi!DC7ry8;v$Q#HPY$bFb%v^1Me%MhM5GFgwt;y1y2~A}-R7r9U@ID?_ zjRfub(7VywQbsM|x?mV5q12t9ML8p^$u#mw&+jnHM+Ov=tQgd$*8IX7t*joe-k;GE zFgs>jbqR&E5QIeg*vsXIsYAv-s3D2vGxG@wVXES;LE!bgMLb*=?6Fh}@M$0Z z{x~l)OX4};eQxNqFyX(HLcVeg#KBrKM@>;4@kZZi;OA2fxs<*wNBT4KciDYs%>CuW zPHUOA(=FDw%^pS`#)#GE%dKK6*WB7Mrn*eJid)c>*%C!>LG}d4;_c(Lha^h18w1WK zDx-rI?@c%fA^D@XkkqS>^Mgn7OWiMTPjG7ykp!(#)zwMhG|Xw9w7Q7{*oG(@L3oQ% zF>6h2FDGKh#xGx%)&Fs@tpx+deMO-)tCtI^oh)+Fq`FZlWLp3?o_SWT%}6b**cG0(JiGm1v~A%Zgrkb}eLaUqKN*Sl+VBFScHZPs`_D^c0C;{_v)_FGH==y% z1Avd6Y(VJkWa#q^)aSlM*!g+YImi^lIG`=Kzh^n}dERj*Nr`3cJs@2>t9aNd>rkoA zdg&8BWGtGl-W1iwvVn--|8o$c5j^A)WCK-4(4dx6t#1VjdzL4&7emQnw;k8SF6&$k z2~pY|^=b0z;sXoN4}S?mC4K(mmsDXt^j?Rkr7%|?@_*=h%b+@GM)ky z$c5GDzC0LjTXOH3m36wa;_a~;mpc%3mVe>XTBFIC*+aHv;BWEI&Se{*D`c1dP5!es zm%RWnQP;jiudVWU+ptrG z=-tG~YRrpo@uTY6kE zBrjdlzMEz&T$W;}Z`l)8y8%NTnh`UhG6@}7B(O%ROWvZ-CR|!YwIchn(!7IOq)JIk#A{Y9c{3F?LV86{W(R*m~=WbRF?efLUS5D;y@@4HGu*pf-N z_HR;?i3b6(&C9*e;jmv>i6~;AM3Q-<4oMud7^)RagN1QD)NJJE-6k_jA*CA;7vdcDI(*5KA=tOi>*K<@zNtgt0i+5u1Cbq8I+6e^TWI;x` zhj1Mo7XH?@tOH&lWOa6V&||JfSTd2Fp0Rj+k*Su1`_;zf=2k1OC2JT;=Qa^&$7Nk8 z4f)>5FY76RhFMuHKSpsSX&*}{-bx;nH&W2Y9d7ze(b(v3t?pAeDXZi*%obUXWrv>Z zIasP156iut%B@palMz>d)0A>9BEh*|uViu2Xh3U(6Civg{kdz7!USBklT(@W2th)C zTtk9Z$oXC7KC*4*G_Oy%+dV~|YyB|M2ehdi5FHW>-Htgzy-^Aw5~8J@zU^e?1_dvv zfBvd5%t=m+--WHpZj~oWSU`5XT=or^F;m#6Y^F^=^5|YO#`kce0%`WOZmlxk8VXG% zWX~ZM0@hFtH*xiCeM1UW*zoAnPl6Dbz_xy-(62LUfYXJ#cTu1#3u0F3?_&KltaOj9 z5vOh3Ph$$htE`!tiAr=647xz!G~68A4bl1q8JKeqHGhXe%aA=HP_1EBvWWoyuzb{L zj+VM*p-&R2zbBjA?WxV7c|=FiUFsFbw?K(32-u4kZQOd~RjMb?j=q^D;|!^hDAcw? z7wDF4k;pD)6)Iy|CwOaoFCPp#SyHHzrmKqH9Jg}^`Ee^9&COd1Vb7WGodikjQ<86b z;E=79BNQT28kv%KWQ;oQC!*q7FJr5nl=HYshA*Uiu`w61!{ZpVq`x+-pMET9PY{)g z0;G?Fxh0v6YeruWWR>`cbS7|8ppNgvus|`v5s*slZ6Vhare;)II+h4T_cz-%~j3xcb;`g z>q}a90MV4wH5Boe8S0HO7U6`pH3bLh8Hig6cH8p)MbQral}64&&N;zkx!7no_@m#2 z<-kG5nRv(YJ}=!c_d5i%2b_kpG2OWbY0>ZDU7$04ablR;uxi;M;Y=U>sdBWq5B%cs z{HGAq0oeu0{ihGilnQg9>3|jtg+mzGc|&VL^<1(~u8*uvF_!F~MCW!51h0$RM8Gvu z8LY=UgT9C{MbMleGL{-q8Q0@|7K?$oVsrUpsVeub5++!zC)?9c)eua@@fBXxEDdax zy_4W&0)jxSlD&DY?yVNTI6LA3`CZ@-Zxa_w+#LHpdwUMHGh9cXelJB;&UoplNr0yg znTRulU$_vb1F_W8F5x8Jkit4sXslo`|8eyLYSgzyHnZKt2_LrB&6ooZRhxO^ z{UTb*jkJS<<(U;B=21iTm=5TDOS3j&hHhWd^_udobJ!&+nx~>`ExF|rQE?I zmLMqb15YlDY4G?kAlMGhWca-sSjZQaO|I5c^;|hO#~ZH_f03;;{R>=nD>9bF#ddf^ zPdxMJ{0@rrajXwC^X1{B7a>G6oPsEYz;4gUytLE;Pc1TPzL#&dRZEIP@WwJ@V)3p@2*A+$se3gQ!0~aPY+ctA5=Tg`u zC=65(Rm?*r5~r;f#9EKxI5Y^jFCV}0|N~zZ;|MKDz*-?wj$A~-E7r*;Q@Dl zl-79F($iXjwB5nCx2YH`^A|4*)m@q}F%go}0C>l|*PU#Cxj*=7XU%x~xb&jrKW9GD z&GYdQUdMUF6!3YmnN>G%xDIRsDNhvf2wtgEPum;*y>IX1RARJ!zb%W5Ht+$aJpaeP zyG`3dn>{aDg1^QeZSU@15E1XLv&xt}53a(GfYt7YkN3f@sjqICQek zzIOFxS99A>Q2*9!*7$e*&uci^XO^gnpI;G))aNl7ku9CbNj5v~kbjDpw=NG|Ag|Zd zfXp_2NHL9*CG$tleGb70ARRPV(WyO({yng%zg2v6hhKaRq?Zx*dnxiA4)3o9roO*4 zypoc1cYLg`*-S=7yl}tv8xbADH{}-68Dg^I#L-K7ybganvF{Sg+z%{5LQ8M6o2Y5h z7|oDg7Yn2AW+S4K%EQvff*ec&E#gH|9N?=N2j+8ks4T*Q8u=~d04Mxo)fDy3pEB*t z7NOG*V^qddHRS8XqTITteU+IhzIz5#$~otw^z~?To+IRGzdfFRQ%gy+9HoK#(l^`2 z!)37dL{!OCpbyRb^*&UsHibYYIJguKN?B9AXRkDa=K6iPLAGUtu^tJfAn#8t|6(1~ zJB-3KURP8X9!n!MAd_>pTKn$(F{qZH%&QqW&VOtX!xJW-s+$^rj41nL5I;}E8(cM9 zmu0Rw4-6_t{}fv7WAGm_`26*sf8|>gCXtLj96xS^IXY5GL%?NMOj<>ZUMDf{tRpe& z7Fj8LT?mB~Q5Q>7jtP0<=Hi_~_i*Dd7+kGs$y3tE)?g#Kfh5O-ofoTs(sO5Z_rOQc z2MY?aOvLJ6JqfqXs3ON!B;Cb0fh=GiLoi85?xdU%a4Lm8f=}#SR3-|Osri%5wbSXT z-4mkdZ1wo!bg|^avHgkxwZf9@G+BmHomXVl6s6(tDJ(5so=7ce`GdW|>*vl)DyC^W z73Z!tLOA-afHEi}R`Y2+BKWq0W5AbjbW3s9Wl8#@cX?oazCy z=kpmxa{x{g4OULwcW&8$THp9@CdI85Scz6YzmH{z0qi|f+;vwaZiO+5-PYbs`!(Mt zrBYX9G|}jmd!9H+41U(q+Pi`H$$H%5*0rUxjJhqCLLxzM4#!wD5j?guw}Z&CqK=x4 zwm7eCj;ElgxseRDTG1hT)N(%`zp6GPs z%s#Rb27bArCPk}XfIuO1Pv!d;_jXozg+@LbrODShvap+n@31l(sG9lYK=fIwU9dj1 z`kAae@N%=D;3F+pvaK%c_3lGQqL-#;F11&gmW5%Z3De%#w*09$R0WkVCy`erV7kp> zaSvX|68gA1yV~mYcl|sG|MYu*Ds%jJ8$8tfN~^z~O?7!@J;fnm%|04Oh4uL=Tc1XX z@_zqdKu=}2bxKB-H{2i(z#14g%GTbvB)wH^pLbzx3Y})p;pXEIuB^`pO%6t-=Ac@K z)lnQ8x)0S!TMBH-Iki!@o01OktKV(YDN2OE2hTevJh2f}N>ly;9syH^b&;}S%NpE45&V&x|fp@Ok+2bX=nlcj0>0#r<`@kmw7*i zl2YNYmSYBah~el3LzG?4UE_GVlns<#1%WeZ9mQm0q^4LSPl2t8T)jCQ^5S$D@m~1R z+=V8IG9K7R-d4V(EG8uH8c~hT9EVjgF=AY@-H}*!cP_}DF*jpBxV^<cJUk*tY;{pMJWAkU?e!l!mytVXkXu^OUg6;4 zW||7ub0OBQ&O|58~MdTDM}CLdt`sb zuG&O^(0d0GEpr;IV5*1mA1tIjQwm>$KO)AHjIzVyAM$+EnQA|R*BlV*mH`ozJbfOs zS5OCS*=^~f?vAy(_8-B-({(9FpM7I*i4t3KRRqbIH7OS#f8{dM&=oV=t4nVlep~gO z<5EX~?rX@2Y1(Px9TDzXx=u=5*sDoVROiq264)79dw9cGv2@kz)LAo9fImo{IC&07 zWGRorV~9GSOvrAr+70CR(f~m}6c?!++hXM{uPc%2vB$={&uJ5Ioh*(%_gtCD+EENS zXVOb?t-qwXOe`rpy7 zHD4n0*}=~Tw)duQ^X*6=AE;+FYH*lFel9iFPvfG9yL_8-kkv#~@dwi5YX{XIKyzMN zTs7AJN?Z`gPY4twqHGsKK>25>uT!u8RmvN__T$um)nk#t z0h&1Es|`W#xk~=golQ-Qd#C|kA>6_hs-tm*07cvzNDmf*>O?*2yZKvwd2e9D`1fxvbX4L3Cyw>@LPr$%b2&5@?m9 z+R^sNLQ~rdciOW3f&7$@eJ?p9g;z;h6k*E?u4d6;V>H|P!vf$OyX=fNPf=R?*fF2+ zo(9c8;}8u-=Ro_d&O&VXS{~)`aL5>Ri8>WNyAQj}w}(odv?H*k$4<@^+Dr{q>`~o@ z?$-7mx3}E|{4WG5F8Vxs^qS|RUfvwJ+j!YED3$=Ep^04WhOO5+j>7Hf3!Q(0hHc^0 z^Ovf=QV*eFZ5}{LRDH4YW4TN&i>xr5AfPb=>x&vZq49Ti8*p3hU+o!U>3-`(x@05S_30zi`>CeS11A{C|%zZYcRcwwy#S zxyzdY4WHQh_Zw-OKTC2UgAQoY(A{WV0wP}V{5^s)TY}l!cqN0|RezYH--!apWIO(c zeEz={4IEtmv1s`6YT)N*Qu9ti03}P?P5Uo!{zXP}wF#%g&1I!MLL;!IiLt}~w@E_* zJABwcTFnH)f3eX7!2hS@XfqDK4FTxuGa*_BO_7Ksr67iOBJ#+4on01w!!4GXEtFYj zhj=|oTg$3DXOvo;ynLx`$#IRri4#vie<7n#ak{>JTOkIVV8ZMA`reLm_T2n#nxKc8LStn;Oh?jjNO^;H3RU*-$fp-=d|{C@w=2;#x0w??%-&Hcfi z|55Ucg`iB_|J@=oG}9!NxWB{t7`z#Ni#r=Ka)}TpsSx+}qNHvAM$yP@dk*})Ie9_g z9oQF8%y~l^6#ivyI4jf^@UL`#56@$Vhe-;W-5<4)il3kWem~fcnLB>D@8g47(eU8* zUmBYBPQiC=pn)>_<=q(?DGtmO<%gjQzaRn=_wzo>3>mgzaki>wLt}-*^*65eknTPv;;U(vATP{k1GuP(@GXimr&EA-KD9e+Tu3*S8`ZS81T|%8-}7;smQUkhajulWD)QBxnL4T{zQQw zlxHGzdL~8lmHo4v{W1MhF*Yi2GP}^B=CwuJHQGilY5XU}8-yFMZ2x2?49YS*zi+n? zs0TS6sSk0-B|P0(qbX7ImwPGOM&V>5|C#@;a4=D&v~E|SFIXO&%vmJ86KM{Tm>e5} z7S(%mO94BSToIPJsZEI4T492+1vP*I25BIbr_@D%DQ}#NvR|9V)i=XjqF zDw~!jh2~ToyW9pK-5~GxrLPwuT%HXo3ZycCfmYvW6v1$_;mF^LLQzzB0%h0#;;`tE zHYxjdq*Sm-XF_#D9^Z1$stl##h4}K+Kk9|aNf6vaHw*D~(C0ymj&zD>n=GO=@RdEa zs-nasv`1v%ar6;B4WR-zvxkwdRE1*_D)}99@W_?xod*z1G&mV|&L75;!8L zp8!a7ZlI<5n?g=Du5@eYBq4!ji7FI!ZYa0`e&L@V5)Ow^9tP?OJt7{Y-uUImpc^|_ zG5XOZ$dEuiAD+Zz6exuiy$GhA!unL9zVSulcSEIzSXpJvax`Nh&auEpNJmku=2tz zr-)%_63_c<*FZMOq>x zF$n-fLhJ`Y^K~%8^;qj;O!HYZT57AL$P)OcZ^<}k)ac<^tngavC%O(Ue-ub#>vy zMy(P&^*48+hfYPv+!?h}{p(}VcYN*~At*}v3pL9Xb6iq?Gf$=6LOpZwM8{JIvXTZc z-5IWJOD)Vj18q!jfMJ4jW}dBiDygg=QF{PW=Pzk0F-yjSj1(tfO+Q$6_+IT6Yu1qU zEP_)v4ecIVMX7Z$$UUAg1RfoxHBuC{C(ZE4QCQI@{?3BXbQ=@$t=6*iwsgQZv zJ%Ti%Qc9c?#Kld#8FpWlOlxs`A*Ko3Luz&IVD*o%fx4)HYR_IZuVYtj>sQN=+gcv$ zgU@ZRn^NaMHr&W7mn(pm%N8o27UdOW>ralOx|~bGo*y_r_OsG-78LM~5-p2L(#HBp zbH!pOMftv3s`l~v6ip4Bv#zMe5^&0S+DMW{RIS&I;;xBT%XJG?E9YeDuX6(Fhmy@} z``AVbbsqPjtf$VH9!W8plj}X z)v9{1fT%JL3R11$bGq-A?Bpa(a^t#Qmg-$TnZknbVcFphz3M#+rlt(LvyC(t#B58O z^uvZM_ARKhgikwJ`~9+pMi)TU;(gqGD;4Ya90?)z2HqX&RD6YbWw9Sd=dj~4{Cf1M z=5!wyGGy2o8dmOvE)8a`onl*;_20u0Z1h$M@;Sb9gbAye#Qa^TEe@A;cYKt(7{Dbf zMU_Cpfv4n-k8=O$`$n4Z?M<2If%b+ZrIQu8Q9_JEsa&kSXVDovZR`gi091=-;taQ1 zcDon9r0zPv){p^S-T)~4H(V`eY7MjCx#bBOMV~Ks7@Qa2S`u0b(`P%bLkKi1r7e$N& zrtSGz$o;s5SlAb*Cdo_{t9&HWz+Sy7V3AE8k(siSNh9-v+TTU(ZFo1*8ft^-C$_R9 zKJ28y2Ojm%>Q84!*7AE{$Do9W2=H5RmQ>Ad>qH&;W?={bTH!@iQdYS^?Go&hEi7J> z@|&rATUG^;&H^~8sY!aGX^)jCdH1Q8b8>y>1UhZY5_t@LH0e^LU^c&I!>5r$^^MbVv%jRu0d5u97MVQc9 zx$C#fb-ybSjilg4uh4J1`Kx4G$99BOdZFH*?mo`}C`n1!B$Q#QQS=Yr>xWWUZGF!y z$s_BO?HU<7KMJ+pTXL6}7I#~szO_cT!6H7<4d&A4V^^s)_ISxRnMBqt36E9X`FLtq z>YQ?5T44Bf(=tnHm)FP!u?mN>XPo{*s~M5rxYS zN%(#%a!3V7kAmpx5kJ^Tm2J5r1>Lc!6d`B_4D7Bri1!YkVY#nUBaP`bZQG%D)x0*% z2UmVehLXCn?h805dBU68*HQ8x_?)vAR&tSSBQ;Vo4riT`dt^zuM}_J!J_V?|FT?ln zJ~FS;kO-7yp~pMi?l)=-D=Xz*fU4%HRn?}as1*rgl=eon_H^GQL!N$czLS#n@OlXW z*u9ZN98w=Xg$-}5pf$n1Z7N;}wEnAFMv8ZTXIrpH2kU7_I`vkz223~}-np{^^WccH zX=Y9eH@-Q_G3@T7(H7Pg<$No1ofwuL@2-^#KK0pKXj%$Wv(0@Oe#hl4_)&ji*Y7xg zfaZ`r0(le?eAIb}g?gOao2OQBTQ;N$aAw!x=7!{Bb^h#_kea=RgjCRE^?{ZYZv$I;BRWf|69QagbQEXS1t_0i3XJ|Y>^0Eua?l8F~ zx@$!D6OOrGC zluMziNWc9?-khNaLMQ4fSkku^$s zlVlbrwSkm@(0!*Xe{W0Ca`dYJBmy879qQn>6R&MSijRuYgFLP4}!6Qv()6h*IuU1 zl;2`qo?HSwnkYUFq%>#?Sm!d$x1Av#4&H}cO11OL`l137NP zi^T~Hc~kn}zad7E?+7cfIiwu^!8lKf^;RH10=8$fw9@yH$?tInvIHtMuG$mEz&NX& z9NJS{mdoWDg1-&syaaFn8h?67N--hFRKkiL>o-4(e(Sje4jb_9{S5&RL~q+rG8_8I zs(ISD6_&NYPi+1VkoS4PKV% zqqU&S$KTnewOt$)5Pk0VSi)nxR8zn6n@Q9c?rsCHAxdm_2QSyH`zS zQXiEyx20^2x5M4{D5v4y(CbnO)wwPSQTpKs4=$|ezt>!31#sZ@2wj{_OUMPp*PMhd z!>VLF9UO`?ca%hcZH6uIkBf9!?D%i)u4MQ0azEy%i7b%+*0^lZzvoj4-v~@n+ zAsHhPd&_VqGk`jp`;ZBhXFu$r8wAc;WmS?tWXp;%-Xxc;q3FFcrUMItR=!?z&rG#g zHQpVd@zx~a6^22~%qeRoDHVe2(gUrBJjCS*{9D?E@;1B*q`8fTTAuj*3Uhdq#PNJ{ zsT7H%1f+4t+GtYXGp*Jh+2<+680C{?!t{^db$ZV4V(S z82?WeC~bQA|CS(7!XW&2u|XIN0%d>NI}ajD+U5kpe>1fLzY!`e(h4w;sQ?xp9=89* z)aq%+ZHOa(6(HGZyVKa#ItBx zv)eG$bSE$*&_`r9`1C`3C9|*#@M?N9Jt}P0eCvBC{2koE^YQY4HFf{DE6+pIQ2A~l zugk?Scs4K%nCXpX4~^HQz)E!7NJbu9SG!@Pqf+<0EG6i^jfz#c=gX>$YG;fMpfG|9%vi zqkt5yASQTpfZf*uk(xKkA&@PfxU3S|#2PiX9wY;9L+QlY4T zJ*1KxeXd_ie9nwA!S%PvWlsw0yvw(uMLB?XbFoS z79@gKLC@rE1ShFflg#n0ZN@&WG;6YM=n;(KvPf8K=!$w#W2G?5nT@z9he9^9hd&Ut z0pfaQkx&rAV9UoS4gT_hjTV^e(vljg0L>#zkVPMevBjLD*#yK~KiTl^Khp@4TJ3_k z3_<7O-=(3Vc@y3xuV72Fb3PhR;>VON18GyzBNUWJ+%{#HhO!0J(ic|Kd46W6-&&sv zURHq7w(Y0PhZ))8M09xabB8rDen&gr2KwX`v(UZVl$Hv&OxGM!F;lzk<=)1SwXVL+ z1$zaSFKK*W-@C27vRJ7si=1}+#FqATRoDxYN8;IMf4L_|vnW$Ys7(bol$EIajuTy3 z9TTjiss0eaX|D3?Am3tuvPvu)x6M%+laNpaEm65rM-}m3VYmVHxIF!o}r4ExP+vi zmj-R(X-yZ6dI-Dcz(>u=n5{omW+|_B(I-w{7ndwDaF6jAhn>_)X_?u&+t$Tu*ARhb zE<3)hAb7&jh!e#&g5gpBiw*$r#PK^ok-l<2xepoi7QnEBo=Q$W_#B0_$$Gy}2w9Qpcn|LObQDW2X=TI?buaOd z_(&a~Z(&Y};&b33Mi;xf#q9JW;-AHh%MaCGF-OvemK}66S296m@Id*&CL}PIA2|oK zkChv*6kHyBV&bg66#^p}F-Gs>E8nLo9uww{^!x|^QWk$>lU$!5s+K$mDcOzi@Z#dK zgtS#%wV>+n9=soW=oo&bEsHLlGncoS9?baVH(yX<*N)FV1#)qaZ+#(xW}Q#v)tGVq z3X;{;Rm(er?_!|1yd@T(gCHt$ zm#dPCb`|!Lmz*VjAKAYxW!v~&JrQN*UemPphCM}{Cul<_k(EZT7=yoenNY-zP z?-f2ozqm26%%_G>r(vsXjdd|;u0+iSMOTr!hvg{WEzZqnA+G$y9mu})Oea{oA>+P% zo#VbtCd&8lJ$PO=f#&f%gNm|xs-Zkkh{vZA-pw)MeF2#gd^|IVIC zH`4(i%{-vE`pie8Y@=*ETo2;o;KH!W3+tNH(ivvNOKy9+x>v~7rS2`rRGsjX{xr#F z(MZ!Sd?)>+YMZLQ`xv20*iyETOY(MLDcnwrW)bK0DXT-OYmb zcvK~Acinag7;lm846zqES#Ep7jg?IF6_<0D$94AWoh8r3))q~Vjpgm9f~TUM$|))O zZQS$b>>8_`e03FPz29Lys!!kGx6Cmx2u~i*(SiLvpA&dLYliZ=#AXPtG#HQfGD)uY zv(2i_wmc5U2iMJqp8a(R3_ouPSAY-FO%CC@>Pk(;vyWyq)ydimGuX#68hNavM6Ys? zY~aFtx~3iO!lR>)NBRVy{}nwyoO{`qTrWNGKS1IC(nXvc|3w$2Daj&1q~Wu}Q=~ES zBL27ZE0!0LOa_GWKb@hGIJjA1nWW9FEv?*$Sy{fcVZq?o|F7iBj*W-yf0Zwu=Y*@O zX+MsBW)Y%{vp0;-?*9pK$aGLnL`6@o2A4@xO(-Hp$7w)kjybrTcC;+BSjoxAT*dLU zb+d5$jNg8Gu&$da?HUdl%T?+rWNf?Ydg^uSed_ky=7WVuGtP%Y2l5aejzn|qPK4Z< zu)~bg_at44%LXHv5Sxa>3gZDsa}^!568_0joUb`k8Nc>X9nS$GKFK3h#Ahz?1D7I! zOT)8_ZALTrCCTrHhNTOzEgv9)(?T`o8o4my(|ZyO8sde#Ovk@rrs}ww93CmmZT-rp zBZM`R&nwi5umf;`y0}t5P0R3y+#$<0c*Ch;1;d{xQIF@X##LM}l|Ii|GtBUV_+W*i z8<=&&Lfb4`xYC$5jq@4TgO#l(>bT9o03{aBBpL1-I9mmp^TkpU=)tlvyg{(HYqK zqbFjPq%gAlde(o8|IUF)rU7Cpe?=ir2`Q8WG+zJ~|2Yqy(-g57lSfm5;VH4^gd6t_ zgxRIz-N{sm@pb(UOF|)r`70S3X?orzRJiTF%c5D)y;b#x9kfm0Ti8HPicPszru>{6 z6NZA3)STMZbo}q_E9_6+wA{2qPiWsjW9%>N%bqp?4oZ z9uSMdL_7VsPu(3|nnX_d&2@KzoHJ6WykGZ3{^7lLt~94Vs@4?+$@4=@>SVW5y1?pQ z>*8y1yZgj)UB>_OY~W=p@vx_bymuFPI=lS3{aqSRz<`*jmP7=uPAb87;`H|=_9l)` zvbgOvF+R^UbNP<}=7-z5k@RC&yg;Tr;9nG{;Y^8~f^J?}|Egr0@s|g8zI(!ZCEdJgRPo&{COV&B%)x;Ee+owO~W#-cju?^b?`N~W7vyX3(2AR*x>8{3k zY(;!sm7NJ`^x;xG0@n=<<|eGA>-YHJs(hF+_8|Zpj%N7m%R|uJU3ZJ#)9L{w?8mP( zcQ2hrG66;i!0GAh3%O%`-4g6WuwPLl|M`DNT616H^gR$d+(Bk|SAHW2(^y0qo0)WF z#On#>Nxp$74`fAd?rerCnWY22(^!1Vb|rFf@-k4MF0i1 zR<-Sg0agySCf;?;jUxZlly#Qa8u)uY^8Kw8i#RfUxv3tI4fcm72;Dgp0Nj^O;5_)_ z#C?&iwfTp{T1JK(V;NdnZw{88xVm-ak{lZoKgyHQZBtEJ_BvRqR5$2|$?=$CaGfY9oq;}ew%J5;tKovL;D=R&qU9qpSg6al#{aCmw?Yk-MJ z_w*ihIk6hAN;L&yyC0RBp~~HLLs4R@`)brihsURs$AFiyU2Ah%_^kDH)B9a=DMg+1#)5FqXdki58Oki4~!5FQ@6^`-bHmnTZJyd5Au{`}8lk-W1=F z3g{K|O{I-s5LL;x<28eC0S5o1@-kDiUce&fG~5U#ysEXNt-~{MUSZQT-qDV!m4Gg+ zqUb^`U}78xcV$f|$MAb3$hQrHFCVEQV!q*{kMzSQzLAPjY&edSisWeoF08vhV54wS zIHPTpLdR7Z?B#d|R22fBe^{p5M$``>1+y3ZII>Ea1p>v4p)o_?0jWNAxd#&_yvEQx zUPjrX%vVzgH?T>C{aIq5Yf!#RTKKPV5)2=qTm5`R$R(ZNOekL& z*Ph^i#`0gaYgx!7z>>ApT-Z??4FZ;_*GH)zn$ZQ4_FT+MiL@_mj|FG%BxC&ib!2>9 ztc>v5)F2xz7lT%y&VVl$?oOl0s8i zrg1shMXS*BHJkfsBi3$a{F`!Qu3^EC05a182E~7JQJD6$0E`m8btV|j{?YgJU8%BJ z>z_=wDtI)whyvizkUX;-(uAn--Tj%;m1tB*5=K|m@7M;FM5C{Z{O>%~`x2xWiuN(>gq_~0aST}|iqlG?c@mtd-M!dq|Fy=Q z%1HZ4Cd3&H@Fauz`aZiQOA+@e(;cj z)NJNyZQ0_b2bCROR3&PfsLz!yU#j5FtFEj`U8bl@{7^2Oi@%nx64)^GzA2#__;hi9 z`qk-%wWv|V4dM~5N-Xn6H~I`NHJW}-=8|vq^|Gk|It2SshHQqA!j3)29*$~1(7>hz zZ&gi`i>=nYk_r)eO0Ar(i4?7lH=Zp>z-+bIbJ7mO=MYEuHQF+b(`TXwRhq|)xU8n4 zd(Oj?aGwoHw<`-p0(aWq?FD-}*x^$zsxq3nXmQn$BxUFujO(_ngMFbFp56$^G@fqw zOT6@fxt+xBS}U|x&RuG1Txe)SKEut$KODTh&2YxNyVs{E1@02MJ#BqFd{LPAwXJJn zXHlH?8thnHPb=tCIz{fG{X(@Cf}5Q8npFiNw|QT%NYG$@!qtvO=Y18K+ecM_g?d)E zf)d#1E!^Elp@N*onG;W3LlfG~9@=hq3V;D(jbp*WdDy@?p^DBvGwq-iIGZZ1zTiS~ z5UR?@JIhxoeANWp>yrlN6k!nVK<_To{CGmH78aya2OEfnikn!i6~@C#KGq>(cXXkG zD9R&NYl>qu7;R=AZM*|d@b#Ehh5UKdx)qdWb8h6pZKsB88`AW^PtgHQ4CbyEWqDwZ zsm6wV|Eq-={*R#XrLOx+u%1@r%GkyQY9~*gRd*UtM2=V^g#cvpPF}e<5f=J{wB`~k zY^-;0-jv&u+9H3Vi3N-n`UAx-{3YoRyb&Hdq_>obJ3VplB)d#NR!->f2lahkK6thUO=Av(Dg zE-z1i6^TQvfiblyhLe>XRbpyJn2S|HfIrCuk*vn~72d@l;&Do2QEXQyww2O(am`;# zHA+_BWGdKww^Iu#N>1bRa#POEh@5J>F+s)Ss-Ue@Z9m=gTyRfszC0qs)>7Ni^}G35 z`*M;qWf&{d*;YB37X;a{lFC^Q-lkX z1!LC0?MXQ$^>{GzV)-hjA{zN9$vU>U?vJn*b%|(&#j)+c(&6DLza&`l20f%8}RHSFH7=yR#;^Yw!nSmR|z@JG*h#Q3S)nSnP9b8Vz& zdUIwQg?TKCCWKzfg~KB2-D^5j!Mcqb?x%2X8zt!)9#Uw$xl|is*@l2kUqE75; zM#*xM@`dn*fP{AoXtkN`vx`#os_sv!Qe&||Vwo$$xT>!3JsLXSN!1gI>{+noyJ>&u zTh|xUQRsJ{sup5-x1^UqVFCS|?Lz)R<~$lHjk)ws2ourGb;b~1Z|>d>>OtnotS{X7 zAE*yiZ3X`|ya~kk`5$;oSYOEUH68`iU=K3}un)cd|54}H(WK9`N++n@;>Ws&_Ahzr z>|Yhv!arXH_OPJdy5N_^OT~Qx$_=!44$he3um=B7(K2=vHwFn7{~Mpfy*nULz1TY= zQr1~TCd7GD>2ebmw6Tm`OeGY7tXxv!d|*_}1D-4U($Qu;Nd5`Xz5F?xqm`1HJDmyT zU!5n?ab?pq{pd>5kKgr-|Js=El2S)zH=ch}W7V@1kUe~hPpQ|goIF55{W~@f6Nyp`=UnA2h>O1TH zw)bK;Gv*8u5%oqbbRvX7@1}Qc-ZZ7f&)Mz%4$Y%EORrZmpI(P^h=-7`olC z(hJ)a9;}=QafTOJVX8oGC8;V--(K@xjeJNuT|w$e7^axddhmgVS4kj;#0~YwRdln} zUvt0LPulU1O)#LEi!P~*O)_%Fkv(fXe!jP%LqUh1_}kQjNf7dbGr9EPOwK(6V}mWf zwa*0AB<@ssRiJhDQE&(n`Xl;1a8sL$K_N znzza7$C1S8&ml>T8)bh_{?VE9Fj(nuE}N{?L#^um70LPsx!`cFv%~``L>`{-aK1Lu zUwdWULax!CC}_8LYlfz>@9$oK%G(z(y?M2o7@d|9WI&hs`TSdcP0N2VuS_*>lXGXk z;07j4lKOGo9t=~*e7|uq@1DD)e1HCAr8mSdmmqMOzsFoyy)%u9s@0){ZTJ77>@D1? z?Am=%C6$(z6p)r~B&54jKw7#}@=;P!kZzbXND7E_NP{rx?iNrwB+lS{zqQWV=Q`K5 z_x=Nb$(-Z4$NkIsQy-;1{7|2{hl%lL`$ z)86!SW+;?FMayJ(Znk+jD6sRgq`Ref0#5p^G_`@Dj{H?bjrZ0zrka4il(Mtb&B%PB zufj@Jc;+t`G|9`s^p2Jto(s-3g+_kGo!6EOlqFGDg_8&Es4psP2~2kEOiq($XQ}lw zs0yZ_-vb0u*-i?mYBk>$)Jy4c$*stqk5THZn?btA^HM<-H zr{`*hXkp7;SLDceS0m_kHtbFK6uc z-Qa%B^Qls=cdtVGuDUphl%x8cuB6qQU`FYjnM79WJ6A5-qRBmv_FTzZ!26hw>gB&y zzwsEPnY?jk!P}@35c9Wa57^JJ`WS*1vw!NA;86h(&0U9s#4J{|PME|$c)f**2jWHg zOV%NoGQH1sR*h;ijz3F`zTs&Q*;Za8N)+HrmR(Bwxy;Gc`NB&AvVHcH;=owxxpv;m z8hk2z)F+0E3u>4iL{VtefeY-*?N0-)H=^IgWAiYIguI&S@2o;Rw6&XN=EK|#BcFbg zb5N!U*sReH3|uJL>{r)AzJj>AE|7*_?5B3b8}$6rBxDIO%Sr-I8E$)`yxNv@_P^RO zT->N{Y+s;tX%E&W%e*h<=+9WePsintWzT$a+NOT0_@axpurDjOJ++;?J=;3!vhu9H ze!Cy$;|WlnNw^KkSK+u7?*Qq|q#NH%>!DAXAf;hxseV;sd&hmRJ3 z_;Bt~prU>_9KDvue%2Z$#3XZ>xF|PmPKH12+usvH)B2@u!!8BC!o$wG=O-bf9hru{ zzIiFv48g~#0eu2W{ z%l*Kw(}-n=WBjoUvUp5E;yJ|81)7jGL6=J2b{j3hDOfa}_CBk-dyv!yYH`WdH>Uq+ zBR!K@H-3+)4>us;ogrpHphU!oq~c>&#SbS(2pv4prR|S23}YItv>Ps}mq>P_!oY%CRyUDhP|}TFI4ooZsCYzLh`>p~U=H!+9nu^h!Ro*q{u9bxFg?x6h`}iBZBZ88-?sKr6>XGgk}b}Zg7>Y$@6rP>TJty zC{7#wgRooxj$GAUd2ZPP`b?A@`zPH>(a`dx_zL8}CddeGC8d&2^aXq?*!!ibrJtxF zvMP=%;nYfenkH(&Ejqj&vpU@RJ>!#vGrJgvnc-)X`Zj+htG%b5tg25uW{2s`--M&o zDsE2QOHtybCppU?jUuJp%Y4+5i4Lk@VFQ0_$2-mpx^K-RM)ka+Df5DM5g7{#WYMrw z%_5U6woTo$tuy25X`D+}?&-5oywutQq!;{Orlp~N83Y+TigR)O^mhosh~fJSOAL&u z^^8#&ufIS%4Amu8#G0xk>de?*e4V5^A1Fq8#5<_ly-n{9pUQQgB195xd@FGNp!uc; zMwmwDl5Oxbr0Fb2U`IgjbG(u7PJhtOK}t=V+?==Mw}swU@O(7(_w+Qa@0iwid;cWckAydnE4-{@*)1vZsM#!tQ}+O zbUw~@$ML8ylYThL+n&YBS>e)fUgqQ0XZEZ9)f3Y8CIc9_PDuyRO=tDjQ~NJC9uW=0 ztCfal>%8N7g*xs6-p1AaoO>#c=<4RE$>k!zG&1E|_%qS#OWbR!$hH-)J}4?jOKRXF zSDL}J`F8BZ>2i2T%h0!OHIhkU_fa1-m$^na7Jo~IE?8z0x2PSt@L|>P3IoaDskaJs z2pTp$PsqAXTD|UI}W21&qtO&_5ogWM&op07VX@lqyHflA(Sfsvc z2=EdvYMV_l2#?1sCDi6vB__^@@DkWB9Rydm>A`%q^G<3Uyi{(XUoz_47qhyOc)#p3j9_Ffvf8!(9xS^z0R;Y{NZ9ihJt@vq7*(c!{UOGtP2Wt!Afy z*n|SzNO@ZNrqex^;mlm&uHsY?5IPKYX)+A6V}5Hk?L8Vbzsfer_2U^CNd!K7;Iqi> zhp~8@H0&@ztY%O|XZlkF8Df1&zMvi67wZ@TC(O~osx>_kdK~%`)XIWb0t2A=p2|Rt zj5C$YWykH=dYg`yyh(#n^E(#9EQSo0-`Q4xk7kIX=(i*!H-Iy_B4#;|7R9An6+ccz zGF5qok*fN~s0j6sKw?Rj4#n1vbNFG$dGqayOCRESnqSXHvalp8o?9W-Snc&ljKpPrk`Mz^~joLfF|p*)d+r7IAz72&921r#QG(f0o*B4GPsN2 zG;}SOARYom#*Ot2CKYvODs%UbzzI2l{eBBo#WXcn2>$-Ym*haFHkMjfYVfa(es$?u zGZTz0Ds4zd*uzxGHz%7Hj^`O_=3pzt)7Pr`kb-F-AMdb7MsQ>!10R`Nb~?SizOhg> zNfmg|Xu_d?8%prTG5>prOkA;fEH%O#lSQ@1(1sbomd2>VW3BHaEHdwNM0Qp99>=g8 zUtom;4HpP$@2ZO(oBzmnV3k{;+dFq)+Y3|DnNO9#E*Uen#a3<~z9;{eW4W7}8beq5 z&8C_D_j3HSiY=g)!08@*)dKSE5Iu!_`SBDmOmgu=nm4E zMa^CP%+dSfG$EcT<4ygEv6g%(31m_;V584&QBYMyjwqExAGtqMtrzRnQc?w3kcVIu zAXBoj+;8OZMkYIcCHK zbIP>lTU0YRG~Dz%v3-xGON1D;Lb(UyE1}<2r!WwaDXi2VFhmx*2bGSY;L$hiB>^{Y8kom>3oXs- z26I`YBwQ;k{ya3n==*W$BM=J{(^`4dwj-f;z$#2U^{TL1!&fzV({Y;f6MK&NHxsj! zZIDbWftj$_0i^qOMyk>G^Eo_wVBN0cw!)KMeB6I#oQ7(#QX93Q+=EuA(f<8LD9*?O z36rC2@MuCN(5*KYx&uF^GCp0@#V-TFqpGA`!>gS-e0#^|$d^aK4w7aSFy%+v$K-3; zMxXotPQGMY-6z8A1n$#3$EAMWW>}Det_8N0ZoQc%Rt#~fQRt;b`m017_ zg8S8^>VDsuGcmXYy2nhS{v4|^!}VO*cVe-AM3T( z!m`o7$|f(Me=)b!|BK;kzN388zJfwZG1G;cyZue&o1)ehv*ZGzSyWhNNwf|ouqr2U1cE!tjl_Y+I(txc*A`(|v21e;Vpw`?Uf>j-{> zd3NtKQtYRJP%Li$+L4TTXjNfaXI!pa_0NC_Lr}Op~^q2QO4h4?Ga5} z&r{@y=tDi$**d z{2oBsnlfyOdK>ypxB!v~|0W!3XL785bR_VOmOplJci|7Be+(wp6O`T6oIvEdL6X7o zTdijgb9fi!{V9(bGIPIw=N}m`{f+pgxh(ejuPZ+O{>NvO^z4JN$c9^gu9Yr${@Ac= zGH_P|6w=>;Z$<_j3SXhFh8yX45tiIe&gDhKFX~fdMEpD4Cd5VzV_~R zhstjyLZ$e_RH6k14SiQ@HzAwhcAdQ8nvftF|E884m&=}@rKs6}<*k>}XqQ9acVYSB zN}->)-v)&+cj42!iFO{(0fJbDJ}=L5!y}qCatYMR0@FPMTmn zY^NH1#7awJD&b75|AUQ2`u17-O!$?p?0D^&2A(!VTKhHMa=->`Eh}?`Mh(>Xm}w8N zJXU#`G za+7K(RFA@Iqb})EwO{{Kfa5cFBPZs+d5HUy**SAWPRc%o`SmDFNSm87Lg7hos~&Fn z*wK5aG07yErt2SZ+sr6|1O-XDfMLvT7`^ruKQmHXK*E~=DyF%;EO#!at*he4=e2K{ zAREUvyJ%hNwPHhW6;8gMvv=*CDp*ymqK+hB8szShY;@s8+Gzw*?pLZ$bQ(DhnkbtN zbpmIf$Z0rT#|(oJVwv^)D_S@4zhNTXIfRjFt{F9!Qz z>S~%Q@di4Njs|D7@o)|@eid>#3*Zw{H6Zm+`YcGD& zpCHpe=(;WVirnRZ`Fw48wQYs}vx*GI@0c-F{Ob89ccyGhqs1|wt-_i-uFS6F6`D2uxk+j}&n8I&VL>le0 zsha(ya=*J4^0ZJZ{RnP!JbB)cx##fv|^#?1r;+d7rdzFe_7Dw&Fa z(wDuy+y|CK@l1-&E+(B(m+F8k#;aTSNAymic1@V5bNj@mKIRnrj5=OA*D8ASO)lT= z>{W42{K*S8Bk1);#eB^-b#4YVLpkK&w7fqfU`eiz$=PMtp2}k!y*Zinvx_d1ws*9? z-5Yp!j$>LiLEtn(2sxO{#hz&)tJ2kTC1bE*7^-W2Oi+DG4C2LIVbDM|BKvx_qu&~X zKEo`5M!Kgb(GFR?UAXZ_u~`0CAflL_n99J{6AWUJqkF7*T?EiloDlJ-`3Vs_eU<+M z{<9gp>(lpd^Cnooc6Ao%ms_pIhEvV_m2NcuRm~#;-T4WmqW}|L)SK7?s~NQ#qigUCtnQSsaOfqsd3eMd=eklw;!KOgw1sx z^D%1Nk}2g5@FFL-c-2}8eY^Plh$$S;*ppvs9y>l)<)mAUovt;!c^XJcJ++GRJ)u6j z40@Xb_3Jp*relw`7K^0$k9x(Px}5JZq&_->PKLx$BvltF|1|+*E9OidO>%d#%cOLw zaTCMb<0MX;)DeSmFZG+{bQu;530ZZx(FR#U@s$)AFVXSKZpjITF5@L!<%kE#3BM?( zxHvzFfxQ8+*b9an9ogYw;B&R(>#kuowS6(l>K61vyxhwCxX%-4+$OukkI?WTs>bCe z{IC9}tO6lF+6gcZh?N#3`C{tld9&Gtx5OKo%4=(?bKH%ue6`Gme|*l6wzn^4kwSFt zvz|69yU{560}LK-cdb>kkD{Fe&;pofVCUn$A>Y3$Q*kLC#> zzFGzib?(%)L#%&@Lg@7L1X%azNnn0LD&*#(To|sB{DUb}v+vk+DTwjvE!J&f!`m;6 zHGMrL&5Ymx?ilW+${gvgBw_mH%0;pgYZ2f5Wz2u637)kPrR`KeATX#5+m4z(FHI() zd0K_rjCSVQYF%4mRnmqty<5cZYH6?_Or|1-Ia7V?U-Ala&SDYbBz#M4M%!2JuNTG?eBurJj5@QT6_o)b=qs+RN@IdU2F$24m5R5S ztU=7PTvdv3(suXLEb21f>7W-`L3s>a3O(#28k&|y$~-IruYfe|gbj;Zm_2yp<73kD zpIAVWgLX^R2bfhp#YGb-6XzX{krLPgFX0w;zW#9NjjeDT!sJ*aO@FIs!Rvzwg2fnv4>@JInSMpiSV%^dY%$6whl`R~^`l;rr|iS8G+ zM(_s)O$#gWd-z%Hsl8h-yI&c}l^N5oL?}TX~Xo}>Rpl;t+y#(uW=k@oN+lO2IwrZJ!FYQFg z2qg?38#J{PYo(eORpwaXh-rS-ct^$le06n$H*Z5kPwp+^4LruIS09(dE?G2OES&;;@TRf|pUVaEEoGH%*b=%OAqGtnv#Eq`GO5LxT9+FtCU0 zUw;AIWu{$Zh{s6l#>_+FpgpMm3@Cg~@lGYnKi=iSHVXfmXlm%-7$mQtW}(v~Zk%hD zIL{xCUITK(xnl%LN4*Ac0Do?0S%V-Naw1k?dBV%0 z5cG!0)Zw#;(30amuqV-PLex-6@i-8itUFP9@jE4x);^25B6kqngE&dC(53wa4{(wN z5z#o-6g&;*72D|#iSA1f=ANAv2(Mk947*z46*sZurN|DOyk#t%gT1??BN0AUPqs)) zE_fy%C&)zlQtc~XX)d+7+efu?J?d)mVE(?=p5FWZrxOkItS=+ZZjP0>_~`rnqle3(o3 z?qp1fgWE3_f8QI3K?6n)my_Vco32|QeYt)B+v?=2qa-}-7jynW95wG9gwTGta<322 z>$Z?)$~!ur??t_8wF+-U#=@fCIh+xDzd9n^*12v^D&};%^cTcP^c7FS; z!ZWY3)%CIP8wRRZUU06RR)SNVTQsPcBN|EodGB>*GN?72DagE~f zqzL{|3e_MSX?4_tYJQ-zA1D9&a}0VEUuN0NcddI8JWN?nfpxTMC5~Cop~SH(r46PC z9eV7CB7~H_m^*^}9&Wl)(T?KLOM7{|@1MII%3ngsZ=|s)NLsiz5&OFQ<6%g~M)-fd zu>U(x!++p{3>lc|+qg~J%mo;o(<=(E!S8i;;($jQhU-R0i8K;I^l+3p^2 zx|$1^T~%+0iMSkH8ggPBi}Ck|p8-{0G2tTI%)D|A_^VF*rrhnt{y+}?ClzntGJazx zg-g2C!0ze&=1TeZ#b`NeVcY5yK--Nyg!@xYU>Wl`rn13__^4_|AYsTskOsVCI~f>{ zuzbwu#eT1&T`{(-@vwT2Ew}`-zzbBieiNj6Ra#1nAL}tpVUr;yd%0F2Vxky(#FFYQFeGIT4W-fjOtFCfA>9+5UyO>Bef)I)W2&XHMhSZFcrX?|1 z&RQwb->3@Kdb4rzGIqvHac}~^>t(k;lj4%$nO3vQ^V~I!5uB?RM&$eIsDd&?FFGO{ z>Kmw9mwgEun1bu%ghq4<%0@>xA~!O0zNt9CyQ;cgQWhU!|GZT|JUVfk8}Z^;eO5}D z0baSt?eNjl- zGFDm#0R!pe)nB#vJScat&y+e`3YZllo(8}RaFw;lnN9H|!2HT)P~Ng5BWE30ZtnQf z5{q~vkZ%`umE?J)SgNuwMiYRomKrINT%L&(x+<7r*k375pSe)~;riiyB+I@gpL=T+ zu6oTyJR;@*0i9Hu!FPq7`gQIvq)!M!YgikzA#ZXKnpyJK^|WN&1NMj)MCkZauoBme zT5V*w_OhMT)6_V~q7`Kfh2f5)41;9EYDyh0tlAWglrNz4^&n4x^uj zzUdYz;EyN~5C^!yy9S(y>2=QfZS{*jf0>76hP{>li;9#RDM%N4+r=S1H1_NKWk`ia z?g%G-GJ97~PXAo!W)Zuu3VxbFbHtSz{o7faal)E>fX0ZGPZXPGNB_ZS+V57U5icgX zhR9}79B!BA>8=6^8heO1nqsFcoFVH+CC34ozezd`cgMGYy?&5>Wdwg{ptGt%KI+#t zz@kzqo6JQ(v(%YCaFTyx@+Y@X6R^BS78v!~6#ll)UI*=HHZ=)>JVLV`Zo|K8zw}r9 znd5_^0+1JAy7Xu_2#bhAOl^D#vu?zRmX<0aF-T+h6j4*faI%F7-54mm*eL8~@C?x~ z+_UK9SA8UlAdcdL9_ZE4z7iFyZH&@qr!F_Gm_svCeV9+aO@gFwP`@9FUzX##KAPYW zHF?q9=;iXy0%>E_%PiKA?}*!t*P?$nD45X4zTWZ8W|sokgzAcRZwN4@DFoPOfdnmP zx_qK0lKLyl5DFx+?qv=^dgUOg@=P7DW3t9_%eWD^aOB6*C0!zr#Cs+J|H z1o%wI4hk^{-^8Z!{=$ZYiwM2lHfC`+5Am3OIQ&@2?GJv(bFM7@3thgn{48=!M^Lwp zq!!tpas2h?pO^b3N&139tx;>y{Tzugp;^DkUTC4GlB@GBfs{+?n;rX6cH??MD2HX0;4Al5mfnkh!TWiSy*)8#)JuP(W^ABr~F( zp-NLgB2vb(NEmA7cDNf7JT=|EdOuuQ)wy+*FOXMatHj}^cs#v1@6+7NZgBXRey%kWsH$xlD@(ewnBKGf3R% zSm^l@9W)zQzi%6(pR?998W=1r?-3v8*RViRf$y;flPT}=Ei>Bv@ytHmc#;bzEOL=z zGvG6lF=#5o%u3btlk3Kt08WgRMhH#5$6cxzj~$H&lpomO_dK#z=hja`dGcgC{J4(u zJfYH`FbSj6mkCP-4$?dI>a3f(%6FX`mwSmHM(d;5BO@_=ct*c^dxeK0EWI$tfHX@9 zDn#)g{c)Clie?t&TtVE~aQ5tW|)czJS?i^qmJoi=( z;%3y-O!ryRp84Ps(LOxh8_=gLq0pH?^a-jy{rmi zk)o-Exmyze^|7pJRHVMu2hBD-BqNZZi|YrVUaDs;z^*3Vl22@G{6>CY?eiLU+ecX{ zuh_>7IArAo*RjF+c~*6-n6O{!KR_63-9+)zEiKrwVgR^F|MN>Yj14-grc}1U=v$+yv`i(UtxDAX*PIY4!S>N{K7h`omABYmPzB zTbngq0|F}AWuH$zOPS5y9@!fl%n9!E+3&x$DRG1vFWFfIFs)HT<1P}`CxCxC84X*( zj?q9_ek(~+`NePb*%DZWn;V(YYsZ*F{>OV6=^8xBw8VF-m!FW4nmS9iR5nB)nKLKCECcsNO z>iV5a%m8jk5#74LYy7}k@$5s+tK4|UUq{#0EO;pG6EhYJ_7RQI+OZU|cD}C-)IP!a zn@~*rH+p{y>LFVYhKEe7U}si11YBw%hfFiFS^SphcnU#i>w0bjKnU9#>rjY8 z1|ec}z>XJ>N6^kp+u;ePnK|B(0u!lmQU2>Ixnh8l*XzCBQDZ7NWHx7+x2%~fz{@d* zYEP6=f=edghubFh3QMK^%)2>dG5(t3Xm1q^z;MZ401DC{JeyAb4@Yt|E@*7a+cnrf z>#!6fv~YR!u7hG9GZivsXPaFJLfj-Ul%%IU5qXm>Fs)=Ew@W0c)TaSt+_SB&P=e_g;Ju6PSoIM$1v)6@1srC}?yoVydX`@Hb&dy~O-Qu!CufiyUk( zm1fxLc<53iZJU?19Hzt#)LCxW1c)L_h6GLc6xS(d?&{A`q-A-f`K?5^~A#KONHlC*s3D_UdKG0PdSm==Rv~#9I44&i8eb zpK(^-?_n5mkv51~HS$;88?SxO$grgbn2Wd54C7kVp_km2qVl=OcLtL`d3*euqVKKQ zq22UqHLr{;!_RKDkuLZnM$}vw5={-Ag9Yhw%FCdW?tNecMz!YtC;PfL81(II@0DHh zOXwYlFK)^@_l9r&-rzPMx^$b1N!r|UT8;Q?ma31q$v~i^AP(tHbOp&%lHUDmvxWZk z+1j1c!;{)h!xk?*!EvP~Km%I;z%M;Ua@;4iccq(0(i@lHJh!}IhmzQY+BO>BzVQrB zy?wKz`05uuxpGmU0i-to;=Q7VqqkcYld5Hx5x#GdsAE2f9Fv1lolMj5-vm4C4`7^N zimorrWHFlZj-b5<{Hhn$n>H9560^??N0JuhPl3{zKlUNs$VOExFk4BL5YW4~+BMRQ z0bjG2MwD}rguWO#T0DM?2!swRd|d4qs?(Ae`r+3KPYRxS;8bixUx4GVPTwH-vL-25 zbY>K4g#ku>eO!*2O5i?L*}48tjOwnh!2H@4ItqNT%E97E_YB(66EdKn9M*_`EESj} zX?@{LjhRgjlG3Wgf>JA+$Ojbt!1zm@T#zA6Q1IXcL}zIbjO<&|B0ATk_AMM&f!W#9 zu$%9>RG+oB7kRUg9GJ48<(TvCxYJB?< z!y}hWy(W^M_6EI*v`BkE$C>|7O2!MRAK&I?e;?ug{L#4bhogMZ{VnHPm2~5zZYJx` zg5&hDA9-=IodB3%zAt12l|X;^Sg*B$mVn4$$=jl+JVu!ZXHOV7ev>OiEc{T*n^`K5 z^y0rc!r;LB{H-r8maD}_-o;;wMVqa39JpTz`F!wzmJQy;Yg^&5rGaZJjBwtYqZAS} zGqq%g!v(8Pfw=oRMSB6G-^*k|nm!})&N_3f?$otYpFmBc3x|zw7qlHmfpqm}nIR58 z%E2)c^xXs`6u>C&X>U#0@eAtGzHRJ%E!B><`)a9-9 z{?jpAn)f5Zgb%Qr>YQPPW9Gp>KHdYWkg~TMGC|;EJc%YDK{st-`%BwhiU=k7e5v_& zHM0!Rg@uS;G0h@uP*Y>DV(zuml8c7l%1kV(O!%1MtH0s}5~ zljr>>>A4Zt^l8&+72Pu_?oXJ%-awFwWJ)b^`$)tXiBYf|e6oSI0@Qanm$J!iuqaJ| zoN}dLe&ru=9y0FB&pPTRrE1SPSL3Z7LDjT(1m$F8<(s4ISDxSq&T}Mu!(*I1oty?V zQ0w9#2}sK$TyugT@Sxh7l|NTdX0BM^7iBo40)pTzM(~`YKzzbs_`Cs`(3{s?;H5>g z%Dje)sBZxA75S;ZBdaA)UmxAah|L93!?jcM{vO@I>4l>sVJu#jjlPE`P9I!EgU6hZ6!xO8oo%@I+mYAsAX3u&!r4 zx(_T2U6(EA%=TvSo!os-asSHJr(=zUNKUYNwzV@3-eE`;XB}N?K;``JRyAR8VMRAc zXKL#ZD_#O(^HWj{Sbc<2<-s^~XIw3;9W15IVO7RD5-|Y5(jvIm-x51UlB*DJ3T3PH zl-*}j;FKmQw}v-a)T-H>g8(iVlST=83?x?%Eb;VDz*7_!vfVmV7(reQkGuHgF^OH; z0D-V=e6h-jzsDTAAtk1@PiV#p0O*UT_RR;!V;eCj*((bsD1oLO+% zqo1<$Q@|ikQiu|-w8n*X|J|4-leXgvtl&|qtd1117q`s`$ju;S`)4C)6Vx@(ljpJ9 z0LB}nXmE(eeK+hrO4;$0h9_n~tFDmZ@n7;aO^wLl)Dunl3{O{%h}<%1s(xsXxQc3f zeaX>0JIf8O9#n{<*j;zTRPOhO zkP<{zfat8kOZ&FC51V>GrA`0xD4!W=4l63V@-0AK%k=%BCiGGe(AGneG+{~O?9)q9 zNZXQFWVC38C}H^2;;*ozS!LQcyqb!XKE68@28L!2OZKPk?_W5-_kP;hEcgz(v8l4$T7dX(UN74$MQx+e!+#()O7&L4=%I7 z4Nz7njya4x2`y6JfiXrIF?;WbJ@4kOygOO{HMRd`fbBN}puS^J@IRvSsbq#0(*unA zi3y}eB~t){>BX$t7k%_-y;BW0eTCh;0bl1w+k$OyvHB8ErY?vv5vhTHJLR zo^{GaQO)SDOTcdhldxQs2AtkJUm&e1v0{wP#Ceoj`7kyh`;nASbv{voaTuo0X+QCN zXGKNfoqx}x!r`9d_}--otN$6#OgM->x;1hP^J~gW*qXK(PlVZ@(wRUx*JssE-(e=d zBx$3ha$sT4Yw7*}F-N-HcOg`Dnf%0ncl(HSY=0*n+R(R(Xyi&(Y!%FM0r59?BpAqc z7MuV^NS*s5xg^pe;phwriyl8p6XP^@df`dbKZeX% z>gCPVSHnAi<^;*xT25xowlsC1Uec67Pnw$(a^mRa@qmmrBGk5y1XJsKwv$Re$^NLHrgwCR z>97N7rk%X&vqK#Q5}S*^Q5cWhECQxlC%G)s%wiuCbZ#b*Xi_?T;{rtwJ|Yl64}Uba zy`BmshJ{!G6DSRoDj$p|gUqegkGl4mtZi0e$Kk9wmCXd;uU5pn2s#P%&4jO1OM`{E z*P?{AwaA-u3ED=|h=5tletRSZUG3ePc9=8?CrAF=V>7N*V^?KahzVMZy|sZ2ect`O zF|J95cA2>3u8GyZYKqE$HsuGt$u=d=kdZ8!GikF%R@hM>M16 zxsALE`u}zgyI}vDK|HM1YO~38V8yCC8UK#}QPH}Ga+*h)aK8$_y!QkxoBv=0$N#22 z_0p8>??Dkk>rNmZ-tOW*D#789)Y1=mO9VksQsprTsEP1XUzu2Nhaet%F@lN-*RGUT zFAi5I99jK-^eUZ74M%X^blJ|{|8mszWA*zxDx?be|56h0xcE2Acu@v^9_(Ujs@(R_ zKXJy7k~Whh$HxjxqBeM=p_F_enOK3pZ>s!iF8I%<+50SI)fNN8?BGN)C_ErCzv$ya z=ekZT?}8#XL&}%Z=e+UB%B35CV{@l>TE|HIAj2TV5L({7ebOp-1d@V&W=4O2mJpD3 z!OIB;5;6iX7Im^au7frW2>h>=ccrTEKia0^EHrw&pw^^12g=g^=h|a^XTCCdGJ6v+ zTEgx=I6WF24D(_FSS7KlZs}Vq%bMi0&bcsEwTm``ye6?9dDBJ$} z*sVlr)A1Xo?)DVWzfLr~JlfW1S&k@`^20|Q>&_k=wWi9wEc9WyA_X6W^TvPC)0i1o zSKi)g1(yTIPFkS)M1@<(WpXSOJs|Gm ze3|9)-h9LkZTdJm;)$HiFi<`&Je%pALD9mOjY7A8z`!gDHXA(Zyh1b8ti6C^a+>2Y z+jGPKsPn~v;AWC`Kf%zz6eaQxgD3GPhjEf^KL|-TGg-iC3U|67dO^v(|+OsR^dE(74GEz`*7)U zN_!D=bbZZu@U85^*>0Zm>|!Q4D6%aV*W#zAtLV_Vym~f1QpKb=T$)EUfB*LR z<#-Dslb&QYb38)ANGk-&Sdwjn?R_%IcD%qQX3FOSXgBf`8!qtTo>{^lGyY^9-K8fd z2i};1KB|WF0FYqLlrQE2a5QyuG5Lqi&!h>K{d(dhb;W&;`hfAgPz8(G-2$_m!w z+@*_G?CVXPxflf%D;=y32XV}vpu}Z@<1wJ+3bcPSHnS8O_I|_n<;sFu#!W6mGC($y z^idDqQ4|s(#CDvvi8x`?YvA8N$tf4xEJOx1+7p*V1zmxvllH?r8JD`Vwv6_5@QgeY z9XLn0UpHy9fN9_z;Yq>tqAZ%ZLd!@l2a{{AFxD+HC#6!VWu2w1??JS@!_+Y86=*H! z$3MH?`E@qGJ75K&&4u$e`%_w9!r&wX1*DND>Sycyi@>bdqhZm-u}*NV_=2X@(FRDR zrjP>H>T}Wff*1WCm|pKa^yV&Lzsv)m9LK>N6nOn}EvrheUz@2##I`l=KLmG-S)ba4 zMep2dp&P8xKJ;;oid72Pg){1Fa>MC!<_7Eu`vmAb_QI4HJSnlOu9`K&vHkO}B8GkM zaSY}EEfo9zhWj@^t^s`dCO`)?HWeNK2q;6YU31<(C7NAR{b6;0o$~ROg*zmjzClz7 zs@`50OW^_Pe#l-pb-{ObtZ-;*AcB&9ePYaZwVR(I;SbD^(5odBCn}DY}*n}zt6Mw>kQ#WW+t{;^f#UqH;9RNZWj$)?!vh*_nv%uh?m|E>P)~W0Awvb z5!4Cu_-Jn{s}voGJffPX5h)VH6?a-jj4X-HqV+G*23ARjmv{#s5_ z^#Er8RGcCZFTnpT>?r0oEJ3)0tZTdQSS#wn6BJlg*^c+9NTq0#5-j6PWno?dXJ#kM zaK%q_?-pAOpQk)L)T)B0-b@p~jQkH#!6$?70(#`MuPvz7wlHseZPOq9xk^f@0gEl! z0l1KWA2+ky!leISux$xHnMkgHEXH=xhMc{=WU>RoaAbSpvnuewBT~Hl#~)B&1>^@m zEt}~WfNY4~rj{9GFiz4xN1@w*n=_3^9q9<-`Rh)s=n%(kz3j$q))b)|Cl5C1SAEw^ zf7s=~E8typ3EG+T5&M9TNcB}b6&uFc762x|T?3*$E$Jh#C?OTl)#o3RH2|nLP;yw4 zoOZ@!oMHekNo|-#v0xB(^F!~xw0mt2w0Yo=wa8@EOb!^xW+&`5@M!mnh`NytPkf^q zo(}TJnxn#XkKA*8-4&^i*v1W%`g#L!B$O&y$t2vc^&+$Sf9d5fCTgp(AIv6-FsQvX z+uHg1mgX3mo7Oc44O1J7^CR=xE5Rhsl?4PovFro6ViVuciac3*V?Ay&K{NX&2u}lH zzlK>BGZ#-?RYl8;-h(K<20Y44fW~SQ`(P&~0~DQaAX{40z3^mcQ8xU74?xKtBAM?w z4CdcyZo3RUisbgdj??ZnYDj1Zu_9SwX+n|xG*K&fM;tifCLHEhU<>@&*}Xaf()r>> zuGYyNXO9twpYVV>4hbjtNO2rJ);pwo(c^v{!D$Z-5Gw-Uo>?pgCRP$70>1g}fn=yG z#}~}L#lsZ~(O{W^D`WlVyx%B?U!Tt82nzYA2l8!FlSvggEHo%8T(ND#!XZ+C(9V?o6R4m6PPYuP| zT@hi_fg)4QxsSDcQ*~MUx1hK|WmP=>hgVjV@E-tTil}z;zhGWJ3@kgaE*!$>!Nrs6XLA!)*-u>N-B5l;J^hplPCil zG)nrx_!2%GM{`Aus@81$M3TRID`?*pk9FcMUCXW5k$o>h|D})28ns?`!Pond+`l>r zK!Z#WXlKeq#_FpBVHnEk51>4hBgoI%GfInSYzShf?%q#c@m*Ju>Zp%hNH+2r@_ zOKuM{P~jkhfjq_SY*lWT$5Rb>m2HJ_p`mnuSR*a)JwgX>%#4TeARCL25laQ&y6jyx zSo5{SaxMt4o{Sob_-ojnugJoN|BN^JX_*4ycyj)w+|G2KX6g?RMg+>*O47{ky1$eJ zWzbzh$p9pl(ZMml#1O(deFtXUpTIL6EbJSw`5#&8=2@jY-Yg6^J5}qVGwH}l2adn7 zC_EH;6ayBf2U3mN6v{jKwugUuW8N$0;9^)a+!fX;s@L#kvSZP=Y%_d%_;-KL*Lc7rAVYlRiV*+798lkq?Mt*S(1X_vT5 zF}rZBHS6QQUJ{@3i}~X@H^Q|S#%~&6%>|||(E+dz@0Tl&CzPVzsdV%b#IOpP_d|Gez^4t zpm=(WQ5?GdZzWdz!aw8!2-S2MQp@n9O&W&pb&LOY%FfJE0UliZzj<(!ZX`4UQ}1gN zEjaGP=PX(a2Vj<;)~Pw+m+Mmf-eDdeR8`WC-l3aekPxIo+@N1yBP_W?%;upUEHr_GYgOZOwBc=W}@8U-E^VErq@ zqZ0uBycG~H14o}@D+uU0y(`0uqNNGl4Q@-_C9v=O0>(|4c&Nu(ag>s1Mc~m}{>{h9 z{!Y3UT$1AOrvKXDe#rWOk}G6tg#4&xu4JJ(GSYR(^787z(BdZr{gc^!(md_3f++*)K3OSa855ZAICei57Bp zc6OD?O^C|C83L)4BBh-h8$qSN%P%H7l)2p1nX=Lgvi{j)KYz}g;HwSSD#|05qWL_Y z$v^TZa@&ZEhRieq6kqKF4<@L>Whd-cIPMfMUXnrNZ&dx{)?v4EcTY5Zb?-RQECLoM zb+Zcs#}I+t`-mX>Nq#5|1boi2T$9>vl>$(j$`YyE_Zdqm`0e*E`|paah;@txA6vaZ z8{+Z%e;9iUsJMcpZ5VeLAh-?=0fM``1cD{FyA#|QoFKu&;2sF>?h-UO!QF$q%RfnW z|J`@L{m%QH(`UNxovx>=x~u!%>8Y+V5N~yK+qNBmoM{}m>(8yP?<@t@uP_H9q^wqE z>JPJ&;oKEGI3z_NtjBghfBtH78G8Exmtb;Qh`Z(t>JDNlPAQ_2cLX^pcOLs%pk-j6 zct#zEdBrvtl!O0j*n6N$U2{5J$H&Ta9q;F+$B>+njxJC^nG*+MAtmBludvlqahReh zEsPUE!UNr#=!cyF`H71ZPM)&bN2_a3jJa?xEk%`RA3qzVf*x#@Ep2yJ!Ysu>`vBQY z56xODEpww0Eu=Rnw~UZS|bA(eQoS+;EaeA3xH` z$Ly={$!VOswb|nX4k{?53LHvEm<^bR! ze?-jc=)*@((|Ji)`n9E8Z3h>O;t2p0=NSapaXh)qxEcV{F^Rpn) zs63mmzBITWvnYl*61+G_>x_P_?QK#Y=1L!R5lFkJ=BNo|XJ`HbObZ!#TUTzn5HVR~ z>Y^fdB<&LP$sR{cWsm+z#%ol6ESYkuoBHUkl6>W2`6eBIlm#&M(7vdqThzxpqoK8O z4Z0g8s|K~H1?YTa*gp*#tMD?WI-i55k%u^jGLKqLXbHlQU1V=X6i1?9#NU$zd9P1v z8g8Wzf0d3xhIvD?KWiYEz+pUYBZ8JFe4Iq&rirkC3W`Sfp>@x>OoS$QCyhnj*06*Y z63A+`VH6!Zuo7G2Shy`G>E$`Xm;XN%aRsU=21C8*$JO69#D~?JbN`g zu*0LWx>boiY{|}k9)u}AAQ()1I8HSn|J6H_pwzPDLJwrMfqiY)Fh<>~S|EmaKwT{Y z4RW+;;0boh%SNw@3*o%rT4rZ=1hqt7aeNL915d=Feks|QaI7*$l_Z(bs zB4Yc)jX()RF<<6n-kO7F;Rugxhd?^rJfcVxrmj#n2u&$u6~U!*JR|oH8~3kK_Xnk_ zB0Nr785{e(Ok-S~H0{gktTswQW@GS0W8A!|v-Hjc_v5WiIrc4kUX_y!%Ut}DHdCA| zdA*QdY&n>(%&G8s_}8s!{?xTMVgGM&$T93!Z`(UA`z@hp869UFF9Q#ZK(nRktA=&1 z)`EsVG$YUMUHQMopITy;Lb`rYIE#;wwElS0aK64wK6j(+W89qb)VGqz%)|(U1`qAJ zU?nQD9820fvI4+`aJXzWMwoCJV+9ngBewvCxmQR=d zrh1`Y(vwPo;eZr_kO$@k4V)d7lW%fnRJ+fpTLi@fp@|J-E2DbVW-3D`R^`%{7=_WC zi7rLwvid?q7@!Qezz({Gt)Kg3`$Jcta9#UtS8ptNwAG%%*>2i)&QpX;jcK8TfA2M3 zA}S1ZstS3_UMRbGhnK{=OWEl65wzQcLZ54?B`MZP$CHVOACdQ%ps;3_iR>G6>aJrk5Ih;>=_icHzr3duT z1BgsxaHsB)+MaWcwZAoBArl$9Sr#NIKSnOB_4WW|YgrGyJLp&v@)4Z2@L0FI$+#iu zI3e>B!feW&S5s|WQv0@*nfned7?U>eqf#s`g+5Op-Ie}k{Kp$ZWP)5&IiX$-_|G{V zRD5N+Pd`2&qDMp?Qe6GGT8N>0f2s{iMeZ*kL`EWM_|P8L2TS0IA&B+pt2Drhne428 zar&hx%Sds$d?>J_mMLWsh(!Z_KH8 zp3BiejaySKhEasL-xTOn{`}d@D$Lb?6Pg0uQs+6Rx zFuFV24un?%I1ljbi7CY1v2f(8b)OJ{u zT~Ewr)w-ZVrNsvmB#q+e{h$CLrv3yj)7a7Ce|m-gb^#P6qjHNIB9>A^5*TeS{oXWV zT4QMbBWH}BP8~c-c|vs4+NaMA-}>S$@YGa`2r4I4 z&lFcGGZ9qSw3GsfV4()B)a;10CmHJJ!Shy%FRXT0*A$VH>#CoOn|J6z2Rl-6q`kAY z-JG6bZ?I3c$vpT-#t;leDFsOMzeZ>9iutRcLf_2+bDldw%R1RZdvZ$_V#@io* zxoHq7I7A%V$#iPoqu?y4ynZvv&Hw%#$~*ZaUUxD}@lofS@c!m9B0=7xH%5awykVTP z7E@jY$AUkVK9OxkyPTpWg9wCaqJ*!t7Q$K9)Uv8}Qe1zEFJ0F;o}Lr5#=eXV>aYsn z%wv(LO-=+ui+Re;!8J5O;iH?*gjn5o_iu4INSGn?xtVG1FBh=X)}pM?saToIav#+L9%Vob*L@d4NkO34rK2feQ@Kn6WaWEYA1QK2Pv zU$^a<@Pl(cE0NU97hdY1D;zGqPGt5RP}4JAE5~PlL>2u&uKBFd1JX-`|3ZiN;pP66 zg`s2gslxf?VCh9`_j%Z2Xp?|7u{wqhPHm0>&>1&w@%^ae&BF}59eKC8 zv^F{lIX8N~tw1w6@{F>naUqso1jg^ZlfVKjO}vMCJ&W;y~ZwjavNPJ#jD05sZhF z-XGPqDRPw#h~mFU+Ti*+8XS>rxi(y5yW6M?_?D4fVXKqcC_N~d%WTn;pobiiF5}C^ z7+mm2Rl8rMVRfUE_6r|>RC~I(JwBb{S$6O!0=@aV75XTe6+PoHo*6w;lEv1!^o$Yx z#p2zS?>j6#3gP#%ooKUN%m-5`zfL_2dB;PYldbgT@7WR2u7W9;paM+d^$mfGbVQ0E zjT#{bCU0Ybl-SsMYZA?ZxNQS9vBuN?GmZxjD|kT(@D98^2fzkn4#JXywUhynI35j< zY!p z8?~RD2)n<(tb1BE0YhT=HasKRRM>+KPoDSUeDsH%v7I(*khMiMQ7%= z*fz!pkU80c(1wM-&^QvD(u$%jR&VTk5!`;9W&e(7LY2PlrZDN7uxFn8*58XshxvUT z4Fjx(8v#3pR$3bIPr8o#?v+AXChHQI31%I zUz6X9PyF(l4j@ym8nhfK zM>=!f$Ns7RL=(>9lV$mnah)wWs<)*}@Bx(wRBaaCWG@;1HP-LkcJl zB{O4!3<+;J!fB8r{+2U{M2~>)IHJDcRk1Y9+5|!@cP@+4&UHk=>2QzU=`X7=$^4>C zIug}lUe@Bq?sPW>(qPaDrZWMuF-?8R?N4k;5^T7>S}AYBCkpARke1|`Us(#FP}jA~ zMH5o8m1&h^ufJ4^ESMG@#Uuq6sYTG<8@4e%^-TjP5VR6M zB)2-sPPGoZTJP+z<>S=OjYf^NUFM4Nt`vrBV%x`6Die(C!hSkV%83o64Bz zw~P2y*c_kOaodPdOUG6O`o?jGEvCVMJfqsjG0XncuJ+6pTa=0BBiibbTBqs-K6GA- zQqY%+&cl2ph42;7*^Xq*#AT&U*Sy?&HDtI*-|qTPjk|tZ+jb^G6=}U}Jd!Hbv{3ye zciAM^+!Gv~hv#l3^SIev3t;;mmwHo6#+eV^7ZZeLIt9%1mJ6eA^Wc7FwB#3g^JN_t z;;8%xYdqmB)IW`yg)2|$Uu>!ul%;aT@bcAoqJZ9@`8H(^a!}4J?h?l3fAOh3`k2b) zHVs2U?#(_h2-cN@UuB6Q1s(wZ%)hEHzF~+=$J#hh&DO69p10`XLt9ZnxgEKD5Jp?XbU37 zC1=_KSKV<53o!`G1FjSb;(|7KRR^a+ zq*UH9)>k!q@4*S@cV6M>aQN9A{=%i&kr7ha7q+nY*0}KM0gteb@tu(8s_&6-2VovJ zJaa#jh{h;ekK+slX2ory-EltzFly0Av0p(nrME__OS!m$pc#A>{WNN@F^(l1(BPeb@{`sYO|nmdeR zORNQ014N5V|6%PBmn_Oe$~XsCcB z-~u70S}C+6bqrcH;I$(^@~{W*fx6{Q9#+U{tmbBN?=H#f_t`2&(P{Dl4^f(5G66ll zW{T??I_+nA(xF*l%J8D0d5=(l2tKH2An7^khQZ1u$e5%S`it3Xh+R`8uhFa6puEi% zgY|^84&a{J4a?b`<~=ya4(=5gFbHsi;Rb^Jh5;n7TtF^xz)t`pG%vVv1b{=$io){V z$k-XkqK3kvY~*BS?*ioEgt!D#6&C(ySSUX5+6cfJ95xJiMF%1O{Ur*EG#e`rlKxR< z1G2IGv&;@;WB=0x3>(LvHfY%X_cqu#e>dVr%Ek?Zq(7*dY&<|n`bU`;$i@o>K_tNZ z_qMozY=6apVdMMT79=j!|0WMR>+iPQ;n*OdtH2H;08wc6zqB0x540SAXfZi~?0FJ9zjI;Oxql<#Q~s~4@cdJ$ebkc00Jv*65003I&ye}U!#cTNIg zz(Kkhz0p#NNA8Nw{t{4H}(y;xv>GGf8;8lnQV{!2SA?Y7@ zyg;tM{Br!OLm{*BUla59Wcl~b{YROL8_31|yQ7()|0&*on?(P?0ry|=u(;WPko1=Y zZgwE|AEhIK=*vGRCimY(fQ0S8&8~ldg6%dih`^^K0CQ4E47xvI=pajsCO0<_lKz3s z1LWrUvjF@%_TNPe+&K$?Blth&+CRaEx*%b2A>isrC@eBOtU$=?vEv{B2cCxw$iwzO zAt_13?Cl*~oPjz#f5u4Fb5zI;rlHD zknPWU0c8K9`9OA#KR4O`j2@8V?@f+BRUmEtb0PjCJeX}AK=PY^xZiNtY8-!QIR7GW z{;31x`isEzhYuk4-z)CFO>+NX;6GvirTjeHzZW4K@WDI)hwv{6&mSDUf2+d-CRhOM zKs4lK0YC%EXchslA(`^u%zF_)54p-;{PW$=->Zv700T59m~;uES)5?`-xVH_Du5maGCTirvI@cC z`dhC1TZV8&g6ZPm$nM7BW^3f=!Vc8o;sT$oLNK_%WNQFM7>MrwCdM>#aCKwzaP)Mu z;`9XSK<+xNLAZtFnUEa9+90Gd3)f2R28^sx76-;c*}cm4 zZL?#r8)U2niw}RL5oKWJ`S>s=AAmr1+0Ozm?vN0h$PLEEu7@LUbJ!snJdcNKUV8Ah zV0Siu+Ie`s7~$an$!r3nFo!}!1C@20-`bqE4O}5P4r6u?FWh?}W_q3Lq+zkYpimqo z)d$dgKJHiH5}%ZVgl}94F!b;+d3^otfiBx=zSFR0?dPg4h+wh>NJ;>)Ih+S+oY7C) zg$Z8Wp3cZE5cgi4j*=OGt`^kg&SJ1$7fB9y85)nACQDs_zJ}%ah$QyY0;wMQN-1P3 zf@>{|d+LwEDrQ$hW19gK-}|o8q{Av!>RQNTeWR3Ts?mEE=>|9V2)`L1U6B%p@sv8g z7Lw`YLzOH*rT0P`mb=m_q z#xd_`-ulbqjW?xq?&ZE&|E!V6T0XLc9E!p6L-ywK8dRHYK+(@fIY2od)3GIbOB^MJ zyE#O0ikxZU^f6Zu+a*aZ5tUE=+qU?3dZhaV(JM ztof_5#|J|?kX|l5{29(=68?nhmNYneT7DJpjFXCcWlaxen6jBs7V#G~Zhk zQC=vFAX^j6a`-T@K4hc@{~q80HQ_s<5%ltp$#E3T{R0MqFSV3F2yX8iL7D77d zb{7MAh_sV;XRT+6A!&*uS&DlQVB)go@zWwAi}p}EeR_dfrt4z^J_)hCgF34Scf{D) zv1WS%Eo6nWsr3!2PvX;C3Im4TDpq50!N>_hahq{|PK-S}iAZ2O>7#UK9SL*j9B(sr znM_aTl&Y+qm`=_yExmg-3J#PVM$T=NdbAd(tia}HcjHx{L%>|feM-tu={%f}Qf>(a z&-V)0e1I7FPfX(Abgt9PK}GWEmEbpFm-Hp8P>Bs-mh=!b9wPx6=Tnb}-0sLi- zG(^xVGC4a4NiE1rydUcjYCls}qHDux@&DAR;$^|vGppRoZ*rXgdRac5)_?ZD;E8Jm z8A(OLE{{*M?um+6Yoo23YV@EWYA-LhAUS#AaY-~bHV}7_^8k`n zilwXCK_JoFc@o^EgFUHBR3tj1bm>lY**Sg*->9~lSb)LlXhF4wlZ_bFlAL4~3qkG(aLg_M545_|9;u(QUa-S&O)e6K zx?aYyq%nDni0{f0_`uPqw8OsvHS0#!@IbzG41Z(s29!iBQn4u9bM%mY?0B4)n1Caj zi|?rOEPdm@6_fI2n7@blCEebAri4S6eLEko2$U50!;vxh0mhlh$Qv+o`GRu+A3 zgbmKiTSxvvb_?F!;&k$z*0_tJYVjtqrnh(T*3bRls!Jl?;}#QrNU)Bx4!5qe_R)9W zmRe#iMtuKLet#u+%=L5eM@H&;!L+%nM>MgjL4w;$Zae5AYoAPA<z_kD&AN`ok4+Ik z5J;cD69I{tpDWhOJT6)ppg%Of^o99A*S&g`&ONHpz6oQNvCHxxBj~~Mn$DYL{H1fr z6Qyud-G%5eBy)=1X`+35km{FTG5bAV7(dHQ(nIw4i_fFp9>wO1uAs>+@w+-J1hY{goiX(=JrzC9{x-KF2+i zj3;Pq>VT}BP-;x)h9cIAYVWLXtaF!Pd|S==VBL2h(hv3dWh#tb1@?BVZD!PyOswcc zjx3wjM@cnuH9>XNdJaF3amIH3Q?<&zcK{rgEY?r5`w-EppqUSSI48NPruay7N|e6m zJzHT#(eW()BCyXQni1P{W7Vgc&7&z+ec2%2z63$0_O5t3C7Lm|_Fj^lsF|!|yPbqb z-*ak4P|DYL6R9vyTLObOz5IompA%m`Hot87S^83XzVVEmR3d}eY|@gy6^{3z*4qW5 zj)fO#`Fxu6^U!~OH$umAz?}E}@jb^whQv*!?DmJyxiG@kbcH&E@`5)%aDhHp@7`QO zoWZtb?aF0h=BF-txnW3=@%jX}g;eoLI)A?vb2Qz%rBf!46iE_He4W*BqPEM(2b$NF zob=KE_>>~#R5YQ~&fl=(q8IwZkH1}kM?|4r0_$yi%8n%Topza+0H`AE#j6A&!= z9M7s{1P&lF>2jd7-7e5Kv*;%A-MI;ypFLSI$2)B~VX9Fk=sg6|iy|!~djI|>XWm5s zf2+u=FhXs=clnr^8fq^?-4Zo!olP|dzDZ&GM4R`z5r})<+M7zFJGSZv8d4x>QH;i? zM5{%LF8vQ!UA8q{*3A9Ny5sZhmYt<_Ypq(EzWald-SSEjWfmGFKc~ zmviqQRnD!}GjbR&C!nkY6-O$2vTeu%MEbP_KL>x=lN^#a+r$4k?aQIV5ef3q9>BeN zJgucstY{DX`F0FROoGFbM1%#80i`HT_9QSN>ql`)_~{bv&Dx#Uz@bH-bu3^1PI*-S zcj2C*=U}b)6``*dgCXk1*M{%E`+Zkhmcl%y@R<=n(!=iv@sHUsxwX3=#z&4rVHZUZ zvoSfGl|;U@G2^#|Z7Xm`LV5t%fuZQiFzW+0=M-VOk1;LhNq=XUZejkF1|i5T5}j27 zaB#Qnh#NP7REM+y>C4pPm1%IQvD@gT2%amFp1>(`72yF_xb1ug+;1w}1H3o(VmJ(9 zI|M^!JZRNo$71;=?x@xC%Yn9T4WfO=cmwRmc$!cNixkgxk9GrvARaWF!_a`#W!8b* zjx-8J!z&$As8;;|=e?5K5BbIo=$D7}0oWf=bHqE#VQ``RU|56lh?oWpAH#$q!8$9p|P;^l=!c2n|Krvss>Y-nakp` z=jWp_&`G?`qM)&G2eDPp>!Cq&(MXI8DNnIZZ=rGVNqlCVMv@pAQ=Vd*evif_B%#=+ z%xL~ii@=%&txh*I6JrNl?bg^t++l+cXR||#4BiLi)+5#@hhR8 zRGv5;$&aXeG3b`{0ju!OWT%M!_r#(nil;J$GSCl3;{^A5*a7vx>@WUoGORO5^ge+G za

3=l0`TXzI%dWec4#Y!PAp8x(7Mz>hANy!=Qcq#(Fx3>Y57fP|piH?jNQO|ieF ze?JkwuZS&(ZwFv!$&pdJifW+<9>Hc_Ln%E36bg-RU{3SKy_x4Jl*CAU2mLjH6F%{s zF)wtivXe-W>@jSy_rOReMd%#GxUTjItY~V164d@l&FzS?v@?|RxCmTT0%nQHdFEHo zK?x?M_y~}gUwkcWvE~5csBpfkoWPdL)IkxZ8JQrZ{ZS5USQRx8KCZ6uUWd>z6kWS*@c^y=agHveah4#+r(ueQ%e86w zmZBl;r0XS(Dwv!W`_Y+la6<;TO>6M6rO@7OkvbU7QIp-(Acqg)s1zy1CuUNzGJE@c+9zNG9q2N;Dukvwc|)zqz~^K{+(^^6XHqp_F~z9Bj_gZ0s`Hj0a(2yw}f@osGUQnD3LSzsFdeUaa$`Hjim(DfMe>IE&Z3DwcN790t|? zP=&lmt1tlk6Xi{@Ocjt3Cx1%ZjF$-r2VOLn>k}a{%}=-j+}&NW7tBkvNZK^yF@iNt z_J7T{y>5NoUuS6MdPRe(=R~bq2lat4O6aTg$H0D-LL<1Bco2d6#iFAKA8XeE-A7X* zyZba&u=T46CzYy^dpQ06)##4zeZ?SC<3KNJwjo)*4=leG0ZiAAuFj~5R)Un*J`y%|A+4Wt9-KYoS0B0}I)SZs4 zd3M>vC%ie1#yTvChnl03GxOmCGGT$YVDY z4Kln8g~&F?F4qfI^*-~2r&rj4*1=FBwPjcf=Ex?JGKdWlArC3KwTWS>u#7*UjmmU% zkQas^!4H0r9Sv*`BGH;0Z1e3m5U&mm#wEZ$vi7Y8VH`5H&+Ke>glXXO_?jiZ39?&2ChKHap$on(gJv zW|?*YY8m+p=XjoYJ2fI|25@bsg^7C8#E;vf86}i)9|Q!{P6pz=8e071sH;r_hU*N& zDY<=2l*T^yRLu>1US8&j?wz^QR{y?PqV3bZ^WnqU&yn_N!9DYu;Y1^`eESA<0XsqI z$~vzPAAT0^Ba&ApYOSrpg0@7jWGUHHhqJmC53yQ=6mC{?wq^-mNXB2oBOBJX;>QJ+ zu8U|#e|?O^$1Pd9Zf8uuA+@2DDj5T5U%~D z)xODRlijIx(3VB=O|K@KsP_Sm?)>iIG@r}ZUB)iAULz=qjbB_i3qFim*5e)i#&-YA0+w#(zO>!kJRGdcq02qavG7?bUngxyvRC1|43|n$6 z?Qx!n?NjypIOfe#qB3!(D#kBf(@ULY5UZA(=H!ivm4_O57>w z4QEZReZal7dAt~h?kGs<2wHgq-&O=PU(1p0ZD+^JtLfgd*ulMqeooY9|IS9_sNaK& zO3Yn{!y}W9J=GhmwO?jGh+L14-+%~f_x-zF#I=pLSA`Xb3#`speWQ@E!cZd_@dJ!- z&q(DPAZn0bEh1{cy9~ODH7Uw03MnYiJ*e2F-1w0^bs?{zw#3LxKt49&By~V)M~wew zu90(GA4Ujq@vZy`suI{K&rn9A^G-6`f#Ya-EcZ;`;D_C}HD3#U9 z69|*KQ%MFy48YsTqV9|x!#Gb*OCS*`5^@fZ*!C854*yos8`m~nVdql7Y=RKGIzw6S zWwbuGej;_C8iA+p(I43$XWfUZT(gjcmEeZwj&X!}IU?yu=*p%g;%TI0!z{AS+~R9l zV*~Aqf%@7$t2f&F6({8`mbg4wr8>_NZulbl^qC{42{T`J6`M|#udXGB)(LB=2>j-N`kxAobu>KLllB%ZJIZf zF6M6Xx!3F_I=S37{f-ZI)0tY4<%hezH75S;NX|*3sQ4t1th& zR=qrKzC;LSnkcd-A&DxWlAG{kCBKctgXIs7E=W`*5q+h-o1>+LksbRQCCmjhJs(i6Z(bPc=Z#e zSITB5yJhcFdSnD@uaPPfc?`dhKB7l%D!-yz%b_~p>hMGj^B!4cBJHU5!vSwuoI+IdPhMU< z?e;*Y0Ig&cA|qmi!HX(ECZwYH8dY3Bk6Go-L}~=7XHFc(Ga(6c#hYNq%G0q8563#i z-Czn&exXTo$9(iGoBR(4yX2N#(o9grsai?&b`OtiVp)o+Vh_lX8-jdX5)oxkJtu1E8)u&Q{bT#Ox-Yx)it-t`(;udaX1|ri;ek3D@>Y~f z7y)}|ZxXO~>79mj*AX&=d8*%I(JM6`owxcZij^xc&fflLc8cWCx1D}KA##~nI@GO^ zHX9lp9ait+rnF^oV1&sRV?5j5BDUT6ItKFY2ZebL@Dy;bk;hbBVCp%b8T!g_Wl4UTy|MW_&kmq$9GS62_xb1UT zsgz2M_^y?`A3ABrlTVA=Cz!aPFC-w}{aVc!HB7D=^U8@3U*wXvH!2;T9>(BbN%uO; zEp%kI56xBp6{F|ZFpCNJ$SVS_L^mHFsRBuZKPY}@LhTKE@_S><{wp2CpTP9)=m+3r zillvA|L8#NEQF&;ev9rG><4=)j9p_&~+)RQUsz zuDaz-|Kt3TyG2nAN;Ow?n*av0$a9zRWWPZn{mff*8U&w_X{&p^glD^hJkg*G;i@jV zv3b8jiYTr}1|Ol9061b$(#G(YVZ=Lu>ZG9UkPmTFv3c0!1w2Wnyb(9mt2vg>SkbR8 zp2UR{d7M0dofi&N@oh5}3=f{Wcw6!)f~WHr#e8R?v!NEZg;3|TcIr4UVWT2PMM(}9 zwGBGzDjaltpX%GA+uJctBg|G;Ykxfq2Y)m@8O`u6Ds=g^itz#i+C#xJu!-+7v^4V{ zI%(t4d1*UWTv8J&MiEetQPfkRNMAK#ro^@syP2I%EHv+6vP+mwS3pQEcWN)VYDT*g zE3A8+O*CGQrZHPy`kp{fY%#;D)%L8Y)v&R9kDjMxKa$Y~Vd!;ev0{#_;QC|u=V#Ia zGV_y`O5!)z78Q-&AlVx{o~||Mj$Aevf>Bp{B=jAP_u9+IN=83Qu9CJEF$iyGPdoNj zN5`&T4>y-&pcCVc_^LRJ@0H&?Y^tZoPdt?SmVRQ19Z0Wh+aA%FRwJ|nnwWUejRvs3 z^UCZ8Z-c8T4R`q`tESgg)r5s;xa(mmew+WMSQyD{9ItRwz>6fkWuDK)cZ(G0S5iujh3i7%xx zd@0QHCJ%8rQD5yk2~O~x8T|o0gk#s3SV<=wUgDf?oQRV<(GElAKGo~-0B3? z0#w{Oy;i7Nju}mJ#tegFELEBjzv5zkg~L@gj8|amJn<)_rd5fn5}MZB)b&%dID3AL z(=@s=<3i;D)dhpO?r~@h)BaTd4b)g_M{@sD^iK@7K?*pUQ2f!JCjkJ{3&1FNd~D~A^iiA0Cm6+P=W z3~O^(#ApbJCLeG2YJ)S@o0~r-V^E>cdR`K@24>2Kfh1d?1o98^jc#PWXvE2U)X&VT z3hhXnVGhVfs*q6cqQ%UaQZNG#ZeU3S`MlT99-Fd`L}ydf`ubB##gcK7TdsYPJ5_IO zdT0N4exJfVFd^9zUQPyUXk@XjwvhwZh z(~)y3h}ZriSA`S4f)sc!$2g6?L()6sHjT}ni=ftM7(1i(#mnwxyw5@nOGYL1mt4c2 zQ;8r4a~*2UcJZEUB8>`XOshZeKh|LsdmnH0H^NnfIN~#zy7YyQ0MkI8w-bTtN)km> zsk`s_U9aQgm*9mp=3wN_D)EUf?CKAH9);r?nb?03C;a>~xTI99q++IN+@71C1cBq} z?KFs<_`vKkba3Wdbk`XAv5K(IK($8n+rtr~XV=aOraS0T&r6ME?0c!V(?1$*3j&88 zo%y0HddV+o1^rkoT=_QbHNL2+ed`~kRO&Wy;#V{eo62WlnkHrn;j2fnF=FMBQL(K; zKtY97FZ-FEkylP%t3HP}7v&Wxpb{&8Ek~6M?^b5tI=dF>>jZG062u)TW zfln;Cqk}?8Y?Lmx2E$4IR9;syGa$X@^5xn;-P-blLZ*MZvvG;jHxAj%;B=*5FRIWW zKBHE&utAep4tTzv*v-%<31m+UCyxQbfOL8?*dat99WtLutXe2|jowrJHDtTohtQ@D zO?!ew!FkSrfNFBEJ+)>Pe0lp*r9H}zD6IN?3vo}y1yzqRjQw4PPC8i@sZaFkCR2`n zGU@UjYjgbZLD=ID?kYWxg*BForu847LXBpoyYIAZbeA7R4T{wEYMWs`6Qb28kUh=6 zVYs94N=(id7Vk(N07~AfC#`1{=zuL_R9b9K_Q1J=YA$jsE@ zyfBJ(gzm`w>atQzhEZQEGkXnWv!Ko-H&fXJB1mgl|8cosI4nWK6u^Vcm04ebJIhki~Aw2Xn>)(hg{%TMcQecE=*QxJo4IGvcT~u|9gu zo;UKiJ74fcLfs=f%Cn*8zye($!X9aunn-HKm8(<~<5%SM8~^aws!&*+7H- zLWVCTkNQD8nPzh&-J6f@;vO0K2ZmE!<>31iVX-#+U@x0il?n#N)R2=kI*0xP@kjU5 z#;vc`ng8_fS!9@FWjE;-vf1IMzOB}S` z6vYfZ@rn>O#}HdC)2C)dC|m1cisAqO-`d3(G9haT-yRiy0QDoYQJ)qSH`ixH3`@8Y zgnjwmkyIqm{3Ro-%3#T^nI*%7&YiGNR(1xkO!Vp5ebz17nB2!_N!?v2YL{W*h#(T= zePE{tB0HoxDNK04`z``rVeauTVgU{1?QZ&JzBH8(YxLzkWnLX08r-jpiY%0vu!Xdd zc{z~J^q0ikoqBlxh?X-Bux>7LZ)iGNnvBNBKWcnAnlC@=@ZIWrj?`la&KO-@b)#@! zU*^*=?ueTP!dr``%Kpqwl%>m#8lYFkV9$mDB?ihv9CzT-_x_Y*K;)9-vrP?Bj#O$< zQ3+JG2-oJ^w~g&mmJ!d!%M-=@Bq)Xbi68rQuZoN~Rm-3l-g|#1Ojl~O>NJ#KWTJgp zO@&!y8Oop$RT)Z|25#sasz`oR(}r&~;u&~#(*yly@@2wti5^mc*ru^uNOc3)!8~TL zpl*9E+t(iIz(b`THH%6)Oj-(N6%?I79|l`FIdQaz1cS?)mCtO{X-cH)sMlv$ESC?k zN?lhapjk+vLr>(5;fdZzvIg7N3!)V}nCQdCf3M%35L%;)A0T6KgGroL9W*=-E#%ojt&8DR9 zO`mK@BkYXOkrPBKae{myv4oX^&rXJ9lJxw(7GFE}LgISuwXH3b>^)enFFqkVQ0jTr z}1T zq^P@zDIt;cbcK6Rf0`jO>gDUy{3NeTWi55|?bTH_jU^wEltAYwk_R~3Yx;+E^MvuY z@La`kI7dtdwXUf1`G*~xquNejd-&?;{a`N7>wb`?iGo4!`;h}tG0hO@-a;GPv? z5!@k&B+K}`6ZK=zt$hFy&DAR>Bq!g5rhbYBUE2#-`VkY8H+vCcIxM~KP0;w=#gE9 z{YUSqieG7F12%qoaPTy6y20wIiA01+6)(A)xV>FaQ7 z5RJIia%HNp;|f0ZW4p|LbQwmdT&bfkYpXSZT>?r^YAc=kp~M@K-sp$=KkOXjG6(m1wrw z-tOnn?+1J_C8(=v4FB%RRBq}BPi$4hyCR!@lkC{j?UPTWILfI_DZ_SpiaZqRhvJxt zIIk0-%X%-J7{vZ<+mPg)_>zB&1W6}31;&Q3PhcCU2-L+J5s#ywO~hz}>6&e3=-dam z?4~0egSrx%A;)`rN|pR2o>TS(jTCc=p!GqFP0=v@7;lQiP@krNhP8*Nw-zVG5Ch4I zFwp|Zis=V71+$q?Ql6L#7ZD7jtr%(S3W5=AY5rH%7Q~!KB2I-}UTuUXMN5ua(f}`k z*D_?F_Xs$tlf%Mg8r2`reFyl3j2jd4s%@lo$DL9ivc?GY|SKAHpRF# zrnx{}C*Iz=*&VV~$dAInN?2ckdj_uqn_nV3An{7#I)$8PxrO`Bd+n&>b5eUtUn$~; zzxfh9Z3Vil2~37Nyi4EInS6bG%y8-TS`NoKpMCJN+TNL*m*V*~CqH*azE#vY;g_-O zGEtEhs5W0K*#h&eENO2R(T9SGb-u}#>Iz!fOvZ||kfg859X<(<(sz32SwihyGyDl|FBUY54A!@C%ql$60jy1t@`1kMmG|UNK;9W7u(HHTHtKU)~}2 zmL!7FgHpQmLT}U79iJm!w*#=Ux>w_sS;AFeVMe>k20`$JLeT$5)>!~W@~qh&hd~>6 zcXu1y2X}XOcWs=(VerA--JQYR-Q5Rwecb=PeRuD^h>oa?>Z;1>j*6%+^PKa`@L$ez zYcPD4Tm9(({z6*_xo~;S6H%^|*%y^vpIrotN5nBg!)sh19lwBbOvxqMh|1(Sl5GkX z*qgFfK*2R!V=+~(pPEj6^T#0l)Kua9I$?aXi`xk6Xw3Lxgyz}ZJOPv49+NOrMEDd1 zT_)GSuJecs%hWl~n1@W%i_2QwP5+mTsK7vqUn_ufPchyjV6dKKUd|$^Lh|J{MEubx zpT9mvDzy;kLU^0QiSmk4Epu4PsQqVG>PI(71CkAQd9+5X{6JdrekMy%gZ?ffSe@l; zQUx2oSodwXJA}QwWw}gke*!A25Z+QjW3b8qJRe-qex3Oc6T%GT?r^p|RPEv2cx`^x zNUvrD>i#GCt~&%@NRvzEk2f(LdI-WdSy)U+4dDTx6&hW&6rMz$D}{+%{Nv6wQ(s5a;ACyE#$n9 z5+&~eZh>*r0*1PHV1B@f;DPQgzk?Za!Ub>80o>|)YSSTm?J!{4H5F>oLuB0QbAESd z4huU?I~~3YgHQ{d5sex(!#hpmb$P732WD(A4u5T;zBWo1m<{KCLW38Wv&-+oQ0@A0 zV53X%h;bhT2Oz7>aRdNDp_B6}p@|EeL|_4)gSFt6QD}zoa3y?wuwfJPI5&JH^#Rnt zxPuvUh9!J)=*~QAa4_*aOR#vT!YDusOBi3I#Nd(2$t{*7N0p`F7HbtgRPE0+4VG5# zG)k!2z;qF7!wP(5RBj_{161hZpJ_9>JU+x(`E6991JXC-D}iwNz;$JV5O}i?9pIt% z-FTk3eLm-mwf~8w3xF@;p>Js`Ur+$74bG{3n+HpT)~fWUg8)=X>69X2ewQl*A#_$H z75?IkF%_60X7ChBaSd!BK_H|s(=~HW({6NJSM2jDRz>$EWmbQ6U({d@YF=$zsOC>e z+|Dx+Tc&!85q_-Z&Hmyz0Vv-}CZIkEz)h$}!7rw9sqUdz5{`z**kY58_0-#YcuG;g z6F$k;oZoue`qSI%2jMYRcTLRxSX$jRbjiW55g^cRxuv`-lfI?mk{>bUhM4CU*RV6@ zkf=E06)+(3Ui0TutF`M1!9}n~wO=@49e!&UQWs{TJG~WgsAX}<{0ly2DbTvzVWZIF zN>4@VK~L20a@-U4RS_#Qz@hdC{>HcRykTE6Tcv22J9f3Jit!Ou=)>kk_ncT{tD5iV zC9YEP{kju#C{Q3H{lWutw6o+qDa8&UE9ujx;OP8^Kz;{U>5p3*{Yq9sF6;3&J4fyk z6(ta2)VD`s7|z_;%d*SQFd%gClrLc_>L)@8NZ`Y3;x(sdme^f6(jjhbUm7ER$8x~5 znYrNKM{XJyEBfrq5}3AHX{Zw)U%;qmn6jD0k>?xR)KL4?mUD1_zGxs&jBM6JVBf=u zek%4eu2~93bJB3+36vU%u}+4D*a0@gnfzHIu2B4YT(EEI5pM=hB5+`&RbkjLHB1%P z;qdT1Lo2%v+r&yX=A76RwCXml_L}0Ygm)RBWYE~XS{zgD#fy=@`Z(7aGhGc!D^{oZ zw`BUvRV$6xfc(2H#?D4pgaOmGd#eXO5rtM8q@8o$&aY+HGm7f?m3usXjZZ_YjjyAx46oY;(xB_;V!X)B%M*F7-KeI{`7@R4m- zlk&$|$9cnCj)VPC6E+Qj*M!)bZp*LctvPMO&~Nb8i!3zjs`NPths^O(5rf1todATj;4{}GxiYh?!#62uXZDEb;2MmLDn#L{B(4)Pj zl0%tSVIgOYJ!-sAwr%AgukU()=NfI+f3|bj;q{n9L+xUNqq=$WkNB43X2ePL5c5*> zk-A3r9_DNxlvv7Ef02gEw_7h%yy4tjm3dwh z+=iN)bh~Gdv^{;-`q!EpVvf#|Uwou7xiaCBX@X|>balP+{uq2_O-}N?|0&mXZk={nv5ju$;Zf!Mw;HTRPlE$>j8cjvcgRnLwG=c`s1~;Q`A-@BQ9%F z5ZFThZRL9j5`*z*WyoShbn&Jr}eR<=8t&-MJ z3IhVPq1n;0tXCr)ALFu)&OA!ZekklJej$``t=LeyYedvrL|ez8-%C5llMxXH2QFyi zwl%O=?~i_`cyhnd9(1WdZ(5c&z7|(5V*(LQS)hN#cO# zw$wCMil?{I2%7D~*}L+E*b$z(9_H?kuJNjRfX?APO^CdcOAlC-BL_UDH6`R?kC1IP zV(^xbc2{TPy~D*TGiy~hvBJ`d&i=U_=GRdd)rU%u`uf|p7$2m5Kg zg$ro-!$cosZ3VinPV9~4v9e2+S5Ls?XoUNGWOwVvzVB=WjnKdE12r`(qCkFU#(NI% zqyABkInuMg!@yEcyoUnoG!>>Z2>R=iRr6h;wD*)Kg=Zbn(*RbuX=+^G#kYW{u5Hy_ zlP}h>W66K1luNylb@}jy?prJFhM%!>ah<2O`o`}EVfVRFg%P91nVn_~H}xdX35Y^xNlBweosvw`>-Nb%sk?_BsW*9YOpUC>s;BphSN0 zM-CPq;V2wC#{F>FIdCqJkKj57oAy@yG%*z<%DlM<5HW|vf_pMk=LaPI5 z!T!GBE|fZ2V3)+IS!5PJhc?xx-DXhN=^2P3&DOP~=JR{CsI3?#H{CZtE`ntIk{RH!`as}aB zo9$0ESc{ee(b!SVRyhVbI4JiYI)o6cw}EL1BMvh8+rI2gvqdPTO}rbrik4L;E<^BH z9qTu|w&TZ+`^g8El7|+drF8oCmGns^2q6y{bsvjQhImw!qalt=z#;vC++LdUK~RyK z(fuE|1M!O!mIbU~=7bi8(HbB+s&i_2BaKt|Xm2Y-;J^oWJEX8?cem-5zBRRAN5%A| zHMy<7yR$_7HS5n5Z}YSj4W0$Kbfv}3JYzz@<`RJow-t`kMVWLy`dS;!gFK_A9uDv1 zg;Cj8rR0^zk?iI|pr(kAhlKmmQFPv zd1SZT)#f#8OJt+~ZH0JEvq{}E!b!>6#4|ipw1{yWFzna{xbJ^@935M_h5I=rFy4LS zuyBDd!M=Qx#qWn_-bfJB{KonkLQ!!B7a+kAWj^fhi}R(uuLM%uyjwfImD?go*B4NP zNOidn@zBcob*0|*+bdvLnYNMOZ*uz`lJOmY<{$Y(^E4Z02KnW-;J3k6L;xdzfKQ!3 z*Rd<$Y81NkF47P6o)>6u)MAn$at6GdGeX4e&U1mphBvIC@B_PCyi?qkjFA^038mTm zBl+1##K^}q<4B5%{6qeye*XLMNdqdr*wQq1?fS$a?ybJ#sCoRD4sV6#Dtxi0UanBO zy4!I6y>%i^<6r@c79BtIMjId<)o@)xPdLqLTOu|m7~FeC=m zef>jGLU;eFNH&bLAqB%LBt#E&R>UTvp&l`5`W~{@6aNg}H;cUe8mvhqXETv+r#u{= zs`WMJ6BJU?i|YU9J#n%9r*IGh^UuWW7s4Y&-s``WgMV_!|0oA3U&_I+6fI}~4b@k+ znUj@8_X`rhMx@Qf!S>Z+;riOg^{;(j@o{aAe=_MzDLv2t92l0b@VYiPH%H0`G$1x5 z76u@V&;Awu*Jk11)cu+#7F8!=<>2@qv%@KGiLg{acIN*%39OqSYZt@>2zkut9dlDO z#5FEf3Kk`buRD(Y{Qv@yfmX^qOdPr1WtNq$CI)u5!uo(KAg^V6R(VF5-1U;Xu@a8& zwdCndz|hrbV;|co9c-ezB^Q6Zmi3!iQsm~sE9>0y3{j*`hb6fyWaW0h`n(Ew9@^O{ zKa07gxz$eE{#xP|;GW|6R!4b8F_@5NkjWREl=b_%m0L4eX|=Bj?)Kfq7lykV`EqH7 zMUpP32>-h`LHgp$Aiizg{torB6KWPoZPhDhwqwCxq`&5S)H=ZqlSx!&zi<`g3Knx9 z6|({e!W*C}e?~zph{7SR0+J$tR7XVMzlhE;ld~*U1JD%lM*y}pK@^$72JV%D>_1f^ z=}<~BXb$78;zc4Egr@V`>8- zQ;Rx06)NhACq>fz4j7JzKvS)N4q|U5vV!3-iH7G4pAg7s*AcB$;N0419kPp_(d$6s z6N>&Z!PG2td0weLWSCKZ_=qc2_rQ>F0ZN)u4FnLSaKi)mvAO;W7VsZJ!9VHsWWO&V zs|X%I{}r5{hX-hV^Jflc^bxiQBu?Q(0BC_4r~EEpx!~NX0_+&3`^y# z;d!X3R-j7n=Ey8IZ~5|br#l@+j%L2=732@lb6_t1Jd%{%uDE6L;gw8Kdvi+AG~Eof zRxW&pZPT~)+BDH%lFWf8s5}rieSuwg^~$>AEuKe^3OE;(n>@>G;V2Y3IEq7%hrlc4 zz%5stT>fd44!B5V7x*Vk!!*uD$Z3=-)05!VSroIov>WjTJChnXTrz)Fjo5Rj@I?_v zQy>#Yloy)nCA>H)GDcJ2-OG16!taPj^5GDE1bygsmBgfc#>9HbZrSk(4;#K2g*WumU?T5 zRbe{o`Br+BFV`h%XIm6%O;olgx*MeUvV5l+>X!{gWnG&pDg4a4w)0RDLHK>TWx#Sp zS=+PB`Hsyj@ED8c_Ih~)TWf3!XKUAcTUn`sLcZ0NicsxzpUepeZ^Ef ztIdTjj!KSJD|p?(ZOaO9e0$-^`H zGv0Nn(~Yyv+HQNd<6%R5aq{=Ebs78sbJ-Z-Um~UdH+`2kmvC@-X3)${~C)^-dF08z0~g$)s+? z@^3_c2N@&m)XKDI0?-7Rr^<(n_0ky}e!I5L;+;5fuX*!!l8Wpk8B9{QiAryhi$t*!;>JS$trWU|hk7W{>H91mKZmyd~7MJ;(Rg2{j9(ud(yP z|E)b;5MZ9|7?70@;ptClR5T7;rzhbDX?YUXt>5}vx75C2*SL9T$9;6?!p`=c{#X0v z%CVJd5(lt64Ro*m?b}{NOBhnVoQTg<-IDd@WH`>k@m5|VPJrJF|J~{*J@pgEJc1+FhE4JnDC|8yzk$&K?S=xMzj{Lc`o7u58;}K-Qa=BW zV*x(}X%K+aEbIr+0Zs850x+jc0Rf!k-2Y&0{zG-<{-<>LA6>FH)A|EKg~7fmk$)Q| zHhKY#Fx>yVioW{we*b^{ax{;xe_WxDVx7KwFdnHetn&YkwOR$_%g!{0r;@~ z`DgyK*MC4%Dczp{jBo!>+fIqc1dssPI9a*=XHE0>`!^Tug`~^Q>VafT%|)vft?tI< zS~BfAvN5*|kA#Db+RO<9Xl`bwzyz+^fC@HCe-1(INd;I|7oyDE6Y4@!d+ZoPujNBD zuFR~H3~qRGg*s4tv%_FPF7Tn*^~aACdY%|drBoK@L*38EEiYiR`=$4#AGTqz0;mZ} z@T!(p{spjlIR>Ea4`8r7JHz)_yc>oWZnY;Ndv#swSuFy-^*PI7>43gJJ|I55U7tyJ zAsjq{jIo0UMF)9z^EiT?hUHvrbnx-FesE&$!^C6F>&x{QQ&6)XFZJ-yGS@z?x>IDU z5u13S<@!86X#jnsYi|}Qa|lnV*boskB?>r7^bzi7um8?@H*IG5q6?pfAuGj&XQuBh zZ8c#&#cxH$x%53eWl7%9d4=B~?p5;at_?k9b2N)=MNU7g?@yMV%!3aj9eAw@w0he( z>9`HiD@=v7y;)|o9Vm`8=R`Qrgd|>x?^9a9{mPg9Ar7?0mIQN^qbll>cVvh+fO^!E zg1Wo>;uM(9Rz^Ay)y2#TYgXv?-Jpot$wbN52Lvk;X+&HGFUjCg_C76qK21isCDeTw z_dBXQ9!F0%Ss%ZOu5?Eg`IO?Fs7WHInEH$Cr&K`FDfKwxu7KRj?L3&IESE>bH8o?hjS5<&zUpdGi$|Xt!5`;A)t7VA5s)TAO@SKW( zTkx&^tP<0nL9nQ9gkg)0!)4FII>mV1wZM}V%qC7E;fRkepP@f4DOt=-aE%HCO$%s> zt0=)!9Y5KC;6t1Te{##W1e^%n5l;?DRrFEd^#f@gQ3-m}udy~mE(agCy|0 zDNa*P!?Z;CkINxL{CQ}nNpc>QpL98()Cf7lFR|{ZN_^ueb2!-4U$N&GBaOSDD%F}6 z!R)bGoCP<*{R758huy&qw%u@UL_n}w`~Wu+AWUAUAo&R(SiUg+rMz)xHHc2A96|*B zLcn45-K0dGQNoM<%tXp<*r1MdV!}S-xXa#lqHf$R1nyy^XJ#Mxo3QJ?IDzo&;5YVZ z=)vxRZuA2R8BSOiJX(NdxEn&Ay?lSMyMMBs;7YVerCLK}tK)Qpo!v#4PeJ|HitjJ& z-t9Ss#pRGUADec=&?l+K2EbzUg}}=KQQ%s${sinB)FU3QRw446b0i3@hs~w)N zWH^S;ZQ+&ST&F>qMqsWr9U%t@bk&Bf*_9}Ja~+xCh1-12$!PBqhixT*X- zHS-9=mbD`?YDS?{ema#N8bD48ZwAYM{ZexP^fQ2eVsk!*NtZj6CsJ|D%vP2J3D|(( zGI<56|3ue^UC=amNQ%{+?v+V~I|>XUKpDfTc=D^2G040?8^assB}pc)*<%YgrG6^Y zrW?!QQ>QZigBx6zxT6_6*aJk(39+1qJm9-J_s^USddW=`TnhRe3@CP0Sc@()IF~za z&ZF5EO9I(}tgnbPn}{s~|2z&U2BJ%i&1hgQzc8uO5R%v%m#55Ci}V5c)*~mO6DuC@IKkw^^R;ea97xrm zL3ch*YA80pn!GZtF$a1e>fz{i^|S!J{|X~cjUOV-2N@Q~vimoW8@NEuWFXk?$E*Px zIHDgSuXD)NH}xivL5v!; zj|L|S=f7RQV{1_qez4zzW+r{7T%7u*vCJxet%!ARcUnJxR%)&!FZ8}7z5AVbJZonOsUz=UznWu4op^D^Ak-s7 zxibhtya{2(H^=?UuKePv=2w+QK1WP3-}YMk9d(Ael9^)INru?OlWMV42V160*T=9Q zod)>~*HH&MvmCRhTGW{?lCak?nJ;erUzPno+4|L1YDmmMjx>LEnmp;o(WFQgnkr%Y zyL0kV-+WGxNtd&>WN3os_i;+6It-i1V2cgBD#fVUQlSO>9BrinuCA0``v6 zb7E%9@BborD#ilybfV72nIVeCW%#)v+=U%w@cNc)h!!%|+bouf#T-E2J1T}YvOWDfYUvR+LFi;y3_psf3$yB(u3H4W-1mIrnxgc+(svFf+xwDc<;Z6DspdVL zz^A4*2h+)eD~@VR9*F-X&B}dAv$P@Y6wlf@g~D1n!QiP!9T~J4QzNYo%SDX6qxf9* zr&z_a=uTc~Qo2;$f;)oU%ADrDt`eqST&wZUP81;UV#Yd^y-JovTHHjtVE^^G#Dl_aL}{tAf7~HVG@pp zN1LvCm#e;+L#@z3;K=o@O#)A(*lE|Xc&8`ayyt|IwbgCkQCh7U`FJYZy@4RKVf!^N zR8_DgHdhWp>B$wTslKj8Vr?_+Y7{FLfxZki)D!fq#s-U(uOAPDv)=L;~ zGs*NL>+~*U16jfGE`_R~jc|Pys_e(Xd#y|r@m4O{H2x#W;pZ7|tg`k8`z8r#CU|?V z0jZ|PlN=H^e!NPHZWK|y-~#flpSm=`F9l0z zW&F_Tvug@{f#*rO?vP@)&&yv%!>%gDj^Tay;V8!Z#wTn>l7D}n?>Z9u1%WZBeLAV` z^xGBydkdp_lq-h2quvIwzj1<_P^8EgKAylU47Db`){~Z4Pzb8eWosT*wMDuQiHaFS z2$)t-*($tVTGMT80t0GC({@yC&emuf>YZrfA_Qz;YDIrfqAO;bZ_DGwU`q<#$mEFK ztm!Q{h-}nsMFfgi-cr3UAYZhsTN4%I7wm&su@<|t$JO9Si89*934VhYobtVSnCCQ! zpyM`IDnt8~zWa(cs?(}Slk}sm-Y(6r{r0ks5Ze2gN<(pHd;s`IOB%6CzRTk3IqJqJ zQrwnfm$FCBk3sT*=)s!eT_70yPsBY|g)DxLAv(ucf_c}6*9;u=ucJhd7TB#cpOC(Q zeGTawehp+wi`ob*f8*wyAp=(`(MrKfqauXYU$mVpGST1h8EiW2T=ymy>yrIt^9Tt! zhw04XJfFr+?(;Zo)Nrs;zGVa3wOX%*7}re=ZiR<|(J^D# z%)b070ntrK{^UyiKi2uL-rh(J(NF$&IMwb5aSt>0Xr(|nu5T>bEaa0lXn*2*y_nF| zUsarMRlHqB@u;tsuxNQ`J9Ia91q(ThI+6R&FZcnGDq{C?nbiUS4P{LYt0*t4QWwD> zl__b?YV}GTv3nc?xuzey{7vPhj&&0Yde*FzC^*(Ns+1*32N4``WLgE*-}`3`jj;t4o`=GU>kG2)D9 zYGtQME$@=P6MXpg-?|H-i*{mXSy`dhJYzcaeLE3;Nq^LA8O|yZs!N$x*ANP|Xc*V$ zuPXP^GCrVAS1gkvdP_apGwn*lHWaHH#RrZwZyJF`Z>PLp>Gil&%2N&tFXtrBfzy@Z zYa~uvln0GGi1(zLPC0VS4o&H1`C?@`ck&3Uhoq&)R#d3&zH@gAa8Gfk@6R?2 z`bSr~b6IcXF&?K8;j0(S6xqc-voL#{c+jt%Nm2y$`MFy1eb8mS{yNN>*H<36BV0F zL-4M9<%y_P=+a_F>0ZJ{sxiV0!+}j==I@=-@hg~LH>gm-?sGE$WgT^!t#vNx=}T!A zqHsH!evK+2jHHjVt9^Gl^okWNWUR+fi)jK<>h}uC7eKHnWcXl*jggo&A|zh#X2c-m z#WbcA?sbG0s?uqE(Dan)?CU!d{Du9dmYWhVqV9efE&k}{KGQL_q!{Dq88)>fxs{ps z2acPId4rn>$se`;JwNd-kiwPiMjz>|UG(E5pcfG$$tyl+U=YrP-CC{2Hh-bLO3x1X z<~MkFP2mZYZ{i-8c)Wi&w|Ab`x=9e|@msC|>(oQq_tm4&rM{m0n?iFs_y%?vs*B3_ za0Z;#5Rvs+Gub=MR8-f7$l&2l-&wZbjh;_}br-jpzRZ5drt&%{`!-TWQ{X1c>b zEJf>qrCYk(LPEKGs-RgH=`8y^po$2@A9>BimE%e?QKDJ;UM4fr+~0Xg-Gc&}xiTP< zKyc5Xfc4sbOaMJ_N2vOjQg-dBq;*2sV&^m##(u8^*Beid3=CU+SiiF&CWTT18u$VHzf?+dEC5W{9Sz zL&YjkmEq)JgF**`_^31|M(mMW%3>h0v zT;&2@)GYEKk^Agd=V-_Ou4;hDSeltG0o>vmb)$8si3l71GX5hq3`eCA z_!+c$*=)HVUv*tIF6p}eWz{XdY{TckiATH@7rUQ%P;y9FWOcMa7b&OjiI` z&PER7tQko}`QZC5#Gj|eOOz#VsbzEemz*GfQq|V2?XArwb2bKO54#-o0qfn=JY7CI zXM&D%15Y82|B3h>@kZIlPNw7@q1_szXJ|b~6#<#}khh}E_ch=}% zieWl{^Iys!2eU5Gm%6CU#>thE3=JT|{SpelXwljnEMMZ`e+z{FYH<*0r@TS~xW5iy z{lfTbvvae5wOGCsM;1=5uhtj1T06xU2B1l=OT@*n?bsDamKVTQn&M$X?mTR#RFYyKTU6cYX^&T(E_+W%~b9X|R zPW5q;nh{xY#IsXwUbc*6N^ahpkAL{bACICxvX|vvmXFfkZ9kb|L*lVF*v?WwfRnsKIWgoU+ot|%| z1l|e1=8f}>9@&QCWTf)mpdv#K28*`qIf;cso0>pN#Co$w&+RsLe1fp69H@UihGhTN4ynE#w|{}q|6}HrndyJU zjF_2NQ{aaIoL~5B(P01s(AYa%cDI16pCcx-KRk1JUk*1PLII+HnjXTDJa-pb0bNTg zmnNR7K`5`}M@b$%ii!$yf#Mt(4K-ND8fSfbOy)9m5j8BzZHN74hv#ion&(!tH4#XT z2pO2@Z*8s0_eEKd2JBXWsQ$J#iKHMTaH>gAn86>9^s+PFdB#8XfY5qprwnj+*_%L6 z|7~8I;|t64mD}lLbT0m%#-BZlUCbMr6FeoC@?3rnCH5JWb2}Z#-uKhPS#YlBFDSprFRBXF>)>-4ospCNn|hNpaVbc{S%mOsdBqm7@<(Dri8ja~-j z3YS{v50tQM)XP5NI8m^MWpH08J81NEC@GHLGopBKk}TI$bKDYr=)KRuD8Am*wOmz7 zcrC)vaRN-*Ifp)^Ly&^*2ng~HLcbF!t-QWhBEdXwxC>u?0^c}n|56gc9AUryojydU z0GeNGzgryH4uHmMD($``R=Yr3DdhB$qOn5%9m%Q)11W7?JsPX5FRzD`6J$M?8zknJ zxNrjT^yg{*>3z^cSHr$iN{%+ZEl#8mDwu;C&=ZRc;}a$LGugl_JS`O&^OtZxF^X_U z6rISr=x;!C0?@7BdPJ)q+yvyB2wSux(?-H415d&yFhlT-E(={)kBE`TFS8@&N%n@~ z@uwK$RR?V8TZ9AUrRy+qE~J_u{EGpA;}6w&@J7p#e=S8TI1>bE4e7u{FaZ}wCdnOa zRA~TWsZNdKDuO9iCU!ooA)D;+C&)`D{ySfQUzh(TIq)_;{CD=C^rDwC(p}7*tPMDV z6MD3@Hr{lDk7+D;aua8js~`wtBKR90?Hc}euXA$Gf>9Z!5})in*}`r5xaIs$P7{bS zE<^{N@!BwB=tuE=jqOw?86`a$2l=_U{nF>sQ3IA1=?YVWP^NYxsM+FeXzJhF5$#HB z1FBoHGe9u3zEL##UG7rZ8Q2;lA4#q7w{tStU&S<%<*iu@KFy&9SQOh71@3$1xSP`` z7QK9ULRRQbZarLEbGBH<+=;y?!B=#iLk({S&p5}y3~voY?9CT00?uySH-iR9t1_=4 zuc0raxj3Z+`|w5rYi{s`5{9fX&@Dcs>jCdfH^9^*#4ATW)Dm~R&5@w&7ME5zvJXh-rZkqhj#w0+#M|@uN`GO$g!2!-n72~KeCHi{ zTVVFR4{t~l|Mq+cwvqT9ZCtYaty%Qp+~-L~*J;(S^c`>u$HbA=xs{$)5T^wGH+FiS zK7LTtavlwRfh<+;)#@sK4NPh*57(kM6-B+26ZW&Z-%F;D)dR44kz#t01K5ok`;8hS z*0m8!MgdE^@OqKgZw8YL0;mvns$+jmoq^sscJliV7>?dFA9xBkNB+OoCbyeZ*P9~N ziq_pInkx^R>RW6fn=AL4?mIBZt#uHqZO0?+vbLsOgSxm4E?oojIeFi9p+aN3X#k!W zcT<5wnDB&?5L!$KstwPuKXp;%2UDRAx?Lrt`Bix{ax=Ak)Ijjb=xa7^OfE$!p6w7ArV4sl_C9PHR@Hd%XyP5mU z0ka;2=p&4!B|jhce&q3xTb}+jL8aEE2RNQuh6Lm576PdG)vvVS-&t2jLwy}*CI5TMS4#BexWY?O9}LMcePK0#{GW#Fy2iK#9pyO z@Ow!@oxyd#_${o~S`&C1td|sTo@Vl|Kk_X%NY{;}XN~JuOjKydN5~++bAbw`Aw#5+ zodpe+7AtkRMIyZ=njCvuRD8NT>&qR57hum>D+%UF(IsPPPn>^qINm-2xa}Q1d6Up{ z6#&i#*G`nZIK&x?CrUK3;`M1J^jj+GoU^;7`nyUz*<9r12N6(>JSO!a>rRCeYH7&a z)2_E4KUZ++(7JhCuZgH(Jb>fwU*waBQ}S>_%*dsr7SVf)75=%ZmAqb`V)$BvUW~$D zbSKNW;pcyUp8ry_<}LKhDnvX|YHnPv)IL~Ez~-A?a3#W_!d^r_F8H2ZET?Dkl^lkx zk!eNAo|f~ni#b;Dikh@eBmJ&Bh7nbzY6sg2F*QPH$^~IQ;536zLkEO!-Jt8J7!J@l z7o0z*ZZ?ZkRfhCN^DO*zQnRk0vQ9LHFd&BD^t}ruYAU@+MM&7Zav;fy|8m*vyFtcuG)HK@L{0K;&V2y5z07g z$)=i`#x60sHT_oi#RNF6)&}+WB_~4rA=EaJFjA_4M0Ya4Krtl9i}$2A30q^8>!h3o zvK%%D0kNG1k2c)S-UiFMF=T8VW2Th%!gfpNva)HdyfcfYz4O#J)>g)Nw27WpH{T&N zW6a%k@Ij0XquoZ~GUkW_QQV|XvF!GB ztija&6OJyLK2O)8(NcoP_~Tr(f=okE2L(o{Z(gjedzdl>!*iqX7)kq)fW79j#zwVI zk|JQsYBc^ccN?hUHSAQNa(B`n^roIc6*HOtN&S5M&C9OrYhm&y)ms3)$yuDe;=(sN zhDgo~G9^%{5fH|N$|3Y38}~2Fl&wbIh%%!V9?ZeQBUDlMLPUrG=VTAf~1iQgl>gDp_LtEvfLM*1-W@U#17~`wxn7n53pwT#gg=(%? zFHg;1Jqn!0@Nn8odD8aYMR_JYErrsUxBzc=uBh|o%i7n!vHPt&0PTu9f#G}_J8-9Y z#85i5No_f<^EMFLLopvEs?mak_^@c3juC(=}G`4+8I8 z7U^`$aU~0ux=b_W>76S2Cai;w`xl=~u&-4}5~YB)izH#1`bG$DNj2j}1tyHXSW`(f zu720lOnxMCP$zxLBKsHKm!{WjMylEo#q6;|d!?e>#xxXq3%ximVzTZdlKIvCgxO(C z@~WAde-Oqb**?4_EVC%4EXl@-80;n<=702bZ-I1Tv$K>7Pv@{eguq?$1Bbn*dewji4R|^5MHG9k9~WDC*N4*DE*)g0+OMy)+#Z%X@dy6<+<(cZL5TPCXh#V!9v z;{EdRD2@c=Zmlk#A$jn-wtdyd@x2;cowEl@e)!H~< z-O&cv-Nsbb)-9(OB^!h2mN2d^=R0A;%G*6$;8Y6OHx+n0I=Z9 zO#jn$3YZ5(qJ7or|0?GH=Xj0%zXy~5Wj}pg0uVJn%>(2>zs~3Q$B<#a2rvgt>Gy(0 z{;y#H^fG{lpPA|Z_Y{7e@V~GAFW1KZ!Vj>pa&e^y`2&c6EL_b0LB#m$^UXs=?BTMs zt=ax^qnRgjGvPw6m`9otq7DES&qfT&R>w#Y_XA9{mqHpuNU@I!3^~M5M|==jXV4Yz zH-I?@5xW^%9m^dbU36WXGzu8%D1E`Bx`E$Ya*MF_y6L^uVSmwiU#>jHr}{Ww>I^I^ z0~Z7#f)on=V_aS~G|i#~Bbb@WV|HE;uDrD2ZI13A7k0ukD3_^M`r7FQ2NG`Rb=6-A z8Dd@b;2VCA%e=vrZ{_p1;4j=KVjLerkJH>bx$M|+WFXR5d#%s)`U35{yVuqmo?s>d zk!-uk${RUjRplC}flkQ{z5z%Jq(<+$gVS7Mk(N3<=VXabrK@_}aJDkw&aIu>^ zK+=1}@SBR%*laDZoM2Hcu0{UWzx`Fs4J|bP10$BJOK_=_^d=6 zn&xs25<*;ULgak%9e&FJg~cF>2xLnz#rJ_cxUyC-?t+J80&jiCI4fhK2kB0v8@!cZ z@X_b1vj>Yx-dWq&(qp*IsIU5-l^-PJQT|(vZ36Ujy^$6p2>20kD7I~HnN<(=7MZT5 z2m8UwD|E2x*m$_3SwC?ATmpKq@xE3e)jMgv^%fw%c5Q6%=tD(=Caiv7XxjXkPK^FG zMcbR+qtNlq&y?E;eG_8v&+|`q2jX3_)CewoD{dsAWznXcC2JKP6|v!7)YuS#KJxi8 zMa9mqL4Ej@ygrZLZg4n2%&uIe`azaL>@>cBbQGd8N3^eOX!)oAus0A|r>?0VW|^rO z! zXi>#RZE7o{-l;FZ13zHmhRbG}cFW|MZ}4kvPHlbrRQrs`N68fu z0qd1$U$s6N4kwbi+K}nwoNrQIu7O2VvB?nhIdUeH7LJ1W=qASdg?1{pkkG zQDK%`Na@TQ5!})N6EP?mLLvrC$+1V=(uU*}V(%7%b+?L?zYT!=i=AjQNY7Z44!3=1 z73_p+#2dk_=Ealq-XPW(MsOlm55`DZT$hAR95~9fac0ZG&({QqyS5B+4b33h7Jup% zMb9eVtkp;-gM?;dY0iL$g}*`F;d9P_c`;YCwU-29RAT;hsw+i2iUv+OC8x{Y+le3jBYrs3Tu7sIu#vFy^{<$8^I% z90Yh}FnanN09j;|*OePJgx`1Mw*|{Rc`Aa^YuLZtf6CWj54plxIijzN>TuP=ZOhFc z^6ZdTcq8jTLifmJjG8;J+W__7{dtx@W5!i*UZNsco`9hd4h;cnWz;%mvt`f8T z6iuu4iR6RnF*Eu2)mw{pP@TpFUv#B2biVECtne+oGENQ zp!kXGlmsb$VY7uX6uQIEPS+h?uW%t9&6z!sI{|^&qQm@FIbp%=17ZD_1r1&K5#J=p z;JbwhKcJ2J8SfL+Zdh-ip5tPQ>QAbVnRmVx#JWazMfJNMu%Nc{58Ec}mkNy^F~P7T zJ{uOladyJ>49BC$o>g?g(G``4q3?Ct`BsZ^S!z`75q zpP?r}K~HqfJ;hj%TX-)EQ;{!|lY+XRV#@(~Uca_`J9}?k26f5$Hs874xzZ5kP&(ww zdHxCeuw+@SqG;H%cCh`bWpCBPK~0Y+T207W0pzUdbYx>AIEnj>CeY6am#>$@dsKy- zBj3*J4PB_{!-DWwDn*+6`;mn9thtUUN5rpe&lr(79N&11o zm?4kB#=Vw1-W}+QlrR18%yi=YwYxTsNgiQO;ZNaZdiQ~+q>_~M@l^?J1>L1#WfEnS zrIh8k^V$oq^VOC(iagnY64TWPEyIe2oeK24V4SlgGh+HhjQRXip`N`S5x=D_DbtUW z3KGo97JrZ)!?1wLMHnXD*Sk>i$w~lW_9PL~VBt>Uf|F{)k&ILr_#ydr_nvGSNsrin z1?Y&$|A0Cc_ig5@o8Q|Gr%!}drfz&CV8mx5{%%!9X2fE|-lv$IZ z^DTDTO6QkP@k*cfU_xWVAkeN~!S_G;6-p^9m|=t-AeukYPnLfkN= zk;YPT{H{CQdO13UN~s@7>`u{YFmhh)CojnlHrze76g3QG^u8uTZNCbotGTmwreNP? z`dZ^L-FDz_K607jpz-tIR$kD<25P5=s~x#tJbRU1RA?E5FzkjzoAb|VB92S~T49WK zU(c6i$7p<%;fj6v+eeO~33Ja*&a!LdI`L3l=U&M)VNbt!?%y}2)#WjYpU`Tm9%-M$ zb{kG*=qbM-Gos;kN6Bf;V>T+$vfu02l|YY6IHzD9kqNJ&;c8#nqp!t(_nnksiRL-h zr&E0Uyod1#vTpO}@g2JHvJ|)jEAqpvRRyNq&!;4}E^5G7kD#;HT*+?Um>lMU;obz((T9MmL{zol-eHBg2o9 z{-GcVQHfZe4=Bs1pH&i_;8>SH(V~#uHp(heF-kX_;v%aubIMH_bzB|c!SMUeT?gMyj7!&8G%N}*k>#-IZR-GUrWO-pL zf(tfQiMU7VP{+4ge^Gy^PL^V%G~vXczd<@(oKA3Y4>dK{I>I~yZ}2pK>~CShc<|Zl zvAw$0IgNu40e+=z5wlSa{l*h3YYfD?omqP;-_^^(wkL_B^~21g*!)2P+XVEk_OsjI z=)koc+0F~M%gMND7Qayt_F%{Rmph=6*LsX?b(V)N@>HEQ zIrK9t3->Zxm>&i&zyEZ(>rZ!g>-XYTTAq9qMeH?kgtgDN@+bNkAFN`O?+GUrhWfyw zcu!n8G?SLsN_Vo`enO_AN#BrgWjUm7yrzDChJl7ce4g-9ups8Lbc}nt^jKXkALG~kpC^{{j$yN7H$qwx_))uJo%e%+WnHdke zgsh@2dT}YztnQAQX@8VEVcyaJ;lgOHJBd}+SMu=eUd3ws0@Qwq{6Wc^3`~;{>d~(k z&0zaq(=$DomsZpCGpUX7A4Oz7F*wX?W`5B8h!QGxQs>hEdV>0BIt(O=#_JH!M#Tln zu4J%G!JcczJG>LE_WVTna~@XuF^EjGv9iiZlPhs-=M+Q!xu8wMy*SPbpy4Q{R8`Ou z@0UYYJks<+l+Up>hql>1AHw>?(cPNt#WginwFbzSo@-_F2-;dHY8EpVNcgIbuegQu zd_^5^9McVcnwGpghRGKqf7I-W#@S!))ZOw6+Buzxzn(Uj} zd#h%fbW@U8johF|29jQLTvyU$OO7-g!^JetEixoeT0Y?95Blsh+uYF2EFJ}g?SD*( z;2Ng+&b2q`xb}6XotS+pVJj&G>yaoTWXraiwuUQ`_M7pE+gdvMeo!Q4_qk9G>KjrdBoF=sK9-@7`>BdTS_gXcr( zb>ULpB4o4<0}ci55p_avI3=O33E#5qAK+G!)Z0#PdSZ5Da)zBK12JfdkeG&oI77^t z8MUXXXW9JCK#qyeh- zF*nD~5B$lD!XI3H;AiL~sxPq$qmks;oqcY%k5j%Zd~P5eY}*`icIl_A5_bt|EZLiV zzIKQci+k6S6-(B^FELn_wZiVhtM_@9lMRQ0%OsmtY(bZ^vEiycN6jcUVPR_Hh4046 zqMpmZUEserIda+{eV29?TeEIrIv8$qLzK(|sn$QU&og_NmD!|837B{+-Rym}v`Eje z8^X=!YTmi6NxYNt*5QIA8;9h!fo$>ei0Bc7=)rRq70h{fQ!hF+<`;T`8IW%R@I@G7 zVDo;gYRa253*nx4ZK#&<@3MylzHQM@bI_ywI&*0}t<w^RI0Q){}+UO(V02I-Ce` zzzp5~-PAD&f57*;D#$yublE=O%@KdnWCFepuxCPDRyz=gp<(_2g!@zO-e(YLhXbJz zc&n3T-{+nk@9SbPL#In@l024D&k5UBQ_hkP)3!-{{afzT#Xi&@?2OqLS|;L5QX))x z>CEK_aZ^WSSMe7QY{b8Q3GW$yU_l@5(cQkI<=Ga+{J1AdNRH#^F^D{H+$QNQKA4bV z;>mtmf%%VATEW@p5AFuwK4Jlp_^Q@Pi$)~2Yl#*OlmDvoJyM${7s`@&aX+y=Jx37u z9+mLxAR0^`!d;SIC>$5K#24!*{QwJYfJkhC43NoI+LsJGJwJL08a;h~M-J=t6pg1P zGIRkG8~&0+D{r^zC54irp85MD6Zw=YEU8VQP}@Jm+PlME6;I1O;}a=&*5p61$ehXI zwJ)gL)qP<>{W5ncmEn5O9xYI5{Iss5Yr7Y(yKlzjPOj?jqI{+Ys`d(md^hxX1yB1XY}8Im(GJ3(4L_i%ib zy7jy3@AT^&k)U2cOC@dMgPG)~e%~bkJu*)YwLrWsrQz^-YwJF6Xb{t9wI^O?_Ob>; za%sQCB?&v5;N{4k{Ud3qpuN4rx3ZJ#^R#&*djfS9u`*}f+sa^EvnDKF?1lab_AZuY zZ@4yL`ymxu7mK(X0{E3YpEY<`Vb>c5?bj1^CEbsPpho*2s3v8_3(E1DV6(ME{Md`^ z{c}L8~YLTm>}k(`nOf?+z~;#U&v3PN@L>FVsGw?Mhwx1 zjrXWy@Qk4DZ1~=BY?tYjqoK%nW66n9Y_8%@@1lL-#=p2O(?O6FMW>Y6PMz6q;f)_< ze%IAMLl*9XpCTSiI1oxnV{7%R+XX!+P^NvFc5=UzVK5Ynh7UB*?E-&IPQx*mPENyf z@4maM@UFg0_9(f*8_8hd^DGu=j1kxrztXa6yRHxloF5mqt|}^kC00GC8{Yj*A>#bX z;^Ggp;&9ZrZ}jWB${hidiJ^6#zHzIYY@fDprB>+^RGos|No%9xWs+Zkn6OL5P0&q= z!o%CtnA__sI>a!{pkA*IeQ`p=T55+*BTwshq0ZC_2M>3xT7@r7*w*)2qC`lx4_j7$ zY`rK$y;lnoi4JMPRm8by-tgN6&36nP7Hw9VD~0UxQEeZZtTS#JEw5k=?gxe&fhpnV zd9UpLbhCcq+hs7Tp?tn`sN*eklF;)sp~CCrH?|cun=6D`EOlum5_?pnV?Ed81LwQ^ zRt1ydi~^=2!AEO_7*8@q?{90(z;q)^z>f*(P>fDku1dLCbitd@xI3ya3PFGUHxJ~V=IO@1(?2yXNLh+b&!F34YwUws6`6B}89aS& zYiQ%3{~=GUR6Q&~@rmVzO7)Y|yU(l`SG#}Z38IfaX$ozxLU>R9Uh!k;tXjY(39lBO zeSp%#(Kz40w+r_8kn5m67AXV($4$z zmaRR;{0@Yv#>RH26=Tbb@+*8TtaVIG?o+3pSJuyRC zPThW5Y04E|!Q`YT*KDd>ymBnxzOnsyu%rw%9hcI#iM$y8vRNZ~fw3vh+%B4Rx7^A( zXzh9W+O}hfh66|W;<06dyHL68?9UaQ=FY+50p!D67A@c-&V<2c&VaPSrN{{|)BSNU zQhwujgnaKDsfXL<)PjpuMI)`8)z+Uefv@^A44+U#vyGjFuJx{8XQ`-!;XNCu zz(JkLhBm(I4ANsMZR4RgN|?nb(l3u|rX`L?R*RxYM@Yxo@`@T;thgzYGRDSNuRgxv zzQnCoi7)^^ed@3tO_5Z!SXol4J+8|O{Ox|vnV>#Ov*<@oz?a66<9%^10{=6}*uu^|l!QqND!PHKOL~d$7$|rTs&lO;!JQ`a9p>>31!s z)V}!sWWdonlY%~DV05#%DSpR$m1Th8D+DX+6I(yO-E%|7>_w6ulnxuypsfu3OO%69 zSYBMhupxKnx2JhpFsxBBrBWOhfv#D(r;L8){9$j$Iilt3*hxe4175m8N{kcUlU3=b zgf2NR@|UW_9GR)>$M}Vn%D>(slV;h{#zW7lA8;>f9ro&&pu-x*0l6U;cXU|fXCbe6ly(MuD`U|Z7cGuIOlvDwOT zSrWe@brVr!Q4=A0{x&js$o1|r8^{dPU|3I*4=W`HwC9N&1Df;mjJ5Z(-0gOe^KRiLxf)&Qr61rq*FKdkM2D;Wx z4zG`2f+XkrqW5Rfx?&9|Ijq!^8OBFjP~MX^CL$melDohi|IF4|TY-t%e^yVij~}&Z z&bydULAcMSU&atpEb>u?f6+%j>o^{1-_`OA6-wh&vU`x_qv|3Oy&a1xR@az6*)&pf7dx6 zGPJIp>)s{jB56Glf_(?&@452wjM6yTTg*Y`0^TF`Q?WC>g3^`RL9Us6U!tDCFUnik zL8%$XFW`*6UgbIUPH<|%r_V242o^70_!lu4m|vN(Nn+NqUQzi4pMiSj+qOfS7A8W$ z^%$-si(Z$ipcY1n$`gQg!psn2Z zMrV@0kmw28yK5$}vD|l1vbN-U`r9EM1TJ)wNX1SteQu4*hcwic=M`{)@@$)payhEl__`T6@ zXmj7bfH&E`qc{FOiqV^HUx;Bo*zWFpkR9oKh!kJQ0&Y*vUfMHUN&hv{=r)2-=4ax> zLTrARLacs-LhOF%Aqhzf52G^MGD)xT%Xx&_*7o=osTp4Ugm(%p5UnU6(VW>bmd}K} z3HJHD>Gx4T89N^`Todo|34MO-O|s9tC%HHgI@BTTg1K*mM7=p<^z}cJ=rKKe@SOP1 zPh>VaGwanv-gRc(I_jLHZawpQ260)PS+SmZ`8f~uha7Pk$2@ryeNB|=*F2wBj_l7v zodHBSr%|bkfcsM2j(;lA5SKJ{>pk`Nr4Yc7J&aVp_?e@e**xY;lA3krYpJUB@=HJh zaP>@~j;@b9{-Db4Ul#&zjyG5I%#qY3%6ydfI>g^Q=<^jOpE(}TzlWQoBrV4~R)F_r!4#wI|$+xinaDW7Kq-fw_Mb@gVZ^CH! z^4a%w@=b@r%1>z{m0!V4zN{AnkI+f9gvjc2!isvnx=SNXbG>t4{YmtCx(&{ln-ZdLqnw$|M53#3;Mq zpa3!ZuP6KAbrUbAPL1ceo^>sG!Hkj1)2)L=7+g;eTREeTk58_2S&YicK5>opM(u7U zXm$?il((Dz8Srx#H}itL^(;FqXv$qC$^H}Hwxn>49 zNL9%eIARhw0*$x!zqqo+bF{0lg-0IKDB93>q`)5b;uFECaf)Tfu%MUFz)`vIV}i(V z^`yyn=}(W7=!JyMc+0;y5SefTRYmkOgTlS^ka9dn;W^7zPWa2{&Z|)HJ7yHFkput{ zk|x)FN5rXEAITBX`Z^GdDz&Q)@sahk^7GLRef7wba+D9_A)1a)D?;LjptORff@Y_s zz|+rfL+L`hHhB1dh0SN!`|*rge=kO#t8tH36bM64Q09SR9CGM1a?dl`@O`9&*Ypto z{-CxVWoQtkeql`pe2CtQV2eQsl7+uJFo-L8ytR*IxJ8&rIlWvmTtzxjNjmYGHDuYH z7qtyb`E|rF?#R59vLr}IGbx59Nq|nLOUo(Uj?8KOnNX^7Z0C0GiLJ9iex<=89mFdU z3Xzc<16?7Xctj7lzd(O@iCwmu?^6$2o_pQvik+WKagUfQ0TO8a<82#ixZ+}

?qH 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

4~fRRKqh~|6p@Qs!tYE;LE4X~jFR=K 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

(lyV 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

&Zm9CoF6xNcS&K+H_4w+pbM&IR( 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 -(Laser Refile/MRR) +(Laser Rifle/MRR) diff --git a/MekHQ/data/names/callsigns.csv b/MekHQ/data/names/callsigns.csv index 4fc2580bb62..f4e5af994d5 100644 --- a/MekHQ/data/names/callsigns.csv +++ b/MekHQ/data/names/callsigns.csv @@ -13,11 +13,14 @@ Acne,1 Acorn,1 Acrobat,1 Active,1 +Actuator,1 Adarna,1 +Admiral,1 Adonis,1 Aegis,1 Aerial,1 Aero,1 +Aether,1 Aethon,1 Afterburner,1 Agent,1 @@ -25,39 +28,61 @@ Agile,1 Agony,1 Agrat,1 Ahab,1 +Ahuizotl,1 +Aikido,1 +Airhorn,1 +Airwave,1 Albatross,1 Albion,1 Alchemist,1 +Alchemy,1 +Alexander,1 +Algorithm,1 +Alias,1 Alien,1 +Allegiance,1 Alleycat,1 +Alpha Strike, 1 Alpha,1 Alphabet,1 Altar,1 Alter,1 +Amarok,1 +Amaterasu,1 Amazon,1 +Ambassador,1 Amber,1 Ambush,1 +Amethyst,1 Ammo,1 Ammut,1 Amore,1 +Amphibian,1 +Amulet,1 Anansi,1 -Anchor,1 Anchor Shot,1 +Anchor,1 Android,1 Andromeda,1 Angel,1 Anger,1 Angry,1 +Angst,1 Angus,1 Animal,1 +Antares,1 +Anthem,1 Antidote,1 Antler,1 +Antsy,1 Anubis,1 Anvil,1 Apex,1 +Aphelion,1 Aphrodite,1 Apocalypse,1 Apollo,1 +Apparition,1 Apple,1 Applejack,1 April,1 @@ -65,22 +90,44 @@ Arachne,1 Arbalest,1 Arcadia,1 Arcana,1 +Arcane,1 +Arcanum,1 Archer,1 Architect,1 Arctic,1 +Arcturus,1 +Ardent,1 Ares,1 +Argo,1 +Argonaut,1 +Argus,1 +Aristocrat,1 Aristotle,1 Ark,1 Arknight,1 -Armor,1 +Armada,1 +Armature,1 +Armbar,1 Armor Bear,1 +Armor,1 +Armory,1 Army,1 +Array,1 Arrow,1 +Arsenal,1 Artemis,1 +Ascent,1 Ascot,1 +Asgard,1 Ash,1 +Ashen,1 +Ashfall,1 Aspen,1 +Asphalt,1 +Assassin,1 Assault,1 +Asteroid,1 +Astral,1 Astro,1 Aswang,1 Atalanta,1 @@ -90,37 +137,58 @@ Atlas,1 Atom,1 Atomic,1 Attila,1 +Auger,1 +Augur,1 Auntie,1 +Aurora,1 Austin,1 Auto,1 +Autocannon,1 Avalanche,1 Avalon,1 +Avast,1 Avenger,1 Avenging Angel,1 Avril,1 Awakening,1 +Axekick,1 Axel,1 Axeman,1 +Axle,1 +Axon,1 Azeban,1 Azrael,1 +B-Boy,1 Babe,1 Babs,1 +Bacchus,1 +Backfist,1 +Backflip,1 +Backlash,1 Backspin,1 +Backstab,1 Backstop,1 +Backstreet,1 Bacon,1 +Baconator,1 Badger,1 Bagman,1 Bagpipe,1 Bailiff,1 Bailout,1 Baja,1 +Balancer,1 Balisong,1 Ballerina,1 Ballistic,1 Bam Bam,1 +Banana,1 +Band,1 Bandit,1 +Bando,1 Bandsaw,1 Bandwagon,1 +Bandwidth,1 Bane,1 Bang,1 Banhammer,1 @@ -130,44 +198,57 @@ Banshee,1 Banter,1 Banzai,1 Barbarian,1 +Barbecue,1 +Barbell,1 Barbican,1 Bardiche,1 Bark,1 Barley,1 -Barn,1 Barn Owl,1 +Barn,1 Barnacle,1 Barnet,1 Baron,1 Barracuda,1 +Barrage,1 Barrel,1 Barreleye,1 Barren,1 Barricade,1 Barrier,1 Barrister,1 +Barter,1 Basher,1 Bashful,1 +Basil,1 Basilisk,1 Bast,1 -Bastard,1 +Bastion,1 Bat,1 Batangas,1 Batfish,1 Baton,1 +Battalion,1 +Battleborn,1 Battleclad,1 Battlecry,1 +Battleline,1 +Battlemech,1 Batty,1 Bayard,1 Bayonet,1 Bayot,1 +Bazaar,1 +Bazooka,1 Beach,1 Beachball,1 +Beacon,1 Beagle,1 Beak,1 Beaker,1 Bear,1 Beast,1 +Beatbox,1 Beauty,1 Beaver,1 Beef,1 @@ -177,6 +258,7 @@ Beetle,1 Behemoth,1 Bell,1 Beloved,1 +Belt,1 Bender,1 Bengal,1 Beowulf,1 @@ -188,31 +270,60 @@ Big Dog,1 Big Texas,1 Big Ticket,1 Bigfoot,1 +Biggie,1 +Bijou,1 Biker,1 +Bilge,1 +Binary,1 Binder,1 +Bingo,1 Bird,1 +Birdbrain,1 Birdhand,1 Birdie,1 +Biscuit,1 Bison,1 -Black,1 Black Sheep,1 +Black,1 Blackbird,1 +Blackdog,1 +Blackflame,1 +Blackhole,1 Blackjack,1 Blacklist,1 +Blackout,1 +Blackthorn,1 +Blackwater,1 Blade,1 +Bladestorm,1 Blaster,1 +Blazar,1 Blaze,1 Blazer,1 +Blight,1 +Blindspot,1 +Blink,1 Blip,1 +Bliss,1 Blister,1 +Blitz,1 +Blizzard,1 Blobfish,1 +Blockade,1 Blond,1 Blondie,1 +Bloodfang,1 Bloodhound,1 +Bloodname,1 +Bloodstone,1 Blowfly,1 Blue,1 Bluebird,1 +Bluff,1 +Blunder,1 +Blur,1 Boar,1 +Boarstrike,1 Bobcat,1 Bobsled,1 Bobtail,1 @@ -222,11 +333,13 @@ Bolt,1 Bomber,1 Bond,1 Bones,1 +Boneyard,1 Bonnie,1 Booger,1 Book,1 Booker,1 Bookie,1 +Boombox,1 Boomer,1 Boomslang,1 Boomstick,1 @@ -234,24 +347,43 @@ Boomtube,1 Booster,1 Boot,1 Bootcamp,1 +Bootleg,1 Boss,1 +Boudica,1 +Boulder,1 Bounty,1 +Bowline,1 +Boxcar,1 +Boxer,1 Brainy,1 Bramble,1 Branth,1 Brass,1 Brave,1 +Bravery,1 +Bravo,1 Brawl,1 Brawler,1 Brawny,1 Breakdown,1 +Breaker,1 +Breakfall,1 +Breakwater,1 +Breeze,1 Brew,1 Brewer,1 Brick,1 Brickwall,1 Brickyard,1 Brig,1 +Brigand,1 +Brilliance,1 +Brim,1 +Brimstone,1 +Brisket,1 +Broadside,1 Broadway,1 +Broccoli,1 Bronco,1 Brood,1 Broody,1 @@ -261,6 +393,8 @@ Brute,1 Brutus,1 Bub,1 Bubbles,1 +Bubblewrap,1 +Buccaneer,1 Buck,1 Bucket,1 Buckeye,1 @@ -280,63 +414,99 @@ Builder,1 Bull,1 Bulldog,1 Bullet,1 +Bulletproof,1 Bullfrog,1 Bullseye,1 +Bulwark,1 Bumble,1 +Bumper,1 Bunker,1 +Bunny,1 Bunyip,1 Buraq,1 Burger,1 Burglar,1 +Burn,1 Burner,1 Burnout,1 +Burst,1 Bus Stop,1 +Bushido,1 Buster,1 +Bustle,1 Busybee,1 Butch,1 Butcher,1 Butler,1 +Buttercup,1 Buzz,1 Buzzard,1 C-Bill,1 Cabal,1 +Cabbage,1 Cabin,1 +Cable,1 Cactus,1 Caesar,1 Caffeine,1 +Cage,1 Cait Sidhe,1 Cajun,1 +Caladrius,1 Calamari,1 Calamity,1 Calculator,1 +Caliber,1 +Caliper,1 Caller,1 Callisto,1 +Calm,1 +Calypso,1 Camel,1 Camelot,1 Camelthorn,1 +Cameo,1 +Camouflage,1 Camper,1 Campfire,1 Campside,1 +Camshaft,1 Candle,1 Candy,1 Canine,1 Canis,1 +Cannibal,1 Cannon,1 +Cannonball,1 Canvas,1 Canyon,1 +Cap,1 +Capella,1 Capital,1 Capo,1 +Capoeira,1 +Capricorn,1 Capstone,1 +Captain,1 Caracel,1 +Carat,1 Caravel,1 Carbon,1 +Cardboard,1 +Cardinal,1 +Cargo,1 +Carnage,1 Carrack,1 +Carrion,1 Carrot,1 +Cartel,1 Carton,1 Carver,1 Caryatid,1 Cascade,1 Casey,1 +Cashflow,1 +Cashmere,1 Casino,1 Casket,1 Caskmate,1 @@ -348,115 +518,177 @@ Caster,1 Castle,1 Cat's Eye,1 Catalyst,1 +Catamaran,1 +Catapult,1 Catastrophe,1 Catfish,1 +Catherine,1 Catman,1 Causeway,1 +Cavalier,1 Caveman,1 +Celeste,1 +Celestial,1 +Cement,1 Centaur,1 +Centauri,1 Centurion,1 Cerberus,1 +Cermak,1 +Cetus,1 Chain,1 Chainsaw,1 +Chalkline,1 Challenger,1 Chamberlain,1 +Chameleon,1 +Champagne,1 +Champion,1 Chancellor,1 +Chandelier,1 Chaos,1 Chaperone,1 +Charcoal,1 Charger,1 +Charlemagne,1 +Charm,1 Charter,1 +Charybdis,1 Chase,1 Chaser,1 +Chasm,1 +Chassis,1 Chatroom,1 Chatter,1 Chatterbox,1 Checkerboard,1 +Checkers,1 Checkmate,1 +Checkpoint,1 +Cheddar,1 Cheetah,1 -Cherry,1 Cherry Bomb,1 +Cherry,1 Chick,1 Chicken,1 Chickenhound,1 Chickpea,1 Chief,1 Chili,1 +Chill,1 Chillbox,1 Chiller,1 Chimera,1 Chinook,1 Chinstrap,1 +Chipmunk,1 Chips,1 +Chipset,1 Chiron,1 Chisel,1 +Chivalry,1 +Chokehold,1 Chopper,1 +Chopstick,1 Chosen,1 -Chrome,1 Chrome Dog,1 +Chrome,1 Chuck,1 Chuckles,1 Chuckwagon,1 Chupacabra,1 Church,1 Cicerone,1 +Cinder,1 Cinderella,1 Cipher,1 +Circe,1 +Circuit,1 Circus,1 +Citrine,1 Citrus,1 City,1 +Citylight,1 +Clairvoyant,1 Clam,1 +Clamp,1 +Clandestine,1 +Clanky,1 +Clash,1 Classic,1 Claw,1 Claymore,1 Clem,1 +Cleopatra,1 Cleric,1 Cletus,1 +Cliff,1 Climax,1 Clip,1 Clipper,1 Cloak,1 Closer,1 Cloud,1 +Cloudburst,1 Clover,1 Clown,1 +Clownfish,1 Club,1 Clue,1 +Cluster,1 Clutch,1 Coach,1 Coast,1 Coastguard,1 Cobalt,1 Cobra,1 +Cobweb,1 Cockatrice,1 Cockroach,1 Coco,1 Cocoa,1 +Coconut,1 Code,1 +Coded,1 Coder,1 Coffee,1 Coffin,1 +Cog,1 Coil,1 Colby,1 Coldface,1 Coldstart,1 +Coldsteel,1 +Collapse,1 Collar,1 Colossus,1 Colt,1 Coma,1 Comanche,1 +Combatant,1 Combo,1 +Combustor,1 Comet,1 +Command,1 Commando,1 +Commlink,1 Conan,1 +Conceal,1 +Conclave,1 +Concrete,1 Condor,1 Confessor,1 +Conjure,1 Conman,1 Conqueror,1 Conquistador,1 Consigliere,1 +Console,1 Constable,1 Contact,1 +Contraband,1 Control,1 +Conveyor,1 Convict,1 Convoy,1 Cookie,1 @@ -466,102 +698,169 @@ Cooler,1 Copper,1 Copperhead,1 Coppertop,1 +Cordon,1 +Core,1 +Corkscrew,1 Corkwood,1 +Corky,1 Corona,1 Coronet,1 Coronis,1 +Corruption,1 +Corsair,1 +Cosmic,1 Cosmo,1 Cosmos,1 Cougar,1 Count,1 +Counter,1 Countess,1 +Courage,1 Courier,1 Courser,1 +Cover,1 Covert,1 Cowboy,1 Coyote,1 Crabshack,1 +Cracked,1 Crackle,1 Crackshot,1 Craft,1 Craftsman,1 Crana,1 Crane,1 +Crank,1 Crash,1 Crashdown,1 +Crashsite,1 +Crater,1 +Craven,1 Crawl,1 Crawler,1 +Crayon,1 Crazy Quilt,1 Creator,1 Creep,1 Creeper,1 Creepy,1 Crepe,1 +Crest,1 +Crimson,1 +Crisp,1 Critical,1 Critter,1 Crocale,1 +Cromwell,1 Crook,1 +Cross,1 +Crossbow,1 +Crosscut,1 Crossfade,1 +Crossfire,1 Crosshairs,1 +Crossover,1 Crow,1 +Crowbar,1 Crown,1 Cruise,1 Cruithe,1 Crumb,1 Crumble,1 Crunch,1 +Crusader,1 Crusher,1 +Cryo,1 +Cryptic,1 Crystal,1 Cub,1 Cube,1 +Cuddles,1 Cueball,1 Cuervo,1 Cujo,1 +Cupcake,1 Cure,1 Curly,1 +Curve,1 Cushion,1 Cutlass,1 +Cutline,1 Cutter,1 +Cutthroat,1 Cyber,1 Cyborg,1 +Cyclone,1 +Cyclops,1 +Cygnus,1 Cynic,1 Cypher,1 Cyrene,1 Daedalus,1 +Daemon,1 Dagger,1 Daily,1 +Daito,1 Dallas,1 +Damage,1 Dame,1 +Damocles,1 Danae,1 Dancer,1 +Dandelion,1 Danger,1 +Daphne,1 Dare,1 -Dark,1 +Daredevil,1 +Darius,1 Dark Angel,1 +Dark,1 +Darklight,1 Darkness,1 +Darkstar,1 Dart,1 Dash,1 +Dashcam,1 +Data,1 Dauntless,1 +DaVinci,1 +Dawn,1 +Dawnfire,1 Daylight,1 Dazzle,1 Deadeye,1 +Deadfall,1 Deadheart,1 Deadlock,1 Deadshot,1 Deadwood,1 +Dealbreaker,1 Dealer,1 -Death,1 Death Angel,1 +Death,1 +Deathdealer,1 Deathstroke,1 +Debonair,1 +Debug,1 +Decay,1 +Deceiver,1 Decker,1 Deckhand,1 Decoy,1 +Decryption,1 Deep Freeze,1 +Deepfield,1 +Deepsea,1 Deer Hunter,1 +Defector,1 Defender,1 Defiant,1 Defrag,1 +Defrost,1 +Dehydrate,1 Dekker,1 +Delta,1 +Deluxe,1 Demeter,1 Demo,1 Demoman,1 @@ -569,39 +868,58 @@ Demon,1 Dempsey,1 Derby,1 Desert,1 +Desolate,1 +Desperado,1 +Destiny,1 Destroyer,1 +Detour,1 Detroit,1 Deuce,1 Devil,1 Devourer,1 +Dewdrop,1 Dexter,1 Diablo,1 Diamond,1 Dice,1 Diesel,1 Digger,1 +Digit,1 +Diligence,1 Dingo,1 Dino,1 Dionysus,1 Dipper,1 +Direwolf,1 Dirk,1 Dirt,1 Dirtnap,1 +Disarm,1 Disco,1 +Disguise,1 Ditch,1 Diva,1 Dive,1 Diver,1 +Divine,1 +Division,1 Dixie,1 Dizzy,1 +Djinn,1 Doc,1 +Dockhand,1 Dockside,1 Doctrine,1 -Dog,1 +Dodger,1 Dog Ear,1 +Dog,1 +Dojang,1 +Dojo,1 +Dolphin,1 Domain,1 Dominion,1 Domino,1 +Doodle,1 Doom,1 Doomsday,1 Dopey,1 @@ -610,157 +928,288 @@ Doubleshot,1 Doughball,1 Downfall,1 Download,1 +Downpour,1 +Downtown,1 Dozer,1 Dozey,1 +Draco,1 Draft,1 Dragnet,1 Dragon,1 Dragonfire,1 +Dragonfly,1 Dragonheart,1 +Dragonkick,1 Dragoon,1 Drake,1 Draugr,1 Dread,1 +Dreadnought,1 Dreamer,1 Dreamland,1 +Drift,1 Drifter,1 +Drill,1 Drillbit,1 +Drippy,1 +Drive,1 +Driveby,1 +Drizzle,1 Droopy,1 Dropkick,1 Dropout,1 +Dropship,1 +Drought,1 +Dryad,1 Duchess,1 Duck,1 +Duckie,1 Dude,1 Duke,1 Dumpling,1 +Dumpster,1 Dune Rat,1 +Dune,1 +Dusk,1 +Dustcloud,1 +Dustdevil,1 +Duster,1 Dusty,1 Dutch,1 Dynamite,1 Dynamo,1 Dynasty,1 -Eagle,1 Eagle Eye,1 +Eagle,1 Earl,1 Earthquake,1 Eastwood,1 Easy,1 +Echelon,1 Echidna,1 Echo,1 +Eclipse,1 Eden,1 Edge,1 +Eggroll,1 +Eidolon,1 Eight-Ball,1 +Elation,1 +Electra,1 +Elemental,1 +Elevator,1 +Elizabeth,1 Elvis,1 +Ember,1 Emerald,1 +Emitter,1 Emperor,1 Empire,1 Empress,1 Encantado,1 +Enchanter,1 +Encrypt,1 Encyclopedia,1 +Endurance,1 Energy,1 +Enforcer,1 +Engage,1 Engine,1 +Enigma,1 Entry,1 +Eon,1 +Eos,1 Epic,1 +Epitome,1 Epoxy,1 +Equinox,1 +Erasure,1 +Eris,1 +Erosion,1 +Escape,1 +Escort,1 +Eskrima,1 +Essence,1 +Esteem,1 +Eternal,1 Eternity,1 +Ethereal,1 +Euphoria,1 Eureka,1 Europa,1 -Evil,1 +Evac,1 +Evader,1 +Eventide,1 Evil Eye,1 +Evil,1 +Excalibur,1 Executioner,1 Executive,1 Executor,1 Exile,1 +Expedition,1 +Explorer,1 Express,1 Expresso,1 +Exquisite,1 +Fade,1 +Faerie,1 Fafnir,1 +Faith,1 Falcon,1 Falconer,1 +Fallout,1 +Fame,1 Fang,1 Farmer,1 +Fastener,1 Fate,1 Father,1 +Fathom,1 Fauchard,1 +Fearless,1 +Feather,1 Feedback,1 +Feint,1 Feline,1 Felis,1 Felix,1 +Fencer,1 Fender,1 +Fenrir,1 +Feral,1 +Ferret,1 Fester,1 Fiber,1 Fiddle,1 Fidget,1 Fido,1 Fiend,1 +Fierce,1 +Figurehead,1 Finder,1 Fine-line,1 Fingers,1 Fire Cat,1 +Fire,1 Fireball,1 Firebird,1 Firebrand,1 +Firefight,1 Firefly,1 Firehawk,1 +Firelance,1 +Firepit,1 +Firestarter,1 Firestorm,1 +Firestrike,1 +Firewall,1 +Firewolf,1 Firewood,1 Fish,1 Fisher,1 Fisherman,1 Fixer,1 Fizz,1 +Flame,1 +Flange,1 +Flank,1 Flanker,1 +Flapjack,1 +Flare,1 +Flaregun,1 Flash,1 +Flashbang,1 +Flashpoint,1 +Flatfoot,1 Flathead,1 Flatline,1 +Flesh,1 Flex,1 +Flicker,1 Flight,1 Flint,1 +Flintlock,1 Flip,1 +Flipflop,1 +Flipper,1 +Flood,1 +Flotsam,1 Flounder,1 Floyd,1 +Fluffy,1 Fluke,1 Flume,1 +Flurry,1 +Flux,1 Flyboy,1 Flycatcher,1 Flynn,1 Focus,1 +Fog,1 +Fogbank,1 +Foghorn,1 +Forager,1 Force,1 +Forest,1 +Forge,1 Fork,1 +Formation,1 +Fortitude,1 Fortress,1 +Fortune,1 +Forward,1 Foul,1 Fox,1 Foxglove,1 +Foxhole,1 Foxtrot,1 Foxy,1 Frag,1 Freak,1 Freakshow,1 +Frederick,1 Freebie,1 +Freebirth,1 +Freebooter,1 Freedom,1 Freight Train,1 +Freight,1 +Frenzy,1 Fresca,1 +Freya,1 Fridge,1 Friendly,1 Frigate,1 +Froggy,1 +Frontkick,1 Frontline,1 Frost,1 +Frostbite,1 Frosty,1 Funnelweb,1 Funny,1 +Furnace,1 Furor,1 Furry,1 Fury,1 Fuse,1 Fusion,1 Fuster,1 -Fuster-Lizard,1 Fuzz,1 Fuzzy,1 Gadget,1 +Gaea,1 Gaggle,1 +Gala,1 Galatea,1 Galaxy,1 Gale,1 +Galileo,1 +Gallant,1 +Galleon,1 +Galley,1 +Gambit,1 Gamble,1 Gambler,1 Gamer,1 @@ -771,45 +1220,65 @@ Ganymede,1 Garbage,1 Gargoyle,1 Garm,1 +Garnet,1 +Garrison,1 Garuda,1 +Gash,1 +Gasket,1 Gaslight,1 Gaspipe,1 +Gassy,1 Gator,1 Gatsby,1 Gaucho,1 +Gauge,1 Gear,1 Gearbox,1 Gecko,1 Geezer,1 +Gemini,1 +Gemstone,1 +Genbu,1 +Generator,1 Geneva,1 +Genghis,1 Genitor,1 +Gentry,1 Geoduck,1 Geryon,1 Getaway,1 Geyser,1 -Ghost,1 Ghost Walker,1 +Ghost,1 +Ghostfire,1 +Ghostwolf,1 Ghoul,1 -Giant,1 Giant-Killer,1 +Giant,1 Gig,1 Giggle,1 Giggles,1 +Gilded,1 Gillarg,1 Ginger,1 Ginsu,1 Giraffe,1 Girder,1 +Gizmo,1 +Glacier,1 Glaive,1 Glare,1 Glass,1 Glassjaw,1 Glasswing,1 Glide,1 +Glimmer,1 +Glint,1 Glissade,1 Glitch,1 Global,1 Glory,1 +Glyph,1 Gnarly,1 Goat,1 Goblin,1 @@ -818,35 +1287,58 @@ Godiva,1 Goggles,1 Goji,1 Gold,1 +Goldenrod,1 Goldfinch,1 +Goldfish,1 Goldhorn,1 +Goliath,1 Gomer,1 Gonzo,1 +Goober,1 Goody,1 Goofy,1 Goose,1 Goosefoot,1 Gopher,1 Gorgon,1 +Gorilla,1 +Grace,1 Graceland,1 Grackle,1 Grand Slam,1 +Grand,1 +Grandeur,1 Grandmaster,1 Grandpa,1 +Grappler,1 +Grave,1 Gravedigger,1 +Graveyard,1 +Graviton,1 +Gravy,1 +Gravytrain,1 +Grayscale,1 Greasy,1 Green,1 Greenroom,1 Gremlin,1 +Grenade,1 Grendel,1 Greyhound,1 Grid,1 +Gridiron,1 +Gridline,1 +Grief,1 Griffin,1 +Grim,1 +Grime,1 Grinch,1 +Grinder,1 Grit,1 Grizzly,1 Groovy,1 Grouch,1 +Grounder,1 Groundhog,1 Growl,1 Grub,1 @@ -854,7 +1346,10 @@ Grump,1 Grumpy,1 Grunt,1 Grunter,1 +Gryphon,1 +Guard,1 Guardian,1 +Guillotine,1 Guinness,1 Guisarme,1 Gumball,1 @@ -862,37 +1357,55 @@ Gumdrop,1 Gun Man,1 Gundog,1 Gundoll,1 +Gunfire,1 Gunhog,1 Gunner,1 +Gunport,1 +Gunpowder,1 Gunrunner,1 Gunslinger,1 Guts,1 +Gutter,1 +Gyro,1 Gyrojet,1 Gyru,1 +Hacker,1 Hacksaw,1 Hades,1 Hagfish,1 Hail,1 +Hailstone,1 Haizum,1 Halbadier,1 Halberd,1 +Halcyon,1 +Halfpipe,1 Halibut,1 +Hallow,1 Halo,1 Hambone,1 +Hamburger,1 Hammer,1 +Hammerfist,1 +Hammock,1 Handmaiden,1 Hang-up,1 Hangar,1 Hangover,1 Hannibal,1 Happy,1 +Harbinger,1 +Harbor,1 Hard Rock,1 Hardball,1 Hardcore,1 Hardline,1 Hardlock,1 Hardpoint,1 +Hardshell,1 Harley,1 +Harmonic,1 +Harness,1 Harpo,1 Harpoon,1 Harpy,1 @@ -900,41 +1413,53 @@ Harrier,1 Harvester,1 Hash,1 Hashcode,1 +Haste,1 Hatchet,1 Hatter,1 +Hauler,1 Haven,1 Havoc,1 Havok,1 Hawk,1 Hawkeye,1 +Hawkstrike,1 +Haymaker,1 Hazard,1 Haze,1 +Hazmat,1 Headcase,1 Headhunter,1 Headless,1 Headshot,1 -Heat,1 Heat Wave,1 +Heat,1 Heatsink,1 +Heatwave,1 Heaven,1 Hecate,1 +Heirloom,1 +Helios,1 Helius,1 Helix,1 Hell Cat,1 Hellbender,1 Hellburner,1 Hellebore,1 +Hellfire,1 Hellhound,1 Hellion,1 +Helm,1 Helo,1 Hemlock,1 Hera,1 Heracles,1 Hercules,1 +Heritage,1 Hermes,1 Hero,1 Herringbone,1 Hex,1 +Hexbolt,1 Hi-Spec,1 Hibagon,1 Hiccup,1 @@ -942,29 +1467,43 @@ Hickory,1 High Life,1 High Roller,1 High Voltage,1 +Highrise,1 Hightail,1 +Hightide,1 Hightower,1 +Highwater,1 +Hinge,1 Hippo,1 +Hippogriff,1 Hipshot,1 +Hitch,1 Hitman,1 Hive,1 Hivemind,1 Hocus,1 +Hoedown,1 Hog,1 Hogweed,1 Hoist,1 Hokum,1 +Hollow,1 Hollywood,1 +Holo,1 Holovid,1 -Holy,1 Holy Man,1 +Holy,1 Homunculus,1 Honor Guard,1 +Honor,1 Hooch,1 +Hood,1 Hook,1 +Hooligan,1 Hoop Snake,1 Hope,1 Hopeful,1 +Hopscotch,1 +Horizon,1 Hornet,1 Horse,1 Horseman,1 @@ -983,56 +1522,93 @@ Howl,1 Howler,1 Hudson,1 Huevos,1 +Huggy,1 Hulk,1 +Hull,1 +Hunchback,1 +Hunt,1 Hunter,1 Huntress,1 Hurricane,1 Husker,1 Husky,1 +Hustle,1 Hyde,1 Hydra,1 +Hydraulic,1 Hyena,1 Hymn,1 Hyper,1 +Hyperion,1 +Hyperlink,1 +Hypernova,1 Hyperpulse,1 Hypnos,1 +Iaido,1 Icarus,1 Ice,1 +Iceberg,1 Icebox,1 +Icebreaker,1 +Icecap,1 +Icefall,1 Icefisher,1 Iceheart,1 +Icehellion,1 Icehouse,1 Iceman,1 Icetea,1 +Icicle,1 Icon,1 Idol,1 +Idun,1 Ifrit,1 Igor,1 Iguana,1 +Illusion,1 Image,1 +Imp,1 Impact,1 Imperator,1 +Imperial,1 +Incant,1 Inch,1 +Incognito,1 +Incubus,1 Indiana,1 +Indomitable,1 Indy,1 +Infernal,1 Inferno,1 Infinity,1 Innovation,1 Intake,1 +Intel,1 Intendant,1 +Interceptor,1 +Intersection,1 Invader,1 +Invocation,1 +Io,1 Ion,1 +Iridescent,1 Iris,1 Irish,1 -Iron,1 Iron Claw,1 Iron Lady,1 Iron Man,1 +Iron,1 +Ironclad,1 +Ironclaw,1 Ironfin,1 Ironhead,1 +Ironheart,1 +Ironpalm,1 +Ironrust,1 Ironsides,1 Ironwood,1 Irony,1 +Ishtar,1 Isis,1 Island,1 Isonade,1 @@ -1040,14 +1616,19 @@ Itchy,1 Ivan,1 Ivanhoe,1 Ivory,1 +Jab,1 Jack,1 Jackal,1 Jackalope,1 Jackdaw,1 +Jackknife,1 Jackpot,1 Jaculus,1 +Jade,1 Jäger,1 +Jagged,1 Jaguar,1 +Jailbreak,1 Jalopy,1 Jambiyah,1 Jammer,1 @@ -1056,50 +1637,82 @@ Janus,1 Jasconius,1 Jasper,1 Java,1 +Javelin,1 Jaws,1 Jayhawk,1 Jazz,1 Jazzman,1 Jeckyl,1 +Jeet,1 Jelly,1 +Jellybean,1 Jenkins,1 Jester,1 Jet,1 Jethro,1 +Jetstream,1 +Jewel,1 Jiangshi,1 +Jib,1 +Jiggly,1 +Jigsaw,1 Jingle,1 Jinx,1 +Jiu,1 Jive,1 +Joan,1 Jockey,1 Johnson,1 Joker,1 Jolly,1 +Jormungandr,1 Jotun,1 +Joust,1 +Joy,1 +Joystick,1 Jubokko,1 Judge,1 Judgement,1 +Judo,1 Juggernaut,1 Juice,1 +Jukebox,1 +Julius,1 Jumbo,1 Jump Jet,1 Jump Point,1 +JumpShip,1 +Jumpstart,1 +Jumpy,1 +Junction,1 +Jungle,1 Junior,1 Junk Yard,1 Junker,1 +Juno,1 +Jury-Rig,1 Justicar,1 Justice,1 Jynx,1 K-9,1 Kaiju,1 Kaiser,1 +Kali,1 Karambit,1 +Karat,1 +Karate,1 Karkinos,1 Karma,1 Kato,1 Kayak,1 +Kazoo,1 +Keelhaul,1 Keeper,1 Keepsake,1 +Kellhound,1 Kelpie,1 +Kenjutsu,1 +Kepler,1 Kerberos,1 Keresh,1 Kestrel,1 @@ -1107,31 +1720,45 @@ Ketchup,1 Kettle,1 Kevlar,1 Key,1 +Keyhole,1 Keymaster,1 Keystone,1 +Keystroke,1 Khepri,1 Khopesh,1 Ki-rian,1 +Kiai,1 Kibble,1 +Kickflip,1 +Kicksnap,1 Kickstart,1 Kid,1 +Killbox,1 Killer,1 Killjoy,1 Kilo,1 +Kindle,1 King,1 +Kingcrab,1 +Kingfisher,1 Kingmaker,1 +Kingpin,1 Kingwood,1 +Kipup,1 Kishi,1 Kite,1 Kitsune,1 Kitten,1 -Kitty,1 Kitty Hawk,1 +Kitty,1 Kiwi,1 -Knife,1 +Knave,1 Knife Edge,1 +Knife,1 Knight,1 Knighthawk,1 +Knockout,1 +Knot,1 Knuckles,1 Kobiyashi,1 Kodama,1 @@ -1140,16 +1767,20 @@ Kojak,1 Kong,1 Koschei,1 Kpinga,1 +Kraken,1 Krampus,1 +Krav,1 Krill,1 Krunch,1 Kukri,1 Kumiho,1 +Kunai,1 Lace,1 Laceleaf,1 Laddie,1 Lady,1 Lager,1 +Lagrange,1 Laker,1 Lamassu,1 Lamb,1 @@ -1157,42 +1788,61 @@ Lamia,1 Lamp,1 Lamplight,1 Lamprey,1 +Lance,1 Lancer,1 Land Shark,1 Landslide,1 Lanik,1 +Lantern,1 +Larceny,1 Lark,1 +Laser,1 Lash,1 Lasher,1 +Lasso,1 +Lastshot,1 +Lathe,1 Latte,1 +Lava,1 Lawyer,1 League,1 Leaky,1 Leech,1 Lefty,1 +Legacy,1 Legal,1 Legend,1 +Lemon,1 Lemonade,1 Leonidas,1 Leshy,1 Lethal,1 Letter,1 +Lever,1 Leviathan,1 +Lexus,1 Liberty,1 Life Size,1 Lifeboat,1 Lifeguard,1 +Lifeline,1 Lifter,1 Light,1 Lightfoot,1 Lighthouse,1 Lightning,1 +Lightningbug,1 +Lightweaver,1 Lily,1 +Limit,1 Limpet,1 +Lincoln,1 Lindorm,1 Lineback,1 +Linebacker,1 Liner,1 Link,1 +Linkage,1 Lion,1 Lionfish,1 Lionheart,1 @@ -1200,12 +1850,19 @@ Lionness,1 Lite,1 Little,1 Live,1 +Loadout,1 +Loaner,1 Lobby,1 Lobo Plumado,1 Lobster,1 +Lock,1 Lockdown,1 Lockheart,1 +Lockpick,1 +Lockup,1 Loco,1 +Lodestar,1 +Logarithm,1 Loki,1 Lone Eagle,1 Lone Star,1 @@ -1220,31 +1877,50 @@ Lookout,1 Loom,1 Loon,1 Loony,1 +Loot,1 +Looter,1 Lostech,1 Loud,1 Lowball,1 +Lowrider,1 +Loyal,1 +Loyalist,1 Lucky,1 Lumberjack,1 +Luminous,1 Lumpy,1 Luna,1 Lunar,1 Lunchbox,1 Lungfish,1 +Lurker,1 +Lustre,1 +Luxury,1 +Lycan,1 Lynx,1 +Lyra,1 Lyric,1 +Macaroni,1 Mace,1 Machete,1 +Machine,1 Macho,1 +Macro,1 Mad Dog,1 Mad Fox,1 Mad Wolf,1 +Madcap,1 Madhat,1 +Madman,1 +Maelstrom,1 Maestro,1 Magellan,1 +Magellanic,1 Magic,1 Magistrate,1 Magma,1 Magnet,1 +Magneto,1 Magnum,1 Magpie,1 Mags,1 @@ -1255,29 +1931,46 @@ Mailman,1 Mailsloth,1 Mainframe,1 Mainsail,1 +Majestic,1 +Majesty,1 +Makeshift,1 Mako,1 Malibu,1 Mallard,1 +Malware,1 Mamba,1 +Mammoth,1 Mandrake,1 +Mango,1 Maniac,1 +Manifold,1 Manta,1 +Manticore,1 Mantis,1 +Mantra,1 Maple,1 +Marauder,1 +Marble,1 Marine,1 Mariner,1 +Mark,1 +Marksman,1 Marlin,1 Marmoset,1 Marquis,1 Marrow,1 Mars,1 +Marshal,1 Marshall,1 Marvel,1 Mascot,1 Mask,1 +Mast,1 Masterkey,1 +Masterpiece,1 Mastodon,1 Matador,1 +Matey,1 Maul,1 Maverick,1 Max,1 @@ -1288,6 +1981,7 @@ Meadowlark,1 Meat,1 Meatball,1 Meathook,1 +Mechanic,1 Medallion,1 Medea,1 Medic,1 @@ -1295,14 +1989,25 @@ Medipack,1 Medusa,1 Megaflop,1 Megasaur,1 +Mekjock,1 +Mekrat,1 +MekWarrior,1 +Melancholy,1 Melon,1 +Memory,1 Menace,1 +Merc,1 Merchant,1 Mercury,1 +Mercy,1 Merlin,1 Merrow,1 +Meta,1 Metal,1 +Metalshard,1 Meteor,1 +Meteorite,1 +Metro,1 Micro,1 Microwave,1 Midas,1 @@ -1310,21 +2015,33 @@ Middleman,1 Milestone,1 Milk,1 Milkman,1 +Milkyway,1 Miller,1 +Mimic,1 Mind Game,1 Minder,1 Minefield,1 +Minerva,1 +Minos,1 Minotaur,1 Minuteman,1 Mirage,1 Misfit,1 +Missile,1 Mist,1 +Mistral,1 +Miter,1 +Mittens,1 Mobile,1 Moccasin,1 +Mocha,1 +Modem,1 +Module,1 Moebius,1 Mohawk,1 Mojo,1 Molly,1 +Momentum,1 Monarch,1 Money,1 Mongoose,1 @@ -1332,20 +2049,26 @@ Mongrel,1 Monk,1 Monkfish,1 Monkshood,1 +Monsoon,1 Monster,1 Mooch,1 -Moon,1 Moon Rabbit,1 +Moon,1 Mooneye,1 Moonflower,1 Moonie,1 Moonlight,1 +Moonrise,1 Moose,1 Moosejaw,1 Moped,1 Moral,1 Moray,1 Morning Star,1 +Morningdew,1 +Morpheus,1 +Morrow,1 +Mortar,1 Motel,1 Mother,1 Mothership,1 @@ -1354,75 +2077,114 @@ Motion,1 Motley,1 Motor Mouth,1 Motown,1 +Mountain,1 Mouse,1 Mover,1 Moxie,1 Mud Hen,1 Mudbug,1 Mudguard,1 +Mudslide,1 Muffin,1 Muffintop,1 Mujina,1 Mumble,1 Mumbles,1 +Mural,1 Murdock,1 +Murk,1 Murky,1 Murmillo,1 Muse,1 Mushroom,1 -Music,1 Music Box,1 +Music,1 Musket,1 Muskrat,1 Mustang,1 +Mutiny,1 Muttonchop,1 Mystic,1 Mystique,1 +Mythic,1 Nacho,1 Nag,1 Naga,1 +Naginata,1 Nail,1 Nails,1 Namazu,1 +Nano,1 +Nanobot,1 +Napoleon,1 Narcissus,1 Nasty,1 +Nautilus,1 Nav,1 +NavPoint,1 Nebula,1 Needlenose,1 +Needles,1 Nekomata,1 +Nemesis,1 Nemo,1 Neo,1 +Neon,1 +Neonwave,1 +Nephilim,1 Neptune,1 +Nereid,1 Nest,1 Neutron,1 +Nexus,1 Nibbler,1 Nibbles,1 Nickel,1 +Niflheim,1 Night,1 Nightcap,1 +Nightfall,1 Nightingale,1 Nightlight,1 Nightmare,1 Nightshade,1 Nightwatch,1 +Nimbus,1 Nine,1 Ningyo,1 Ninja,1 Nitro,1 Noble,1 +Node,1 Noir,1 +Nokken,1 Nolan,1 Nomad,1 Noodle,1 +Noodles,1 Noose,1 Northman,1 +Northwind,1 +Nova,1 +Nozzle,1 Nugget,1 Nuke,1 +Null,1 Numbers,1 +Nunchaku,1 Nuts,1 +Nutty,1 +Nyx,1 Oak,1 +Oar,1 +Oatmeal,1 +Oberon,1 +Oblivion,1 +Obscure,1 +Obsidian,1 Ocean,1 Oceanside,1 +Ocelot,1 Odin,1 Odinson,1 Odysseus,1 @@ -1433,33 +2195,53 @@ Old Man,1 Olympia,1 Omaha,1 Omega,1 +Omen,1 +Omni,1 +Oni,1 Onikuma,1 Onyx,1 +Opal,1 +Opaque,1 +Optic,1 Optimal,1 +Opulence,1 Opus,1 Oracle,1 Orbit,1 +Orc,1 Orchid,1 Origin,1 Oriole,1 Orion,1 +Ornate,1 Orthrus,1 Oscar,1 +Osiris,1 Osprey,1 Ostrich,1 +Otter,1 Otterhound,1 Ouroboros,1 Outback,1 Outcast,1 +Outland,1 Outlaw,1 +Outpost,1 +Output,1 Outreach,1 Outrider,1 Outrigger,1 Oven,1 +Overboard,1 +Overcast,1 Overclock,1 +Overdrive,1 +Overhaul,1 Overhead,1 Overheat,1 Overlord,1 +Overpass,1 +Overrun,1 Overwatch,1 Owl,1 Owlman,1 @@ -1467,13 +2249,17 @@ Ox,1 Ozark,1 Pacer,1 Packer,1 +Packet,1 Packrat,1 Pagan,1 Paingod,1 Paint,1 Painter,1 +Paladin,1 Palerider,1 Palisade,1 +Palladium,1 +Palmstrike,1 Pamola,1 Pan,1 Panabas,1 @@ -1487,75 +2273,114 @@ Panzer,1 Paradise,1 Parrot,1 Parry,1 +Parsec,1 +Particle,1 Partisan,1 +Passion,1 Passkey,1 Password,1 -Patch,1 Patch-Eye,1 +Patch,1 Patches,1 +Patchwork,1 Pathfinder,1 +Patrol,1 Patton,1 Pavement,1 Payback,1 Payday,1 +Payload,1 Payroll,1 Peace,1 Peacebreaker,1 Peach,1 -Peanut,1 +Peacock,1 +Peaks,1 Peanut Butter,1 +Peanut,1 Pearl,1 +Peddler,1 +Pegasus,1 Pelican,1 Penghou,1 Penguin,1 +Pepper,1 +Pericles,1 +Permafrost,1 Perseus,1 Peryton,1 +Phalanx,1 Phantom,1 Phobia,1 +Phobos,1 +Phoenix,1 Photon,1 Picker,1 Pickle,1 Pickup,1 Picnic,1 Pidge,1 +Pierce,1 Pigeon,1 Pigpen,1 +Pike,1 Pilgrim,1 +Pillager,1 Pillbug,1 +Pilot,1 Pinball,1 +Pincer,1 +Pine,1 +Pinecone,1 Ping Pong,1 Pinkie,1 Pinky,1 +Pinnacle,1 Pinto,1 Pioneer,1 Pipeline,1 Piper,1 +Piranha,1 Pirate,1 Pirhana,1 +Pisces,1 Pistol,1 Piston,1 Pitbull,1 Pitcher,1 +Pitfall,1 Pitman,1 +Pitstop,1 +Pivot,1 +Pixel,1 Pixie,1 Pizza,1 Plan B,1 +Plank,1 Plankton,1 Plasma,1 +Platinum,1 +Plato,1 Player,1 +Plaza,1 Plink,1 Plinkie,1 Plover,1 +Plow,1 +Plucky,1 Plumado,1 +Plume,1 Pluto,1 Pocus,1 Pod,1 Pointer,1 Poison,1 Poker,1 -Polar,1 Polar Bear,1 +Polar,1 +Polaris,1 Polecat,1 +Polished,1 Pollen,1 Polt,1 Poltergeist,1 @@ -1563,36 +2388,56 @@ Pontoon,1 Pony,1 Pooka,1 Pop,1 +Popcorn,1 Pope,1 Popper,1 +Popshot,1 Porcupine,1 Porthos,1 Poseidon,1 Possum,1 +Poster,1 +Powderkeg,1 +Powergrid,1 Powerhawk,1 Powerline,1 Prancer,1 Predator,1 +Premier,1 +Prestige,1 Pretzel,1 Prickleback,1 Prime,1 Primer,1 Primetime,1 +Primo,1 Prince,1 Princess,1 +Prism,1 +Privateer,1 +Privilege,1 +Probe,1 Prodigy,1 Professor,1 Promenade,1 +Prophecy,1 +Protector,1 +Protocol,1 Proton,1 Provocator,1 Prowl,1 Prowler,1 Proxima,1 Psyche,1 +Psychic,1 Psycho,1 +Pudding,1 Puddles,1 Puffbird,1 Puffin,1 +Puffy,1 +Pugilist,1 +Pulley,1 Pulsar,1 Pulse,1 Pumpkin,1 @@ -1604,80 +2449,137 @@ Puppet Master,1 Pursuer,1 Pylon,1 Pyramid,1 +Pyre,1 Pyro,1 +Python,1 +Qilin,1 +Quackers,1 Quagmire,1 +Quake,1 +Quantum,1 +Quark,1 Quarterback,1 -Queen,1 +Quarterdeck,1 +Quartz,1 +Quasar,1 Queen Bee,1 +Queen,1 Queenie,1 +Quick,1 Quicksand,1 Quickshot,1 +Quicksilver,1 +Quickstrike,1 Quiet,1 +Quiver,1 Rabbit,1 Rabble,1 +Raccoon,1 Racer,1 Racetrack,1 Rack,1 +Racket,1 Radar,1 +Radburn,1 +Radiance,1 +Radiant,1 Radiowave,1 Raft,1 Rafter,1 +Ragnar,1 Raid,1 Raider,1 -Rain,1 +Raijin,1 Rain Bird,1 +Rain,1 Rainbow,1 +Rainfall,1 Rambo,1 Ramen,1 Rampage,1 Rampart,1 +Ramps,1 Rancher,1 Range-Bull,1 Ranger,1 +Ransack,1 Ranseur,1 +Raptor,1 Rascal,1 Raspberry,1 Rat Party,1 Ratchet,1 Rattler,1 Rattlesnake,1 +Rattletrap,1 +Ravage,1 +Ravager,1 Raven,1 +Ravenstar,1 Rawhide,1 Raygun,1 +Rayshine,1 Razor,1 Razorback,1 Razorcat,1 +Razorline,1 Reach,1 Readout,1 Ready,1 Reaper,1 Rebel,1 +Reboot,1 Rebound,1 Recharge,1 Reckless,1 Recluse,1 Recoil,1 -Red,1 +Recon,1 +Recycler,1 Red Comet,1 Red Flag,1 Red Star,1 Red Velvet,1 +Red,1 Redcap,1 Redeemer,1 Redeye,1 +Redline,1 Redwing,1 Redwood,1 +Refactor,1 Referee,1 +Refined,1 +Reforge,1 +Refraction,1 Refrigerator,1 +Refuge,1 +Regal,1 +Regulator,1 +Regulus,1 Reheat,1 Rein,1 +Relay,1 Relentless,1 +Reliant,1 +Relic,1 +Remnant,1 +Renaissance,1 Renegade,1 +Replicant,1 Repo,1 +Repurpose,1 Rescue,1 Reset,1 +Resilient,1 +Resistor,1 +Resolute,1 +Resolve,1 +Resource,1 Retcon,1 +Reticle,1 Retriever,1 +Revenant,1 Revere,1 Revolt,1 Rewire,1 @@ -1687,19 +2589,28 @@ Rhea,1 Rhino,1 Rhyme,1 Rib-Eye,1 +Riches,1 Rico,1 Ricochet,1 Riddler,1 Rider,1 +Ridge,1 +Ridgeback,1 Ridgehound,1 +Riffraff,1 Rifle,1 +Rifleman,1 +Rift,1 Rig,1 +Rigel,1 Rigs,1 Ringer,1 Ringman,1 Ringo,1 +Riot,1 Ripley,1 Ripper,1 +Ripple,1 Ripsaw,1 Riptide,1 Risky,1 @@ -1707,21 +2618,29 @@ Rival,1 River,1 Rivet,1 Roach,1 +Roadkill,1 Roadrash,1 +Roc,1 Rock,1 Rocket,1 +Rockfall,1 Rockjaw,1 +Rockslide,1 Rocky,1 Rodeo,1 Rogue,1 +Roguewave,1 Rolex,1 Roller,1 Roman,1 Romeo,1 Rommel,1 Ronin,1 +Roofline,1 +Rooftop,1 Rook,1 Rookie,1 +Roosevelt,1 Rooster,1 Root,1 Rose,1 @@ -1733,32 +2652,53 @@ Roughneck,1 Roughrider,1 Roulette,1 Rounder,1 +Roundhouse,1 Roundup,1 +Router,1 Rover,1 +Rowboat,1 Royal,1 Royale,1 Rubber Duck,1 +Rubber,1 +Rubberband,1 +Rubble,1 Rubius,1 -Rude,1 +Ruby,1 Rude Dog,1 +Rude,1 Rufus,1 Rugby,1 Ruin,1 Rumble,1 +Runesmith,1 Rusalka,1 Rush,1 +Rushhour,1 +Rustbucket,1 +Rustler,1 +Ruststorm,1 Rusty,1 Ruthless,1 Saber,1 +Sabertooth,1 +Sabotage,1 +Sabre,1 Safety,1 +Sage,1 Sahara,1 +Sailor,1 Saint,1 Salad,1 Salesman,1 Salsa,1 +Salt,1 Salty Dog,1 Salvage,1 +Salvager,1 Samnite,1 +Samurai,1 +Sanctum,1 Sand Wedge,1 Sandbag,1 Sandcastle,1 @@ -1768,16 +2708,25 @@ Sandstorm,1 Sandwich,1 Sapper,1 Sapphire,1 +Saros,1 Sasquatch,1 +Satellite,1 Satori,1 +Saturn,1 Satyr,1 Sauce,1 +Sausage,1 Savage,1 +Savate,1 +Savior,1 Savvy,1 Sawblade,1 Sawbuck,1 Saxon,1 +Scallywag,1 +Scalpel,1 Scandal,1 +Scanner,1 Scar,1 Scarab,1 Scarecrow,1 @@ -1785,16 +2734,24 @@ Scarface,1 Scarlet,1 Scars,1 Scary,1 +Scavenger,1 Scepter,1 +Schooner,1 +Scimitar,1 +Scissor,1 +Scofflaw,1 Scooter,1 Scope,1 Scorch,1 Score,1 Scorpion,1 +Scorpius,1 Scoundrel,1 Scout,1 Scramble,1 +Scrap,1 Scrapheap,1 +Scrapmetal,1 Scrapper,1 Scrappy,1 Scratchy,1 @@ -1805,37 +2762,65 @@ Scruffer,1 Scruffy,1 Scuba,1 Sculpin,1 +Scurvy,1 +Scylla,1 Scythe,1 Sea Dragon,1 Sea Lord,1 Sea Witch,1 +Seabird,1 +Seafoam,1 Seagull,1 +Seahorse,1 Sealion,1 Searchlight,1 +Seaspray,1 Secutor,1 Seelie,1 +Seer,1 +Selene,1 Selket,1 Selkie,1 Senate,1 Seneschal,1 Sentinel,1 Sentry,1 +Seraph,1 Servo,1 Set Up,1 +Set,1 Sever,1 Severe,1 +Shade,1 Shades,1 Shadow,1 +Shadowbane,1 +Shadowcat,1 +Shadowrun,1 +Shadowstar,1 +Shadowstrike,1 Shady,1 Shaft,1 +Shaggy,1 Shakespeare,1 Shamrock,1 Shank,1 +Shaolin,1 +Shard,1 Shark,1 +Sharpshooter,1 +Sharpshot,1 +Shatter,1 +Shear,1 Sheet Metal,1 -Shell,1 Shell Shock,1 +Shell,1 +Shellcode,1 +Shelter,1 +Shield,1 Shifty,1 +Shinobi,1 +Shipwreck,1 Shiv,1 Shiva,1 Shiver,1 @@ -1846,6 +2831,8 @@ Shockwave,1 Shoe,1 Shogun,1 Shooter,1 +Shore,1 +Shoreline,1 Short Straw,1 Shortlist,1 Shortstop,1 @@ -1854,47 +2841,71 @@ Shorty,1 Shotel,1 Shotgun,1 Shout Out,1 +Shovel,1 Showbiz,1 Showboat,1 Showdown,1 Showtime,1 Shrapnel,1 +Shredder,1 Shrike,1 +Shroud,1 Shut-In,1 Shutter,1 Sicilian,1 Sickle,1 Sidearm,1 Sidecar,1 +Sidekick,1 Sideshow,1 Sidetrack,1 +Sidewalk,1 Sidewinder,1 Siege,1 Sierra,1 Siesta,1 +Sif,1 +Sigil,1 +Signal,1 Signals,1 Signs,1 +Sigurd,1 +Silat,1 Silent,1 -Silver,1 +Silhouette,1 +Silk,1 Silver Eagle,1 +Silver,1 Silverback,1 Simurgh,1 +Sinkhole,1 Sinner,1 Siren,1 +Sirius,1 +Sirocco,1 Sixpack,1 Sizzler,1 Sjambok,1 +Skadi,1 Skater,1 Skeet,1 Skidoo,1 +Skiff,1 Skippy,1 Skull,1 +Skullcap,1 Skulls,1 Skunk,1 +Sky,1 +Skyfall,1 +Skyline,1 +Skyward,1 Slab,1 Slacker,1 +Slag,1 Slam,1 Slammer,1 +Slappy,1 Slapshot,1 Slash,1 Slasher,1 @@ -1910,18 +2921,27 @@ Sliderule,1 Slim,1 Slingshot,1 Slipstick,1 +Sliver,1 Sloth,1 Slow Burn,1 +Slug,1 Slugger,1 Sluggo,1 +Sly,1 Smallfry,1 +Smash,1 Smiley,1 Smog,1 Smoke,1 +Smokehouse,1 Smokes,1 +Smokescreen,1 +Smokestack,1 Smolder,1 -Snake,1 +Smuggler,1 Snake Eyes,1 +Snake,1 +Snakebite,1 Snakeroot,1 Snakewood,1 Snap,1 @@ -1932,40 +2952,67 @@ Sneak,1 Sneaky,1 Sneezy,1 Snicker,1 +Snickers,1 Snip,1 +Sniper,1 +Snippy,1 Snitch,1 Snoop,1 Snow,1 +Snowball,1 Snowbank,1 Snowbird,1 Snowbound,1 +Snowcone,1 +Snowfall,1 +Snowflake,1 +Snowfox,1 Snowman,1 +Snowstorm,1 +Snowwolf,1 Sobek,1 Socket,1 Socks,1 +Socrates,1 Soda Pop,1 Softpoint,1 +Soggy,1 +Solar,1 +Solarflare,1 Solitaire,1 Solomon,1 +Solstice,1 Sombrero,1 Sonic,1 Sorcerer,1 Sound,1 +Sounder,1 Sourbug,1 Sourdough,1 Sourpuss,1 +Sovereign,1 Spacer,1 Spade,1 +Spaghetti,1 +Spam,1 Spaniel,1 +Spar,1 Spare Parts,1 +Spark,1 Sparkle,1 Sparkles,1 +Sparkplug,1 Sparky,1 Sparrow,1 Sparrowhawk,1 +Spartacus,1 Spartan,1 Spear,1 +Spearhead,1 +Specter,1 +Spectra,1 Spectre,1 +Spectrum,1 Speedbird,1 Speedy,1 Spender,1 @@ -1973,47 +3020,75 @@ Spetum,1 Sphinx,1 Spice,1 Spicy,1 -Spider,1 Spider Monkey,1 +Spider,1 Spigot,1 Spike,1 +Spindle,1 +Spindrift,1 +Spinkick,1 +Spire,1 Spirit,1 Spitfire,1 +Splendor,1 Splinter,1 +Splitkick,1 Spoiler,1 Spook,1 Spooky,1 +Spork,1 Sport,1 Spotter,1 Sprawl,1 +Spraycan,1 +Spring,1 Sprinkles,1 Sprint,1 +Sprite,1 Sprocket,1 Spud,1 +Spyglass,1 +Squadron,1 +Squall,1 +Squatter,1 Squib,1 Squirrel,1 Squirt,1 +Squishy,1 Stag,1 +Stairwell,1 Stake,1 Stalk,1 Stalker,1 Stallion,1 +Stalwart,1 Star,1 Starbird,1 +Starboard,1 +Starbreaker,1 Starbuck,1 Starburst,1 +Stardust,1 Starfire,1 Starfish,1 Stargazer,1 Starlight,1 +Starlit,1 Starman,1 +Starpath,1 +Starward,1 Static,1 Station,1 +Steadfast,1 +Stealth,1 Steamer Duck,1 Steamfitter,1 Steel,1 Steeljaw,1 +Steeltoe,1 +Steelwolf,1 Stick,1 +Stiletto,1 Sting,1 Stinger,1 Stingray,1 @@ -2021,42 +3096,79 @@ Stinky,1 Stitch,1 Stoic,1 Stomp,1 +Stone,1 Stonefish,1 Stonehenge,1 Stork,1 Storm,1 +Stormbreaker,1 Stormcloud,1 +Stowaway,1 Stranger,1 +Strap,1 Stray,1 Streak,1 +Streetlamp,1 +Streetwise,1 +Strength,1 Strike,1 Striker,1 Strip,1 Stripe,1 Strix,1 +Stronghold,1 +Strut,1 +Stuka,1 Sturm,1 +Sublime,1 +Submerge,1 +Subnet,1 +Subroutine,1 +Subway,1 Sugar Rush,1 Sulphur,1 Summit,1 +Summoner,1 Sundance,1 +Sundawn,1 +Sunder,1 +Sundog,1 +Sundown,1 Sunfish,1 +Sunflare,1 Sunflower,1 +Sunrise,1 +Sunset,1 Sunshine,1 Sunspot,1 +Supercell,1 +Supernova,1 +Supreme,1 Sureshot,1 Surf,1 Surfer,1 +Surge,1 +Surgical,1 +Survivor,1 Swamp,1 +Swan,1 +Swarm,1 +Swashbuckler,1 Sweep,1 Sweeper,1 Sweetness,1 Swerve,1 Swift,1 +Swindler,1 Swing,1 Switch,1 Switchback,1 Swordfish,1 +Swordplay,1 +Sylph,1 +Sync,1 Syrup,1 +Sysop,1 T-Bone,1 Taboo,1 Taiaha,1 @@ -2064,33 +3176,48 @@ Tailgate,1 Taipan,1 Takoba,1 Talibong,1 +Talisman,1 Talky,1 Talon,1 Tango,1 Taniwha,1 Tank,1 +Tankbuster,1 +Tanker,1 Tanuki,1 Tapper,1 Taproom,1 +Tarantula,1 Tarbox,1 Tarise,1 +Tarmac,1 Tartan,1 Tarzan,1 Taskmaster,1 Tasty,1 +Tater,1 Teacher,1 +Tech,1 +Techno,1 Teflon,1 +Telescope,1 Telltale,1 Tempest,1 +Templar,1 Tempo,1 Tengu,1 +Terminal,1 Termite,1 Terrier,1 Terrorbird,1 Tesla,1 Tesseract,1 +Tetrad,1 Texas,1 Thane,1 +Thatcher,1 +Thaumaturge,1 +Thaw,1 The Count,1 Theropod,1 Thief,1 @@ -2105,14 +3232,25 @@ Thrash,1 Thrasher,1 Thread,1 Thresher,1 +Throttle,1 Thud,1 Thumper,1 Thunder,1 +Thunderbird,1 +Thunderbolt,1 +Thunderclap,1 Thunderer,1 Tick,1 +Tickles,1 +Ticktock,1 +Tide,1 +Tidepool,1 Tiger,1 +Tigerclaw,1 Tigertail,1 Tikbalang,1 +Timber,1 +Timberwolf,1 Timekeeper,1 Timeline,1 Timeslice,1 @@ -2123,27 +3261,42 @@ Tinkerbell,1 Tiny,1 Tire Fire,1 Titan,1 -Toad,1 +Titanium,1 Toad-fox,1 +Toad,1 Toaster,1 Toggle,1 Tombstone,1 +Toolbox,1 Toothsome,1 +Topaz,1 Torch,1 +Torchlight,1 Tornado,1 Toro,1 Torpedo,1 +Torque,1 +Torrent,1 Totem,1 Tots,1 Toucan,1 +Tower,1 Toxic,1 +Tracer,1 +Traceroute,1 +Track,1 Tracker,1 Tracks,1 Tractor,1 +Traffic,1 Trail Boss,1 +Trailblaze,1 Train,1 +Traitor,1 Trampoline,1 Tranquility,1 +Transistor,1 +Transit,1 Trap,1 Trapline,1 Trapper,1 @@ -2151,8 +3304,11 @@ Trash,1 Trashcan,1 Traveller,1 Tread,1 +Treadmark,1 +Trebuchet,1 Tremor,1 Triceratops,1 +Trickshot,1 Tricky,1 Trident,1 Trig,1 @@ -2161,6 +3317,7 @@ Trilobyte,1 Trip,1 Tripwire,1 Triton,1 +Triumph,1 Trojan,1 Troll,1 Trooper,1 @@ -2171,17 +3328,22 @@ Trout,1 Truce,1 Truck,1 Trucker,1 +Tsuchigumo,1 +Tsunami,1 Tugboat,1 Tulpa,1 -Tumble,1 Tumble Weed,1 +Tumble,1 +Tundra,1 Tupilaq,1 Turbine,1 Turbo,1 Turk,1 Turnaround,1 +Turncoat,1 Turtle,1 Tusk,1 +Tutankhamun,1 Twilight,1 Twinkle,1 Twister,1 @@ -2189,34 +3351,56 @@ Twitch,1 Two Times,1 Two-Dog,1 Two-Strike,1 +Typhon,1 +Typhoon,1 Tyrant,1 +Ullr,1 Ultimatum,1 +Umbra,1 Umpire,1 +Unbroken,1 Uncle,1 Underboss,1 +Underground,1 +Underhand,1 +Underpass,1 Undertaker,1 Undertow,1 Undine,1 +Unforged,1 Unicorn,1 Unicycle,1 Union,1 Unity,1 Universal,1 Unruly,1 +Unseen,1 +Updraft,1 +Upgrade,1 +Uptown,1 +Urbanite,1 +Ursa,1 Vagrant,1 +Valhalla,1 Valkyrie,1 Valor,1 +Valve,1 Vamp,1 Vampire,1 Vanara,1 Vandal,1 Vanguard,1 +Vanish,1 Vapor,1 Vapour,1 +Varnish,1 +Vault,1 Vector,1 +Vega,1 Vegas,1 Veil,1 Velocity,1 +Velvet,1 Vengeance,1 Venom,1 Venture,1 @@ -2224,14 +3408,24 @@ Vespa,1 Vespid,1 Vice,1 Victor,1 +Victoria,1 +Victory,1 Vigil,1 +Vigilant,1 Viking,1 Viper,1 +Virtual,1 +Virtue,1 +Vise,1 Vision,1 +Vivid,1 Vixen,1 +Vlad,1 Vodka,1 Vogel,1 Voice,1 +Void,1 +Volley,1 Voltage,1 Voodoo,1 Vortex,1 @@ -2242,51 +3436,87 @@ Vulture,1 Wagon,1 Waihaka,1 Wak-Wak,1 +Wakes,1 Walker,1 Wall,1 Wallaby,1 +Wallride,1 Walnuts,1 +War,1 Warbird,1 +Warden,1 +Warehouse,1 +Warhammer,1 Warhorse,1 Warlock,1 Warlord,1 Warmaster,1 Warpath,1 Warren,1 +Warrior,1 +Warship,1 Wasp,1 +Wasteland,1 +Wastelander,1 +Watermelon,1 Watson,1 +Wave,1 +Wavecrest,1 +Waveform,1 +Waypoint,1 +Weapon,1 Weasel,1 Webcap,1 Wedge,1 Weevil,1 +Weld,1 +Welder,1 Werewolf,1 Wharf,1 Wheel,1 +Wheely,1 Whelk,1 Whetstone,1 Whiplash,1 +Whirlpool,1 Whirlwind,1 +Whiskers,1 Whiskey,1 Whistle,1 Whistler,1 White Rose,1 White Tiger,1 +Whitecap,1 Who,1 Wicked,1 +Widow,1 Wiggly,1 +Wight,1 Wild Goose,1 +Wild,1 Wildcat,1 +Wildebeest,1 Wildfire,1 Willow,1 -Windchild,1 +Willowisp,1 +Winch,1 +Windchill,1 +Windfall,1 +Windjammer,1 Windsock,1 +Windstorm,1 Wingman,1 Winter,1 Wiseman,1 +Wisp,1 Wizard,1 Wobbegong,1 Wolf,1 +Wolfhound,1 Wolfman,1 +Wolfpack,1 +Wolfsbane,1 +Wolverine,1 Wombat,1 Wonderboy,1 Woodhen,1 @@ -2297,34 +3527,52 @@ Worm,1 Worrywart,1 Wraith,1 Wrangler,1 +Wrath,1 +Wreckage,1 +Wrecker,1 Wren,1 Wrench,1 Wrestler,1 +Wushu,1 Wyrm,1 Wyvern,1 X-Ray,1 +Xebec,1 Xerxes,1 +Yak,1 Yankee,1 +Yard,1 +Yardarm,1 Yellowjacket,1 Yeti,1 Ymir,1 Yogi,1 Young Gun,1 Yowie,1 +Yuki-Onna,1 Yukon,1 Zap,1 Zapper,1 +Zealot,1 Zebra,1 +Zebrafinch,1 Zebroid,1 +Zebu,1 Zen,1 Zenith,1 Zephyr,1 +Zephyrwolf,1 Zeppelin,1 Zero,1 +ZeroDay,1 Zeus,1 Zinger,1 Zipper,1 +Zippy,1 +Zircon,1 +Zodiac,1 Zombie,1 Zoom,1 +Zorilla,1 Zorro,1 Zulu,1 diff --git a/MekHQ/data/names/surnames.csv b/MekHQ/data/names/surnames.csv index 5ea5b5f8e32..cf4c446d6b5 100644 --- a/MekHQ/data/names/surnames.csv +++ b/MekHQ/data/names/surnames.csv @@ -24766,8 +24766,8 @@ Ethnic Code,Name,Weight 5,Zurbrugg,1 5,Zweig,2 5,Zylberstein,1 -5,felder,1 -5,rauffenburg,1 +5,Felder,1 +5,Rauffenburg,1 5,von,1 6,Aafjes,1 6,Aalbers,1 diff --git a/MekHQ/data/scenariomodifiers/AlliedASFAce01.xml b/MekHQ/data/scenariomodifiers/AlliedASFAce01.xml index ca99a609492..b9670782c05 100644 --- a/MekHQ/data/scenariomodifiers/AlliedASFAce01.xml +++ b/MekHQ/data/scenariomodifiers/AlliedASFAce01.xml @@ -12,9 +12,9 @@ -2 2 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedAirSupport.xml b/MekHQ/data/scenariomodifiers/AlliedAirSupport.xml index ae8d3c4a4b9..de8e555dd3c 100644 --- a/MekHQ/data/scenariomodifiers/AlliedAirSupport.xml +++ b/MekHQ/data/scenariomodifiers/AlliedAirSupport.xml @@ -8,9 +8,9 @@ -3 -3 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedAirSupportBombers.xml b/MekHQ/data/scenariomodifiers/AlliedAirSupportBombers.xml index 7590d716595..7c348634d84 100644 --- a/MekHQ/data/scenariomodifiers/AlliedAirSupportBombers.xml +++ b/MekHQ/data/scenariomodifiers/AlliedAirSupportBombers.xml @@ -8,9 +8,9 @@ -3 -3 false - true + false true - true + false false 5 @@ -26,5 +26,8 @@ SameEdge Player false + + BOMBER + diff --git a/MekHQ/data/scenariomodifiers/AlliedArtyGarrison.xml b/MekHQ/data/scenariomodifiers/AlliedArtyGarrison.xml index 98aa708094e..dd300d6c0b0 100644 --- a/MekHQ/data/scenariomodifiers/AlliedArtyGarrison.xml +++ b/MekHQ/data/scenariomodifiers/AlliedArtyGarrison.xml @@ -12,9 +12,9 @@ 1 0 false - true + false true - true + false false 10 diff --git a/MekHQ/data/scenariomodifiers/AlliedArtySupport.xml b/MekHQ/data/scenariomodifiers/AlliedArtySupport.xml index d5b58f4a286..98759955b0e 100644 --- a/MekHQ/data/scenariomodifiers/AlliedArtySupport.xml +++ b/MekHQ/data/scenariomodifiers/AlliedArtySupport.xml @@ -12,9 +12,9 @@ 1 0 false - true + false false - true + false true 9 diff --git a/MekHQ/data/scenariomodifiers/AlliedCavLance.xml b/MekHQ/data/scenariomodifiers/AlliedCavLance.xml index e865b5cfc37..ca31ac98980 100644 --- a/MekHQ/data/scenariomodifiers/AlliedCavLance.xml +++ b/MekHQ/data/scenariomodifiers/AlliedCavLance.xml @@ -12,9 +12,9 @@ -2 3 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedHorseCav.xml b/MekHQ/data/scenariomodifiers/AlliedHorseCav.xml index 6f4d2d41937..8e6a56e45d6 100644 --- a/MekHQ/data/scenariomodifiers/AlliedHorseCav.xml +++ b/MekHQ/data/scenariomodifiers/AlliedHorseCav.xml @@ -12,9 +12,9 @@ -2 0 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedMekAceCRD.xml b/MekHQ/data/scenariomodifiers/AlliedMekAceCRD.xml index 834c5583243..cbf12f4eee3 100644 --- a/MekHQ/data/scenariomodifiers/AlliedMekAceCRD.xml +++ b/MekHQ/data/scenariomodifiers/AlliedMekAceCRD.xml @@ -12,9 +12,9 @@ -2 2 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedMekAceGLT.xml b/MekHQ/data/scenariomodifiers/AlliedMekAceGLT.xml index 2bd55edba66..a75ebdb9e57 100644 --- a/MekHQ/data/scenariomodifiers/AlliedMekAceGLT.xml +++ b/MekHQ/data/scenariomodifiers/AlliedMekAceGLT.xml @@ -12,9 +12,9 @@ -2 2 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedMekAceGOL.xml b/MekHQ/data/scenariomodifiers/AlliedMekAceGOL.xml index 4431784250a..f42d504686c 100644 --- a/MekHQ/data/scenariomodifiers/AlliedMekAceGOL.xml +++ b/MekHQ/data/scenariomodifiers/AlliedMekAceGOL.xml @@ -12,9 +12,9 @@ -2 2 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedMekAceOTT.xml b/MekHQ/data/scenariomodifiers/AlliedMekAceOTT.xml index 56c6ded9e5f..8987212e5fa 100644 --- a/MekHQ/data/scenariomodifiers/AlliedMekAceOTT.xml +++ b/MekHQ/data/scenariomodifiers/AlliedMekAceOTT.xml @@ -12,9 +12,9 @@ -2 2 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedMekAceTBT.xml b/MekHQ/data/scenariomodifiers/AlliedMekAceTBT.xml index 1ec218702b5..1e9e727f994 100644 --- a/MekHQ/data/scenariomodifiers/AlliedMekAceTBT.xml +++ b/MekHQ/data/scenariomodifiers/AlliedMekAceTBT.xml @@ -12,9 +12,9 @@ -2 2 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedMekGarrison.xml b/MekHQ/data/scenariomodifiers/AlliedMekGarrison.xml index 5a533ee2420..432fb2ba54d 100644 --- a/MekHQ/data/scenariomodifiers/AlliedMekGarrison.xml +++ b/MekHQ/data/scenariomodifiers/AlliedMekGarrison.xml @@ -14,7 +14,7 @@ false true true - false + true false 10 diff --git a/MekHQ/data/scenariomodifiers/AlliedOfficerMek.xml b/MekHQ/data/scenariomodifiers/AlliedOfficerMek.xml index 8e391424ede..66702c59dae 100644 --- a/MekHQ/data/scenariomodifiers/AlliedOfficerMek.xml +++ b/MekHQ/data/scenariomodifiers/AlliedOfficerMek.xml @@ -12,9 +12,9 @@ 0 0 false - true + false true - true + false false 5 @@ -33,5 +33,8 @@ SameEdge Player false + + COMMAND + diff --git a/MekHQ/data/scenariomodifiers/AlliedTraineesAir.xml b/MekHQ/data/scenariomodifiers/AlliedTraineesAir.xml index c542231e580..a9faaa5d686 100644 --- a/MekHQ/data/scenariomodifiers/AlliedTraineesAir.xml +++ b/MekHQ/data/scenariomodifiers/AlliedTraineesAir.xml @@ -12,9 +12,9 @@ 9 0 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedTraineesGround.xml b/MekHQ/data/scenariomodifiers/AlliedTraineesGround.xml index 3bfd740ce1d..fbd67d87f38 100644 --- a/MekHQ/data/scenariomodifiers/AlliedTraineesGround.xml +++ b/MekHQ/data/scenariomodifiers/AlliedTraineesGround.xml @@ -12,9 +12,9 @@ 0 0 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/AlliedTurrets.xml b/MekHQ/data/scenariomodifiers/AlliedTurrets.xml index abd0088b436..64ea0eb8502 100644 --- a/MekHQ/data/scenariomodifiers/AlliedTurrets.xml +++ b/MekHQ/data/scenariomodifiers/AlliedTurrets.xml @@ -12,9 +12,9 @@ 7 0 false - true + false true - true + false 10 diff --git a/MekHQ/data/scenariomodifiers/EnemyAirSupportBombers.xml b/MekHQ/data/scenariomodifiers/EnemyAirSupportBombers.xml index 725b949f2f8..a97f7e0973f 100644 --- a/MekHQ/data/scenariomodifiers/EnemyAirSupportBombers.xml +++ b/MekHQ/data/scenariomodifiers/EnemyAirSupportBombers.xml @@ -37,5 +37,8 @@ 5 None + + BOMBER + diff --git a/MekHQ/data/scenariomodifiers/EnemyCommanderMek.xml b/MekHQ/data/scenariomodifiers/EnemyCommanderMek.xml index 6f5fc888dc0..7a85f15a03d 100644 --- a/MekHQ/data/scenariomodifiers/EnemyCommanderMek.xml +++ b/MekHQ/data/scenariomodifiers/EnemyCommanderMek.xml @@ -33,5 +33,8 @@ SameEdge OpFor false + + COMMAND + diff --git a/MekHQ/data/scenariomodifiers/EnemyOfficerMek.xml b/MekHQ/data/scenariomodifiers/EnemyOfficerMek.xml index b443355d68a..f60732b771c 100644 --- a/MekHQ/data/scenariomodifiers/EnemyOfficerMek.xml +++ b/MekHQ/data/scenariomodifiers/EnemyOfficerMek.xml @@ -33,5 +33,8 @@ SameEdge OpFor false + + COMMAND + diff --git a/MekHQ/data/scenariomodifiers/FacilityAlliedEvac.xml b/MekHQ/data/scenariomodifiers/FacilityAlliedEvac.xml index e09500965a0..0583bd8dcda 100644 --- a/MekHQ/data/scenariomodifiers/FacilityAlliedEvac.xml +++ b/MekHQ/data/scenariomodifiers/FacilityAlliedEvac.xml @@ -8,9 +8,9 @@ -1 0 true - true + false true - true + false false 10 diff --git a/MekHQ/data/scenariomodifiers/HouseOfficerAir.xml b/MekHQ/data/scenariomodifiers/HouseOfficerAir.xml index 8da55adb10d..9c62c932a2a 100644 --- a/MekHQ/data/scenariomodifiers/HouseOfficerAir.xml +++ b/MekHQ/data/scenariomodifiers/HouseOfficerAir.xml @@ -30,5 +30,8 @@ SameEdge Player false + + INTERCEPTOR + diff --git a/MekHQ/data/scenariomodifiers/HouseOfficerGround.xml b/MekHQ/data/scenariomodifiers/HouseOfficerGround.xml index 37c40526853..25f1d6dc7a9 100644 --- a/MekHQ/data/scenariomodifiers/HouseOfficerGround.xml +++ b/MekHQ/data/scenariomodifiers/HouseOfficerGround.xml @@ -34,5 +34,8 @@ SameEdge Player false + + COMMAND + diff --git a/MekHQ/data/scenariomodifiers/LiaisonAir.xml b/MekHQ/data/scenariomodifiers/LiaisonAir.xml index 59c3c891d11..d497d8a037b 100644 --- a/MekHQ/data/scenariomodifiers/LiaisonAir.xml +++ b/MekHQ/data/scenariomodifiers/LiaisonAir.xml @@ -1,5 +1,5 @@ - Your employer has deployer an observer to monitor your performance. + Your employer has deployed an observer to monitor your performance. true PreForceGeneration @@ -30,5 +30,8 @@ SameEdge Player false + + INTERCEPTOR + diff --git a/MekHQ/data/scenariomodifiers/LiaisonGround.xml b/MekHQ/data/scenariomodifiers/LiaisonGround.xml index 3c5965f7608..5c2350e18e9 100644 --- a/MekHQ/data/scenariomodifiers/LiaisonGround.xml +++ b/MekHQ/data/scenariomodifiers/LiaisonGround.xml @@ -1,5 +1,5 @@ - Your employer has deployer an observer to monitor your performance. + Your employer has deployed an observer to monitor your performance. AllGroundTerrain SpecificGroundTerrain @@ -34,5 +34,8 @@ SameEdge Player false + + COMMAND + diff --git a/MekHQ/data/scenariomodifiers/LocalGarrisonInfantry.xml b/MekHQ/data/scenariomodifiers/LocalGarrisonInfantry.xml index 3e38fe8a327..98aed00da7c 100644 --- a/MekHQ/data/scenariomodifiers/LocalGarrisonInfantry.xml +++ b/MekHQ/data/scenariomodifiers/LocalGarrisonInfantry.xml @@ -12,7 +12,7 @@ 3 0 false - true + false true false diff --git a/MekHQ/data/scenariomodifiers/PrimaryAlliesAir.xml b/MekHQ/data/scenariomodifiers/PrimaryAlliesAir.xml index 5047e32dfee..e12bd082a61 100644 --- a/MekHQ/data/scenariomodifiers/PrimaryAlliesAir.xml +++ b/MekHQ/data/scenariomodifiers/PrimaryAlliesAir.xml @@ -8,9 +8,9 @@ -3 0 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/PrimaryAlliesGround.xml b/MekHQ/data/scenariomodifiers/PrimaryAlliesGround.xml index 99f4f27b6c8..bbaa6687024 100644 --- a/MekHQ/data/scenariomodifiers/PrimaryAlliesGround.xml +++ b/MekHQ/data/scenariomodifiers/PrimaryAlliesGround.xml @@ -12,9 +12,9 @@ -2 0 false - true + false true - true + false false 5 diff --git a/MekHQ/data/scenariomodifiers/airBattleModifiers.xml b/MekHQ/data/scenariomodifiers/airBattleModifiers.xml index 7ea90faecfd..a467550236e 100644 --- a/MekHQ/data/scenariomodifiers/airBattleModifiers.xml +++ b/MekHQ/data/scenariomodifiers/airBattleModifiers.xml @@ -26,9 +26,10 @@ Need ability to allow player to specifically deploy DropShips HotDrop.xml - + GoodIntel.xml @@ -83,5 +84,8 @@ EnemyASFAce02.xml + + PrimaryAlliesAir.xml + diff --git a/MekHQ/data/scenariomodifiers/groundBattleModifiers.xml b/MekHQ/data/scenariomodifiers/groundBattleModifiers.xml index 4ca3b24a95f..6c9280ce926 100644 --- a/MekHQ/data/scenariomodifiers/groundBattleModifiers.xml +++ b/MekHQ/data/scenariomodifiers/groundBattleModifiers.xml @@ -56,12 +56,14 @@ EnemyHotDrop.xml - + + GoodIntel.xml @@ -243,5 +245,8 @@ EnemyASFAce02.xml + + PrimaryAlliesGround.xml + diff --git a/MekHQ/data/scenariomodifiers/modifiermanifest.xml b/MekHQ/data/scenariomodifiers/modifiermanifest.xml index 43d4d7a16ad..a22200a4f3a 100644 --- a/MekHQ/data/scenariomodifiers/modifiermanifest.xml +++ b/MekHQ/data/scenariomodifiers/modifiermanifest.xml @@ -62,12 +62,14 @@ EnemyHotDrop.xml - + + GoodIntel.xml diff --git a/MekHQ/data/scenariotemplates/Assassination.xml b/MekHQ/data/scenariotemplates/Assassination.xml index d0d8bbfb75f..b7bb16165c4 100644 --- a/MekHQ/data/scenariotemplates/Assassination.xml +++ b/MekHQ/data/scenariotemplates/Assassination.xml @@ -111,6 +111,10 @@ OppositeEdge Player false + + COMMAND + APC + diff --git a/MekHQ/data/scenariotemplates/Chasing a Rumor.xml b/MekHQ/data/scenariotemplates/Chasing a Rumor.xml new file mode 100644 index 00000000000..2be638cc823 --- /dev/null +++ b/MekHQ/data/scenariotemplates/Chasing a Rumor.xml @@ -0,0 +1,146 @@ + + + Chasing a Rumor + SPECIAL_LOSTECH + Fight to secure the hidden cache + Intel suggests there is a hidden cache in this region. Unfortunately, enemy forces seem to have caught wind of this intel. The objective is to either destroy or force them to retreat. At least 50% of the enemy must be neutralized, while maintaining 50% of friendly forces. + + If this scenario is lost, so too is the hidden cache. + + + false + 0 + 0 + 5 + AllGroundTerrain + true + 5 + + + + Player + + -1 + false + -2 + 0 + true + true + true + true + false + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + + 5 + 0 + 0 + 1.0 + Player + 0 + 1 + 4 + 0 + 50 + 0 + None + false + + + + OpFor + + -1 + false + -2 + 0 + true + false + true + false + false + + 5 + 0 + 2 + 2.0 + OpFor + 1 + 5 + 4 + 0 + 50 + 0 + OppositeEdge + Player + false + + + + + + + OpFor + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Destroy or rout 50% of the following force(s) and unit(s): + NONE + ForceWithdraw + 50 + true + None + + + + Player + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Preserve 50% of the following force(s) and unit(s): + NONE + Preserve + 50 + true + None + + + diff --git a/MekHQ/data/scenariotemplates/Close Air Support.xml b/MekHQ/data/scenariotemplates/Close Air Support.xml index f1e149f8def..760d64ed185 100644 --- a/MekHQ/data/scenariotemplates/Close Air Support.xml +++ b/MekHQ/data/scenariotemplates/Close Air Support.xml @@ -2,7 +2,7 @@ Close Air Support Provide air support, minimize losses. - Provide air support to allied ground forces under heavy attack. Prioritize minimizing casualties while neutralizing enemy units. Expect anti-air defenses. Success depends on keeping at least 50% of allies alive and destroying half of the enemy force. + Provide air support to allied ground forces under heavy attack. Prioritize minimizing casualties while neutralizing enemy units. Success depends on keeping at least 50% of allies alive and destroying half of the enemy force. false diff --git a/MekHQ/data/scenariotemplates/Convoy Escort.xml b/MekHQ/data/scenariotemplates/Convoy Escort.xml index 1fbd7b5757a..1fd03e1c6ed 100644 --- a/MekHQ/data/scenariotemplates/Convoy Escort.xml +++ b/MekHQ/data/scenariotemplates/Convoy Escort.xml @@ -74,6 +74,10 @@ OppositeEdge Convoy false + + RAIDER + CAVALRY + @@ -84,9 +88,9 @@ -2 0 false - true + false true - true + false false 2 @@ -110,8 +114,12 @@ false CIVILIAN - CARGO SUPPORT + SUPPORT + CARGO + CARGO + CARGO + APC APC diff --git a/MekHQ/data/scenariotemplates/Convoy Interdiction.xml b/MekHQ/data/scenariotemplates/Convoy Interdiction.xml index 7e231b20085..e5ec14a33db 100644 --- a/MekHQ/data/scenariotemplates/Convoy Interdiction.xml +++ b/MekHQ/data/scenariotemplates/Convoy Interdiction.xml @@ -106,8 +106,12 @@ false CIVILIAN - CARGO SUPPORT + SUPPORT + CARGO + CARGO + CARGO + APC APC diff --git a/MekHQ/data/scenariotemplates/Convoy Raid.xml b/MekHQ/data/scenariotemplates/Convoy Raid.xml index 8d9653acdee..e1af1038685 100644 --- a/MekHQ/data/scenariotemplates/Convoy Raid.xml +++ b/MekHQ/data/scenariotemplates/Convoy Raid.xml @@ -77,7 +77,7 @@ -1 false - -2 + 1 0 true false @@ -107,7 +107,6 @@ CIVILIAN CARGO - SUPPORT APC diff --git a/MekHQ/data/scenariotemplates/Critical Convoy Escort.xml b/MekHQ/data/scenariotemplates/Critical Convoy Escort.xml index dc428b31aba..ab007f7f749 100644 --- a/MekHQ/data/scenariotemplates/Critical Convoy Escort.xml +++ b/MekHQ/data/scenariotemplates/Critical Convoy Escort.xml @@ -74,6 +74,10 @@ OppositeEdge Convoy false + + RAIDER + CAVALRY + @@ -84,9 +88,9 @@ -2 0 false - true + false true - true + false false 2 @@ -110,8 +114,12 @@ false CIVILIAN - CARGO SUPPORT + SUPPORT + CARGO + CARGO + CARGO + APC APC diff --git a/MekHQ/data/scenariotemplates/Deep Raid Defense.xml b/MekHQ/data/scenariotemplates/Deep Raid Defense.xml index c81cdf3c67a..bdce300677f 100644 --- a/MekHQ/data/scenariotemplates/Deep Raid Defense.xml +++ b/MekHQ/data/scenariotemplates/Deep Raid Defense.xml @@ -2,7 +2,7 @@ Deep Raid Defense Defend grounded DropShip from raid. - An enemy raid deep behind friendly lines is targeting a grounded DropShip. Protect it for 30 turns until liftoff, ensuring it does not become crippled. Defeat at least 50% of the enemy raiders while preserving 50% of your forces during the defense. + An enemy raid deep behind friendly lines is targeting a grounded DropShip. Protect it until liftoff, ensuring it does not become crippled. Defeat at least 50% of the enemy raiders while preserving 50% of your forces during the defense. false false @@ -178,6 +178,19 @@ 0 None false + + ARTILLERY + MISSILE_ARTILLERY + MIXED_ARTILLERY + FIRE_SUPPORT + FIRE_SUPPORT + FIRE_SUPPORT + FIRE_SUPPORT + SR_FIRE_SUPPORT + SR_FIRE_SUPPORT + SR_FIRE_SUPPORT + SR_FIRE_SUPPORT + diff --git a/MekHQ/data/scenariotemplates/Deep Raid.xml b/MekHQ/data/scenariotemplates/Deep Raid.xml index 4626c7f3f47..7fe6d4b6ab8 100644 --- a/MekHQ/data/scenariotemplates/Deep Raid.xml +++ b/MekHQ/data/scenariotemplates/Deep Raid.xml @@ -2,7 +2,7 @@ Deep Raid Destroy or cripple DropShip. - A grounded DropShip must be destroyed or crippled within 30 turns before it lifts off. This raid takes place deep behind enemy lines, and the DropShip’s defenders are not important. Preserve 50% of your forces during the operation to ensure mission success. + A grounded DropShip must be destroyed or crippled before it lifts off. This raid takes place deep behind enemy lines, and the DropShip’s defenders are not important. Preserve 50% of your forces during the operation to ensure mission success. false false diff --git a/MekHQ/data/scenariotemplates/DropShip Raid.xml b/MekHQ/data/scenariotemplates/DropShip Raid.xml index 0061339572f..1cb2c775b90 100644 --- a/MekHQ/data/scenariotemplates/DropShip Raid.xml +++ b/MekHQ/data/scenariotemplates/DropShip Raid.xml @@ -2,7 +2,7 @@ DropShip Raid Destroy or cripple DropShip. - A DropShip has been intercepted shortly after landing. You have 30 turns to destroy or cripple it before it can lift off. The defenders are not the priority. Preserve 50% of your forces to ensure mission success. + A DropShip has been intercepted shortly after landing. Destroy or cripple it before it can lift off. The defenders are not the priority. Preserve 50% of your forces to ensure mission success. false false diff --git a/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player - Low-Atmosphere.xml b/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player - Low-Atmosphere.xml new file mode 100644 index 00000000000..61966491d67 --- /dev/null +++ b/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player - Low-Atmosphere.xml @@ -0,0 +1,139 @@ + + + Emergency Convoy Defense + SPECIAL_RESUPPLY + Escort DropShip, defeat enemy forces. + The convoy carrying your resupply has come under attack. Ensure the supplies do not fall into enemy hands by destroying 50% of enemy forces. Preserve 50% of your own forces to secure mission success. + false + false + + + true + 17 + 50 + 5 + LowAtmosphere + false + 5 + + + + Player + + -1 + false + -3 + 0 + true + true + true + true + false + + 10 + + 5 + 0 + 0 + 1.0 + Player + 0 + 1 + 4 + 0 + + 50 + 5 + None + false + + + + OpFor + + -1 + false + -3 + 0 + true + false + true + false + false + + 5 + 0 + 2 + 2.0 + OpFor + 1 + 5 + 4 + 0 + + 50 + 5 + OppositeEdge + Player + false + + + + + + + OpFor + + + + + ScenarioVictory + Fixed + 3 + + + + + ScenarioDefeat + Fixed + 3 + + + + Destroy or rout 50% of the following force(s) and unit(s): + NONE + ForceWithdraw + 50 + 0 + true + None + + + + Player + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Preserve 50% of the following force(s) and unit(s): + NONE + Preserve + 50 + true + None + + + diff --git a/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player - VTOL.xml b/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player - VTOL.xml new file mode 100644 index 00000000000..83706629f16 --- /dev/null +++ b/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player - VTOL.xml @@ -0,0 +1,172 @@ + + + Emergency Convoy Defense + SPECIAL_RESUPPLY + Prevent your resupply falling into the hands of the enemy. + The convoy carrying your resupply has come under attack. Ensure the supplies do not fall into enemy hands by destroying 50% of enemy forces. Preserving friendly forces is a secondary priority. + false + false + + + false + 50 + 50 + 5 + AllGroundTerrain + true + 5 + + + + Player + + -1 + false + -2 + 0 + true + true + true + true + false + + 10 + + 4 + 0 + 0 + 1.0 + Player + 0 + 1 + 4 + 0 + + 0 + 0 + None + false + + + + OpFor + + -1 + false + 1 + 0 + true + false + true + false + false + + 0 + + 5 + 0 + 2 + 1.0 + OpFor + 1 + 5 + 4 + 1 + + 50 + 0 + None + false + + + + Air Cavalry + + -1 + false + 5 + 0 + true + false + true + false + false + + 0 + + 5 + 0 + 2 + 1.0 + Air Cavalry + 1 + 5 + 4 + 1 + + 50 + 0 + None + false + + + + + + + OpFor + Air Cavalry + + + + + ScenarioVictory + Fixed + 3 + + + + + ScenarioDefeat + Fixed + 3 + + + + Destroy or rout 50% of the following force(s) and unit(s): + NONE + ForceWithdraw + 50 + 0 + true + None + + + + Player + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Preserve 50% of the following force(s) and unit(s): + NONE + Preserve + 50 + true + None + + + diff --git a/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player.xml b/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player.xml new file mode 100644 index 00000000000..285f872b9cb --- /dev/null +++ b/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player.xml @@ -0,0 +1,143 @@ + + + Emergency Convoy Defense + SPECIAL_RESUPPLY + Prevent your resupply falling into the hands of the enemy. + The convoy carrying your resupply has come under attack. Ensure the supplies do not fall into enemy hands by destroying 50% of enemy forces. Preserving friendly forces is a secondary priority. + false + false + + + false + 25 + 25 + 5 + AllGroundTerrain + true + 5 + + + + Player + + -1 + false + -2 + 0 + true + true + true + true + false + + 10 + + 4 + 0 + 0 + 1.0 + Player + 0 + 1 + 4 + 0 + + 0 + 0 + None + false + + + + OpFor + + -1 + false + 1 + 0 + true + false + true + false + false + + 0 + + 5 + 0 + 2 + 2.0 + OpFor + 1 + 5 + 4 + 1 + + 50 + 0 + None + false + + ANTI_AIRCRAFT + + + + + + + + OpFor + + + + + ScenarioVictory + Fixed + 3 + + + + + ScenarioDefeat + Fixed + 3 + + + + Destroy or rout 50% of the following force(s) and unit(s): + NONE + ForceWithdraw + 50 + 0 + true + None + + + + Player + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Preserve 50% of the following force(s) and unit(s): + NONE + Preserve + 50 + true + None + + + diff --git a/MekHQ/data/scenariotemplates/Emergency Convoy Defense.xml b/MekHQ/data/scenariotemplates/Emergency Convoy Defense.xml new file mode 100644 index 00000000000..f594990f396 --- /dev/null +++ b/MekHQ/data/scenariotemplates/Emergency Convoy Defense.xml @@ -0,0 +1,192 @@ + + + Emergency Convoy Defense + SPECIAL_RESUPPLY + Prevent your resupply falling into the hands of the enemy. + The convoy carrying your resupply has come under attack. Ensure the supplies do not fall into enemy hands by destroying 50% of enemy forces. Preserving friendly forces is a secondary priority. + false + false + + + false + 25 + 25 + 5 + AllGroundTerrain + true + 5 + + + + Player + + -1 + false + -2 + -3 + true + true + true + true + false + + 9 + + 4 + 0 + 0 + 1.0 + Player + 0 + 1 + 4 + 0 + + 0 + 0 + false + + + + OpFor + + -1 + false + 1 + 0 + true + false + true + false + false + + 0 + + 5 + 0 + 2 + 1.0 + OpFor + 1 + 5 + 4 + 1 + + 50 + 0 + None + false + + + + Resupply Convoy + + -1 + false + 1 + 0 + false + false + true + false + false + + 10 + + 9 + 0 + 1 + 1 + Resupply Convoy + 1 + 1 + 4 + 1 + + 50 + 0 + None + false + + CARGO + APC + + + + + + + + Resupply Convoy + + + + + + Crippled units only count if the opposing force is routed. + + Preserve 50% of the following force(s) and unit(s): + NONE + Preserve + 50 + 0 + true + None + + + + OpFor + + + + + ScenarioVictory + Fixed + 3 + + + + + ScenarioDefeat + Fixed + 3 + + + + Destroy or rout 50% of the following force(s) and unit(s): + NONE + ForceWithdraw + 50 + 0 + true + None + + + + Player + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Preserve 50% of the following force(s) and unit(s): + NONE + Preserve + 50 + true + None + + + diff --git a/MekHQ/data/scenariotemplates/Frontline VIP Capture.xml b/MekHQ/data/scenariotemplates/Frontline VIP Capture.xml index 924cd6f6987..34d5a7c5c6b 100644 --- a/MekHQ/data/scenariotemplates/Frontline VIP Capture.xml +++ b/MekHQ/data/scenariotemplates/Frontline VIP Capture.xml @@ -111,6 +111,10 @@ OppositeEdge Player false + + COMMAND + APC + @@ -142,59 +146,59 @@ true None + + + OpFor + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Destroy or rout 50% of the following force(s) and unit(s): + NONE + ForceWithdraw + 50 + true + None + + + + Player + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Preserve 50% of the following force(s) and unit(s): + NONE + Preserve + 50 + true + None + - - - OpFor - - - - - ScenarioVictory - Fixed - 1 - - - - - ScenarioDefeat - Fixed - 1 - - - - Destroy or rout 50% of the following force(s) and unit(s): - NONE - ForceWithdraw - 50 - true - None - - - - Player - - - - - ScenarioVictory - Fixed - 1 - - - - - ScenarioDefeat - Fixed - 1 - - - - Preserve 50% of the following force(s) and unit(s): - NONE - Preserve - 50 - true - None - diff --git a/MekHQ/data/scenariotemplates/Heavy Recon Evasion.xml b/MekHQ/data/scenariotemplates/Heavy Recon Evasion.xml index 7bfb2977d11..134c87a1d14 100644 --- a/MekHQ/data/scenariotemplates/Heavy Recon Evasion.xml +++ b/MekHQ/data/scenariotemplates/Heavy Recon Evasion.xml @@ -2,7 +2,7 @@ Heavy Recon Evasion Evade patrols while scanning enemy heavies. - Enemy patrols are active, and your mission is to scan the enemy’s heavy units. Survive for the duration of the scan without being detected. Preserve at least 50% of your forces until the turn limit is reached to ensure mission success. + Enemy patrols are active, and your mission is to scan the enemy’s heavy units. Survive for the duration of the scan. Preserve at least 50% of your forces until the turn limit is reached to ensure mission success. false false @@ -114,7 +114,7 @@ Preserve 50 false - 2 + 1 ScaledToPrimaryUnitCount diff --git a/MekHQ/data/scenariotemplates/Intercept Engagement.xml b/MekHQ/data/scenariotemplates/Intercept Engagement.xml index 2f7b5460a14..3f35ec5e221 100644 --- a/MekHQ/data/scenariotemplates/Intercept Engagement.xml +++ b/MekHQ/data/scenariotemplates/Intercept Engagement.xml @@ -74,6 +74,10 @@ OppositeEdge Player false + + RAIDER + CAVALRY + diff --git a/MekHQ/data/scenariotemplates/Interception Defense.xml b/MekHQ/data/scenariotemplates/Interception Defense.xml index 23e495923eb..422da33fd93 100644 --- a/MekHQ/data/scenariotemplates/Interception Defense.xml +++ b/MekHQ/data/scenariotemplates/Interception Defense.xml @@ -130,9 +130,7 @@ - Destroy 50% of the following force(s) and unit(s) before they can reach the - destination - edge: + Destroy 50% of the following force(s) and unit(s) before they can reach the destination edge: NONE Destroy 50 diff --git a/MekHQ/data/scenariotemplates/Irregular Force Assault.xml b/MekHQ/data/scenariotemplates/Irregular Force Assault.xml index 85de5013732..be0fe74e41b 100644 --- a/MekHQ/data/scenariotemplates/Irregular Force Assault.xml +++ b/MekHQ/data/scenariotemplates/Irregular Force Assault.xml @@ -153,6 +153,10 @@ OppositeEdge Player false + + FIRE_SUPPORT + INF_SUPPORT + diff --git a/MekHQ/data/scenariotemplates/Isolated DropShip Defense.xml b/MekHQ/data/scenariotemplates/Isolated DropShip Defense.xml index e83c3efa233..af5cd30d79d 100644 --- a/MekHQ/data/scenariotemplates/Isolated DropShip Defense.xml +++ b/MekHQ/data/scenariotemplates/Isolated DropShip Defense.xml @@ -2,7 +2,7 @@ Isolated DropShip Defense Defend isolated DropShip from interception. - A DropShip has been intercepted shortly after landing and is isolated behind enemy lines. Defend it for 30 turns until it can lift off, preventing it from being crippled. Defeat at least 50% of the attacking forces while preserving 50% of your own units during the engagement. + A DropShip has been intercepted shortly after landing and is isolated behind enemy lines. Defend it until it can lift off, preventing it from being crippled. Defeat at least 50% of the attacking forces while preserving 50% of your own units during the engagement. false false @@ -179,6 +179,19 @@ 0 None false + + ARTILLERY + MISSILE_ARTILLERY + MIXED_ARTILLERY + FIRE_SUPPORT + FIRE_SUPPORT + FIRE_SUPPORT + FIRE_SUPPORT + SR_FIRE_SUPPORT + SR_FIRE_SUPPORT + SR_FIRE_SUPPORT + SR_FIRE_SUPPORT + diff --git a/MekHQ/data/scenariotemplates/Low-Atmosphere DropShip Assault.xml b/MekHQ/data/scenariotemplates/Low-Atmosphere DropShip Assault.xml index bcd6d442ecf..be716e4d862 100644 --- a/MekHQ/data/scenariotemplates/Low-Atmosphere DropShip Assault.xml +++ b/MekHQ/data/scenariotemplates/Low-Atmosphere DropShip Assault.xml @@ -147,32 +147,32 @@ true None + + + Player + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Preserve 50% of the following force(s) and unit(s): + NONE + Preserve + 50 + true + None + - - - Player - - - - - ScenarioVictory - Fixed - 1 - - - - - ScenarioDefeat - Fixed - 1 - - - - Preserve 50% of the following force(s) and unit(s): - NONE - Preserve - 50 - true - None - diff --git a/MekHQ/data/scenariotemplates/Low-Atmosphere DropShip Escort.xml b/MekHQ/data/scenariotemplates/Low-Atmosphere DropShip Escort.xml index 5e311109a49..5926a4adba5 100644 --- a/MekHQ/data/scenariotemplates/Low-Atmosphere DropShip Escort.xml +++ b/MekHQ/data/scenariotemplates/Low-Atmosphere DropShip Escort.xml @@ -168,6 +168,9 @@ OppositeEdge Player false + + INTERCEPTOR + diff --git a/MekHQ/data/scenariotemplates/Low-Atmosphere Reinforcements Intercepted.xml b/MekHQ/data/scenariotemplates/Low-Atmosphere Reinforcements Intercepted.xml index a5a78268e36..c29a978a99c 100644 --- a/MekHQ/data/scenariotemplates/Low-Atmosphere Reinforcements Intercepted.xml +++ b/MekHQ/data/scenariotemplates/Low-Atmosphere Reinforcements Intercepted.xml @@ -81,6 +81,9 @@ OppositeEdge Player false + + INTERCEPTOR + diff --git a/MekHQ/data/scenariotemplates/Picket Line Breakthrough.xml b/MekHQ/data/scenariotemplates/Picket Line Breakthrough.xml index 4b3b117f3ea..5ef9eb3e441 100644 --- a/MekHQ/data/scenariotemplates/Picket Line Breakthrough.xml +++ b/MekHQ/data/scenariotemplates/Picket Line Breakthrough.xml @@ -74,6 +74,10 @@ 0 None false + + RAIDER + CAVALRY + diff --git a/MekHQ/data/scenariotemplates/Recon Evasion.xml b/MekHQ/data/scenariotemplates/Recon Evasion.xml index 28a4c294f03..d1873c84347 100644 --- a/MekHQ/data/scenariotemplates/Recon Evasion.xml +++ b/MekHQ/data/scenariotemplates/Recon Evasion.xml @@ -2,7 +2,7 @@ Recon Evasion Evade patrols until scan is complete. - Enemy patrols are sweeping the area, and your forces need to evade detection until scans of the enemy are complete. Survive for the turn limit while preserving at least 50% of your forces to ensure success. + Enemy patrols are sweeping the area, and your forces need to evade until scans of the enemy are complete. Survive for the turn limit while preserving at least 50% of your forces to ensure success. false false @@ -85,6 +85,11 @@ OppositeEdge Player false + + RECON + RAIDER + CAVALRY + @@ -114,7 +119,7 @@ Preserve 50 false - 2 + 1 ScaledToPrimaryUnitCount diff --git a/MekHQ/data/scenariotemplates/Reconnaissance Interdiction.xml b/MekHQ/data/scenariotemplates/Reconnaissance Interdiction.xml index fd5d3412f8d..e86db4af2c1 100644 --- a/MekHQ/data/scenariotemplates/Reconnaissance Interdiction.xml +++ b/MekHQ/data/scenariotemplates/Reconnaissance Interdiction.xml @@ -85,6 +85,11 @@ OppositeEdge Player false + + RECON + RAIDER + CAVALRY + @@ -141,7 +146,7 @@ Preserve 50 false - 2 + 1 ScaledToPrimaryUnitCount diff --git a/MekHQ/data/scenariotemplates/Reinforcements Intercepted.xml b/MekHQ/data/scenariotemplates/Reinforcements Intercepted.xml index 5d4ddfbbd40..64d64772d37 100644 --- a/MekHQ/data/scenariotemplates/Reinforcements Intercepted.xml +++ b/MekHQ/data/scenariotemplates/Reinforcements Intercepted.xml @@ -81,6 +81,11 @@ OppositeEdge Player false + + RECON + RAIDER + CAVALRY + diff --git a/MekHQ/data/scenariotemplates/Remote VIP Capture.xml b/MekHQ/data/scenariotemplates/Remote VIP Capture.xml index 43f45bd129f..0c9a6f8f172 100644 --- a/MekHQ/data/scenariotemplates/Remote VIP Capture.xml +++ b/MekHQ/data/scenariotemplates/Remote VIP Capture.xml @@ -111,6 +111,10 @@ OppositeEdge Player false + + COMMAND + APC + @@ -142,59 +146,59 @@ true None + + + OpFor + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Destroy or rout 50% of the following force(s) and unit(s): + NONE + ForceWithdraw + 50 + true + None + + + + Player + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Preserve 50% of the following force(s) and unit(s): + NONE + Preserve + 50 + true + None + - - - OpFor - - - - - ScenarioVictory - Fixed - 1 - - - - - ScenarioDefeat - Fixed - 1 - - - - Destroy or rout 50% of the following force(s) and unit(s): - NONE - ForceWithdraw - 50 - true - None - - - - Player - - - - - ScenarioVictory - Fixed - 1 - - - - - ScenarioDefeat - Fixed - 1 - - - - Preserve 50% of the following force(s) and unit(s): - NONE - Preserve - 50 - true - None - diff --git a/MekHQ/data/scenariotemplates/ScenarioManifest.xml b/MekHQ/data/scenariotemplates/ScenarioManifest.xml index fa42da703a1..d6c8958d8b2 100644 --- a/MekHQ/data/scenariotemplates/ScenarioManifest.xml +++ b/MekHQ/data/scenariotemplates/ScenarioManifest.xml @@ -1,5 +1,5 @@ - + 0 Annihilation.xml @@ -82,106 +82,98 @@ 20 - Frontline VIP Capture.xml + Heavy Recon Evasion.xml 21 - Heavy Recon Evasion.xml + Hostile Facility.xml 22 - Hostile Facility.xml + Intercept Engagement.xml 23 - Intercept Engagement.xml + Interception Defense.xml 24 - Interception Defense.xml + Irregular Force Assault.xml 25 - Irregular Force Assault.xml + Irregular Force Suppression.xml 26 - Irregular Force Suppression.xml + Isolated DropShip Defense.xml 27 - Isolated DropShip Defense.xml + Low-Atmosphere Air Intercept.xml 28 - Low-Atmosphere Air Intercept.xml + Low-Atmosphere DropShip Assault.xml 29 - Low-Atmosphere DropShip Assault.xml + Low-Atmosphere DropShip Escort.xml 30 - Low-Atmosphere DropShip Escort.xml + Minor Engagement.xml 31 - Minor Engagement.xml + Picket Line Breakthrough.xml 32 - Picket Line Breakthrough.xml + Pivotal Battle.xml 33 - Pivotal Battle.xml + Recon Evasion.xml 34 - Recon Evasion.xml + Reconnaissance Interdiction.xml 35 - Reconnaissance Interdiction.xml + Skirmish.xml 36 - Remote VIP Capture.xml + Skirmish Disruption.xml 37 - Skirmish.xml + Space Aerospace Intercept.xml 38 - Skirmish Disruption.xml + Space Blockade Run.xml 39 - Space Aerospace Intercept.xml + Space DropShip Intercept.xml 40 - Space Blockade Run.xml + Tactical Withdrawal.xml 41 - Space DropShip Intercept.xml + VIP Ambush.xml 42 - Tactical Withdrawal.xml - - - 43 - VIP Capture.xml - - - 44 VIP Defense.xml - 45 + 43 Engagement.xml diff --git a/MekHQ/data/scenariotemplates/Space Blockade Run.xml b/MekHQ/data/scenariotemplates/Space Blockade Run.xml index df46580fda2..ebdc70f3e63 100644 --- a/MekHQ/data/scenariotemplates/Space Blockade Run.xml +++ b/MekHQ/data/scenariotemplates/Space Blockade Run.xml @@ -185,7 +185,7 @@ -1 false 9 - 2 + 0 true false true @@ -204,9 +204,12 @@ 50 0 - SameEdge + OppositeEdge Player false + + INTERCEPTOR + diff --git a/MekHQ/data/scenariotemplates/Space DropShip Intercept.xml b/MekHQ/data/scenariotemplates/Space DropShip Intercept.xml index 1d9195f62af..84d28f0eda0 100644 --- a/MekHQ/data/scenariotemplates/Space DropShip Intercept.xml +++ b/MekHQ/data/scenariotemplates/Space DropShip Intercept.xml @@ -105,7 +105,7 @@ -1 false 9 - 2 + 0 true true true @@ -158,9 +158,12 @@ 50 0 - SameEdge + OppositeEdge Player false + + ESCORT + @@ -189,7 +192,7 @@ 50 0 false - SameEdge + OppositeEdge Player false diff --git a/MekHQ/data/scenariotemplates/Space Reinforcements Intercepted.xml b/MekHQ/data/scenariotemplates/Space Reinforcements Intercepted.xml index 0d26af9a88f..f3e868d75ee 100644 --- a/MekHQ/data/scenariotemplates/Space Reinforcements Intercepted.xml +++ b/MekHQ/data/scenariotemplates/Space Reinforcements Intercepted.xml @@ -85,6 +85,9 @@ OppositeEdge Player false + + INTERCEPTOR + diff --git a/MekHQ/data/scenariotemplates/VIP Capture.xml b/MekHQ/data/scenariotemplates/VIP Ambush.xml similarity index 67% rename from MekHQ/data/scenariotemplates/VIP Capture.xml rename to MekHQ/data/scenariotemplates/VIP Ambush.xml index 224e7979771..e48fb2ec2be 100644 --- a/MekHQ/data/scenariotemplates/VIP Capture.xml +++ b/MekHQ/data/scenariotemplates/VIP Ambush.xml @@ -1,8 +1,8 @@ - VIP Capture - Capture VIP, disable or force ejection. - The objective is to capture a high-value target by disabling their unit or forcing an ejection, then securing the ejected individual. The target must remain alive and cannot escape the battlefield. Preserve 50% of your forces, and destroy at least 50% of the VIP’s bodyguards. + VIP Ambush + Ambush & eliminate VIP, evade reinforcements. + Eliminate a high-value VIP target. Neutralize the target quickly and avoid getting caught by incoming enemy reinforcements. Precision and speed are critical for mission success. false @@ -60,7 +60,7 @@ -1 false -2 - 0 + -3 true false true @@ -111,6 +111,10 @@ OppositeEdge Player false + + COMMAND + APC + @@ -142,59 +146,63 @@ true None + + + OpFor + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Destroy or rout 50% of the following force(s) and unit(s): + NONE + ForceWithdraw + 50 + true + None + + + + Player + + + + + ScenarioVictory + Fixed + 1 + + + + + ScenarioDefeat + Fixed + 1 + + + + Crippled units do not count towards this objective. + + Reach the far edge of the map with at least 50% of the following force(s) + and unit(s): + NONE + ReachMapEdge + 50 + 0 + true + None + - - - OpFor - - - - - ScenarioVictory - Fixed - 1 - - - - - ScenarioDefeat - Fixed - 1 - - - - Destroy or rout 50% of the following force(s) and unit(s): - NONE - ForceWithdraw - 50 - true - None - - - - Player - - - - - ScenarioVictory - Fixed - 1 - - - - - ScenarioDefeat - Fixed - 1 - - - - Preserve 50% of the following force(s) and unit(s): - NONE - Preserve - 50 - true - None - diff --git a/MekHQ/data/scenariotemplates/VIP Defense.xml b/MekHQ/data/scenariotemplates/VIP Defense.xml index e877012e1fb..f34f91f1aac 100644 --- a/MekHQ/data/scenariotemplates/VIP Defense.xml +++ b/MekHQ/data/scenariotemplates/VIP Defense.xml @@ -82,6 +82,10 @@ SameEdge Player false + + COMMAND + APC + @@ -115,7 +119,7 @@ - + VIP @@ -141,7 +145,7 @@ 0 true None - + OpFor diff --git a/MekHQ/data/stratconbiomedefinitions/StratconBiomeManifest.xml b/MekHQ/data/stratconbiomedefinitions/StratconBiomeManifest.xml index 8f9ddb95201..3c9d2548f4a 100644 --- a/MekHQ/data/stratconbiomedefinitions/StratconBiomeManifest.xml +++ b/MekHQ/data/stratconbiomedefinitions/StratconBiomeManifest.xml @@ -474,6 +474,10 @@ Badlands data/images/stratcon/Stratcon_Mars.png + + ColdFacility + data/images/stratcon/Stratcon_Cold_Facility.png + ColdForest data/images/stratcon/Stratcon_Cold_Forest.png @@ -510,6 +514,10 @@ Forest data/images/stratcon/Stratcon_Forest.png + + FrozenFacility + data/images/stratcon/Stratcon_Frozen_Facility.png + FrozenSea data/images/stratcon/Stratcon_Frozen_Sea.png @@ -546,6 +554,10 @@ HotHillsWet data/images/stratcon/Stratcon_Hot_Hills_Wet.png + + HotFacility + data/images/stratcon/Stratcon_Hot_Facility.png + HotSea data/images/stratcon/Stratcon_Hot_Sea.png @@ -602,6 +614,10 @@ Swamp data/images/stratcon/Stratcon_Swamp.png + + TemperateFacility + data/images/stratcon/Stratcon_Temperate_Facility.png + Tundra data/images/stratcon/Stratcon_Tundra.png @@ -638,8 +654,8 @@ data/images/stratcon/Bases/Stratcon_Base_Real_Allied.png - FacilityAlliedSupplyDepot - data/images/stratcon/Bases/Stratcon_Factory_Real_Allied.png + FacilityAlliedSpacePort + data/images/stratcon/Bases/Stratcon_Airbase_Real_Allied.png FacilityAlliedDataCenter @@ -687,8 +703,8 @@ data/images/stratcon/Bases/Stratcon_Base_Real_Hostile.png - FacilityHostileSupplyDepot - data/images/stratcon/Bases/Stratcon_Factory_Real_Hostile.png + FacilityHostileSpacePort + data/images/stratcon/Bases/Stratcon_Airbase_Real_Hostile.png FacilityHostileDataCenter diff --git a/MekHQ/data/stratconcontractdefinitions/ExtractionRaid.xml b/MekHQ/data/stratconcontractdefinitions/ExtractionRaid.xml index b3c67bdccc0..ad9d64c43b5 100644 --- a/MekHQ/data/stratconcontractdefinitions/ExtractionRaid.xml +++ b/MekHQ/data/stratconcontractdefinitions/ExtractionRaid.xml @@ -31,9 +31,7 @@ SpecificScenarioVictory -0.5 - VIP Capture.xml - Remote VIP Capture.xml - Frontline VIP Capture.xml + VIP Ambush.xml Covert Strike.xml Convoy Interdiction.xml Convoy Raid.xml diff --git a/MekHQ/data/stratconcontractdefinitions/GuerillaWarfare.xml b/MekHQ/data/stratconcontractdefinitions/GuerillaWarfare.xml index 21625a06bd9..f94f920e120 100644 --- a/MekHQ/data/stratconcontractdefinitions/GuerillaWarfare.xml +++ b/MekHQ/data/stratconcontractdefinitions/GuerillaWarfare.xml @@ -36,7 +36,7 @@ Heavy Recon Evasion.xml Intercept Engagement.xml Recon Evasion.xml - Remote VIP Capture.xml + VIP Ambush.xml Skirmish.xml Tactical Withdrawal.xml diff --git a/MekHQ/data/stratconcontractdefinitions/ReconRaid.xml b/MekHQ/data/stratconcontractdefinitions/ReconRaid.xml index dd9f25cd280..594f7864b25 100644 --- a/MekHQ/data/stratconcontractdefinitions/ReconRaid.xml +++ b/MekHQ/data/stratconcontractdefinitions/ReconRaid.xml @@ -33,7 +33,7 @@ Recon Evasion.xml Heavy Recon Evasion.xml - Remote VIP Capture.xml + VIP Ambush.xml diff --git a/MekHQ/data/stratconfacilities/AlliedDataCenter.xml b/MekHQ/data/stratconfacilities/AlliedDataCenter.xml index e2a09989363..0affbbbc009 100644 --- a/MekHQ/data/stratconfacilities/AlliedDataCenter.xml +++ b/MekHQ/data/stratconfacilities/AlliedDataCenter.xml @@ -3,7 +3,7 @@ Data Center DataCenter Allied - true - This facility reveals all information about this track as long as it is active. + true + Increases the scan range of all friendly forces by 1. true diff --git a/MekHQ/data/stratconfacilities/AlliedSpacePort.xml b/MekHQ/data/stratconfacilities/AlliedSpacePort.xml new file mode 100644 index 00000000000..af0bdf92d00 --- /dev/null +++ b/MekHQ/data/stratconfacilities/AlliedSpacePort.xml @@ -0,0 +1,9 @@ + + HostileSpacePort.xml + Space Port + SpacePort + Allied + 2 + Provides 2 SP/month of allied logistical support. + true + diff --git a/MekHQ/data/stratconfacilities/AlliedSupplyDepot.xml b/MekHQ/data/stratconfacilities/AlliedSupplyDepot.xml deleted file mode 100644 index 664fe14ab88..00000000000 --- a/MekHQ/data/stratconfacilities/AlliedSupplyDepot.xml +++ /dev/null @@ -1,9 +0,0 @@ - - HostileSupplyDepot.xml - Supply Depot - SupplyDepot - Allied - 1 - Provides 1 SP/month of allied logistical support. - true - diff --git a/MekHQ/data/stratconfacilities/HostileSpacePort.xml b/MekHQ/data/stratconfacilities/HostileSpacePort.xml new file mode 100644 index 00000000000..ca3f0e87996 --- /dev/null +++ b/MekHQ/data/stratconfacilities/HostileSpacePort.xml @@ -0,0 +1,11 @@ + + 0.15 + AlliedSpacePort.xml + SpacePort + HostileBVBudgetIncrease.xml + HostileBVBudgetIncrease.xml + SpacePort + Opposing + Hostile forces on this track are significantly larger due to improved logistics. + false + diff --git a/MekHQ/data/stratconfacilities/HostileSupplyDepot.xml b/MekHQ/data/stratconfacilities/HostileSupplyDepot.xml deleted file mode 100644 index 5d766d4e352..00000000000 --- a/MekHQ/data/stratconfacilities/HostileSupplyDepot.xml +++ /dev/null @@ -1,11 +0,0 @@ - - 0.05 - AlliedSupplyDepot.xml - Supply Depot - HostileBVBudgetIncrease.xml - HostileBVBudgetIncrease.xml - SupplyDepot - Opposing - Hostile forces on this track are larger due to improved logistics. - false - diff --git a/MekHQ/data/stratconfacilities/facilitymanifest.xml b/MekHQ/data/stratconfacilities/facilitymanifest.xml index 174604d5649..5669a1ea52e 100644 --- a/MekHQ/data/stratconfacilities/facilitymanifest.xml +++ b/MekHQ/data/stratconfacilities/facilitymanifest.xml @@ -4,7 +4,7 @@ AlliedTankBase.xml AlliedAirBase.xml AlliedArtilleryBase.xml - AlliedSupplyDepot.xml + AlliedSpacePort.xml AlliedDataCenter.xml AlliedIndustrialFacility.xml AlliedCommsCenter.xml @@ -16,7 +16,7 @@ HostileTankBase.xml HostileAirBase.xml HostileArtilleryBase.xml - HostileSupplyDepot.xml + HostileSpacePort.xml HostileDataCenter.xml HostileIndustrialFacility.xml HostileCommsCenter.xml diff --git a/MekHQ/data/universe/academies/Unit Education.xml b/MekHQ/data/universe/academies/Unit Education.xml index 1ba13370568..8b8dde8c01d 100644 --- a/MekHQ/data/universe/academies/Unit Education.xml +++ b/MekHQ/data/universe/academies/Unit Education.xml @@ -172,8 +172,68 @@ Gunnery/Spacecraft, Piloting/Spacecraft 2470 Basic Battle Armor Training - Gunnery/BattleArmor + Gunnery/BattleArmor, Anti-Mek 3050 0 + + In-House NCO Bootcamp + Basic Training + true + The In-House NCO Candidate Bootcamp is led by veteran personnel, blending physical conditioning, simulations, and practical exercises to instill leadership, cohesion, and loyalty. + true + Terra + 2300 + 0 + Early Childhood + Early Childhood + 70 + 9 + 16 + In-House NCO Graduate + Leadership + 2300 + 0 + + + In-House Warrant Officer Bootcamp + Basic Training + true + Led by veteran experts, in-unit Warrant Officer bootcamps focus on advanced technical skills, leadership development, and unit cohesion, preparing candidates for specialized support roles. + true + Terra + 2300 + 0 + Early Childhood + Early Childhood + 70 + 9 + 16 + In-House Warrant Officer Graduate + Administration, Negotiation + 2300 + 0 + + + In-House Officer Bootcamp + Basic Training + true + Officer Bootcamp emphasizes leadership, tactical acumen, and strategic planning, blending classroom instruction, simulations, and field exercises to prepare candidates for command roles. + true + Terra + 2300 + 0 + Early Childhood + Early Childhood + 70 + 9 + 16 + In-House Field Officer Graduate + Leadership, Tactics + 2300 + In-House Command Officer Graduate + Leadership, Strategy + 2300 + 0 + diff --git a/MekHQ/data/universe/systems.xml b/MekHQ/data/universe/systems.xml index 51eec658d68..79e10930062 100644 --- a/MekHQ/data/universe/systems.xml +++ b/MekHQ/data/universe/systems.xml @@ -325878,7 +325878,7 @@ Jonathan has a 20 hour day and takes 745 standard days to orbit its sun. 1277 71.853 -152.286 - F2V + G0V 3 Zhào's World @@ -325921,7 +325921,7 @@ Jonathan has a 20 hour day and takes 745 standard days to orbit its sun. Low Breathable Nitrogen and Oxygen, plus trace gases - 0.95 + 1.1 29 36 Reptiles @@ -325929,7 +325929,7 @@ Jonathan has a 20 hour day and takes 745 standard days to orbit its sun. 3.9 12600 5.2795 - Goei's Hold 1 (King) + Goei's Hold 1 (Mahabohdi) Goei's Hold 2 Goei's Hold 3 green52 @@ -633821,7 +633821,12 @@ Alexandra is the 3rd planet in the Tanis system and is relatively temperate, tho New Riells i Viabrea III New Riells i Viabrea IV - EK-1 + Kalinda + medium + oddmoon2 + + + Unknown Name medium oddmoon2 diff --git a/MekHQ/docs/Stratcon and Against the Bot/Against_the_Bot_Starter_Guide_v4.pdf b/MekHQ/docs/Archive Stuff/Against_the_Bot_Starter_Guide_v4.pdf similarity index 100% rename from MekHQ/docs/Stratcon and Against the Bot/Against_the_Bot_Starter_Guide_v4.pdf rename to MekHQ/docs/Archive Stuff/Against_the_Bot_Starter_Guide_v4.pdf diff --git a/MekHQ/docs/Stratcon and Against the Bot/Against_the_bot.txt b/MekHQ/docs/Archive Stuff/Against_the_bot.txt similarity index 100% rename from MekHQ/docs/Stratcon and Against the Bot/Against_the_bot.txt rename to MekHQ/docs/Archive Stuff/Against_the_bot.txt diff --git a/MekHQ/docs/Stratcon and Against the Bot/AtB documentation.19_8 (WIP).txt b/MekHQ/docs/Archive Stuff/AtB documentation.19_8 (WIP).txt similarity index 100% rename from MekHQ/docs/Stratcon and Against the Bot/AtB documentation.19_8 (WIP).txt rename to MekHQ/docs/Archive Stuff/AtB documentation.19_8 (WIP).txt diff --git a/MekHQ/docs/Asset Licenses/.gitkeep b/MekHQ/docs/Asset Licenses/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/MekHQ/docs/DistributedMekHQ.md b/MekHQ/docs/DistributedMekHQ (Depreciated).md similarity index 100% rename from MekHQ/docs/DistributedMekHQ.md rename to MekHQ/docs/DistributedMekHQ (Depreciated).md diff --git a/MekHQ/docs/Personnel Modules/Awards Module.pdf b/MekHQ/docs/Personnel Modules/Awards Module.pdf index bd73e3891032b79b6867b80e04dbdb198881d9cf..9ddf54487f82d554b745ba673d37e0b0cb020993 100644 GIT binary patch delta 133049 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%0DIyo6)hW*3IrsOX8PL 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 delta 102262 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 |C*k3Vs8jVegqNo**BZ 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 IEKj4q0IvxdUH||9 delta 31045 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 diff --git a/MekHQ/docs/Personnel Modules/Random Personalities.pdf b/MekHQ/docs/Personnel Modules/Random Personalities.pdf index f04381191b7152f8ed24d6c10b5df23f244b12a3..e52748777782ac01f7a129c4c0d8af990f07c318 100644 GIT binary patch delta 12400 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 YfA*!l&_f6#akGKg5UHpnm8B5>2bNzhq5uE@ delta 11363 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ʄ&ZU=~rmR&x}kuwzvb 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

    x77Sk5 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

    R10$uK;` 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 literal 0 HcmV?d00001 diff --git a/MekHQ/docs/Stratcon and Against the Bot/Combat Teams, Roles, Training & Reinforcements.pdf b/MekHQ/docs/Stratcon and Against the Bot/Combat Teams, Roles, Training & Reinforcements.pdf new file mode 100644 index 0000000000000000000000000000000000000000..25f3524aa10b70b18f7fda7e693b0f1873f7be1a GIT binary patch literal 129309 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 z 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

    x2JKg#maeeqDRR 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 ShbP#_B*3lK(Xn)}lKdB#VtNAr literal 0 HcmV?d00001 diff --git a/MekHQ/docs/Stratcon and Against the Bot/MekHQ Morale.pdf b/MekHQ/docs/Stratcon and Against the Bot/MekHQ Morale.pdf index 6bd1fee30b8ee83000ba96779de5630c1d79ef60..0b7afbd8e7d2998bb96e68b3375b7bfdb6516493 100644 GIT binary patch literal 72395 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

    U(@ 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 z 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 z%0D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

    VdwZ9MUI-3hh-P)>W&zZGGc!{Oiy7S~M+*y! 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 F+=T=ipyQ9 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 O!A$Vv literal 0 HcmV?d00001 diff --git a/MekHQ/docs/Stratcon and Against the Bot/Unit Markets.pdf b/MekHQ/docs/Stratcon and Against the Bot/Unit Markets.pdf index ccf13929597d06f57f0affaf107e048679d9564a..aa6d03f586bca5392aa8e7d189e29dffefe51565 100644 GIT binary patch delta 44887 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 diff --git a/MekHQ/docs/history.txt b/MekHQ/docs/history.txt index c5e399531bb..8486efbdcf8 100644 --- a/MekHQ/docs/history.txt +++ b/MekHQ/docs/history.txt @@ -1,6 +1,158 @@ MEKHQ VERSION HISTORY: --------------- -0.50.02-SNAPSHOT +0.50.03-SNAPSHOT ++ PR #5568: Spare parts rules reference adjustment ++ PR #5559: Refactored Combat Team Handling in Scenarios for Better null Safety ++ Fix #5572: don't attempt to sum NaN tonnage equipment ++ PR #5577: Leadership Units & Support Point Scarcity Hot Fixes ++ PR #5582: Updated Rank Systems Version in ranks.xml ++ Fix #5188, #5410: Corrected GUI Theme Retrieval to Use selectedTheme ++ PR #5585: Replaced Placeholder Challenge Skulls with Final ++ Fix #5584: Fixed NPE in ScenarioTableModel ++ Fix #5590: Fixed Incorrect Cargo Sizes for Resupplies ++ PR #5595: Fixed Abandoned Convoys; Updated Abandoned Convoy Dialog ++ PR #5597: Added Better Tooltips for Force Roles ++ PR #5598: Updated Resupply & Contract Automation Dialogs; Fixed Multiple Resupply Bugs ++ PR #5599: Improved Loot Good Event Language ++ PR #5600: Added Clarification to Reinforcement Dialog ++ Fix MM #5547: spelling, fixes issue ++ PR #5602: Adjusted AtB Bonus Events ++ Fix MM ##5138: fixes capitalization of names ++ PR #5606: fix moons listed for Tantara ++ PR #5610: Corrected StratCon Menu Actions for Scenario and Force Management ++ PR #5611: Corrected Default Theme to Empty String ++ Fix #3352: add additional sortable columns to the contract table ++ PR #5614: Fixed Cargo Capacity Calculation with New Container Pattern ++ PR #5617: Restrict Distance Label to AtB Contracts Only ++ PR #5619: Refactored PostScenarioDialogHandler to use Generic Scenario ++ PR #5620: Autologistics Reporting fixes ++ PR #5624: Updated Frontline Deployment Instructions ++ Fix #5612: Add defensive check on cancel button ++ PR #5625: Updated StratCon Data Center to Increase Scan Range Instead of Sector Reveal ++ Fix #5604: Save refit config from MHQ MekLab tab ++ Fix #5328: Adjust acquisition modifiers for support vehicles ++ Fix #4876: Add sortable columns in the finance tab ++ PR #5644: Fixed Value Display in Resupplies ++ Fix #5646: Spelling Mistakes ++ PR #5650: Address potential int and double issue from SpotBugs ++ PR #5651: Don't use floats in for loops ++ PR #5652: Adds synchronization to getForces since setForces is synchronized ++ PR #5653: Keep odd number checks using modulus ++ Fix #5604: Save refit config from MHQ MekLab tab ++ PR #5618: Enable Auto-Resolve Button Conditionally for Scenarios ++ PR #5616: Adds some helpful tooltips for contracts ++ PR #5682: Refactored Crew Assignment Event Handling ++ PR #5684: Added Report for Returning StratCon Forces ++ PR #5690: Added Potential Difficulty Spikes to Garrison-Type Contracts ++ PR #5692: Removed Undeploy Option (again); Added Reset Scenario Deployment Option ++ PR #5693: Removed SP Cost for GM Reinforcement Attempts in StratCon ++ PR #5698: Fixed Multiple StratCon Bugs ++ PR #5700: Refactored Scenario Reporting for Legacy AtB and StratCon Differentiation ++ PR #5701: Corrected Weight Class Initialization of Artillery Units ++ Fix #5688: Fixed Typo in Payment Overages Message ++ Fix #5628: Fixed Personality Loading ++ Fix #5612: Fixed Unit Name Reset ++ Fix #5705: Refactored Personnel Market Generation & Fixed Capital/Hiring Hall Checks ++ PR #5707: Corrected Available Force Check ++ PR #5708: Added a Soft Cap to Support Point Generation ++ Fix #5570: Fixed Maternity Leave Date Handling ++ PR #5699: Moved ACAR Module to MegaMek ++ PR #5709: Added MHQDialogImmersive for Immersive Dialog Functionality that Immersively Improves Immersion ++ Fix #3752: Added Handling for Unsupported Unit Types ++ Fix #5717: Removed End of Contract and Manual Resupplies; Fixed Smuggler Prices; Added Remove SP Button ++ PR #5735: Fix: compilation error due to a function being set as static but acessing instance data ++ PR #5781: Feat/ACAR formations reintroduced ++ Fix #5739: Updated 'Clear Units' Button to Correctly Handle StratCon Scenarios ++ Fix #5740,#5580: Fixed Role Eligibility Checks and Ensured Proper Role Assignment ++ Fix #5746: Added Clan and Inner Sphere Resupply Restrictions ++ Fix #5766, #5769: Refactored Education Tag-Along Logic ++ PR #5771: Removed EnemyDropShip and GroundedEnemyDropShip Modifiers ++ Fix #5770: Added null Protection to OutstandingScenariosNagLogic for Outstanding Scenario Nags ++ Fix #5765: Log Prohibited Unit Skips in Resupplies & Prevent Invalid Part Additions ++ Fix #5760: Fixed Combat Team Status Logic for Convoy and Support Force ++ PR #5783: Update Sector Display in Briefing Room to Show Full Grid Reference ++ Fix #5779: Adjust Bounds Calculation in getUnoccupiedAdjacentCoords ++ Fix #5714: Fixed Maternity Leave... yet again ++ Fix #5716: Fixed Force Commander Handling and Improve XML Persistence ++ Fix #5727: Fixed Routed Contracts Resulting in Truncated Payouts ++ Fix #5726: Fix Support Point Acquisition Visual Bug ++ Fix #5736: Preventing Negative Personnel Income ++ PR #5754: Corrected Birthday Generation for Personnel ++ Fix #5753: Added 'null' Protection to AtBDynamicScenarioFactory ++ Fix #5751: Added Exception Handling for Out-of_Bounds Tab Index Selection ++ PR #5731: Remove all the damage applier classes because they currently reside on MegaMek ++ Fix #5713, #5737 Updated & Fixed Required Combat Team Calculations; + Reworked Combat Team Requirement Contract Pay Modifiers; Added Contract Difficulty Modifier ++ PR #4963: Implemented Campaign Options IIC - Full rework of all MekHQ options ++ Fix #3650, #4006, #4712, #5593: Added Ability to Link Units in MekHQ ++ PR #5757: Warnings on Campaign Load ++ PR #5790: Removed Legacy AtB Scenarios from StratCon Campaigns ++ PR #5791: Updated Contract Role Requirements ++ Fix #5777: QuadMeks (and QuadVees) that use Gunner & Pilot can be crewed ++ PR #5795: Replaced a Number of Placeholder Flavor Text & Fixed Encoding Error ++ PR #5796: Fixed Incorrect Condition for VTOL Unit Check ++ PR #5797: Refactored Portrait Mass Assignment Controls to Use Buttons ++ Fix #5750: Added Contract Breach CVP Penalties to StratCon Contracts ++ Fix #6416: Ghostbusting - Reset isDestroyed for entities when returning to MekHQ ++ Fix #5591: Improved Vocational (Idle) Experience Gain; Expanded Immersive Dialog Functionality ++ Fix #5745: Added null Protection to Loot Saving ++ Fix #5761: Fixed Incorrect Pass/Fail Logic in Initial Education Evaluation ++ Fix #5801: Added Enemy Morale Spike to Justify Contract Extension Clause ++ Fix #5800: Corrected Scenario info Visibility Logic in StratconPanel ++ PR #5806: Refactored BV Display Logic in StratconScenario.java ++ PR #5813: Replaced ':' with '@' in internal AI Bot Name ++ Fix #5724: Fix astech time calculations for units that do not require maintenance ++ Fix #5818: Fixed assigning/unassigning transports from TOE at the Force level & infantry compartments use the correct weight for infantry squads ++ Fix #3701: Corrected preset picker display on Linux ++ Fix #5810: Fixed peacetime operating cost settings ++ Fix #5826: Fixed Scenario Info Display for Uncloaked Scenarios ++ PR #5830: Convoy Message Copy Editing ++ Fix #5831: Added null Protection for Units Involved in Automated Mothballing ++ PR #5832: Corrected MHQDialogImmersive Constructor Usage ++ PR #5835: Implemented Granular autoLogistics Settings ++ Fix #5836: Combat Force commanders should be updated when a Force's Commander is updated ++ PR #5839: Refactored HPG Network Logic and Improved Visualization Clarity ++ Fix #5840: Refactored Loot Preview to Use JEditorPane not JTextArea ++ Fix #5841: Corrected isUseAtB to account for useStratCon ++ Fix #5844: Units can only be assigned a transport that is in the TOE ++ PR #5847: Added Methods for Star Radius Estimation and Spectral Type Parsing ++ PR #5848: Fixed Parsing of routedPayout to Handle Non-Numeric Characters ++ PR #5850: Rebranded Idle XP into Vocational XP ++ Fix #5718: Added GM Option to Assign Random SPAs ++ Fix #5817: Fixed Force Updates after Removal in TOEMouseAdapter ++ Fix #5822: Updated Premade Presets for 50.03 ++ Fix #5828: Restored Missing Kill Credits for DropShips and Turrets ++ PR #5854: Refactored Preset Dialog Chooser to Include Description ++ Fix #5857, #5862: Fixed Incorrect Tooltips in Campaign Options IIC ++ Fix #5860: Fixed Loading & Saving of Some Campaign Options ++ PR #5864: Remove Unnecessary Edge Trigger Logging ++ PR #5866: Temporarily Removed CamOps Contract Market ++ PR #5812: adding new GUI for Princess ACAR ++ PR #5870: Adjusted Randomness of Turning Points and Highlighted Them in Briefing Room ++ PR #5875: Rolled Back Window Size Setting ++ PR #5878: Fixed Mothball Info Saving & Loading Bug ++ PR #5879: Added Jackson Dependencies for YAML Processing in MekHQ ++ Fix #5799: Differentiating between IS & Clan DHS & unbreaking commas in Parts in use ++ Fix #5811: Fixed Multiple Follow-up Contract Bugs ++ Fix #5823: Added Weight Bypass Based on Role ++ Fix #5882: Fixed Clan Bidding ++ Fix #5883: Corrected Edge Cost Calculations ++ Fix #5884: Refactored Age Checks; Updated Unit Tests for Procreation and Marriage ++ Fix #5885: Corrected Morale Logic and Contract Type Checks ++ Fix #5886: Added Movement Filter to Entity Generation Logic ++ PR #5889: Replaced Internal Dissension Events in StratCon ++ PR #5891: Added 1,247 New Callsigns ++ PR #5893: Immersively Updated the Immersive Dialog to Be More Immersive ++ PR #5894: Added missing minimaps for MekHQ ++ PR #5900: Fixed Negative Contract Base Pay ++ PR #5874: Feature: minimap themes ++ PR #5908: Restored Missing Column Labels for Planetary Acquisition ++ Fix #5910: Added Fallback for AtB Bonus Unit Creation ++ PR #5912: Implemented NewsDialog for Immersive News Display ++ PR #5915: Update scenario panel facility objective text + + +0.50.02 (2024-12-30 2130 UTC) + Fix #846, #5185: Fix BattleArmor customization/refit overweight check. + PR #5206: Re-enabled CamOps Contract Market Method + PR #5207: Re-enabled Confirm Preset Option for New Campaign Preset Picker @@ -44,6 +196,161 @@ MEKHQ VERSION HISTORY: + PR #5266: Refactored Morale Calculations and Logging + PR #5268: Refactored Strategic Formations + Fix #5149: Better error message for null values in contract fields ++ Fix #5060: BriefingTab Fix ++ Fix #4733: Implemented Advanced StratCon Reinforcements ++ PR #5274: Improved Logging for Force Generation Process. ++ Fix #5114: Campaign now uses pickRandomCamouflage when initialized ++ PR #5280: Centralized Formation Size Logic & Adjusted Contract Required Lance Count ++ PR #5281: Refactored Leadership Unit Selection Logic ++ PR #5283: Adjusted Support Points Negotiation Logic ++ Fix #5285: Fixed SVArmor Price Calculation ++ Fix #5288: Added Recalculation of Scenario Objectives when Assigning Forces ++ Fix #5122 & #5084: Adjusted TO&E Menu Option Availability ++ Fix #5195: Made completion/finished by day calculation unambiguous ++ Fix #5256: Refactored MekHQ Unit's Gunners to be Set to ensure gunners are unique ++ PR #5294: Fixed Typo in 'Fourth Succession War' Naming ++ PR #5296: Simplified Modifier Briefing Text in AtBScenarioModifier ++ PR #5297: Enhanced Formatting in StratCon Scenario Information ++ Fix #5301: Fixed Theatre of War Faction Checks ++ Fix #5302: Improved Unit Substitution ++ Fix #5311: Fixed Marriage Announcements Not Appearing in Daily Activity Log ++ Fix #5318: Corrected Calculation of Age Difference When Marrying Personnel ++ Fix #5333: Show Hired personnel's name change ++ Fix #5323: Removed VIP Capture Scenarios, Added Role Templates, Fixed Objectives ++ Fix #5324: Refactored Cargo Capacity Calculations and Reporting ++ PR #5340: Renamed Strategic Formations to Combat Teams ++ PR #5351: Refactored Required Lances Calculation Logic ++ Fix #5348: Refactored Objective Time Scaling to Handle Edge Cases ++ Fix #5350: Corrected Briefing Room Unit Editing Menu Condition ++ Fix #5345: Reassigned StratCon Force Assignment Picker as Modal ++ PR #5357: Fixed Multiple-Nag Bug by Refactoring in-app New Campaign Handling (Possible fix for #4767) ++ PR #5359: Simplified Splash Screen Button Text ++ PR #5361: Refactored StratCon Tab Layout and Improved UI Design ++ PR #5362: Refactored StratCon Scenario Deployment to Support Scout Role Behavior ++ Fix #5322: Fixed Incorrect Color Usage for Quality Decline Messages ++ PR #5368: Env var mm.profile=dev makes log print on console ++ Fix #5322: Fixed Incorrect Color Usage for Quality Decline Messages ++ Fix #5330: Refactored Combat Team Validation Logic, Loosened Combat Team Requirements ++ PR #5358: Disabled Startup Screen Buttons during Actions ++ PR #5363: Added Requirement for Training Lances to be Deployed to a StratCon Track ++ PR #5364: Renamed 'Unassigned' Combat Team Role to 'In Reserve' ++ PR #5365: Refactored Logic for Retrieving Deployable Combat Forces ++ Fix #5380: Removed Monthly & Mission Accomplished XP Awards from Children & Dependents ++ PR #5382: Establishment of a Requested Stock % feature, and automating weekly checks to keep spare parts in stock ++ Fix #5383: Fixed save bug ++ PR #5389: Abstract Combat Auto Resolve ++ Fix #5304: Fixed Force Commanders Changing on Load ++ Fix #5390: Adjusted Dependent Addition & Removal Logic ++ Fix #5391: Refactored StratconScenarioWizard to use a Scroll Pane ++ Fix #5392: Updated StratCon Deployment Mechanics ++ PR #5393: Fixed Experience Rating Parsing to Handle Invalid Character ++ Fix #5394: Corrected Scenario Briefings by Removing Explicit Turn Limits ++ PR #5397: Prevent Reserved Formations from Being Deployed to StratCon ++ PR #5399: Capped Negotiated StratCon Support Points at Sector Count ++ PR #5400: Removed Contract Breach References in Scenario and Loan Events ++ PR #5401: Set Default Maintenance Multiplier for Salvaged Units ++ PR #5403: Disabled Commit Forces Button when No Forces Selected ++ PR #5158: Implemented Resupply Module ++ PR #5407: Updated Name of I18n Class ++ PR #5411: Updated Damage Application in ACAR ++ PR #5415: Fixed Resolve Unit Test to Only Run when Asked ++ Fix #2800: Fixed Force Deployment Order Bug ++ Fix #4430: Fixed NPE in Caused by Deployment Attempt in Integrated Command ++ Fix #5299: Added Missing Tile Images to Stratcon Biome Manifest ++ Fix #5317: Set Minimum Target Skill Level in Mass Training Dialog to 1 ++ Fix #5344: Improved StratCon Force Picker & Renamed LayeredForceIconOperationalStatus to OperationalStatus ++ Fix #5369: Fixed Divorce Logic ++ Fix #5408: Fixed Passenger Capacity Calculation in CamOps Reputation ++ Fix #5409: Refactored StratCon Campaign Management Dialog ++ PR #5416: Fixed Incorrect Ordering in Personnel Status Change Switch ++ PR #5417: Refactored Personnel Handling on New Day ++ PR #5423: Updated TO&E to Use "Support" Term instead of "Non-Combat" ++ PR #5425: Added Notification for Users Attempting to Manually Deploy on Integrated Commands ++ PR #5427: Fixed Unit Check in Combat Team Status Evaluation ++ PR #5428: Added Combat Teams & Support Forces Documentation ++ PR #5429: Archived Outdated Against the Bot Documentation ++ PR #5435: Halved Duration of Recon Scenarios ++ PR #5436: Adjusted Scenario Modifiers BV & Unit Count Contribution ++ PR #5437: Added Further Edge Case Handling for Turn Limit Scenario Objectives ++ PR #5438: fix: quick bandaid for gun emplacements in ACAR ++ PR #5443: Updated Naming of AtBLanceRole Class, Added Auxiliaries Combat Role ++ PR #5444: Removed Remaining Instance of Undeploy Functionality when StratCon is Enabled ++ PR #5446: Updated Convoy Scenarios to Exclude BV and Unit Count Contribution for Convoy Units ++ PR #5447: Refactored Conventional Fighter Handling in StratCon ++ Fix #5449: Fixed Force Removal Bug in TO&E, Fixed Convoy-Support Force Option Bug #5449 ++ Fix #5450: Fixed Combat Team Eligibility Check by Rolling Back Use of getAllUnits() ++ Fix #5439: ACAR mode selection ++ PR #5460: Refined Deploy/Undeploy Menu Behavior for GM Permissions ++ Fix #5442: Improved Reinforcement Transparency and User Control ++ Fix #5465: Corrected Date of Resupply Interception Scenarios & Added Clarity to Scenario Names ++ Fix #3639, #4036: Fixed Monthly Facility Support Point Gains & Added Functionality to Allied Industrial Facilities ++ PR #5472: Renamed Supply Depots to Space Ports, Buffed Support Point Gain ++ PR #5473: Renamed Fight, Defense, and Scouting Combat Role Names ++ PR #5475: Fixed Resupply Smuggler Dialog Formatting ++ PR #5476: Added Missing Anti-Mek Skill to Battle Armor Basic Training In-Unit Education Curriculum ++ Fix #5491: Fixed Transportation Capacity and Requirement Calculations ++ Fix #5478: Refactored Loot Description for Clarity ++ PR #5493: Added Name for Resupply Loot ++ PR #5494: Fixed Marshalling and Unmarshalling of stratConScenarioType ++ PR #5495, #5496: Updated Documentation for Personnel Modules, Unit Markets, Force Documentation ++ Fix #5453: Refactored Resupply Logic and Streamlined Cargo Calculations ++ PR #5456: Added Motive System Exclusion in Resupply Eligibility ++ PR #5458: Clarified Speaker Names in Resupply Dialogs ++ PR #5459: Added Notification for Dispatched Convoys During Resupplies ++ Fix #5461: Fixed Legacy AtB Battle Chance Spinner Array Indexing Bug ++ Fix #5464: Adjusted Force Multipliers in Emergency Convoy Defense - VTOL Scenario Template ++ PR #5489: Fixed NPE when Searching for Primary Force in StratCon Scenario Generation ++ PR #5477: Rewrote & Reworked Training Combat Role ++ Fix #5506: Updated Force Refresh Logic in TOEMouseAdapter ++ Fix #5512: Added Compatibility Handlers for Legacy Save File Support ++ Fix #5500: Update contract scenarios to replace VIP Capture objectives ++ PR #5504: Fixed Duplicated autoAwards Trigger in New Day ++ PR #5501: Updated Resupply Mechanics, Added Documentation ++ Fix #5480: AutoLogistics requesting incorrect parts when variants of the same part exist in some cases ++ Fix #5479: AutoLogistics orders reset values ++ PR #5509: ACAR Improvements- Damage delivery more granular, fixed error with morale never checking ++ PR #5528: Updated Space Scenario OpFor Deployment & Arrival ++ PR #5526: Renamed 'requiredScenario' to 'turningPoint' in StratCon ++ PR #5525: Refactored Support Point Negotiation into a Dedicated Class; Fixed Initial Support Point Pool Bug ++ Fix #5515: Added "Refused Engagement" ScenarioStatus ++ Fix #5517: Refactored Personnel Filtering Logic for Field Kitchens...Again ++ Fix #5519: Fixed Recon Scenario Spawn; Fixed Bugs Related to Role Name Changes ++ Fix #5529: Fixed Training Logic to Handle Empty Skill Lists ++ PR #5532: ACAR Documentation ++ PR #5522: Implemented Nag Dialog Visual & Code Improvements ++ PR #5514: Added Travel Description Report to Transit Automation ++ Fix #5221: Adjusted Required Scenario Distribution to More Evenly Award CVP; + Reduced Scenario Sizes for House & Integrated Commands ++ PR #5540: Refactored Pilot Starting Age Calculation ++ PR #5541: Further Adjustments to Starting Ages ++ PR #5536: Corrected Auxiliary Deployment Restrictions ++ PR #5537: Corrected Reinforcement Interception Map Type ++ Fix #5534: Fixed Typo in Resupply Roleplay Event ++ Fix #5535: Adjusted Force Role Weighting in DropShip Defense Scenario Templates ++ PR #5545: Temporarily Disabled Non-Launch Ready Options ++ Fix #5498: Corrected Loading & Unloading of Prior Failed Entrance Exams ++ Fix #5543: Refactored ScenarioType Parsing Logic ++ Fix #5542: Remove User Preferences Handling from BatchXPDialog ++ PR #5551: Refactored Initial Education Logic ++ PR #5557: Refactored Ally and Enemy Rating Calculations for Contracts ++ Fix #5556: Refactored Combat Roles for Clarity ++ PR #5560: Fixed Support Point Negotiation Report ++ PR #5561: Added Missing in-House Bootcamps for NCOs, Warrant Officers, and Officers ++ PR #5562: Fixed Entrance Exam Logic and Added Tooltip Display ++ PR #5563: Log jvm parameters ++ PR #5565: Refactored StratCon Reinforcement Dialog & Added GM Reinforcement Option ++ PR #5566: Refactored Nag Dialog Checks to Improve Performance ++ Fix #5667: Reworked Morale Logic ++ PR #5670: Refactored Font Color Handling for Cargo Report ++ Fix #5662: Restore Default Font Color when Leadership Selection Valid ++ Fix #5661: Removed Hardcoded Primary Ally Modifier Chances ++ Fix #5648: Expanded and Fixed Cargo Capacity Logic ++ PR #5674: Expand Special Unit Generation in AtB Unit Market ++ Fix #5668, #5640: Multiple Resupply Bug Fixes ++ PR #5676: Corrected Transportation Unit Type Check ++ Fix #5663: Addresses where the financeTable is empty ++ Fix #5664: Expanded Contract Automation Functionality to Manually Created AtB Contracts ++ Fix #5728: fixed error with resolver test 0.50.01 (2024-11-10 1800 UTC) + PR #4810: Respecting Trademarks (Mech to Mek) diff --git a/MekHQ/mmconf/campaignPresets/Campaign Operations.xml b/MekHQ/mmconf/campaignPresets/CampaignOperations.xml similarity index 95% rename from MekHQ/mmconf/campaignPresets/Campaign Operations.xml rename to MekHQ/mmconf/campaignPresets/CampaignOperations.xml index 052ff00b9c3..c96b14c0dd8 100644 --- a/MekHQ/mmconf/campaignPresets/Campaign Operations.xml +++ b/MekHQ/mmconf/campaignPresets/CampaignOperations.xml @@ -1,17 +1,14 @@ - + 4 - Campaign Operations - This preset is designed to follow the rules outlined in campaign operations. Please note that not all CamOps systems have been transferred to MekHQ, so some bookkeeping may be necessary. - 3035-01-01 - MERC - - SSLDF - + This preset is designed to follow the rules outlined in Campaign Operations. Please note that not all CamOps systems have been transferred to MekHQ, so some bookkeeping will be necessary. + 3151-01-01 2 - false + true 0 false + 4 true true true @@ -94,6 +91,30 @@ 4 4 + + HEAT_SINK + 0 + 0 + 4 + 4 + 4 + + + MEK_LOCATION + 0 + 0 + 4 + 4 + 4 + + + PHYSICAL_WEAPON + 0 + 0 + 4 + 4 + 4 + POD_SPACE 0 @@ -102,6 +123,14 @@ 4 4 + + UNKNOWN_LOCATION + 0 + 0 + 4 + 4 + 4 + true CAMPAIGN_OPS @@ -117,9 +146,9 @@ 1 0 0 - 0 - 10 - 2 + 0 + 10 + 2 0 0 1 @@ -177,6 +206,13 @@ false true 1 + 250 + 200 + 100 + 0 + 500 + 500 + 50 false false false @@ -195,10 +231,10 @@ false false false - false + true YEARS false - MONTHS_YEARS + YEARS false false true @@ -227,19 +263,19 @@ 1.5 1.28 - 0.5 - 12.8 + 0.6 0.6 - 3.2 + 1.0 1.6 + 12.8 + 0.5 + 3.2 6.4 - 0.6 - 1.0 1500 CSB,2250 CSB,900 CSB,900 CSB,900 CSB,900 CSB,900 CSB,1500 CSB,900 CSB,960 CSB,960 CSB,750 CSB,1000 CSB,1000 CSB,1000 CSB,1000 CSB,800 CSB,800 CSB,800 CSB,800 CSB,400 CSB,1500 CSB,400 CSB,500 CSB,500 CSB,500 CSB,500 CSB,0 CSB,0 CSB BOTH false - false + true true false 5 @@ -259,13 +295,13 @@ false 60 - false + true false false 45 0.6 false - false + true false true @@ -275,7 +311,7 @@ 3 MONTHLY false - false + true true true true @@ -284,7 +320,7 @@ 3 false 3 - false + true true true true @@ -300,10 +336,10 @@ 12 true 10 - true + false 10 5 - true + false false 0 true @@ -313,29 +349,29 @@ true 13 SPOUSE - false + true true true false true - false + true true 4 10 false - 100 - 10 - 500 - 160 - 10 - 20 - 30 55 5 + 30 + 160 + 10 + 5 30 + 500 + 10 55 - 5 + 20 + 100 20 NONE @@ -347,7 +383,7 @@ 20 true true - false + true 30 10 @@ -361,13 +397,13 @@ false 900 true - false + true true 50 MOTHERS false - false - true + true + false false false 3 @@ -397,13 +433,13 @@ true NONE - false - false - true false - true false + true true + false + false + true true true @@ -414,28 +450,28 @@ 613.1 27.5 - 14.7 - 14504.0 - 491.8 5155.0 - 100.1 1119.0 - 176.1 - 2196.5 + 14.7 249.5 + 2196.5 + 176.1 + 14504.0 + 100.1 + 491.8 500.0 20.4 - 11.8 - 12870.0 - 302.5 3788.0 - 38.8 670.0 - 80.0 - 1421.0 + 11.8 140.2 + 1421.0 + 80.0 + 12870.0 + 38.8 + 302.5 true true @@ -443,7 +479,7 @@ true false true - false + true true true false @@ -472,25 +508,25 @@ Campaign Ops true - 3 - 11 + 4 4 - 10 + 6 8 + 11 + 3 + 10 11 - 4 - 6 0.3 false NONE - true + false 30 0 false true NONE - 800 + 400 false true 100 @@ -499,6 +535,9 @@ Xotl,Total Warfare false REGULAR + PRINCESS + false + 100 95,100,95,0,95,25 false false @@ -518,7 +557,7 @@ false true true - 0,0,0,0 + 0,0,0,0,0 false true true @@ -526,10 +565,7 @@ true 3 1 - false true - 500000 - 10 false false false @@ -565,37 +601,27 @@ - Piloting/Mek - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Gunnery/Mek - 7 + Administration + 10 false - 2 + 1 3 4 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 + 8,4,4,4,4,4,-1,-1,-1,-1,-1 - Piloting/Aerospace + Anti-Mek 8 false 2 3 4 5 - 8,4,4,4,4,4,4,4,4,-1,-1 + 12,6,6,6,6,6,6,6,6,-1,-1 - Gunnery/Aerospace + Artillery 7 false 2 @@ -605,37 +631,27 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Piloting/Ground Vehicle - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Piloting/VTOL - 8 + Astech + 10 false - 2 + 1 3 4 5 - 8,4,4,4,4,4,4,4,4,-1,-1 + 12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - Piloting/Naval - 8 + Doctor + 11 false - 2 + 1 3 4 5 - 8,4,4,4,4,4,4,4,4,-1,-1 + 16,8,8,8,8,8,-1,-1,-1,-1,-1 - Gunnery/Vehicle + Gunnery/Aerospace 7 false 2 @@ -644,16 +660,6 @@ 5 16,8,8,8,8,8,8,8,-1,-1,-1 - - Piloting/Aircraft - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - Gunnery/Aircraft 7 @@ -665,17 +671,17 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Piloting/Spacecraft - 8 + Gunnery/BattleArmor + 7 false 2 3 4 5 - 8,4,4,4,4,4,4,4,4,-1,-1 + 16,8,8,8,8,8,8,8,-1,-1,-1 - Gunnery/Spacecraft + Gunnery/Mek 7 false 2 @@ -685,7 +691,7 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Artillery + Gunnery/ProtoMek 7 false 2 @@ -695,7 +701,7 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Gunnery/BattleArmor + Gunnery/Spacecraft 7 false 2 @@ -705,7 +711,7 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Gunnery/ProtoMek + Gunnery/Vehicle 7 false 2 @@ -715,127 +721,137 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Small Arms - 7 + Hyperspace Navigation + 8 false - 2 + 1 3 4 5 8,4,4,4,4,4,4,4,4,-1,-1 - Anti-Mek - 8 - false - 2 + Leadership + 0 + true + 1 3 4 5 - 12,6,6,6,6,6,6,6,6,-1,-1 + 12,6,6,6,6,6,6,6,6,6,6 - Tech/Mek - 10 + MedTech + 11 false 1 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 16,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - Tech/Mechanic + Negotiation 10 false 1 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,4,4 - Tech/Aero - 10 + Piloting/Aerospace + 8 false - 1 + 2 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Tech/BattleArmor - 10 + Piloting/Aircraft + 8 false - 1 + 2 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Tech/Vessel - 10 + Piloting/Ground Vehicle + 8 false - 1 + 2 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Astech - 10 + Piloting/Mek + 8 false - 1 + 2 3 4 5 - 12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Doctor - 11 + Piloting/Naval + 8 false - 1 + 2 3 4 5 - 16,8,8,8,8,8,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - MedTech - 11 + Piloting/Spacecraft + 8 false - 1 + 2 3 4 5 - 16,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Hyperspace Navigation + Piloting/VTOL 8 false - 1 + 2 3 4 5 8,4,4,4,4,4,4,4,4,-1,-1 - Administration + Scrounge 10 false 1 3 4 5 - 8,4,4,4,4,4,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,4,4 - Tactics + Small Arms + 7 + false + 2 + 3 + 4 + 5 + 8,4,4,4,4,4,4,4,4,-1,-1 + + + Strategy 0 true 1 @@ -845,7 +861,7 @@ 12,6,6,6,6,6,6,6,6,6,6 - Strategy + Tactics 0 true 1 @@ -855,34 +871,54 @@ 12,6,6,6,6,6,6,6,6,6,6 - Negotiation + Tech/Aero 10 false 1 3 4 5 - 8,4,4,4,4,4,4,4,4,4,4 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 - Leadership - 0 - true + Tech/BattleArmor + 10 + false 1 3 4 5 - 12,6,6,6,6,6,6,6,6,6,6 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 - Scrounge + Tech/Mechanic 10 false 1 3 4 5 - 8,4,4,4,4,4,4,4,4,4,4 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 + + + Tech/Mek + 10 + false + 1 + 3 + 4 + 5 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 + + + Tech/Vessel + 10 + false + 1 + 3 + 4 + 5 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 @@ -897,10 +933,10 @@ - Piloting/Ground Vehicle::Veteran + Anti-Mek::Veteran Gunnery/ProtoMek::Veteran + Piloting/Ground Vehicle::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -914,12 +950,12 @@ + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran Gunnery/Aircraft::Veteran - Gunnery/BattleArmor::Veteran - Gunnery/Aerospace::Veteran Gunnery/Spacecraft::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Mek::Veteran + Gunnery/Aerospace::Veteran + Gunnery/BattleArmor::Veteran Gunnery/Vehicle::Veteran @@ -962,12 +998,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular @@ -998,12 +1034,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular @@ -1018,12 +1054,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular @@ -1038,12 +1074,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular @@ -1092,18 +1128,18 @@ - Piloting/Mek::Regular + Gunnery/Mek::Regular Piloting/Aircraft::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular + Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular Gunnery/ProtoMek::Regular + Anti-Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular @@ -1120,12 +1156,12 @@ + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran Gunnery/Aircraft::Veteran - Gunnery/BattleArmor::Veteran - Gunnery/Aerospace::Veteran Gunnery/Spacecraft::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Mek::Veteran + Gunnery/Aerospace::Veteran + Gunnery/BattleArmor::Veteran Gunnery/Vehicle::Veteran @@ -1145,10 +1181,10 @@ Piloting/Aerospace::Regular Piloting/Naval::Regular + Gunnery/ProtoMek::Regular Piloting/Ground Vehicle::Regular Piloting/Spacecraft::Regular Piloting/VTOL::Regular - Gunnery/ProtoMek::Regular Piloting/Aircraft::Regular Piloting/Mek::Regular @@ -1165,10 +1201,10 @@ - Piloting/Ground Vehicle::Veteran + Anti-Mek::Veteran Gunnery/ProtoMek::Veteran + Piloting/Ground Vehicle::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -1182,13 +1218,13 @@ - Gunnery/BattleArmor::Regular + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/Spacecraft::Regular Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular + Gunnery/Spacecraft::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular - Gunnery/Mek::Regular Artillery::Regular @@ -1203,18 +1239,18 @@ - Piloting/Mek::Regular + Gunnery/Mek::Regular Piloting/Aircraft::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular + Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular Gunnery/ProtoMek::Regular + Anti-Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular @@ -1233,10 +1269,10 @@ - Piloting/Ground Vehicle::Veteran + Anti-Mek::Veteran Gunnery/ProtoMek::Veteran + Piloting/Ground Vehicle::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -1265,18 +1301,18 @@ - Piloting/Mek::Regular + Gunnery/Mek::Regular Piloting/Aircraft::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular + Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular Gunnery/ProtoMek::Regular + Anti-Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular @@ -1293,18 +1329,18 @@ - Piloting/Mek::Regular + Gunnery/Mek::Regular Piloting/Aircraft::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular + Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular Gunnery/ProtoMek::Regular + Anti-Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular @@ -1323,18 +1359,18 @@ - Piloting/Mek::Veteran + Gunnery/Mek::Veteran Piloting/Aircraft::Veteran Gunnery/Spacecraft::Veteran Piloting/Spacecraft::Veteran + Piloting/Mek::Veteran Piloting/VTOL::Veteran Piloting/Naval::Veteran + Gunnery/BattleArmor::Veteran Gunnery/Aerospace::Veteran Piloting/Aerospace::Veteran - Anti-Mek::Veteran - Gunnery/BattleArmor::Veteran - Gunnery/Mek::Veteran Gunnery/ProtoMek::Veteran + Anti-Mek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/Aircraft::Veteran Gunnery/Vehicle::Veteran @@ -1397,12 +1433,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular @@ -1418,10 +1454,10 @@ - Piloting/Ground Vehicle::Veteran + Anti-Mek::Veteran Gunnery/ProtoMek::Veteran + Piloting/Ground Vehicle::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -1436,12 +1472,12 @@ + Gunnery/Mek::Green + Gunnery/ProtoMek::Green Gunnery/Aircraft::Green - Gunnery/BattleArmor::Green - Gunnery/Aerospace::Green Gunnery/Spacecraft::Green - Gunnery/ProtoMek::Green - Gunnery/Mek::Green + Gunnery/Aerospace::Green + Gunnery/BattleArmor::Green Gunnery/Vehicle::Green @@ -1456,12 +1492,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular diff --git a/MekHQ/mmconf/campaignPresets/Campaign Operations (StratCon).xml b/MekHQ/mmconf/campaignPresets/CampaignOperationsStratCon.xml similarity index 94% rename from MekHQ/mmconf/campaignPresets/Campaign Operations (StratCon).xml rename to MekHQ/mmconf/campaignPresets/CampaignOperationsStratCon.xml index df5731efdb9..545c94b00eb 100644 --- a/MekHQ/mmconf/campaignPresets/Campaign Operations (StratCon).xml +++ b/MekHQ/mmconf/campaignPresets/CampaignOperationsStratCon.xml @@ -1,17 +1,14 @@ - - 3 - Campaign Operations (StratCon - Beta) - This preset follows the rules outlined in campaign operations but with StratCon enabled, allowing you to play a single-player CamOps campaign against the bot. Like the Campaign Operations preset, not all CamOps systems have been transferred to MekHQ, so some bookkeeping may be necessary. + + 3 - Campaign Operations (StratCon) + This preset follows the rules outlined in Campaign Operations but with StratCon enabled, allowing you to play a single-player CamOps campaign against the bot. Like the Campaign Operations preset, not all CamOps systems have been transferred to MekHQ, so some bookkeeping will be necessary. 3151-01-01 - MERC - - SSLDF - 2 - false + true 0 false + 4 true true true @@ -94,6 +91,30 @@ 4 4 + + HEAT_SINK + 0 + 0 + 4 + 4 + 4 + + + MEK_LOCATION + 0 + 0 + 4 + 4 + 4 + + + PHYSICAL_WEAPON + 0 + 0 + 4 + 4 + 4 + POD_SPACE 0 @@ -102,6 +123,14 @@ 4 4 + + UNKNOWN_LOCATION + 0 + 0 + 4 + 4 + 4 + true CAMPAIGN_OPS @@ -117,9 +146,9 @@ 1 0 0 - 0 - 10 - 2 + 0 + 10 + 2 0 0 1 @@ -177,6 +206,13 @@ false true 1 + 250 + 200 + 100 + 0 + 500 + 500 + 50 false false false @@ -195,10 +231,10 @@ false false false - false + true YEARS false - MONTHS_YEARS + YEARS false false true @@ -227,19 +263,19 @@ 1.5 1.28 - 0.5 - 12.8 0.6 - 3.2 1.6 - 6.4 + 0.5 0.6 + 12.8 1.0 + 3.2 + 6.4 1500 CSB,2250 CSB,900 CSB,900 CSB,900 CSB,900 CSB,900 CSB,1500 CSB,900 CSB,960 CSB,960 CSB,750 CSB,1000 CSB,1000 CSB,1000 CSB,1000 CSB,800 CSB,800 CSB,800 CSB,800 CSB,400 CSB,1500 CSB,400 CSB,500 CSB,500 CSB,500 CSB,500 CSB,0 CSB,0 CSB BOTH false - false + true true false 5 @@ -259,13 +295,13 @@ false 60 - false + true false false 45 0.6 false - false + true false true @@ -275,7 +311,7 @@ 3 MONTHLY false - false + true true true true @@ -284,7 +320,7 @@ 3 false 3 - false + true true true true @@ -300,10 +336,10 @@ 12 true 10 - true + false 10 5 - true + false false 0 true @@ -313,30 +349,30 @@ true 13 SPOUSE - false + true true true false true - false + true true 4 10 false - 100 - 10 - 500 - 160 + 5 + 20 10 - 20 30 - 55 + 20 5 + 100 + 10 30 + 160 + 55 55 - 5 - 20 + 500 NONE false @@ -347,27 +383,27 @@ 20 true true - false + true 30 - 10 50 + 10 10 NONE true true true - false + true 900 true - false + true true 50 MOTHERS false - false - true + true + false false false 3 @@ -397,13 +433,13 @@ true NONE - false - false - true - false - true false + true true + true + false + false + false true true @@ -412,30 +448,30 @@ 5.4757,-7.0,0.0709 2.4641,-7.0,0.0752 - 613.1 27.5 14.7 - 14504.0 491.8 - 5155.0 + 14504.0 + 2196.5 100.1 + 249.5 + 5155.0 1119.0 + 613.1 176.1 - 2196.5 - 249.5 - 500.0 20.4 11.8 - 12870.0 302.5 - 3788.0 + 12870.0 + 1421.0 38.8 + 140.2 + 3788.0 670.0 + 500.0 80.0 - 1421.0 - 140.2 true true @@ -443,7 +479,7 @@ true false true - false + true true true false @@ -472,25 +508,25 @@ Campaign Ops true - 3 - 11 4 - 10 8 - 11 + 3 4 + 11 6 + 10 + 11 0.3 false NONE - true + false 30 0 false true - NONE - 800 + ATB_MONTHLY + 400 false true 100 @@ -499,8 +535,11 @@ Xotl,Total Warfare false REGULAR + ABSTRACT_COMBAT + false + 100 95,100,95,0,95,25 - true + false true false true @@ -512,13 +551,13 @@ 5 2 1 - false - false + true + true false - false + true true true - 0,0,0,0 + 0,0,0,0,0 false true true @@ -526,10 +565,7 @@ true 3 1 - true true - 500000 - 10 false false false @@ -565,37 +601,27 @@ - Piloting/Mek - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Gunnery/Mek - 7 + Administration + 10 false - 2 + 1 3 4 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 + 8,4,4,4,4,4,-1,-1,-1,-1,-1 - Piloting/Aerospace + Anti-Mek 8 false 2 3 4 5 - 8,4,4,4,4,4,4,4,4,-1,-1 + 12,6,6,6,6,6,6,6,6,-1,-1 - Gunnery/Aerospace + Artillery 7 false 2 @@ -605,37 +631,27 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Piloting/Ground Vehicle - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Piloting/VTOL - 8 + Astech + 10 false - 2 + 1 3 4 5 - 8,4,4,4,4,4,4,4,4,-1,-1 + 12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - Piloting/Naval - 8 + Doctor + 11 false - 2 + 1 3 4 5 - 8,4,4,4,4,4,4,4,4,-1,-1 + 16,8,8,8,8,8,-1,-1,-1,-1,-1 - Gunnery/Vehicle + Gunnery/Aerospace 7 false 2 @@ -644,16 +660,6 @@ 5 16,8,8,8,8,8,8,8,-1,-1,-1 - - Piloting/Aircraft - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - Gunnery/Aircraft 7 @@ -665,17 +671,17 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Piloting/Spacecraft - 8 + Gunnery/BattleArmor + 7 false 2 3 4 5 - 8,4,4,4,4,4,4,4,4,-1,-1 + 16,8,8,8,8,8,8,8,-1,-1,-1 - Gunnery/Spacecraft + Gunnery/Mek 7 false 2 @@ -685,7 +691,7 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Artillery + Gunnery/ProtoMek 7 false 2 @@ -695,7 +701,7 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Gunnery/BattleArmor + Gunnery/Spacecraft 7 false 2 @@ -705,7 +711,7 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Gunnery/ProtoMek + Gunnery/Vehicle 7 false 2 @@ -715,127 +721,137 @@ 16,8,8,8,8,8,8,8,-1,-1,-1 - Small Arms - 7 + Hyperspace Navigation + 8 false - 2 + 1 3 4 5 8,4,4,4,4,4,4,4,4,-1,-1 - Anti-Mek - 8 - false - 2 + Leadership + 0 + true + 1 3 4 5 - 12,6,6,6,6,6,6,6,6,-1,-1 + 12,6,6,6,6,6,6,6,6,6,6 - Tech/Mek - 10 + MedTech + 11 false 1 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 16,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - Tech/Mechanic + Negotiation 10 false 1 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,4,4 - Tech/Aero - 10 + Piloting/Aerospace + 8 false - 1 + 2 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Tech/BattleArmor - 10 + Piloting/Aircraft + 8 false - 1 + 2 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Tech/Vessel - 10 + Piloting/Ground Vehicle + 8 false - 1 + 2 3 4 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Astech - 10 + Piloting/Mek + 8 false - 1 + 2 3 4 5 - 12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Doctor - 11 + Piloting/Naval + 8 false - 1 + 2 3 4 5 - 16,8,8,8,8,8,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - MedTech - 11 + Piloting/Spacecraft + 8 false - 1 + 2 3 4 5 - 16,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,-1,-1 - Hyperspace Navigation + Piloting/VTOL 8 false - 1 + 2 3 4 5 8,4,4,4,4,4,4,4,4,-1,-1 - Administration + Scrounge 10 false 1 3 4 5 - 8,4,4,4,4,4,-1,-1,-1,-1,-1 + 8,4,4,4,4,4,4,4,4,4,4 - Tactics + Small Arms + 7 + false + 2 + 3 + 4 + 5 + 8,4,4,4,4,4,4,4,4,-1,-1 + + + Strategy 0 true 1 @@ -845,7 +861,7 @@ 12,6,6,6,6,6,6,6,6,6,6 - Strategy + Tactics 0 true 1 @@ -855,34 +871,54 @@ 12,6,6,6,6,6,6,6,6,6,6 - Negotiation + Tech/Aero 10 false 1 3 4 5 - 8,4,4,4,4,4,4,4,4,4,4 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 - Leadership - 0 - true + Tech/BattleArmor + 10 + false 1 3 4 5 - 12,6,6,6,6,6,6,6,6,6,6 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 - Scrounge + Tech/Mechanic 10 false 1 3 4 5 - 8,4,4,4,4,4,4,4,4,4,4 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 + + + Tech/Mek + 10 + false + 1 + 3 + 4 + 5 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 + + + Tech/Vessel + 10 + false + 1 + 3 + 4 + 5 + 12,6,6,6,6,6,-1,-1,-1,-1,-1 @@ -897,10 +933,10 @@ + Anti-Mek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/ProtoMek::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -914,12 +950,12 @@ + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran Gunnery/Aircraft::Veteran Gunnery/BattleArmor::Veteran Gunnery/Aerospace::Veteran Gunnery/Spacecraft::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Mek::Veteran Gunnery/Vehicle::Veteran @@ -962,12 +998,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular @@ -998,12 +1034,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular @@ -1018,12 +1054,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular @@ -1038,12 +1074,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular @@ -1092,17 +1128,17 @@ - Piloting/Mek::Regular Piloting/Aircraft::Regular + Gunnery/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular - Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular + Gunnery/Aerospace::Regular Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular + Anti-Mek::Regular Gunnery/ProtoMek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular @@ -1120,12 +1156,12 @@ + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran Gunnery/Aircraft::Veteran Gunnery/BattleArmor::Veteran Gunnery/Aerospace::Veteran Gunnery/Spacecraft::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Mek::Veteran Gunnery/Vehicle::Veteran @@ -1146,9 +1182,9 @@ Piloting/Aerospace::Regular Piloting/Naval::Regular Piloting/Ground Vehicle::Regular + Gunnery/ProtoMek::Regular Piloting/Spacecraft::Regular Piloting/VTOL::Regular - Gunnery/ProtoMek::Regular Piloting/Aircraft::Regular Piloting/Mek::Regular @@ -1165,10 +1201,10 @@ + Anti-Mek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/ProtoMek::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -1182,13 +1218,13 @@ - Gunnery/BattleArmor::Regular + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular + Gunnery/BattleArmor::Regular Gunnery/Spacecraft::Regular Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular Gunnery/Vehicle::Regular - Gunnery/Mek::Regular Artillery::Regular @@ -1203,17 +1239,17 @@ - Piloting/Mek::Regular Piloting/Aircraft::Regular + Gunnery/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular - Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular + Gunnery/Aerospace::Regular Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular + Anti-Mek::Regular Gunnery/ProtoMek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular @@ -1233,10 +1269,10 @@ + Anti-Mek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/ProtoMek::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -1265,17 +1301,17 @@ - Piloting/Mek::Regular Piloting/Aircraft::Regular + Gunnery/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular - Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular + Gunnery/Aerospace::Regular Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular + Anti-Mek::Regular Gunnery/ProtoMek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular @@ -1293,17 +1329,17 @@ - Piloting/Mek::Regular Piloting/Aircraft::Regular + Gunnery/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular - Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular + Gunnery/Aerospace::Regular Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular + Anti-Mek::Regular Gunnery/ProtoMek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular @@ -1323,17 +1359,17 @@ - Piloting/Mek::Veteran Piloting/Aircraft::Veteran + Gunnery/Mek::Veteran Gunnery/Spacecraft::Veteran Piloting/Spacecraft::Veteran + Piloting/Mek::Veteran Piloting/VTOL::Veteran Piloting/Naval::Veteran - Gunnery/Aerospace::Veteran Piloting/Aerospace::Veteran - Anti-Mek::Veteran + Gunnery/Aerospace::Veteran Gunnery/BattleArmor::Veteran - Gunnery/Mek::Veteran + Anti-Mek::Veteran Gunnery/ProtoMek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/Aircraft::Veteran @@ -1397,12 +1433,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular @@ -1418,10 +1454,10 @@ + Anti-Mek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/ProtoMek::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -1436,12 +1472,12 @@ + Gunnery/Mek::Green + Gunnery/ProtoMek::Green Gunnery/Aircraft::Green Gunnery/BattleArmor::Green Gunnery/Aerospace::Green Gunnery/Spacecraft::Green - Gunnery/ProtoMek::Green - Gunnery/Mek::Green Gunnery/Vehicle::Green @@ -1456,12 +1492,12 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular diff --git a/MekHQ/mmconf/campaignPresets/New Pilot Program.xml b/MekHQ/mmconf/campaignPresets/NewPilotProgram.xml similarity index 81% rename from MekHQ/mmconf/campaignPresets/New Pilot Program.xml rename to MekHQ/mmconf/campaignPresets/NewPilotProgram.xml index bd0e31edf3e..06ea0ae60f9 100644 --- a/MekHQ/mmconf/campaignPresets/New Pilot Program.xml +++ b/MekHQ/mmconf/campaignPresets/NewPilotProgram.xml @@ -1,17 +1,14 @@ - - 1 - New Pilot Program (StratCon - Beta) - This preset is a lower difficulty version of the 'Complete Experience' preset, offering an experience tailored for newer players or those who prefer an easier game. Most options are still enabled, providing the full MekHQ experience. + + 1 - New Pilot Program (StratCon) + This preset is a lower difficulty version of the 'Complete Experience' preset, offering an experience tailored for newer players or those who prefer an easier game. Most options are still enabled, providing the full MekHQ experience. 3151-01-01 - MERC - - SSLDF - 2 false 0 false + 4 true true true @@ -94,6 +91,30 @@ 4 4 + + HEAT_SINK + 0 + 0 + 4 + 4 + 4 + + + MEK_LOCATION + 0 + 0 + 4 + 4 + 4 + + + PHYSICAL_WEAPON + 0 + 0 + 4 + 4 + 4 + POD_SPACE 0 @@ -102,6 +123,14 @@ 4 4 + + UNKNOWN_LOCATION + 0 + 0 + 4 + 4 + 4 + true CAMPAIGN_OPS @@ -117,9 +146,9 @@ 0 0 0 - 5 - 7 - 1 + 1 + 7 + 1 0 0 1 @@ -139,7 +168,7 @@ 1 Negotiation true - 3 + 2 1 0 2 @@ -164,23 +193,30 @@ true 0 0 - false + true 4 10 - false + true 7 -2 - true + false false false false true true - 0 + 2 + 250 + 200 + 100 + 0 + 500 + 500 + 50 false false - false - false + true + true false true true @@ -204,7 +240,7 @@ true true false - false + true false true 1 @@ -227,14 +263,14 @@ 1.5 1.28 - 0.5 - 0.6 0.6 1.0 1.6 - 3.2 - 6.4 12.8 + 6.4 + 0.5 + 0.6 + 3.2 1500 CSB,2250 CSB,900 CSB,900 CSB,900 CSB,900 CSB,900 CSB,1500 CSB,900 CSB,960 CSB,960 CSB,750 CSB,1000 CSB,1000 CSB,1000 CSB,1000 CSB,800 CSB,800 CSB,800 CSB,800 CSB,400 CSB,1500 CSB,400 CSB,500 CSB,500 CSB,500 CSB,500 CSB,0 CSB,0 CSB BOTH @@ -275,7 +311,7 @@ 1 MONTHLY true - false + true true true true @@ -306,9 +342,9 @@ false false 0 - true + false 1 - false + true 150 true 13 @@ -318,25 +354,25 @@ true true true - false + true true 4 10 false - 500 + 10 20 - 10 - 5 + 500 + 55 100 + 160 5 55 - 160 + 10 + 5 + 30 30 - 55 20 - 10 - 30 DICE_ROLL false @@ -347,35 +383,35 @@ 20 true true - false + true 10 + 30 50 10 - 30 DICE_ROLL true true true - false + true 900 true false - true + false 50 MOTHERS false - false + true true false false 3 - false + true DICE_ROLL true false - true + false 500 2000 true @@ -387,7 +423,7 @@ true false false - 14 + 12 1.0 true 1000 @@ -395,15 +431,15 @@ false 10000 true - NONE + EXPONENTIAL true + true false - false + true false false - true - true + false true true @@ -412,43 +448,43 @@ 5.4757,-7.0,0.0709 2.4641,-7.0,0.0752 - 27.5 - 2196.5 - 14504.0 + 176.1 14.7 + 27.5 249.5 - 176.1 - 491.8 - 1119.0 - 5155.0 + 14504.0 613.1 + 1119.0 + 2196.5 100.1 + 5155.0 + 491.8 - 20.4 - 1421.0 - 12870.0 + 80.0 11.8 + 20.4 140.2 - 80.0 - 302.5 - 670.0 - 3788.0 + 12870.0 500.0 + 670.0 + 1421.0 38.8 + 3788.0 + 302.5 true true true true false - true - false + false + true true true false false - false + true false true true @@ -472,14 +508,14 @@ Campaign Ops true - 4 - 11 - 3 4 6 8 - 10 11 + 11 + 3 + 4 + 10 0.3 false @@ -487,11 +523,11 @@ true 30 0 - true + false true ATB_MONTHLY 400 - false + true true 100 5 @@ -499,10 +535,13 @@ Xotl,Total Warfare false ULTRA_GREEN + ABSTRACT_COMBAT + true + 100 95,100,95,0,95,25 - true + false true - false + true true true true @@ -513,23 +552,20 @@ 2 1 false - false + true false - false - true + true + false true - 0,0,0,0 + 0,0,0,0,0 false false false false - true + false 3 1 - true true - 500000 - 10 false false false @@ -542,7 +578,7 @@ 3 25 25 - true + false -1,0,1,2,4,8 0,0,0,0,0,0 -1,0,1,2,4,8 @@ -553,7 +589,7 @@ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -10,-10,-2,0,1 -10,-10,-7,-4,-1 - false + true true 10 -3 @@ -565,27 +601,17 @@ - Piloting/Mek - 8 - false - 2 - 3 - 4 - 5 - 20,10,20,30,40,50,60,70,80,90,100 - - - Gunnery/Mek - 7 + Administration + 10 false - 2 + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Aerospace + Anti-Mek 8 false 2 @@ -595,7 +621,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/Aerospace + Artillery 7 false 2 @@ -605,28 +631,28 @@ 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Ground Vehicle - 8 + Astech + 10 false - 2 + 1 3 4 5 - 20,10,20,30,40,50,60,70,80,90,100 + 20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - Piloting/VTOL - 8 + Doctor + 11 false - 2 + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Naval - 8 + Gunnery/Aerospace + 7 false 2 3 @@ -635,7 +661,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/Vehicle + Gunnery/Aircraft 7 false 2 @@ -645,8 +671,8 @@ 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Aircraft - 8 + Gunnery/BattleArmor + 7 false 2 3 @@ -655,7 +681,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/Aircraft + Gunnery/Mek 7 false 2 @@ -665,8 +691,8 @@ 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Spacecraft - 8 + Gunnery/ProtoMek + 7 false 2 3 @@ -685,7 +711,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Artillery + Gunnery/Vehicle 7 false 2 @@ -695,139 +721,139 @@ 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/BattleArmor - 7 + Hyperspace Navigation + 8 false - 2 + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/ProtoMek - 7 - false - 2 + Leadership + 0 + true + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Small Arms - 7 + MedTech + 11 false - 2 + 1 3 4 5 - 20,10,20,30,40,50,60,70,80,90,100 + 20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - Anti-Mek - 8 + Negotiation + 10 false - 2 + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/Mek - 10 + Piloting/Aerospace + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/Mechanic - 10 + Piloting/Aircraft + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/Aero - 10 + Piloting/Ground Vehicle + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/BattleArmor - 10 + Piloting/Mek + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/Vessel - 10 + Piloting/Naval + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Astech - 10 + Piloting/Spacecraft + 8 false - 1 + 2 3 4 5 - 20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + 20,10,20,30,40,50,60,70,80,90,100 - Doctor - 11 + Piloting/VTOL + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - MedTech - 11 + Scrounge + 10 false 1 3 4 5 - 20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + 20,10,20,30,40,50,60,70,80,90,100 - Hyperspace Navigation - 8 + Small Arms + 7 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Administration - 10 - false + Strategy + 0 + true 1 3 4 @@ -845,9 +871,9 @@ 20,10,20,30,40,50,60,70,80,90,100 - Strategy - 0 - true + Tech/Aero + 10 + false 1 3 4 @@ -855,7 +881,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Negotiation + Tech/BattleArmor 10 false 1 @@ -865,9 +891,9 @@ 20,10,20,30,40,50,60,70,80,90,100 - Leadership - 0 - true + Tech/Mechanic + 10 + false 1 3 4 @@ -875,7 +901,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Scrounge + Tech/Mek 10 false 1 @@ -884,10 +910,20 @@ 5 20,10,20,30,40,50,60,70,80,90,100 - - - - Terrain Master [Nightwalker] (CamOps) + + Tech/Vessel + 10 + false + 1 + 3 + 4 + 5 + 20,10,20,30,40,50,60,70,80,90,100 + + + + + Terrain Master [Nightwalker] (CamOps) tm_nightwalker Nightwalker ignores all darkness move penalties when using Walk / Cruise movement but does not affect gunnery modifiers for lighting conditions. 150 @@ -897,10 +933,10 @@ + Anti-Mek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/ProtoMek::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -914,12 +950,12 @@ + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran Gunnery/Aircraft::Veteran Gunnery/BattleArmor::Veteran Gunnery/Aerospace::Veteran Gunnery/Spacecraft::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Mek::Veteran Gunnery/Vehicle::Veteran @@ -938,63 +974,138 @@ - Cross-Country (CamOps) - cross_country - Allows a unit to use terrain normally restricted to said unit at the cost of increased MP. - 100 + Blood Stalker (CamOps) + blood_stalker + Unit has -1 bonus against designated target, +2 penalty against all others; lasts until target retreats, can only be used once per battle. + 120 1 - Piloting/Ground Vehicle::Regular + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular + Gunnery/Aircraft::Regular + Gunnery/BattleArmor::Regular + Gunnery/Aerospace::Regular + Gunnery/Spacecraft::Regular + Gunnery/Vehicle::Regular - Natural Aptitude, Piloting (aToW) - aptitude_piloting - Roll 3d6 and take the best two for piloting checks - 500 - 1 + Jumping Jack (CamOps) + jumping_jack + Unit only suffers a +1 to-hit penalty for jumping, rather than a +3 to-hit penalty. + 150 + 6 - Piloting/Aerospace::Veteran - Piloting/Naval::Veteran - Piloting/Ground Vehicle::Veteran - Piloting/Spacecraft::Veteran - Piloting/VTOL::Veteran - Piloting/Aircraft::Veteran - Piloting/Mek::Veteran + Gunnery/ProtoMek::Regular + Piloting/Mek::Regular - Blood Stalker (CamOps) - blood_stalker - Unit has -1 bonus against designated target, +2 penalty against all others; lasts until target retreats, can only be used once per battle. - 120 - 1 + Tech Specialist, Internal (Unofficial) + tech_internal_specialist + -1 to repair and maintenance checks when working with internal systems + 100 + 2 + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mek::Regular + Tech/Mechanic::Regular + Tech/BattleArmor::Regular + + + + RangeMaster (CamOps) + range_master + Range modifiers are swapped with short range (Medium, Long, or Extreme). + 100 + 3 + + + + + + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular - Jumping Jack (CamOps) - jumping_jack - Unit only suffers a +1 to-hit penalty for jumping, rather than a +3 to-hit penalty. + Dodge (MaxTech) + dodge_maneuver + Enables the unit to make a dodge maneuver instead of a physical attack. + This maneuver adds +2 to the BTH to physical attacks against the unit. + NOTE: The dodge maneuver is declared during the weapons phase. + Note: This ability is only used for BattleMeks. + 100 + 1 + + + + + + Gunnery/ProtoMek::Regular + Piloting/Mek::Regular + + + + Sniper (CamOps) + sniper + Range penalties are halved. + 150 + 1 + + + + + + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran + Gunnery/Aircraft::Veteran + Gunnery/BattleArmor::Veteran + Gunnery/Aerospace::Veteran + Gunnery/Spacecraft::Veteran + Gunnery/Vehicle::Veteran + + + + Terrain Master [Mountaineer] (CamOps) + tm_mountaineer + Movement in rubble or rough cost 1 less MP and elevation changes also cost 1 MP less. + The mountaineer also recieves a -1 to PSR checks for the given terrain. + 150 + 3 + + + + + + Anti-Mek::Veteran + Piloting/Ground Vehicle::Veteran + Gunnery/ProtoMek::Veteran + Piloting/Mek::Veteran + + + + Melee Master (CamOps) + melee_master + Enables the unit to do one additional kick, punch, or club attack on the same opponent. 150 6 @@ -1007,30 +1118,133 @@ - Sandblaster (CamOps) - sandblaster - A pilot with this ability gets a +4, +3, or +2 to the cluster table - at short, medium, or long/extended range, respectively, but only with a specialized weapon. + Natural Aptitude, Gunnery (aToW) + aptitude_gunnery + Roll 3d6 and take the best two for gunnery checks + 500 + 0 + + + + + + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran + Gunnery/Aircraft::Veteran + Gunnery/BattleArmor::Veteran + Gunnery/Spacecraft::Veteran + Gunnery/Aerospace::Veteran + Gunnery/Vehicle::Veteran + + + + Zweihander (CamOps) + zweihander + You can do more damage with two-handed physical attacks at the risk of damaging yourself. 100 - 3 + 6 + + + + + + Piloting/Mek::Regular + + + + Tech Specialist, Armor (Unofficial) + tech_armor_specialist + -1 to repair and maintenance checks when working with armor + 50 + 2 + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mek::Regular + Tech/Mechanic::Regular + Tech/BattleArmor::Regular + + + + Weathered (Unofficial) + weathered + A pilot with this ability does not suffer the initial -1 to hit due to weather conditions. + 100 + 2 + + + + + + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular - Multi-Tasker (CamOps) - multi_tasker - Secondary target modifiers are reduced by one. + All Weather (Unofficial) + allweather + A pilot with this ability does not suffer the PSR penalties due to weather. + 100 + 2 + + + + + + Piloting/Aerospace::Regular + Piloting/Naval::Regular + Gunnery/ProtoMek::Regular + Piloting/Ground Vehicle::Regular + Piloting/VTOL::Regular + Piloting/Aircraft::Regular + Piloting/Mek::Regular + + + + Urban Guerrilla (CamOps) + urban_guerrilla + Urban Guerrilla gives infantry a better chance of survival in urban or suburban environments (+1 to be hit) + It also keeps them from taking double damage in the open when in pavement or road terrain types. + Finally, urban guerrillas can call on local support once per scenario. + 50 + 3 + + + + + + Small Arms::Regular + + + + Melee Specialist (CamOps) + melee_specialist + Enables the unit to do 1 additional point of damage with physical attacks and applies a -1 to-hit modifier to physical attacks. + Note: This ability is only used for BattleMeks. + 100 + 3 + + + + + + Gunnery/ProtoMek::Regular + Piloting/Mek::Regular + + + + Oblique Attacker (CamOps) + oblique_attacker + The penalty for indirect fire is reduced by one and can also use indirect fire with out a spotter. 50 2 @@ -1038,19 +1252,164 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular - RangeMaster (CamOps) - range_master - Range modifiers are swapped with short range (Medium, Long, or Extreme). + Terrain Master [Swamp Beast] (CamOps) + tm_swamp_beast + Movement in mud or swamp costs 1 less MP. + Using run/flank MP while in mud/swamp gives +1 BTH when being attacked + 150 + 3 + + + + + + Anti-Mek::Veteran + Piloting/Ground Vehicle::Veteran + Gunnery/ProtoMek::Veteran + Piloting/Mek::Veteran + + + + Gunnery Specialization (aToW) + specialist + A pilot who specializes in a type of weapon receives a -1 to-hit modifier when using weapons of that type and a + +1 to-hit modifier when using other types of weapons. + 50 + 4 + + + + + + Gunnery/Mek::Green + Gunnery/ProtoMek::Green + Gunnery/Aircraft::Green + Gunnery/BattleArmor::Green + Gunnery/Spacecraft::Green + Gunnery/Aerospace::Green + Gunnery/Vehicle::Green + + + + Shaky Stick (CamOps) + shaky_stick + Ground units have a harder time hitting this airborne target. Attacking units get a +1 tohit penalty + 100 + 3 + + + + + + Piloting/Aerospace::Regular + Piloting/Ground Vehicle::Regular + Piloting/Spacecraft::Regular + Piloting/VTOL::Regular + Piloting/Aircraft::Regular + + + + Hopping Jack (Unofficial) + hopping_jack + Unit only suffers a +2 to-hit penalty for jumping, rather than a +3 to-hit penalty. + 50 + 3 + + jumping_jack + + + + Gunnery/ProtoMek::Regular + Piloting/Mek::Regular + + + + Animal Mimicry (CamOps) + animal_mimic + Allows a Quad Mek or ProtoMek, or a bipedal Mek or ProtoMek with the animalistic quirk, the following benefits. + -1 MP per hex of Woods and Jungle terrain. + In addition quads gain a -1 to any quad related PSR check. + NOTE: The quirk should be used only on bipedal units that have an animal like appearance. + 50 + 1 + + + + + + Gunnery/ProtoMek::Regular + Piloting/Mek::Regular + + + + Cross-Country (CamOps) + cross_country + Allows a unit to use terrain normally restricted to said unit at the cost of increased MP. + 100 + 1 + + + + + + Piloting/Ground Vehicle::Regular + + + + Natural Aptitude, Piloting (aToW) + aptitude_piloting + Roll 3d6 and take the best two for piloting checks + 500 + 1 + + + + + + Piloting/Aerospace::Veteran + Piloting/Naval::Veteran + Piloting/Ground Vehicle::Veteran + Piloting/Spacecraft::Veteran + Piloting/VTOL::Veteran + Piloting/Aircraft::Veteran + Piloting/Mek::Veteran + + + + Cluster Master (Unofficial) + cluster_master + A pilot with this ability gets a +2 to the cluster hit table + 50 + 6 + cluster_hitter + + cluster_hitter + + + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran + Gunnery/Aircraft::Veteran + Gunnery/Aerospace::Veteran + Gunnery/Spacecraft::Veteran + Gunnery/BattleArmor::Veteran + Gunnery/Vehicle::Veteran + + + + Sandblaster (CamOps) + sandblaster + A pilot with this ability gets a +4, +3, or +2 to the cluster table + at short, medium, or long/extended range, respectively, but only with a specialized weapon. 100 3 @@ -1058,19 +1417,19 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular - Hot Dog (CamOps) - hot_dog - Reduce heat-related target rolls (e.g ammo, damage, shutdown) by 1. + Multi-Tasker (CamOps) + multi_tasker + Secondary target modifiers are reduced by one. 50 2 @@ -1078,25 +1437,27 @@ - Piloting/Aerospace::Regular - Piloting/Mek::Regular + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular + Gunnery/Aircraft::Regular + Gunnery/BattleArmor::Regular + Gunnery/Aerospace::Regular + Gunnery/Spacecraft::Regular + Gunnery/Vehicle::Regular - Dodge (MaxTech) - dodge_maneuver - Enables the unit to make a dodge maneuver instead of a physical attack. - This maneuver adds +2 to the BTH to physical attacks against the unit. - NOTE: The dodge maneuver is declared during the weapons phase. - Note: This ability is only used for BattleMeks. - 100 - 1 + Hot Dog (CamOps) + hot_dog + Reduce heat-related target rolls (e.g ammo, damage, shutdown) by 1. + 50 + 2 - Gunnery/ProtoMek::Regular + Piloting/Aerospace::Regular Piloting/Mek::Regular @@ -1112,17 +1473,17 @@ - Piloting/Mek::Regular Piloting/Aircraft::Regular + Gunnery/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular - Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular + Gunnery/Aerospace::Regular Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular + Anti-Mek::Regular Gunnery/ProtoMek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular @@ -1130,24 +1491,19 @@ - Sniper (CamOps) - sniper - Range penalties are halved. - 150 - 1 + Pain Resistance (MaxTech) + pain_resistance + When making consciousness rolls, +1 is added to all rolls. +Also, +damage received from ammo explosions is reduced to 1. +Note: This ability is only used for BattleMeks. + 50 + 2 - - Gunnery/Aircraft::Veteran - Gunnery/BattleArmor::Veteran - Gunnery/Aerospace::Veteran - Gunnery/Spacecraft::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Mek::Veteran - Gunnery/Vehicle::Veteran - Maneuvering Ace (CamOps) @@ -1166,29 +1522,48 @@ Piloting/Aerospace::Regular Piloting/Naval::Regular Piloting/Ground Vehicle::Regular + Gunnery/ProtoMek::Regular Piloting/Spacecraft::Regular Piloting/VTOL::Regular - Gunnery/ProtoMek::Regular Piloting/Aircraft::Regular Piloting/Mek::Regular - Terrain Master [Mountaineer] (CamOps) - tm_mountaineer - Movement in rubble or rough cost 1 less MP and elevation changes also cost 1 MP less. - The mountaineer also recieves a -1 to PSR checks for the given terrain. - 150 - 3 + Blind Fighter (Unofficial) + blind_fighter + A pilot with this ability does not suffer the initial -1 to hit due to darkness. + 100 + 2 - Piloting/Ground Vehicle::Veteran - Gunnery/ProtoMek::Veteran - Piloting/Mek::Veteran - Anti-Mek::Veteran + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular + Gunnery/Aircraft::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular + Gunnery/Vehicle::Regular + + + + Mr/Ms Fix-it (unofficial) + tech_fixer + Ignore first point of penalty for repair and maintenance on low quality equipment + 50 + 2 + + + + + + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mek::Regular + Tech/Mechanic::Regular + Tech/BattleArmor::Regular @@ -1202,13 +1577,13 @@ - Gunnery/BattleArmor::Regular + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular + Gunnery/BattleArmor::Regular Gunnery/Spacecraft::Regular Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular Gunnery/Vehicle::Regular - Gunnery/Mek::Regular Artillery::Regular @@ -1223,17 +1598,17 @@ - Piloting/Mek::Regular Piloting/Aircraft::Regular + Gunnery/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular - Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular + Gunnery/Aerospace::Regular Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular + Anti-Mek::Regular Gunnery/ProtoMek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular @@ -1253,25 +1628,66 @@ + Anti-Mek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/ProtoMek::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran - Melee Master (CamOps) - melee_master - Enables the unit to do one additional kick, punch, or club attack on the same opponent. - 150 - 6 + Maintainer (Unofficial) + tech_maintainer + The tech is slow and methodical at their work. They take a +1 penalty to their repair + rolls, but are better at maintaining units than most, with a -1 bonus. + + 100 + 1 - Gunnery/ProtoMek::Regular - Piloting/Mek::Regular + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mek::Regular + Tech/Mechanic::Regular + Tech/BattleArmor::Regular + + + + Engineer (unofficial) + tech_engineer + -2 on refit rolls + 100 + 2 + + + + + + Tech/Vessel::Veteran + Tech/Aero::Veteran + Tech/Mek::Veteran + Tech/Mechanic::Veteran + Tech/BattleArmor::Veteran + + + + Tech Specialist, Weapon (Unofficial) + tech_weapon_specialist + -1 to repair and maintenance checks when working with weapons + 50 + 2 + + + + + + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mek::Regular + Tech/Mechanic::Regular + Tech/BattleArmor::Regular @@ -1286,17 +1702,17 @@ Piloting/Aircraft::Regular - Piloting/Mek::Regular + Gunnery/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular - Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular + Gunnery/Aerospace::Regular Gunnery/BattleArmor::Regular + Anti-Mek::Regular Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular @@ -1314,17 +1730,17 @@ Piloting/Aircraft::Regular - Piloting/Mek::Regular + Gunnery/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular - Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular + Gunnery/Aerospace::Regular Gunnery/BattleArmor::Regular + Anti-Mek::Regular Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular @@ -1343,17 +1759,17 @@ - Piloting/Mek::Veteran Piloting/Aircraft::Veteran + Gunnery/Mek::Veteran Gunnery/Spacecraft::Veteran Piloting/Spacecraft::Veteran + Piloting/Mek::Veteran Piloting/VTOL::Veteran Piloting/Naval::Veteran - Gunnery/Aerospace::Veteran Piloting/Aerospace::Veteran - Anti-Mek::Veteran + Gunnery/Aerospace::Veteran Gunnery/BattleArmor::Veteran - Gunnery/Mek::Veteran + Anti-Mek::Veteran Gunnery/ProtoMek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/Aircraft::Veteran @@ -1361,148 +1777,60 @@ - Natural Aptitude, Gunnery (aToW) - aptitude_gunnery - Roll 3d6 and take the best two for gunnery checks - 500 - 0 - - - - - - Gunnery/BattleArmor::Veteran - Gunnery/Aircraft::Veteran - Gunnery/Spacecraft::Veteran - Gunnery/Aerospace::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Vehicle::Veteran - Gunnery/Mek::Veteran - - - - Zweihander (CamOps) - zweihander - You can do more damage with two-handed physical attacks at the risk of damaging yourself. + Sensor Geek (Unofficial) + sensor_geek + A pilot with this ability gets a -2 bonus on sensor checks. + Note that this is really only a bonus, if inclusive sensor ranges are in use. 100 - 6 - - - - - - Piloting/Mek::Regular - - - - Urban Guerrilla (CamOps) - urban_guerrilla - Urban Guerrilla gives infantry a better chance of survival in urban or suburban environments (+1 to be hit) - It also keeps them from taking double damage in the open when in pavement or road terrain types. - Finally, urban guerrillas can call on local support once per scenario. - 50 - 3 + 2 - Small Arms::Regular + Piloting/Aerospace::Green + Piloting/Naval::Green + Gunnery/ProtoMek::Green + Piloting/Ground Vehicle::Green + Piloting/VTOL::Green + Gunnery/BattleArmor::Green + Piloting/Aircraft::Green + Piloting/Mek::Green - Melee Specialist (CamOps) - melee_specialist - Enables the unit to do 1 additional point of damage with physical attacks and applies a -1 to-hit modifier to physical attacks. - Note: This ability is only used for BattleMeks. + Cluster Hitter (CamOps) + cluster_hitter + A pilot with this ability gets a +1 to the cluster hit table 100 3 - + oblique_attacker::sandblaster + Gunnery/Mek::Regular Gunnery/ProtoMek::Regular - Piloting/Mek::Regular - - - - Oblique Attacker (CamOps) - oblique_attacker - The penalty for indirect fire is reduced by one and can also use indirect fire with out a spotter. - 50 - 2 - - - - - Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular Gunnery/Vehicle::Regular - Terrain Master [Swamp Beast] (CamOps) - tm_swamp_beast - Movement in mud or swamp costs 1 less MP. - Using run/flank MP while in mud/swamp gives +1 BTH when being attacked - 150 - 3 - - - - - - Piloting/Ground Vehicle::Veteran - Gunnery/ProtoMek::Veteran - Piloting/Mek::Veteran - Anti-Mek::Veteran - - - - Gunnery Specialization (aToW) - specialist - A pilot who specializes in a type of weapon receives a -1 to-hit modifier when using weapons of that type and a - +1 to-hit modifier when using other types of weapons. + Some Like It Hot (Unofficial) + some_like_it_hot + A pilot with this ability does not suffer the initial -1 to hit due to heat. 50 - 4 + 2 - Gunnery/BattleArmor::Green - Gunnery/Aircraft::Green - Gunnery/Spacecraft::Green - Gunnery/Aerospace::Green - Gunnery/ProtoMek::Green - Gunnery/Vehicle::Green - Gunnery/Mek::Green - - - - Cluster Hitter (CamOps) - cluster_hitter - A pilot with this ability gets a +1 to the cluster hit table - 100 - 3 - - oblique_attacker::sandblaster - - - - Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular - Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular Gunnery/Mek::Regular - Gunnery/Vehicle::Regular + Gunnery/Aerospace::Regular @@ -1538,41 +1866,5 @@ Piloting/Mek::Veteran - - Shaky Stick (CamOps) - shaky_stick - Ground units have a harder time hitting this airborne target. Attacking units get a +1 tohit penalty - 100 - 3 - - - - - - Piloting/Aerospace::Regular - Piloting/Ground Vehicle::Regular - Piloting/Spacecraft::Regular - Piloting/VTOL::Regular - Piloting/Aircraft::Regular - - - - Animal Mimicry (CamOps) - animal_mimic - Allows a Quad Mek or ProtoMek, or a bipedal Mek or ProtoMek with the animalistic quirk, the following benefits. - -1 MP per hex of Woods and Jungle terrain. - In addition quads gain a -1 to any quad related PSR check. - NOTE: The quirk should be used only on bipedal units that have an animal like appearance. - 50 - 1 - - - - - - Gunnery/ProtoMek::Regular - Piloting/Mek::Regular - - diff --git a/MekHQ/mmconf/campaignPresets/Tabula Rasa.xml b/MekHQ/mmconf/campaignPresets/Tabula Rasa.xml deleted file mode 100644 index 63341e1fcac..00000000000 --- a/MekHQ/mmconf/campaignPresets/Tabula Rasa.xml +++ /dev/null @@ -1,1540 +0,0 @@ - - - 5 - Tabula Rasa - This preset is a blank slate, with most options disabled, giving you the freedom to customize it exactly to your needs. - 3151-01-01 - MERC - - SSLDF - - 2 - false - - 0 - false - true - true - true - true - true - false - false - false - true - - - ARMOUR - 0 - 0 - 4 - 4 - 4 - - - AMMUNITION - 0 - 0 - 4 - 4 - 4 - - - WEAPON - 0 - 0 - 4 - 4 - 4 - - - GENERAL_LOCATION - 0 - 0 - 4 - 4 - 4 - - - ENGINE - 0 - 0 - 4 - 4 - 4 - - - GYRO - 0 - 0 - 4 - 4 - 4 - - - ACTUATOR - 0 - 0 - 4 - 4 - 4 - - - ELECTRONICS - 0 - 0 - 4 - 4 - 4 - - - GENERAL - 0 - 0 - 4 - 4 - 4 - - - POD_SPACE - 0 - 0 - 4 - 4 - 4 - - - true - CAMPAIGN_OPS - false - false - false - false - 1.0 - 0 - 0 - 0 - 25 - 0 - 0 - 0 - 5 - 7 - 1 - 0 - 0 - 1 - 1 - 3 - 5 - 100 - 10 - true - false - true - true - false - false - true - false - false - 1 - Automatic Success - false - 3 - 0 - 0 - 2 - 1 - 2 - 1 - 2 - false - NEUTRAL - true - false - 4 - false - 2 - 5.0 - 0.0 - 0.0 - 0.0 - true - false - false - false - 0 - 0 - false - 4 - 10 - false - 7 - -1 - true - false - false - false - false - false - 0 - false - false - false - false - false - false - false - false - false - false - true - false - false - false - false - false - false - false - false - YEARS - false - MONTHS_YEARS - false - false - true - false - false - false - false - false - 1 - 15 - 1 - false - false - 25 - NONE - PRISONER - true - false - false - false - false - true - true - true - false - 1.5 - 1.28 - - 0.6 - 0.6 - 12.8 - 3.2 - 1.0 - 6.4 - 0.5 - 1.6 - - 1500 CSB,2250 CSB,900 CSB,900 CSB,900 CSB,900 CSB,900 CSB,1500 CSB,900 CSB,960 CSB,960 CSB,750 CSB,1000 CSB,1000 CSB,1000 CSB,1000 CSB,800 CSB,800 CSB,800 CSB,800 CSB,400 CSB,1500 CSB,400 CSB,500 CSB,500 CSB,500 CSB,500 CSB,0 CSB,0 CSB - BOTH - false - false - true - false - 5 - true - true - true - true - true - true - true - true - true - true - true - true - - false - 60 - - false - false - false - 45 - 0.6 - false - false - - false - false - false - false - false - 3 - MONTHLY - false - false - true - true - true - false - 36 - 3 - false - 3 - false - true - true - true - true - true - true - true - true - true - false - 3 - 3 - 12 - true - 10 - true - 10 - 5 - true - false - 0 - false - 1 - false - 150 - true - 13 - SPOUSE - false - false - true - false - true - false - true - 4 - 10 - false - - 500 - 55 - 20 - 20 - 160 - 30 - 30 - 10 - 55 - 5 - 5 - 10 - 100 - - NONE - false - false - true - 10 - 5000 - 14 - 20 - true - true - false - - 50 - 10 - 10 - 30 - - NONE - true - true - true - false - 900 - true - false - true - 50 - MOTHERS - false - false - false - false - false - 3 - false - NONE - true - false - true - 500 - 2000 - false - 3 - 5 - true - true - true - true - false - false - 14 - 1.0 - true - 1000 - 10000 - false - 10000 - true - NONE - - true - false - true - false - false - true - false - - true - true - false - 2.0E-5 - 5.4757,-7.0,0.0709 - 2.4641,-7.0,0.0752 - - 5155.0 - 491.8 - 14.7 - 176.1 - 613.1 - 2196.5 - 1119.0 - 100.1 - 249.5 - 27.5 - 14504.0 - - - 3788.0 - 302.5 - 11.8 - 80.0 - 500.0 - 1421.0 - 670.0 - 38.8 - 140.2 - 20.4 - 12870.0 - - false - false - false - false - false - false - false - false - false - false - false - false - false - false - false - false - ANNUAL - false - 1.0 - 1.0 - 1.0 - 1.0 - 1.0 - 1.0 - 0.4,0.45,0.48,0.5,0.55,0.65 - 0.2 - 0.0 - 0.5 - false - false - false - 30 - Disabled - true - - 4 - 4 - 11 - 10 - 6 - 11 - 3 - 8 - - 0.3 - false - NONE - true - 30 - 0 - false - true - NONE - 800 - false - true - 100 - 0 - false - Xotl,Total Warfare - false - REGULAR - 95,100,95,0,95,25 - false - false - false - true - true - true - false - false - false - 5 - 2 - 1 - false - false - false - false - true - true - 0,0,0,0 - false - false - false - false - true - 3 - 1 - false - true - 500000 - 10 - false - false - false - false - false - 5 - 5 - 25 - 1 - 3 - 25 - 25 - true - -1,0,1,2,4,8 - 0,0,0,0,0,0 - -1,0,1,2,4,8 - false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false - - - 0 - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - -10,-10,-10,-10,-10 - -10,-10,-10,-10,-10 - false - true - 10 - -10 - -10 - 0 - -2 - 0 - -10 - - - - Piloting/Mek - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Gunnery/Mek - 7 - false - 2 - 3 - 4 - 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 - - - Piloting/Aerospace - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Gunnery/Aerospace - 7 - false - 2 - 3 - 4 - 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 - - - Piloting/Ground Vehicle - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Piloting/VTOL - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Piloting/Naval - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Gunnery/Vehicle - 7 - false - 2 - 3 - 4 - 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 - - - Piloting/Aircraft - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Gunnery/Aircraft - 7 - false - 2 - 3 - 4 - 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 - - - Piloting/Spacecraft - 8 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Gunnery/Spacecraft - 7 - false - 2 - 3 - 4 - 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 - - - Artillery - 7 - false - 2 - 3 - 4 - 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 - - - Gunnery/BattleArmor - 7 - false - 2 - 3 - 4 - 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 - - - Gunnery/ProtoMek - 7 - false - 2 - 3 - 4 - 5 - 16,8,8,8,8,8,8,8,-1,-1,-1 - - - Small Arms - 7 - false - 2 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Anti-Mek - 8 - false - 2 - 3 - 4 - 5 - 12,6,6,6,6,6,6,6,6,-1,-1 - - - Tech/Mek - 10 - false - 1 - 3 - 4 - 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 - - - Tech/Mechanic - 10 - false - 1 - 3 - 4 - 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 - - - Tech/Aero - 10 - false - 1 - 3 - 4 - 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 - - - Tech/BattleArmor - 10 - false - 1 - 3 - 4 - 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 - - - Tech/Vessel - 10 - false - 1 - 3 - 4 - 5 - 12,6,6,6,6,6,-1,-1,-1,-1,-1 - - - Astech - 10 - false - 1 - 3 - 4 - 5 - 12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - - - Doctor - 11 - false - 1 - 3 - 4 - 5 - 16,8,8,8,8,8,-1,-1,-1,-1,-1 - - - MedTech - 11 - false - 1 - 3 - 4 - 5 - 16,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - - - Hyperspace Navigation - 8 - false - 1 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,-1,-1 - - - Administration - 10 - false - 1 - 3 - 4 - 5 - 8,4,4,4,4,4,-1,-1,-1,-1,-1 - - - Tactics - 0 - true - 1 - 3 - 4 - 5 - 12,6,6,6,6,6,6,6,6,6,6 - - - Strategy - 0 - true - 1 - 3 - 4 - 5 - 12,6,6,6,6,6,6,6,6,6,6 - - - Negotiation - 10 - false - 1 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,4,4 - - - Leadership - 0 - true - 1 - 3 - 4 - 5 - 12,6,6,6,6,6,6,6,6,6,6 - - - Scrounge - 10 - false - 1 - 3 - 4 - 5 - 8,4,4,4,4,4,4,4,4,4,4 - - - - - Terrain Master [Nightwalker] (CamOps) - tm_nightwalker - Nightwalker ignores all darkness move penalties when using Walk / Cruise movement but does not affect gunnery modifiers for lighting conditions. - 0 - 1 - - - - - - Piloting/Ground Vehicle::Veteran - Gunnery/ProtoMek::Veteran - Anti-Mek::Veteran - Piloting/Mek::Veteran - - - - Weapon Specialist (CamOps) - weapon_specialist - A pilot who specializes in a particular weapon receives a -2 to hit modifier on all attacks with that weapon. - 0 - 2 - - - - - - Gunnery/BattleArmor::Veteran - Gunnery/Aircraft::Veteran - Gunnery/Spacecraft::Veteran - Gunnery/Aerospace::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Vehicle::Veteran - Gunnery/Mek::Veteran - - - - Foot Cavalry (CamOps) - foot_cav - Foot Infantry gain 1 extra MP and allows for foot infantry to move and shoot when using two support weapons. - 0 - 3 - - - - - - Small Arms::Regular - - - - Cross-Country (CamOps) - cross_country - Allows a unit to use terrain normally restricted to said unit at the cost of increased MP. - 0 - 1 - - - - - - Piloting/Ground Vehicle::Regular - - - - Blood Stalker (CamOps) - blood_stalker - Unit has -1 bonus against designated target, +2 penalty against all others; lasts until target retreats, can only be used once per battle. - 0 - 1 - - - - - - Gunnery/BattleArmor::Regular - Gunnery/Aircraft::Regular - Gunnery/Spacecraft::Regular - Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular - Gunnery/Vehicle::Regular - Gunnery/Mek::Regular - - - - Jumping Jack (CamOps) - jumping_jack - Unit only suffers a +1 to-hit penalty for jumping, rather than a +3 to-hit penalty. - 0 - 6 - - - - - - Gunnery/ProtoMek::Regular - Piloting/Mek::Regular - - - - Sandblaster (CamOps) - sandblaster - A pilot with this ability gets a +4, +3, or +2 to the cluster table - at short, medium, or long/extended range, respectively, but only with a specialized weapon. - 0 - 3 - - - - - - Gunnery/BattleArmor::Regular - Gunnery/Aircraft::Regular - Gunnery/Spacecraft::Regular - Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular - Gunnery/Vehicle::Regular - Gunnery/Mek::Regular - - - - Multi-Tasker (CamOps) - multi_tasker - Secondary target modifiers are reduced by one. - 0 - 2 - - - - - - Gunnery/BattleArmor::Regular - Gunnery/Aircraft::Regular - Gunnery/Spacecraft::Regular - Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular - Gunnery/Vehicle::Regular - Gunnery/Mek::Regular - - - - RangeMaster (CamOps) - range_master - Range modifiers are swapped with short range (Medium, Long, or Extreme). - 0 - 3 - - - - - - Gunnery/BattleArmor::Regular - Gunnery/Aircraft::Regular - Gunnery/Spacecraft::Regular - Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular - Gunnery/Vehicle::Regular - Gunnery/Mek::Regular - - - - Hot Dog (CamOps) - hot_dog - Reduce heat-related target rolls (e.g ammo, damage, shutdown) by 1. - 0 - 2 - - - - - - Piloting/Aerospace::Regular - Piloting/Mek::Regular - - - - Dodge (MaxTech) - dodge_maneuver - Enables the unit to make a dodge maneuver instead of a physical attack. - This maneuver adds +2 to the BTH to physical attacks against the unit. - NOTE: The dodge maneuver is declared during the weapons phase. - Note: This ability is only used for BattleMeks. - 0 - 1 - - - - - - Gunnery/ProtoMek::Regular - Piloting/Mek::Regular - - - - Environmental Specialist (CamOps) - env_specialist - Environmental specialists are adept in certain planetary conditions. - This gives the unit bonuses to PSR and movement penalties (halves MP / PSR) as well to hit rolls (-1). - 0 - 1 - - - - - - Piloting/Mek::Regular - Piloting/Aircraft::Regular - Gunnery/Spacecraft::Regular - Piloting/Spacecraft::Regular - Piloting/VTOL::Regular - Piloting/Naval::Regular - Gunnery/Aerospace::Regular - Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular - Piloting/Ground Vehicle::Regular - Gunnery/Aircraft::Regular - Gunnery/Vehicle::Regular - - - - Sniper (CamOps) - sniper - Range penalties are halved. - 0 - 1 - - - - - - Gunnery/BattleArmor::Veteran - Gunnery/Aircraft::Veteran - Gunnery/Spacecraft::Veteran - Gunnery/Aerospace::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Vehicle::Veteran - Gunnery/Mek::Veteran - - - - Maneuvering Ace (CamOps) - maneuvering_ace - Enables the unit to move laterally like a Quad. - Quads can move laterally for 1 less MP. - Aerospace units can perform maneuvers for 1 less thrust point. - Units also receive a -1 BTH to rolls against skidding, sideslipping, and going out of control. - 0 - 3 - - - - - - Piloting/Aerospace::Regular - Piloting/Naval::Regular - Piloting/Ground Vehicle::Regular - Piloting/Spacecraft::Regular - Piloting/VTOL::Regular - Gunnery/ProtoMek::Regular - Piloting/Aircraft::Regular - Piloting/Mek::Regular - - - - Terrain Master [Mountaineer] (CamOps) - tm_mountaineer - Movement in rubble or rough cost 1 less MP and elevation changes also cost 1 MP less. - The mountaineer also recieves a -1 to PSR checks for the given terrain. - 0 - 3 - - - - - - Piloting/Ground Vehicle::Veteran - Gunnery/ProtoMek::Veteran - Anti-Mek::Veteran - Piloting/Mek::Veteran - - - - Oblique Artilleryman (CamOps) - oblique_artillery - Reduces scatter distance by two hexes. - 0 - 2 - - - - - - Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular - Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular - Gunnery/Vehicle::Regular - Artillery::Regular - - - - Eagle's Eyes (CamOps) - eagle_eyes - Acts as an active probe or extends range of existing active probe. Also makes avoiding minefields easier - 0 - 2 - - - - - - Piloting/Mek::Regular - Piloting/Aircraft::Regular - Gunnery/Spacecraft::Regular - Piloting/Spacecraft::Regular - Piloting/VTOL::Regular - Piloting/Naval::Regular - Gunnery/Aerospace::Regular - Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular - Piloting/Ground Vehicle::Regular - Gunnery/Aircraft::Regular - Gunnery/Vehicle::Regular - - - - Terrain Master [Forest Ranger] (CamOps) - tm_forest_ranger - Movement in woods or jungle costs 1 less MP. - Using walk/cruise MP while in woods/jungle gives +1 BTH when being attacked. - Additionally, you gain -1 to PSR checks in Jungle terrain. - 0 - 3 - - - - - - Piloting/Ground Vehicle::Veteran - Gunnery/ProtoMek::Veteran - Anti-Mek::Veteran - Piloting/Mek::Veteran - - - - Melee Master (CamOps) - melee_master - Enables the unit to do one additional kick, punch, or club attack on the same opponent. - 0 - 6 - - - - - - Gunnery/ProtoMek::Regular - Piloting/Mek::Regular - - - - Human TRO (CamOps) - human_tro - The Human TRO has studied a given type of battlefield unit giving the pilot a bonus when rolling for critical hit. This applies a +1 when determining criticals for a specific unit type (Mek, Aero, etc.) - 0 - 2 - - - - - - Piloting/Mek::Regular - Piloting/Aircraft::Regular - Gunnery/Spacecraft::Regular - Piloting/Spacecraft::Regular - Piloting/VTOL::Regular - Piloting/Naval::Regular - Gunnery/Aerospace::Regular - Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular - Piloting/Ground Vehicle::Regular - Gunnery/Aircraft::Regular - Gunnery/Vehicle::Regular - - - - Forward Observer (CamOps) - forward_observer - Improves the accuracy of your artillery. Fire Adjustment gets a -2 modifier. Spotting gets an additional -1 tohit modifier for each point of gunnery below 4. - 0 - 3 - - - - - - Piloting/Mek::Regular - Piloting/Aircraft::Regular - Gunnery/Spacecraft::Regular - Piloting/Spacecraft::Regular - Piloting/VTOL::Regular - Piloting/Naval::Regular - Gunnery/Aerospace::Regular - Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular - Piloting/Ground Vehicle::Regular - Gunnery/Aircraft::Regular - Gunnery/Vehicle::Regular - - - - Tactical Genius (CamOps) - tactical_genius - A pilot who has a Tactical Genius may reroll their initiative once per turn. - The second roll must be accepted. - Note: Only one Tactical Genius may be utilized per team. - 0 - 1 - - - - - - Piloting/Mek::Veteran - Piloting/Aircraft::Veteran - Gunnery/Spacecraft::Veteran - Piloting/Spacecraft::Veteran - Piloting/VTOL::Veteran - Piloting/Naval::Veteran - Gunnery/Aerospace::Veteran - Piloting/Aerospace::Veteran - Anti-Mek::Veteran - Gunnery/BattleArmor::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Mek::Veteran - Piloting/Ground Vehicle::Veteran - Gunnery/Aircraft::Veteran - Gunnery/Vehicle::Veteran - - - - Zweihander (CamOps) - zweihander - You can do more damage with two-handed physical attacks at the risk of damaging yourself. - 0 - 6 - - - - - - Piloting/Mek::Regular - - - - Urban Guerrilla (CamOps) - urban_guerrilla - Urban Guerrilla gives infantry a better chance of survival in urban or suburban environments (+1 to be hit) - It also keeps them from taking double damage in the open when in pavement or road terrain types. - Finally, urban guerrillas can call on local support once per scenario. - 0 - 3 - - - - - - Small Arms::Regular - - - - Melee Specialist (CamOps) - melee_specialist - Enables the unit to do 1 additional point of damage with physical attacks and applies a -1 to-hit modifier to physical attacks. - Note: This ability is only used for BattleMeks. - 0 - 3 - - - - - - Gunnery/ProtoMek::Regular - Piloting/Mek::Regular - - - - Oblique Attacker (CamOps) - oblique_attacker - The penalty for indirect fire is reduced by one and can also use indirect fire with out a spotter. - 0 - 2 - - - - - - Gunnery/BattleArmor::Regular - Gunnery/Aircraft::Regular - Gunnery/Spacecraft::Regular - Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular - Gunnery/Vehicle::Regular - Gunnery/Mek::Regular - - - - Terrain Master [Swamp Beast] (CamOps) - tm_swamp_beast - Movement in mud or swamp costs 1 less MP. - Using run/flank MP while in mud/swamp gives +1 BTH when being attacked - 0 - 3 - - - - - - Piloting/Ground Vehicle::Veteran - Gunnery/ProtoMek::Veteran - Anti-Mek::Veteran - Piloting/Mek::Veteran - - - - Gunnery Specialization (aToW) - specialist - A pilot who specializes in a type of weapon receives a -1 to-hit modifier when using weapons of that type and a - +1 to-hit modifier when using other types of weapons. - 0 - 4 - - - - - - Gunnery/BattleArmor::Green - Gunnery/Aircraft::Green - Gunnery/Spacecraft::Green - Gunnery/Aerospace::Green - Gunnery/ProtoMek::Green - Gunnery/Vehicle::Green - Gunnery/Mek::Green - - - - Cluster Hitter (CamOps) - cluster_hitter - A pilot with this ability gets a +1 to the cluster hit table - 0 - 3 - - oblique_attacker::sandblaster - - - - Gunnery/BattleArmor::Regular - Gunnery/Aircraft::Regular - Gunnery/Spacecraft::Regular - Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular - Gunnery/Vehicle::Regular - Gunnery/Mek::Regular - - - - Golden Goose (CamOps) - golden_goose - Ground attacks are easier. Strike gives -1 BTH and Bombing is -2. Also decreases scatter distance by 2 hexes - 0 - 3 - - - - - - Piloting/Aerospace::Veteran - Piloting/Spacecraft::Veteran - Piloting/VTOL::Veteran - Piloting/Aircraft::Veteran - - - - Terrain Master [Frogman] (CamOps) - tm_frogman - Movement in depth 2 or greater water costs 1 MP less. - Also grants -1 to all PSR checks (including physicals) as well as -2 to Crush Depth checks. - 0 - 3 - - - - - - Gunnery/ProtoMek::Veteran - Piloting/Mek::Veteran - - - - Shaky Stick (CamOps) - shaky_stick - Ground units have a harder time hitting this airborne target. Attacking units get a +1 tohit penalty - 0 - 3 - - - - - - Piloting/Aerospace::Regular - Piloting/Ground Vehicle::Regular - Piloting/Spacecraft::Regular - Piloting/VTOL::Regular - Piloting/Aircraft::Regular - - - - Animal Mimicry (CamOps) - animal_mimic - Allows a Quad Mek or ProtoMek, or a bipedal Mek or ProtoMek with the animalistic quirk, the following benefits. - -1 MP per hex of Woods and Jungle terrain. - In addition quads gain a -1 to any quad related PSR check. - NOTE: The quirk should be used only on bipedal units that have an animal like appearance. - 0 - 1 - - - - - - Gunnery/ProtoMek::Regular - Piloting/Mek::Regular - - - - diff --git a/MekHQ/mmconf/campaignPresets/Complete Experience.xml b/MekHQ/mmconf/campaignPresets/TheCompleteExperience.xml similarity index 82% rename from MekHQ/mmconf/campaignPresets/Complete Experience.xml rename to MekHQ/mmconf/campaignPresets/TheCompleteExperience.xml index a59809d15bd..bc958215c21 100644 --- a/MekHQ/mmconf/campaignPresets/Complete Experience.xml +++ b/MekHQ/mmconf/campaignPresets/TheCompleteExperience.xml @@ -1,17 +1,15 @@ - - 2 - Complete Experience (StratCon - Beta) + + 2 - The Complete Experience (StratCon) This preset represents the complete MekHQ experience, with nearly all options enabled and adjusted to provide a balanced, long-lasting campaign. It's perfect for long nights spent battling Princess using the StratCon ruleset but may be too much for newer players. 3151-01-01 MERC - - SSLDF - 2 false 0 false + 4 true true true @@ -94,6 +92,30 @@ 4 4 + + HEAT_SINK + 0 + 0 + 4 + 4 + 4 + + + MEK_LOCATION + 0 + 0 + 4 + 4 + 4 + + + PHYSICAL_WEAPON + 0 + 0 + 4 + 4 + 4 + POD_SPACE 0 @@ -102,6 +124,14 @@ 4 4 + + UNKNOWN_LOCATION + 0 + 0 + 4 + 4 + 4 + true CAMPAIGN_OPS @@ -117,9 +147,9 @@ 0 0 0 - 5 - 7 - 1 + 1 + 7 + 1 0 0 1 @@ -147,10 +177,10 @@ 2 1 2 - true + false NEUTRAL - true - false + false + true 4 false 2 @@ -159,24 +189,31 @@ 0.0 0.0 true - false + true false true 0 0 - false + true 4 10 true 7 -1 - true + false false true true true true 1 + 250 + 200 + 100 + 0 + 500 + 500 + 50 false false true @@ -204,7 +241,7 @@ true true false - false + true false true 1 @@ -227,14 +264,14 @@ 1.5 1.28 - 3.2 - 6.4 - 12.8 - 1.6 - 0.6 0.6 + 1.6 0.5 + 0.6 + 12.8 1.0 + 3.2 + 6.4 1500 CSB,2250 CSB,900 CSB,900 CSB,900 CSB,900 CSB,900 CSB,1500 CSB,900 CSB,960 CSB,960 CSB,750 CSB,1000 CSB,1000 CSB,1000 CSB,1000 CSB,800 CSB,800 CSB,800 CSB,800 CSB,400 CSB,1500 CSB,400 CSB,500 CSB,500 CSB,500 CSB,500 CSB,0 CSB,0 CSB BOTH @@ -275,7 +312,7 @@ 3 MONTHLY true - false + true true true true @@ -318,64 +355,64 @@ true true true - false + true true 4 10 false - 100 - 30 - 5 + 5 20 - 30 + 10 + 30 20 - 500 - 5 - 55 + 5 + 100 10 + 30 160 + 55 55 - 10 + 500 DICE_ROLL false - true + false 10 5000 14 20 true true - false + true - 10 + 30 50 + 10 10 - 30 DICE_ROLL true true true - false + true 900 true false - true + false 50 MOTHERS false - false + true true false false 3 - false + true DICE_ROLL true false - true + false 500 2000 true @@ -397,13 +434,13 @@ true EXPONENTIAL - false - true - false - false - true false + true true + true + false + false + false true true @@ -412,43 +449,43 @@ 5.4757,-7.0,0.0709 2.4641,-7.0,0.0752 + 27.5 + 14.7 491.8 14504.0 - 249.5 2196.5 - 14.7 - 27.5 100.1 - 613.1 + 249.5 + 5155.0 1119.0 + 613.1 176.1 - 5155.0 + 20.4 + 11.8 302.5 12870.0 - 140.2 1421.0 - 11.8 - 20.4 38.8 - 500.0 + 140.2 + 3788.0 670.0 + 500.0 80.0 - 3788.0 true true true true false - true - false + false + true true true - true + false true - false + true false true true @@ -472,14 +509,14 @@ Campaign Ops true - 10 - 11 - 11 - 8 - 4 4 + 8 3 + 4 + 11 6 + 10 + 11 0.3 false @@ -491,7 +528,7 @@ true ATB_MONTHLY 400 - false + true true 100 5 @@ -499,8 +536,11 @@ Xotl,Total Warfare false REGULAR + ABSTRACT_COMBAT + false + 100 95,100,95,0,95,25 - true + false true true true @@ -512,13 +552,13 @@ 5 2 1 - false - false + true + true false - false - true + true + false true - 0,0,0,0 + 0,0,0,0,0 false true true @@ -526,10 +566,7 @@ true 3 1 - true true - 500000 - 10 false false false @@ -565,27 +602,17 @@ - Piloting/Mek - 8 - false - 2 - 3 - 4 - 5 - 20,10,20,30,40,50,60,70,80,90,100 - - - Gunnery/Mek - 7 + Administration + 10 false - 2 + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Aerospace + Anti-Mek 8 false 2 @@ -595,7 +622,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/Aerospace + Artillery 7 false 2 @@ -605,28 +632,28 @@ 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Ground Vehicle - 8 + Astech + 10 false - 2 + 1 3 4 5 - 20,10,20,30,40,50,60,70,80,90,100 + 20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - Piloting/VTOL - 8 + Doctor + 11 false - 2 + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Naval - 8 + Gunnery/Aerospace + 7 false 2 3 @@ -635,7 +662,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/Vehicle + Gunnery/Aircraft 7 false 2 @@ -645,8 +672,8 @@ 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Aircraft - 8 + Gunnery/BattleArmor + 7 false 2 3 @@ -655,7 +682,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/Aircraft + Gunnery/Mek 7 false 2 @@ -665,8 +692,8 @@ 20,10,20,30,40,50,60,70,80,90,100 - Piloting/Spacecraft - 8 + Gunnery/ProtoMek + 7 false 2 3 @@ -685,7 +712,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Artillery + Gunnery/Vehicle 7 false 2 @@ -695,139 +722,139 @@ 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/BattleArmor - 7 + Hyperspace Navigation + 8 false - 2 + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Gunnery/ProtoMek - 7 - false - 2 + Leadership + 0 + true + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Small Arms - 7 + MedTech + 11 false - 2 + 1 3 4 5 - 20,10,20,30,40,50,60,70,80,90,100 + 20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - Anti-Mek - 8 + Negotiation + 10 false - 2 + 1 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/Mek - 10 + Piloting/Aerospace + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/Mechanic - 10 + Piloting/Aircraft + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/Aero - 10 + Piloting/Ground Vehicle + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/BattleArmor - 10 + Piloting/Mek + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Tech/Vessel - 10 + Piloting/Naval + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Astech - 10 + Piloting/Spacecraft + 8 false - 1 + 2 3 4 5 - 20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + 20,10,20,30,40,50,60,70,80,90,100 - Doctor - 11 + Piloting/VTOL + 8 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - MedTech - 11 + Scrounge + 10 false 1 3 4 5 - 20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + 20,10,20,30,40,50,60,70,80,90,100 - Hyperspace Navigation - 8 + Small Arms + 7 false - 1 + 2 3 4 5 20,10,20,30,40,50,60,70,80,90,100 - Administration - 10 - false + Strategy + 0 + true 1 3 4 @@ -845,9 +872,9 @@ 20,10,20,30,40,50,60,70,80,90,100 - Strategy - 0 - true + Tech/Aero + 10 + false 1 3 4 @@ -855,7 +882,7 @@ 20,10,20,30,40,50,60,70,80,90,100 - Negotiation + Tech/BattleArmor 10 false 1 @@ -865,9 +892,9 @@ 20,10,20,30,40,50,60,70,80,90,100 - Leadership - 0 - true + Tech/Mechanic + 10 + false 1 3 4 @@ -875,7 +902,17 @@ 20,10,20,30,40,50,60,70,80,90,100 - Scrounge + Tech/Mek + 10 + false + 1 + 3 + 4 + 5 + 20,10,20,30,40,50,60,70,80,90,100 + + + Tech/Vessel 10 false 1 @@ -897,10 +934,10 @@ - Piloting/Ground Vehicle::Veteran + Anti-Mek::Veteran Gunnery/ProtoMek::Veteran + Piloting/Ground Vehicle::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran @@ -914,12 +951,12 @@ + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran Gunnery/Aircraft::Veteran - Gunnery/BattleArmor::Veteran - Gunnery/Aerospace::Veteran Gunnery/Spacecraft::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Mek::Veteran + Gunnery/Aerospace::Veteran + Gunnery/BattleArmor::Veteran Gunnery/Vehicle::Veteran @@ -938,9 +975,85 @@ - Cross-Country (CamOps) - cross_country - Allows a unit to use terrain normally restricted to said unit at the cost of increased MP. + Blood Stalker (CamOps) + blood_stalker + Unit has -1 bonus against designated target, +2 penalty against all others; lasts until target retreats, can only be used once per battle. + 120 + 1 + + + + + + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular + Gunnery/Aircraft::Regular + Gunnery/Spacecraft::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular + Gunnery/Vehicle::Regular + + + + Jumping Jack (CamOps) + jumping_jack + Unit only suffers a +1 to-hit penalty for jumping, rather than a +3 to-hit penalty. + 150 + 6 + + + + + + Gunnery/ProtoMek::Regular + Piloting/Mek::Regular + + + + Tech Specialist, Internal (Unofficial) + tech_internal_specialist + -1 to repair and maintenance checks when working with internal systems + 100 + 2 + + + + + + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mechanic::Regular + Tech/Mek::Regular + Tech/BattleArmor::Regular + + + + RangeMaster (CamOps) + range_master + Range modifiers are swapped with short range (Medium, Long, or Extreme). + 100 + 3 + + + + + + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular + Gunnery/Aircraft::Regular + Gunnery/Spacecraft::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular + Gunnery/Vehicle::Regular + + + + Dodge (MaxTech) + dodge_maneuver + Enables the unit to make a dodge maneuver instead of a physical attack. + This maneuver adds +2 to the BTH to physical attacks against the unit. + NOTE: The dodge maneuver is declared during the weapons phase. + Note: This ability is only used for BattleMeks. 100 1 @@ -948,69 +1061,176 @@ - Piloting/Ground Vehicle::Regular + Gunnery/ProtoMek::Regular + Piloting/Mek::Regular - Natural Aptitude, Piloting (aToW) - aptitude_piloting - Roll 3d6 and take the best two for piloting checks - 500 + Sniper (CamOps) + sniper + Range penalties are halved. + 150 1 - Piloting/Aerospace::Veteran - Piloting/Naval::Veteran + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran + Gunnery/Aircraft::Veteran + Gunnery/Spacecraft::Veteran + Gunnery/Aerospace::Veteran + Gunnery/BattleArmor::Veteran + Gunnery/Vehicle::Veteran + + + + Terrain Master [Mountaineer] (CamOps) + tm_mountaineer + Movement in rubble or rough cost 1 less MP and elevation changes also cost 1 MP less. + The mountaineer also recieves a -1 to PSR checks for the given terrain. + 150 + 3 + + + + + + Anti-Mek::Veteran + Gunnery/ProtoMek::Veteran Piloting/Ground Vehicle::Veteran - Piloting/Spacecraft::Veteran - Piloting/VTOL::Veteran - Piloting/Aircraft::Veteran Piloting/Mek::Veteran - Blood Stalker (CamOps) - blood_stalker - Unit has -1 bonus against designated target, +2 penalty against all others; lasts until target retreats, can only be used once per battle. - 120 - 1 + Melee Master (CamOps) + melee_master + Enables the unit to do one additional kick, punch, or club attack on the same opponent. + 150 + 6 + + + + + + Gunnery/ProtoMek::Regular + Piloting/Mek::Regular + + + + Natural Aptitude, Gunnery (aToW) + aptitude_gunnery + Roll 3d6 and take the best two for gunnery checks + 500 + 0 + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran + Gunnery/Aircraft::Veteran + Gunnery/Aerospace::Veteran + Gunnery/Spacecraft::Veteran + Gunnery/BattleArmor::Veteran + Gunnery/Vehicle::Veteran + + + + Zweihander (CamOps) + zweihander + You can do more damage with two-handed physical attacks at the risk of damaging yourself. + 100 + 6 + + + + + + Piloting/Mek::Regular + + + + Tech Specialist, Armor (Unofficial) + tech_armor_specialist + -1 to repair and maintenance checks when working with armor + 50 + 2 + + + + + + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mechanic::Regular + Tech/Mek::Regular + Tech/BattleArmor::Regular + + + + Weathered (Unofficial) + weathered + A pilot with this ability does not suffer the initial -1 to hit due to weather conditions. + 100 + 2 + + + + + + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/Aerospace::Regular Gunnery/Vehicle::Regular - Jumping Jack (CamOps) - jumping_jack - Unit only suffers a +1 to-hit penalty for jumping, rather than a +3 to-hit penalty. - 150 - 6 + All Weather (Unofficial) + allweather + A pilot with this ability does not suffer the PSR penalties due to weather. + 100 + 2 + Piloting/Aerospace::Regular + Piloting/Naval::Regular + Piloting/Ground Vehicle::Regular Gunnery/ProtoMek::Regular + Piloting/VTOL::Regular + Piloting/Aircraft::Regular Piloting/Mek::Regular - Sandblaster (CamOps) - sandblaster - A pilot with this ability gets a +4, +3, or +2 to the cluster table - at short, medium, or long/extended range, respectively, but only with a specialized weapon. + Urban Guerrilla (CamOps) + urban_guerrilla + Urban Guerrilla gives infantry a better chance of survival in urban or suburban environments (+1 to be hit) + It also keeps them from taking double damage in the open when in pavement or road terrain types. + Finally, urban guerrillas can call on local support once per scenario. + 50 + 3 + + + + + + Small Arms::Regular + + + + Melee Specialist (CamOps) + melee_specialist + Enables the unit to do 1 additional point of damage with physical attacks and applies a -1 to-hit modifier to physical attacks. + Note: This ability is only used for BattleMeks. 100 3 @@ -1018,19 +1238,14 @@ - Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular - Gunnery/Spacecraft::Regular Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular - Gunnery/Vehicle::Regular + Piloting/Mek::Regular - Multi-Tasker (CamOps) - multi_tasker - Secondary target modifiers are reduced by one. + Oblique Attacker (CamOps) + oblique_attacker + The penalty for indirect fire is reduced by one and can also use indirect fire with out a spotter. 50 2 @@ -1038,19 +1253,58 @@ + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular - RangeMaster (CamOps) - range_master - Range modifiers are swapped with short range (Medium, Long, or Extreme). + Terrain Master [Swamp Beast] (CamOps) + tm_swamp_beast + Movement in mud or swamp costs 1 less MP. + Using run/flank MP while in mud/swamp gives +1 BTH when being attacked + 150 + 3 + + + + + + Anti-Mek::Veteran + Gunnery/ProtoMek::Veteran + Piloting/Ground Vehicle::Veteran + Piloting/Mek::Veteran + + + + Gunnery Specialization (aToW) + specialist + A pilot who specializes in a type of weapon receives a -1 to-hit modifier when using weapons of that type and a + +1 to-hit modifier when using other types of weapons. + 50 + 4 + + + + + + Gunnery/Mek::Green + Gunnery/ProtoMek::Green + Gunnery/Aircraft::Green + Gunnery/Aerospace::Green + Gunnery/Spacecraft::Green + Gunnery/BattleArmor::Green + Gunnery/Vehicle::Green + + + + Shaky Stick (CamOps) + shaky_stick + Ground units have a harder time hitting this airborne target. Attacking units get a +1 tohit penalty 100 3 @@ -1058,37 +1312,50 @@ - Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular - Gunnery/Spacecraft::Regular + Piloting/Aerospace::Regular + Piloting/Ground Vehicle::Regular + Piloting/Spacecraft::Regular + Piloting/VTOL::Regular + Piloting/Aircraft::Regular + + + + Hopping Jack (Unofficial) + hopping_jack + Unit only suffers a +2 to-hit penalty for jumping, rather than a +3 to-hit penalty. + 50 + 3 + + jumping_jack + + + Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular - Gunnery/Vehicle::Regular + Piloting/Mek::Regular - Hot Dog (CamOps) - hot_dog - Reduce heat-related target rolls (e.g ammo, damage, shutdown) by 1. + Animal Mimicry (CamOps) + animal_mimic + Allows a Quad Mek or ProtoMek, or a bipedal Mek or ProtoMek with the animalistic quirk, the following benefits. + -1 MP per hex of Woods and Jungle terrain. + In addition quads gain a -1 to any quad related PSR check. + NOTE: The quirk should be used only on bipedal units that have an animal like appearance. 50 - 2 + 1 - Piloting/Aerospace::Regular + Gunnery/ProtoMek::Regular Piloting/Mek::Regular - Dodge (MaxTech) - dodge_maneuver - Enables the unit to make a dodge maneuver instead of a physical attack. - This maneuver adds +2 to the BTH to physical attacks against the unit. - NOTE: The dodge maneuver is declared during the weapons phase. - Note: This ability is only used for BattleMeks. + Cross-Country (CamOps) + cross_country + Allows a unit to use terrain normally restricted to said unit at the cost of increased MP. 100 1 @@ -1096,7 +1363,102 @@ + Piloting/Ground Vehicle::Regular + + + + Natural Aptitude, Piloting (aToW) + aptitude_piloting + Roll 3d6 and take the best two for piloting checks + 500 + 1 + + + + + + Piloting/Aerospace::Veteran + Piloting/Naval::Veteran + Piloting/Ground Vehicle::Veteran + Piloting/Spacecraft::Veteran + Piloting/VTOL::Veteran + Piloting/Aircraft::Veteran + Piloting/Mek::Veteran + + + + Cluster Master (Unofficial) + cluster_master + A pilot with this ability gets a +2 to the cluster hit table + 50 + 6 + cluster_hitter + + cluster_hitter + + + Gunnery/Mek::Veteran + Gunnery/ProtoMek::Veteran + Gunnery/Aircraft::Veteran + Gunnery/BattleArmor::Veteran + Gunnery/Spacecraft::Veteran + Gunnery/Aerospace::Veteran + Gunnery/Vehicle::Veteran + + + + Sandblaster (CamOps) + sandblaster + A pilot with this ability gets a +4, +3, or +2 to the cluster table + at short, medium, or long/extended range, respectively, but only with a specialized weapon. + 100 + 3 + + + + + + Gunnery/Mek::Regular Gunnery/ProtoMek::Regular + Gunnery/Aircraft::Regular + Gunnery/Spacecraft::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular + Gunnery/Vehicle::Regular + + + + Multi-Tasker (CamOps) + multi_tasker + Secondary target modifiers are reduced by one. + 50 + 2 + + + + + + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular + Gunnery/Aircraft::Regular + Gunnery/Spacecraft::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular + Gunnery/Vehicle::Regular + + + + Hot Dog (CamOps) + hot_dog + Reduce heat-related target rolls (e.g ammo, damage, shutdown) by 1. + 50 + 2 + + + + + + Piloting/Aerospace::Regular Piloting/Mek::Regular @@ -1112,42 +1474,37 @@ - Piloting/Mek::Regular + Gunnery/Mek::Regular Piloting/Aircraft::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular + Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular Gunnery/ProtoMek::Regular + Anti-Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular - Sniper (CamOps) - sniper - Range penalties are halved. - 150 - 1 + Pain Resistance (MaxTech) + pain_resistance + When making consciousness rolls, +1 is added to all rolls. +Also, +damage received from ammo explosions is reduced to 1. +Note: This ability is only used for BattleMeks. + 50 + 2 - - Gunnery/Aircraft::Veteran - Gunnery/BattleArmor::Veteran - Gunnery/Aerospace::Veteran - Gunnery/Spacecraft::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Mek::Veteran - Gunnery/Vehicle::Veteran - Maneuvering Ace (CamOps) @@ -1165,30 +1522,49 @@ Piloting/Aerospace::Regular Piloting/Naval::Regular + Gunnery/ProtoMek::Regular Piloting/Ground Vehicle::Regular Piloting/Spacecraft::Regular Piloting/VTOL::Regular + Piloting/Aircraft::Regular + Piloting/Mek::Regular + + + + Blind Fighter (Unofficial) + blind_fighter + A pilot with this ability does not suffer the initial -1 to hit due to darkness. + 100 + 2 + + + + + + Gunnery/Mek::Regular Gunnery/ProtoMek::Regular - Piloting/Aircraft::Regular - Piloting/Mek::Regular + Gunnery/Aircraft::Regular + Gunnery/BattleArmor::Regular + Gunnery/Aerospace::Regular + Gunnery/Vehicle::Regular - Terrain Master [Mountaineer] (CamOps) - tm_mountaineer - Movement in rubble or rough cost 1 less MP and elevation changes also cost 1 MP less. - The mountaineer also recieves a -1 to PSR checks for the given terrain. - 150 - 3 + Mr/Ms Fix-it (unofficial) + tech_fixer + Ignore first point of penalty for repair and maintenance on low quality equipment + 50 + 2 - Piloting/Ground Vehicle::Veteran - Gunnery/ProtoMek::Veteran - Piloting/Mek::Veteran - Anti-Mek::Veteran + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mechanic::Regular + Tech/Mek::Regular + Tech/BattleArmor::Regular @@ -1202,13 +1578,13 @@ - Gunnery/BattleArmor::Regular + Gunnery/Mek::Regular + Gunnery/ProtoMek::Regular Gunnery/Aircraft::Regular - Gunnery/Spacecraft::Regular Gunnery/Aerospace::Regular - Gunnery/ProtoMek::Regular + Gunnery/Spacecraft::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular - Gunnery/Mek::Regular Artillery::Regular @@ -1223,18 +1599,18 @@ - Piloting/Mek::Regular + Gunnery/Mek::Regular Piloting/Aircraft::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular + Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular - Gunnery/Mek::Regular Gunnery/ProtoMek::Regular + Anti-Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular @@ -1253,25 +1629,66 @@ - Piloting/Ground Vehicle::Veteran + Anti-Mek::Veteran Gunnery/ProtoMek::Veteran + Piloting/Ground Vehicle::Veteran Piloting/Mek::Veteran - Anti-Mek::Veteran - Melee Master (CamOps) - melee_master - Enables the unit to do one additional kick, punch, or club attack on the same opponent. - 150 - 6 + Maintainer (Unofficial) + tech_maintainer + The tech is slow and methodical at their work. They take a +1 penalty to their repair + rolls, but are better at maintaining units than most, with a -1 bonus. + + 100 + 1 - Gunnery/ProtoMek::Regular - Piloting/Mek::Regular + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mechanic::Regular + Tech/Mek::Regular + Tech/BattleArmor::Regular + + + + Engineer (unofficial) + tech_engineer + -2 on refit rolls + 100 + 2 + + + + + + Tech/Vessel::Veteran + Tech/Aero::Veteran + Tech/Mechanic::Veteran + Tech/Mek::Veteran + Tech/BattleArmor::Veteran + + + + Tech Specialist, Weapon (Unofficial) + tech_weapon_specialist + -1 to repair and maintenance checks when working with weapons + 50 + 2 + + + + + + Tech/Vessel::Regular + Tech/Aero::Regular + Tech/Mechanic::Regular + Tech/Mek::Regular + Tech/BattleArmor::Regular @@ -1285,18 +1702,18 @@ + Gunnery/Mek::Regular Piloting/Aircraft::Regular - Piloting/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular + Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Anti-Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular @@ -1313,18 +1730,18 @@ + Gunnery/Mek::Regular Piloting/Aircraft::Regular - Piloting/Mek::Regular Gunnery/Spacecraft::Regular Piloting/Spacecraft::Regular + Piloting/Mek::Regular Piloting/VTOL::Regular Piloting/Naval::Regular + Gunnery/BattleArmor::Regular Gunnery/Aerospace::Regular Piloting/Aerospace::Regular - Anti-Mek::Regular - Gunnery/BattleArmor::Regular Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Anti-Mek::Regular Piloting/Ground Vehicle::Regular Gunnery/Aircraft::Regular Gunnery/Vehicle::Regular @@ -1343,166 +1760,78 @@ - Piloting/Mek::Veteran + Gunnery/Mek::Veteran Piloting/Aircraft::Veteran Gunnery/Spacecraft::Veteran Piloting/Spacecraft::Veteran + Piloting/Mek::Veteran Piloting/VTOL::Veteran Piloting/Naval::Veteran + Gunnery/BattleArmor::Veteran Gunnery/Aerospace::Veteran Piloting/Aerospace::Veteran - Anti-Mek::Veteran - Gunnery/BattleArmor::Veteran - Gunnery/Mek::Veteran Gunnery/ProtoMek::Veteran + Anti-Mek::Veteran Piloting/Ground Vehicle::Veteran Gunnery/Aircraft::Veteran Gunnery/Vehicle::Veteran - Natural Aptitude, Gunnery (aToW) - aptitude_gunnery - Roll 3d6 and take the best two for gunnery checks - 500 - 0 - - - - - - Gunnery/BattleArmor::Veteran - Gunnery/Aircraft::Veteran - Gunnery/Spacecraft::Veteran - Gunnery/Aerospace::Veteran - Gunnery/ProtoMek::Veteran - Gunnery/Vehicle::Veteran - Gunnery/Mek::Veteran - - - - Zweihander (CamOps) - zweihander - You can do more damage with two-handed physical attacks at the risk of damaging yourself. + Sensor Geek (Unofficial) + sensor_geek + A pilot with this ability gets a -2 bonus on sensor checks. + Note that this is really only a bonus, if inclusive sensor ranges are in use. 100 - 6 - - - - - - Piloting/Mek::Regular - - - - Urban Guerrilla (CamOps) - urban_guerrilla - Urban Guerrilla gives infantry a better chance of survival in urban or suburban environments (+1 to be hit) - It also keeps them from taking double damage in the open when in pavement or road terrain types. - Finally, urban guerrillas can call on local support once per scenario. - 50 - 3 + 2 - Small Arms::Regular + Piloting/Aerospace::Green + Piloting/Naval::Green + Piloting/Ground Vehicle::Green + Gunnery/ProtoMek::Green + Gunnery/BattleArmor::Green + Piloting/VTOL::Green + Piloting/Aircraft::Green + Piloting/Mek::Green - Melee Specialist (CamOps) - melee_specialist - Enables the unit to do 1 additional point of damage with physical attacks and applies a -1 to-hit modifier to physical attacks. - Note: This ability is only used for BattleMeks. + Cluster Hitter (CamOps) + cluster_hitter + A pilot with this ability gets a +1 to the cluster hit table 100 3 - + oblique_attacker::sandblaster + Gunnery/Mek::Regular Gunnery/ProtoMek::Regular - Piloting/Mek::Regular - - - - Oblique Attacker (CamOps) - oblique_attacker - The penalty for indirect fire is reduced by one and can also use indirect fire with out a spotter. - 50 - 2 - - - - - Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular - Gunnery/Mek::Regular + Gunnery/Aerospace::Regular + Gunnery/BattleArmor::Regular Gunnery/Vehicle::Regular - Terrain Master [Swamp Beast] (CamOps) - tm_swamp_beast - Movement in mud or swamp costs 1 less MP. - Using run/flank MP while in mud/swamp gives +1 BTH when being attacked - 150 - 3 - - - - - - Piloting/Ground Vehicle::Veteran - Gunnery/ProtoMek::Veteran - Piloting/Mek::Veteran - Anti-Mek::Veteran - - - - Gunnery Specialization (aToW) - specialist - A pilot who specializes in a type of weapon receives a -1 to-hit modifier when using weapons of that type and a - +1 to-hit modifier when using other types of weapons. + Some Like It Hot (Unofficial) + some_like_it_hot + A pilot with this ability does not suffer the initial -1 to hit due to heat. 50 - 4 + 2 - Gunnery/BattleArmor::Green - Gunnery/Aircraft::Green - Gunnery/Spacecraft::Green - Gunnery/Aerospace::Green - Gunnery/ProtoMek::Green - Gunnery/Vehicle::Green - Gunnery/Mek::Green - - - - Cluster Hitter (CamOps) - cluster_hitter - A pilot with this ability gets a +1 to the cluster hit table - 100 - 3 - - oblique_attacker::sandblaster - - - - Gunnery/Aircraft::Regular - Gunnery/BattleArmor::Regular - Gunnery/Aerospace::Regular - Gunnery/Spacecraft::Regular - Gunnery/ProtoMek::Regular Gunnery/Mek::Regular - Gunnery/Vehicle::Regular + Gunnery/Aerospace::Regular @@ -1538,41 +1867,5 @@ Piloting/Mek::Veteran - - Shaky Stick (CamOps) - shaky_stick - Ground units have a harder time hitting this airborne target. Attacking units get a +1 tohit penalty - 100 - 3 - - - - - - Piloting/Aerospace::Regular - Piloting/Ground Vehicle::Regular - Piloting/Spacecraft::Regular - Piloting/VTOL::Regular - Piloting/Aircraft::Regular - - - - Animal Mimicry (CamOps) - animal_mimic - Allows a Quad Mek or ProtoMek, or a bipedal Mek or ProtoMek with the animalistic quirk, the following benefits. - -1 MP per hex of Woods and Jungle terrain. - In addition quads gain a -1 to any quad related PSR check. - NOTE: The quirk should be used only on bipedal units that have an animal like appearance. - 50 - 1 - - - - - - Gunnery/ProtoMek::Regular - Piloting/Mek::Regular - - diff --git a/MekHQ/mmconf/log4j2.xml b/MekHQ/mmconf/log4j2.xml index 3a626d0b0ef..4b04808b0d7 100644 --- a/MekHQ/mmconf/log4j2.xml +++ b/MekHQ/mmconf/log4j2.xml @@ -5,7 +5,7 @@ - + @@ -17,24 +17,30 @@ + + + + + + diff --git a/MekHQ/resources/mekhq/resources/AssignForceToTransport.properties b/MekHQ/resources/mekhq/resources/AssignForceToTransport.properties new file mode 100644 index 00000000000..0c48da9b289 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/AssignForceToTransport.properties @@ -0,0 +1,47 @@ +# The first piece of each key is the class or superclass it's used in +AssignForceToTransportMenu.transportSpaceRemaining.text={0} | Space Remaining: {1} +AssignForceToTransportMenu.warningCouldNotLoadUnit.text=Unable to load {0} to {1}. + +# These keys are used - they're dynamically selected using a Campaign Transport Type's enum name +AssignForceToTransportMenu.TACTICAL_TRANSPORT.text=Assign Force to Tactical Transport +AssignForceToTransportMenu.SHIP_TRANSPORT.text=Assign Force to Ship Transport + +# These keys are used - they're dynamically selected using the TransporterType enum +AssignForceToTransportMenu.ASF_BAY.text=ASF Bay +AssignForceToTransportMenu.HEAVY_VEHICLE_BAY.text=Heavy Vehicle Bay +AssignForceToTransportMenu.NAVAL_REPAIR_FACILITY.text=Naval Repair Facility +AssignForceToTransportMenu.DROPSHUTTLE_BAY.text=Dropshuttle Bay +AssignForceToTransportMenu.LIGHT_VEHICLE_BAY.text=Light Vehicle Bay +AssignForceToTransportMenu.SUPER_HEAVY_VEHICLE_BAY.text=Super Heavy Vehicle Bay +AssignForceToTransportMenu.MEK_BAY.text=Mek Bay +AssignForceToTransportMenu.PROTO_MEK_BAY.text=ProtoMek Bay +AssignForceToTransportMenu.SMALL_CRAFT_BAY.text=Small Craft Bay +AssignForceToTransportMenu.INFANTRY_BAY.text=Infantry Bay +AssignForceToTransportMenu.BATTLE_ARMOR_BAY.text=Battle Armor Bay +AssignForceToTransportMenu.INFANTRY_COMPARTMENT.text=Infantry Compartment +AssignForceToTransportMenu.BATTLE_ARMOR_HANDLES.text=Battle Armor Handles +AssignForceToTransportMenu.BATTLE_ARMOR_HANDLES_TANK.text=Battle Armor Handles (Tank) +AssignForceToTransportMenu.CLAMP_MOUNT_MEK.text=Clamp Mount +AssignForceToTransportMenu.CLAMP_MOUNT_TANK.text=Clamp Mount Tank +AssignForceToTransportMenu.PROTO_MEK_CLAMP_MOUNT.text=ProtoMek Clamp Mount +AssignForceToTransportMenu.DOCKING_COLLAR.text=Docking Collar +AssignForceToTransportMenu.TANK_TRAILER_HITCH.text=Trailer Hitch + +TOEMouseAdapter.unassign.TACTICAL_TRANSPORT.text=Unassign Force from Tactical Transport +TOEMouseAdapter.unassign.SHIP_TRANSPORT.text=Unassign Force from Ship Transport + +TOEMouseAdapter.unassignFrom.TACTICAL_TRANSPORT.text=Unassign Transported Units from Tactical Transport +TOEMouseAdapter.unassignFrom.SHIP_TRANSPORT.text=Unassign Transported Units from Ship Transport + +AtBGameThread.loadTransportDialog.TACTICAL_TRANSPORT.title=Load Units onto Transport? +AtBGameThread.loadTransportDialog.TACTICAL_TRANSPORT.text=Would you like the units assigned to {0} to deploy loaded? + +AtBGameThread.loadTransportDialog.LOAD_DROPSHIP_DIALOG_TITLE.title =Load DropShips onto Transport? +AtBGameThread.loadTransportDialog.LOAD_DROPSHIP_DIALOG_TEXT.text =Would you like the DropShip(s) assigned to {0} to deploy loaded into its bays? +AtBGameThread.loadTransportDialog.LOAD_SMALL_CRAFT_DIALOG_TITLE.title =Load Small Craft onto Transport? +AtBGameThread.loadTransportDialog.LOAD_SMALL_CRAFT_DIALOG_TEXT.text =Would you like the small craft assigned to {0} to deploy loaded into its bays? +AtBGameThread.loadTransportDialog.LOAD_FTR_DIALOG_TEXT.text =Would you like the fighter(s) assigned to {0} to deploy loaded into its bays? +AtBGameThread.loadTransportDialog.LOAD_FTR_DIALOG_TITLE.title =Load Fighters onto Transport? +AtBGameThread.loadTransportDialog.LOAD_GND_DIALOG_TEXT.text =Would you like the ground unit(s) assigned to {0} to deploy loaded into its bays? +AtBGameThread.loadTransportDialog.LOAD_GND_DIALOG_TITLE.title =Load Ground Units onto Transport? + diff --git a/MekHQ/resources/mekhq/resources/AtBDynamicScenarioFactory.properties b/MekHQ/resources/mekhq/resources/AtBDynamicScenarioFactory.properties index 0cc7c2b92b6..ff0fbdba11c 100644 --- a/MekHQ/resources/mekhq/resources/AtBDynamicScenarioFactory.properties +++ b/MekHQ/resources/mekhq/resources/AtBDynamicScenarioFactory.properties @@ -1,5 +1,5 @@ # reportResultsOfBidding -bidAwayForcesVerbose.text=%s has bid away the following forces:
    %s +bidAwayForcesVerbose.text=%s has bid away the following forces:
    %s
    bidAwayForcesLogger.text=%s has bid away the following forces: bidAwayForces.text=%s has bid away %s unit%s. nothingBidAway.text=%s has not bid away any forces. diff --git a/MekHQ/resources/mekhq/resources/AtBStratCon.properties b/MekHQ/resources/mekhq/resources/AtBStratCon.properties index 511cc6ab4fe..9e39565a6cc 100644 --- a/MekHQ/resources/mekhq/resources/AtBStratCon.properties +++ b/MekHQ/resources/mekhq/resources/AtBStratCon.properties @@ -1,11 +1,12 @@ regular.text=Regular Reinforcement Roll -lanceInFightRole.text=Improved Reinforcement Roll +auxiliary.text=Improved Reinforcement Roll fromChainedScenario.text=Lance already deployed -lblDefensiveMinefieldCount.text=Defensive Minefield Count: %d +lblDefensiveMinefieldCount.text=Minefield Count: %d lblSelectIndividualUnits.text=Select individual units (%d max) -lblDefensivePostureInstructions.Text=This lance is on a defensive deployment and you may deploy additional infantry, battle armor, or minefields. +lblFrontlineInstructions.text=This lance is on a frontline deployment, and may deploy\ + \ additional infantry, battle armor, or minefields. lblLeadershipInstructions.Text=The force commander's leadership allows the deployment of\ \ additional auxiliary units - choose from the list below.\
    \ @@ -16,25 +17,20 @@ selectForceForTemplate.Text=Select a force from the list below.\
    If multiple forces are selected, only the first will be deployed. selectReinforcementsForTemplate.Text=Select reinforcements from the list below.\
    \ -
    Each attempt will cost 1 Support Point and may be unsuccessful. -selectReinforcementsForTemplateNoSupportPoints.Text=Unable to assign reinforcements. No Support Points available. - -reinforcementsNoSupportPoints.text=Attempting to reinforce scenario %s, %sAutomatic Failure%s You\ - \ have no remaining Support Points. -reinforcementsNoAdmin.text=Attempting to reinforce scenario %s, %sERROR%s nobody has been\ - \ assigned to an Admin/Command position. Roll automatically fails. No Support Points were\ - \ spent in this attempt. -reinforcementsNoAdminSkill.text=Attempting to reinforce scenario %s, %sERROR%s %s does not have the\ - \ Administration skill. Roll automatically fails. No Support Points were spent in this\ - \ attempt. +
    Each attempt will cost Support Points and may be unsuccessful. +selectReinforcementsForTemplateNoSupportPoints.Text=Unable to assign reinforcements. No\ + \ Support Points available. + reinforcementsAttempt.text=Attempting to reinforce scenario %s, roll %s%s vs. %s: +reinforcementsAttempt.text.gm=Attempting to reinforce scenario %s: reinforcementsCriticalFailure.text=%sCritical Command Failure%s. Reinforcement attempt\ - \ fails and Support Point is lost. + \ fails and Support Points are lost. reinforcementsSuccess.text=%sReinforcement Success%s. +reinforcementsAutomaticSuccess.text=%sAutomatic Success%s. reinforcementsSuccessRouted.text=%sReinforcement Success%s. The enemy has routed\ \ and is unable to intercept your reinforcements. -reinforcementsCommandFailure.text=%sCommand Failure%s. Reinforcement attempt\ - \ fails and Support Point is lost. +reinforcementsCommandFailure.text=%sCommand Failure%s. Reinforcements will arrive, but have\ + \ been delayed. reinforcementsInterceptionAttempt.text=Due to a %sCommand Failure%s your reinforcements are\ \ out of position. Enemy forces were dispatched in an attempt to capitalize on this tactical error. reinforcementsErrorNoCommander.text=%sError%s. There is no commander assigned to this force.\ @@ -42,14 +38,63 @@ reinforcementsErrorNoCommander.text=%sError%s. There is no commander assi reinforcementsErrorUnableToFetchCommander.text= %sError%s. We were unable to fetch the\ \ commander using the commander ID logged for this force. You should report this bug.\ \ Reinforcement attempt fails and Support Point is lost. -reinforcementCommanderNoSkill.text=%sAutomatic Evasion Failure%s. The commander of the\ - \ reinforcements does not possess the Tactics skill. They were unable to evade enemy\ - \ forces. An interception scenario has occurred. The reinforcing force has already been assigned\ - \ to this new scenario. reinforcementEvasionSuccessful.text=%sEvasion Success%s (%s vs. %s). The commander of the\ \ reinforcements was able to use their Tactics skill to evade the enemy long enough to\ \ reach safety. Reinforcements were delayed, but successful. +reinforcementEvasionSuccessful.noSkill=%sEvasion Success%s (%s vs. %s). The commander of the\ + \ reinforcements does not possess the Tactics skill. Despite this they were still able to\ + \ evade the enemy long enough to reach safety. Reinforcements were delayed, but successful. reinforcementEvasionUnsuccessful.text=%sEvasion Unsuccessful%s (%s vs. %s). The commander\ \ of the reinforcements attempted to use their Tactics skill to evade the enemy\ \ interception, but was unsuccessful. An interception scenario has occurred. The reinforcing\ - \ force has already been assigned to this new scenario. \ No newline at end of file + \ force has already been assigned to this new scenario. + +trackScenarioOdds.text=Track Scenario Odds: %d%% +btnRemoveSP.text=Remove SP (GM) +btnAddSP.text=Add SP (GM) +btnAddCVP.text=Add CVP (GM) +btnRemoveCVP.text=Remove CVP (GM) + +force.undeployed=%s has returned from deployment. + +requestingResupply.title=Requesting Resupply +supplyPointExpenditure.text=How many Support Points would you like to spend? +btnConfirm.text=Confirm + +scenarioSetupWizard.title=Scenario Setup Wizard +unitSelectLabelDefaultValue.text=0 selected +incomingTransmission.title=++INCOMING TRANSMISSION++ +reinforcementConfirmation.introduction=%s, I've run the numbers. If we reinforce now, here is our\ + \ likelihood of success. Forces assigned to Maneuver or Auxiliary duties are more\ + \ likely to succeed than other forces.

    +reinforcementConfirmation.breakdown=Breakdown
    +reinforcementConfirmation.breakdown.total=Target Number +reinforcementConfirmation.addendum=For more information, please see 'Combat Teams, Roles, Training\ + \ & Reinforcements.pdf'.\ +
    This can be found in 'MekHQ/Docs/Stratcon and Against the Bot'. +reinforcementConfirmation.spinnerLabel=Support Points +reinforcementConfirmation.spinnerLabel.tooltip=Each Support Point, after the first, will reduce the\ + \ target number by 2. +reinforcementConfirmation.confirmButton=Reinforce +reinforcementConfirmation.confirmButton.tooltip=Make reinforcement attempt. +reinforcementConfirmation.confirmButton.gm=Reinforce (GM) +reinforcementConfirmation.confirmButton.gm.tooltip=Make a reinforcement attempt, bypassing the\ + \ reinforcement check and Support Point cost. +reinforcementConfirmation.cancelButton=Cancel +unitsSelectedLabel.bv=selected (ignores crew skill) +unitsSelectedLabel.count=selected + +### Support Point Negotiation +supportPoints.maximum=Your Admin/Transport personnel cannot use their Administration\ + \ skill to create additional Support Points for contract %s, as you have already %sreached the\ + \ maximum%s of %s Support Point%s. + +supportPoints.initial=Through the Administration skill of your Admin/Transport personnel\ + \ contract %s will begin with %s%s%s Support Point%s. +supportPoints.initial.noAdministrators=Your unit has no Admin/Transport personnel. Therefore, contract %s\ + \ will not begin with %sany%s Support Points. + +supportPoints.weekly.noAdministrators=Your unit has no Admin/Transport personnel. Therefore,\ + \ you will not gain %sany%s additional Support Points this week. +supportPoints.weekly=The skill of your Admin/Transport personnel has created %s%s%s\ + \ additional Support Point%s for contract %s. \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/AutoResolveBehaviorSettingsDialog_en.properties b/MekHQ/resources/mekhq/resources/AutoResolveBehaviorSettingsDialog_en.properties new file mode 100644 index 00000000000..bf4ced59a46 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/AutoResolveBehaviorSettingsDialog_en.properties @@ -0,0 +1,4 @@ +AutoResolveBehaviorSettingsDialog.title=Auto Resolve Help +AutoResolveBehaviorSettingsDialog.autoResolveHelpPath=docs/help/en/AutoResolve.html +AutoResolveBehaviorSettingsDialog.help=Auto Resolve Help +AutoResolveBehaviorSettingsDialog.helpTooltip=Open the Auto Resolve Help documentation in a new window \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/Campaign.properties b/MekHQ/resources/mekhq/resources/Campaign.properties index cf419998269..ce735dc81a4 100644 --- a/MekHQ/resources/mekhq/resources/Campaign.properties +++ b/MekHQ/resources/mekhq/resources/Campaign.properties @@ -45,6 +45,14 @@ anniversaryRecruitment.text=%s celebrates %s%s%s years with %s! #### Personnel Removal personnelRemoval.text=Old personnel records have been tidied away. +#### Personnel Recruitment +personnelRecruitmentFinancesReason.text=Recruitment of %s +personnelRecruitmentInsufficientFunds.text=Insufficient funds to recruit %s +personnelRecruitmentFormerSurname.text=(Formerly %s) +personnelRecruitmentBondsman.text=as a bondsman +personnelRecruitmentPrisoner.text=as a prisoner +personnelRecruitmentAddedToRoster.text=%s%s has been added to the personnel roster%s. + #### Turnover and Retention turnoverJointDeparture.text=departed with their spouse. turnoverJointDepartureChild.text=departed with a parent. @@ -74,21 +82,29 @@ turnoverPersonnelKilled.text=You have personnel who have left the unit or divorce.text=%s has divorced %s. #### Unsorted Campaign Resources -dependentLeavesForce.text=%s is no longer travelling with the force. +dependentLeavesForce.text=%s is no longer traveling with the force. dependentJoinsForce.text=%s has started traveling with the force. relativeJoinsForce.text=%s has started traveling with the force. They are %s's %s. relativeJoinsForceSpouse.text=spouse 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! +atbScenarioToday.atb=Scenario "{0}" is today, deploy a force from your TOE! +atbScenarioToday.stratCon=Scenario "{0}" is today, deploy a force to the Area of Operations! atbScenarioTodayWithForce.format=Scenario "{0}" is today, {1} has been deployed! generalFallbackAddress.text=Commander +generalFallbackAddressInformal.text=Boss +informalAddressMale.text=Bossman +informalAddressFemale.text=Bossma'am +weeklyStockCheck.text=Weekly Stock Check: %d different kinds of parts are under requested stock levels, orders have been put in to buy them garrisonDutyRouted.text=Long-ranged sensors detect no enemy activity in the AO. contractMoraleReport.text=Current enemy condition is %s on contract %s.\
    \
    %s -stratConWeeklySupportPoints.text=The skill of your Admin/Transport personnel has created %s%s%s\ - \ additional Support Points for contract %s. -stratConWeeklySupportPointsFailed.text=Your Admin/Transport personnel %sfailed%s to create\ - \ any additional Support Points for contract %s. \ No newline at end of file +understrength.text=%s is %sunder strength%s and is not counting towards the minimum number\ + \ of Combat Teams required by your employer. Your employer is demanding that all relevant Combat\ + \ Teams contain at least %s combat units. Consider reassigning this Combat Team to Reserve\ + \ or Auxiliary. +contractBreach.text=Failure to meet the requirements of contract %s resulted in the %sloss of a\ + \ CVP%s. +gainedExperience.text=%s%s%s personnel
    gained vocational\ + \ XP. \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/CampaignGUI.properties b/MekHQ/resources/mekhq/resources/CampaignGUI.properties index 9cebb0b90ac..d0e434490cf 100644 --- a/MekHQ/resources/mekhq/resources/CampaignGUI.properties +++ b/MekHQ/resources/mekhq/resources/CampaignGUI.properties @@ -49,6 +49,7 @@ miMHQOptions.text=MekHQ Options... miMHQOptions.toolTipText=This launches the MekHQ Options Dialog. menuThemes.text=Themes menuExit.text=Exit +mekSelectorDialog.unsupported.gunEmplacement=Gun Emplacements are %snot supported%s by MekHQ. # Marketplace Menu menuMarket.text=Marketplace @@ -107,6 +108,10 @@ menuHelp.text=Help menuAbout.text=About... #End Menu Resources +# In-App New Campaign +savePrompt.title=Save First? +savePrompt.text=Do you want to save the game before starting a new campaign? + ## Unsorted Resources btnAdvanceDay.text=Advance Day btnAdvanceDay.toolTipText=Advance forward one day. @@ -174,8 +179,9 @@ btnPrintRS.toolTipText=Print record sheets for all currently assigned units. btnPrintRS.text=Print Sheets btnGetMul.toolTipText=Get a MUL file of all assigned units that can be loaded into MegaMek btnGetMul.text=Export MUL File -btnClearAssignedUnits.toolTipText=Clear all assigned units for this scenario -btnClearAssignedUnits.text=Clear Units +btnClearAssignedUnits.toolTipText=Clear all assigned units for this scenario. Restricted to GM Mode\ + \ if it is a scenario assigned to the Area of Operations. +btnClearAssignedUnits.text=Reset Deployment btnResolveScenario.toolTipText=Bring up a wizard that will guide you through the process of resolving this scenario either by MUL files from a MegaMek game or by manually editing for tabletop games. btnResolveScenario.text=Resolve Manually btnAutoResolveScenario.toolTipText=Start a game of MegaMek with all the assigned units played by bots.
    At the game's conclusion, you will be presented with a series of dialogs for resolving the scenario. @@ -232,5 +238,3 @@ dialogOverdueLoans.text=You must resolve overdue loans before advancing the day. dialogCheckDueScenarios.title=Scenarios Must Be Completed dialogCheckDueScenarios.text=You must complete scenarios with a date of today or earlier before advancing the day. - -spareBonusPartExchange.text=Bonus Part Exchange Program diff --git a/MekHQ/resources/mekhq/resources/CampaignHasProblemOnLoad.properties b/MekHQ/resources/mekhq/resources/CampaignHasProblemOnLoad.properties new file mode 100644 index 00000000000..c93826346e4 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/CampaignHasProblemOnLoad.properties @@ -0,0 +1,36 @@ +cancel.button=Cancel +continue.button=Continue Regardless + +CANT_LOAD_FROM_NEWER_VERSION.message={0}, we seem to be having a problem with our command and control\ + \ software. Checking the data, it looks like we might have a version mismatch. +CANT_LOAD_FROM_NEWER_VERSION.ooc=A campaign can never be loaded into an older version. + +CANT_LOAD_FROM_OLDER_VERSION.message={0}, we seem to be having a problem with our command and control\ + \ software. Checking the data, it looks like we still need to update our systems. +CANT_LOAD_FROM_OLDER_VERSION.ooc=

    To avoid file corruption and ensure a smooth experience, load\ + \ and save your campaign in each Milestone released after the version your campaign was last\ + \ saved in.

    \ +
    \ +

    1. Load your campaign in the next Milestone.

    \ +

    2. Save and close it.

    \ +

    3. Repeat for the next Milestone.

    \ +
    \ +

    After catching up with all Milestones, you can safely upgrade to the latest development\ + \ version.

    \ +
    \ +

    Warning: The 'continue regardless' button is included to help development and testing\ + \ and is not intended for general use.

    \ +
    \ +

    The MekHQ team will not offer assistance if you ignore this warning.

    + +ACTIVE_OR_FUTURE_CONTRACT.message=

    {0}, our command and control software license doesn't support\ + \ this action.

    \ +
    \ +

    We can continue, but it will void the warranty. +ACTIVE_OR_FUTURE_CONTRACT.ooc=

    A lot of information is initialized when a contract is created,\ + \ changing versions mid-contract is not supported.

    \ +
    \ +

    Warning: The 'continue regardless' button is included to help development and testing\ + \ and is not intended for general use.

    \ +
    \ +

    The MekHQ team will not offer assistance if you ignore this warning.

    \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties index c0561a6fc86..9618bab2451 100644 --- a/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties +++ b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties @@ -1,1014 +1,2159 @@ -### Generic Properties -Cancel.text=Cancel -Cancel.toolTipText=Cancel out of the dialog. No changes made will be saved. -Confirm.text=Confirm -Exponential.text=* e ^ ( -InvalidOptions.title=Error: Invalid Option Selection -PowerOfTen.text=* 10 ^ +## Main Dialog +lblApplySettings.text=Apply Settings +lblApplySettings.tooltip=Apply the current campaign options to the campaign. +lblSavePreset.text=Save Preset +lblSavePreset.tooltip=Save the current campaign options as a new preset. +lblLoadPreset.text=Load Preset +lblLoadPreset.tooltip=Load an existing preset. +lblCancel.text=Cancel Changes +lblCancel.tooltip=Cancel all changes and close the dialog. + +## SelectPresetDialog Class +# SelectPresetDialog +presetDialog.title=Select a Campaign Preset +presetDialog.description=Choose a preset to tailor your experience. Each option provides a\ + \ well-balanced challenge, suitable for both newcomers and seasoned players. +lblPresetDialogSelect.text=Confirm +lblPresetDialogSelect.tooltip=Confirm the currently selected preset. +lblPresetDialogCustomize.text=Customize +lblPresetDialogCustomize.tooltip=Select a preset, then open the campaign options menu. +lblPresetDialogCancel.text=Cancel +lblPresetDialogCancel.tooltip=Return to the prior screen. + +## Campaign Options Pane +campaignOptions.title=Campaign Options +generalPanel.title=General +humanResourcesParentTab.title=Human Resources +advancementParentTab.title=Advancement +logisticsAndMaintenanceParentTab.title=Logistics +strategicOperationsParentTab.title=Operations + +## General Tab Class +# createGeneralTab +lblGeneral.text=Welcome, Commander +lblGeneralBody.text=

    "War has been described as an art, as a science, as an extension\ + \ of diplomacy, and as an expression of national policy. In fact, it has become a business, pure\ + \ and simple. Like all government-run ventures, government armies suffer the woes of bureaucratic\ + \ incompetence but get along because they are the only game in town. As a small businessman\ + \ competing in a wide-open market, the mercenary leader does not have this advantage, and it\ + \ behooves him to run his firm wisely, efficiently, and economically."

    \ +

    From "The Soldier's Balance Sheet" by Colonel David "Bear" Tarquin

    \ +
    \ +
    \u270E Custom system unique to MekHQ.\ +
    \u2318 Documentation included in MekHQ/docs.\ +
    \u26A0 Tooltip contains important information.\ +
    \u2714 Tooltip contains a recommendation.
    +lblName.text=Unit Name +lblName.tooltip=The most important decision you will ever make. +lblNameGenerator.text=Regenerate Name +lblNameGenerator.tooltip=Generate a new random name. +lblFaction.text=Unit Faction \u2714 +lblFaction.tooltip=Which faction should your campaign belong to?\ +
    \ +
    Recommended: Mercenary +lblReputation.text=Reputation \u2714 +lblReputation.tooltip=Which reputation method should your campaign be graded against?\ +
    \ +
    Recommended: Campaign Operations +lblManualUnitRatingModifier.text=Manual Modifier +lblManualUnitRatingModifier.tooltip=This allows you to manually adjust your reputation rating. +lblDate.text=Start Date +lblDate.tooltip=When should the campaign begin? +lblCamo.text=Unit Camouflage +lblCamo.tooltip=Pick a camouflage scheme for the units in your campaign. +lblIcon.text=Unit Insignia +lblIcon.tooltip=Pick an insignia for your campaign. Or leave blank to use an image based on your\ + \ campaign faction. + +# createFurtherReadingPanel +lblFurtherReadingPanel.text=Suggested Reading +lblBMMPanel.text=BATTLEMECH MANUAL (BMM) +lblBMMPanelBody.text=The BattleMech Manual, built from the ground up with the latest rules, is\ + \ specifically designed for BattleTech players seeking an all-'Mek combat experience. It serves\ + \ as the ideal starting point for new players learning the battle rules. +lblTotalWarfarePanel.text=TOTAL WARFARE (TW) +lblTotalWarfarePanelBody.text=Total Warfare is the definitive rulebook for experienced BattleTech\ + \ players, serving as a comprehensive reference guide rather than an introductory teaching tool.\ + \ For construction rules related to the various units featured in Total Warfare, refer to the\ + \ TechManual. +lblCampaignOperationsPanel.text=CAMPAIGN OPERATIONS (CamOps) +lblCampaignOperationsPanelBody.text=Campaign Operations offers rules for creating and managing\ + \ forces, whether it is a struggling mercenary battalion or a well-equipped House regiment. The\ + \ book's final sections introduce diverse options for campaign play, enabling players to run\ + \ thrilling campaigns of virtually any type with their newly created forces. Campaign Operations\ + \ serves as the foundational rulebook for modern MekHQ. + +## RepairAndMaintenanceTab +repairsAndMaintenanceContentTabs.title=Maintenance & Repairs + +repairTab.title=Repairs +repairTab.border="Victory depends upon the proper use of combined arms \u2014 light, medium, heavy,\ + \ assault, air, infantry, and the rest. Variety is the spice of battle."\ +
    Captain Natasha Kerensky, Wolf's Dragoons + +maintenanceTab.title=Maintenance +maintenanceTab.border="One 'Mek lance can beat one mob, large or small, any time."\ +
    Dr. Shindu Banzai, Fundamentals of Military Science\ +
    Lecture at the New Avalon Institute of Science, 3017
    + +# createRepairTab +lblRepairTab.text=Repair Options +lblUseEraModsCheckBox.text=Use Era Modifiers for Repair Rolls +lblUseEraModsCheckBox.tooltip=Use faction-specific era mods for repair rolls. +lblAssignedTechFirstCheckBox.text=Place Assigned Technicians to the Top of the List +lblAssignedTechFirstCheckBox.tooltip=In the repair bay, if the unit has an assigned technician,\ + \ that technician is placed at the top of the available technician list. +lblResetToFirstTechCheckBox.text=After Repairs Jump to the Technician at the Top of the List +lblResetToFirstTechCheckBox.tooltip=After any repair/salvage task, always select the technician at\ + \ the top of the list. +lblUseQuirksBox.text=Use Quirks +lblUseQuirksBox.tooltip=Allow units to have quirks. +lblUseAeroSystemHitsBox.text=Damage Aero System Parts by Hits Taken +lblUseAeroSystemHitsBox.tooltip=Changes repair/replacement time and difficulty of Aero large and \ + small craft systems (Avionics, FCS, etc). +lblRepairTabRight.text=Component Damage \u270E +lblUseDamageMargin.text=Damage Parts by Margin of Failure +lblUseDamageMargin.tooltip=Instead of destroying/damaging parts only by elite techs, parts are\ + \ damaged/destroyed when the tech roll fails by a given margin. +lblDamageMargin.text=Margin of Failure +lblDamageMargin.tooltip=Determines how bad a failure must be before an item is damaged or destroyed. +lblDestroyPartTarget.text=Equipment Survival Target Number +lblDestroyPartTarget.tooltip=Equipment hit in battle will survive if this target number is beat on\ + \ 2d6. + +# createMaintenanceTab +lblMaintenanceTab.text=Maintenance Options + +lblCheckMaintenance.text=Enable Unit Maintenance +lblCheckMaintenance.tooltip=If checked, each unit will make a parts-specific maintenance check at\ + \ the end of each maintenance cycle +lblMaintenanceDays.text=Base Maintenance Cycle +lblMaintenanceDays.tooltip=This setting determines how frequently maintenance checks are made while\ + \ on non-Garrison contracts. Peacetime frequency is x4 this value. +lblMaintenanceBonus.text=Maintenance Modifier +lblMaintenanceBonus.tooltip=When performing maintenance, modify the target number by this value. +lblDefaultMaintenanceTime.text=Default Maintenance Time \u2714 +lblDefaultMaintenanceTime.tooltip=This setting determines what maintenance time modifier is applied\ + \ to new units. The higher this value, the longer maintenance takes, but the easier it is.\ +
    \ +
    Recommended: 4 +lblUseQualityMaintenance.text=Use Quality Modifiers \u2714 +lblUseQualityMaintenance.tooltip=If checked, quality modifiers will be added to maintenance checks.\ +
    Note: While this option is RAW it creates an unusual situation where some units\ + \ race to the best quality, while others race to the bottom.\ +
    \ +
    Recommended: disable this option. +lblReverseQualityNames.text=Reverse Quality Names \u270E \u26A0 +lblReverseQualityNames.tooltip=If checked, equipment and unit quality names will be reversed so that\ + \ A is the best and F is the worst. This only affects equipment and unit quality ratings.\ + \ All other ratings are unaffected. In most cases, other ratings already run A-F.\ +
    \ +
    Warning: This option is not reflected in the 'Operations/Finances/Price Multipliers'\ + \ tab. +lblUseRandomUnitQualities.text=Random New Unit Quality \u270E +lblUseRandomUnitQualities.tooltip=Determines whether the quality of new units is randomized based\ + \ on market or salvage status. Otherwise, quality will always default to D. +lblUsePlanetaryModifiers.text=Use Planetary Modifiers +lblUsePlanetaryModifiers.tooltip=Determines whether certain planetary conditions will impact\ + \ maintenance checks based on repair location. +lblUseUnofficialMaintenance.text=Only Damage Worst Rated Equipment \u270E \u26A0 +lblUseUnofficialMaintenance.tooltip=Determines whether parts should only be damaged by maintenance\ + \ if they're already at the worst rating (F by default).\ +
    \ +
    Warning: this option is only relevant if 'Use Quality Modifiers' is enabled. +lblLogMaintenance.text=Log Maintenance Results +lblLogMaintenance.tooltip=Determines whether the results of each maintenance check should be added\ + \ to 'mekhq.log' + +## SuppliesAndAcquisitionTab +suppliesAndAcquisitionContentTabs.title=Supplies and Acquisition + +acquisitionTab.title=Acquisitions & Deliveries +acquisitionTab.border="Forget the casualties. How much did we make?"\ +
    From the comedy holoplay Minor Major\ +
    Andurien Broadcasting Corporation
    + +planetaryAcquisitionTab.title=Planetary Acquisitions +planetaryAcquisitionTab.border="The lowest, dirtiest, nastiest kind of assignment a hard-luck\ + \ bastard merc can draw... is whatever mission he's on at the moment."\ +
    Major Fran Delmarre\ +
    12th Star Guards
    + +techLimitsTab.title=Tech Limits +techLimitsTab.border="In a universe where we fight with the ghosts of a golden age, technology is a\ + \ reminder: we are bound by history, but not defined by it."\ +
    Engineer Talia "Patch" Kessler\ +
    Noble's Faulty Firepower
    + +# createAcquisitionTab +lblAcquisitionTab.text=Acquisition & Delivery Options + +# createAcquisitionPanel +lblAcquisitionPanel.text=Acquisitions +lblChoiceAcquireSkill.text=Acquisitions Skill +lblChoiceAcquireSkill.tooltip=What skill should be used when performing acquisition checks? +lblSupportStaffOnly.text=Only Support Personnel Make Acquisition Rolls +lblSupportStaffOnly.tooltip=Determines whether anyone with the specified skill can make acquisition\ + \ checks or just those with support roles. +lblAcquireClanPenalty.text=Penalty for Clan Equipment +lblAcquireClanPenalty.tooltip=The penalty for acquisition checks when attempting to acquire Clan\ + \ equipment. +lblAcquireISPenalty.text=Penalty for Inner Sphere Equipment +lblAcquireISPenalty.tooltip=The penalty for acquisition checks when attempting to acquire non-Clan\ + \ equipment. +lblAcquireWaitingPeriod.text=Acquisition Roll Period Frequency +lblAcquireWaitingPeriod.tooltip=How many days should pass between acquisition attempts being refreshed? +lblMaxAcquisitions.text=Max Acquisition Rolls per Period +lblMaxAcquisitions.tooltip=How many times can a character make an acquisition check per period? + +# createAutoLogisticsPanel +lblAutoLogisticsPanel.text=AutoLogistics +lblAutoLogisticsMekHead.text='Mek Head Restock Percent +lblAutoLogisticsMekHead.tooltip=autoLogistics counts the number of each 'Mek head type in use and\ + \ then automatically orders replacement spares equal to the percentage set in this option. +lblAutoLogisticsMekLocation.text='Mek Location Restock Percent +lblAutoLogisticsMekLocation.tooltip=autoLogistics counts the number of each 'Mek location in use and\ + \ then automatically orders replacement spares equal to the percentage set in this option. +lblAutoLogisticsNonRepairableLocation.text=Special Location Restock Percent +lblAutoLogisticsNonRepairableLocation.tooltip=autoLogistics counts the number of each special\ + \ location in use and then automatically orders replacement spares equal to the percentage set in\ + \ this option. Special locations are locations you would not normally want to restock such as\ + \ vehicle locations, or 'Mek center torsos. +lblAutoLogisticsArmor.text=Armor Restock Percent +lblAutoLogisticsArmor.tooltip=autoLogistics counts the tonnage of armor in use and then\ + \ automatically orders replacement spares equal to the percentage set in this option. +lblAutoLogisticsAmmunition.text=Ammunition Restock Percent +lblAutoLogisticsAmmunition.tooltip=autoLogistics counts the tonnage of ammo in use and then\ + \ automatically orders replacement spares equal to the percentage set in this option. +lblAutoLogisticsHeatSink.text=Heat Sink Restock Percent +lblAutoLogisticsHeatSink.tooltip=autoLogistics counts the number of heat sinks in use and then\ + \ automatically orders replacement spares equal to the percentage set in this option. +lblAutoLogisticsOther.text=Other Restock Percent +lblAutoLogisticsOther.tooltip=autoLogistics counts each part in use, that is not covered by one of\ + \ the other options, and automatically orders replacement spares equal to the percentage set in\ + \ this options. + +# createDeliveryPanel +lblDeliveryPanel.text=Deliveries +lblNDiceTransitTime.text=Delivery Time +lblNDiceTransitTime.tooltip=How many dice are rolled to determine delivery time? +lblConstantTransitTime.text=d6+ +lblConstantTransitTime.tooltip=How long is added to the delivery time roll? +lblAcquireMosBonus.text=Delivery Time Reduction +lblAcquireMosBonus.tooltip=How much should the delivery time be reduced per margin of success? +lblAcquireMinimum.text=Minimum Delivery Time +lblAcquireMinimum.tooltip=What is the minimum delivery duration? +transitUnitNamesDays.text=Days +transitUnitNamesWeeks.text=Weeks +transitUnitNamesMonths.text=Months + +# createPlanetaryAcquisitionTab +lblPlanetaryAcquisitionTab.text=Planetary Acquisition Options \u270E + +# createOptionsPanel +lblUsePlanetaryAcquisitions.text=Use Planetary Acquisitions \u26A0 \u2714 +lblUsePlanetaryAcquisitions.tooltip=Supplies will be searched for on specific planets up to a\ + \ certain jump radius away, starting on the current planet.\ +
    \ +
    To find a given part, an availability roll must first succeed to identify a contact and then\ + \ parts may be rolled for until a failure.\ +
    \ +
    Planetary socio-industrial ratings can affect the target roll.\ +
    \ +
    All delivery times are based on the number of recharges necessary (7 days), the in-system\ + \ transit time at both points, and 1d6 extra days per jump.\ +
    \ +
    Warning: will increase load times when advancing day.\ +
    \ +
    Recommended: Disable for large campaigns. +lblMaxJumpPlanetaryAcquisitions.text=Maximum Jump Distance +lblMaxJumpPlanetaryAcquisitions.tooltip=Maximum number of jumps away to search for supplies. +lblPlanetaryAcquisitionsFactionLimits.text=Faction Supply Limits +lblPlanetaryAcquisitionsFactionLimits.tooltip=Limit acquisitions based on the source faction's\ + \ relationship to the campaign faction. +lblDisallowPlanetaryAcquisitionClanCrossover.text=Disallow Clan-Inner Sphere Supply Sharing +lblDisallowPlanetaryAcquisitionClanCrossover.tooltip=If checked, Inner Sphere factions will not be\ + \ able to find supplies on Clan-controlled worlds, and vice versa. +lblDisallowClanPartsFromIS.text=Disallow Clan Parts from Non-Clan Factions +lblDisallowClanPartsFromIS.tooltip=If checked, you will not be able to purchase clan parts from\ + \ non-Clan factions. +lblPenaltyClanPartsFromIS.text=Non-Clan Acquisition Penalty +lblPenaltyClanPartsFromIS.tooltip=The penalty for acquiring clan parts from non-Clan factions when\ + \ not disallowed entirely.\ +
    \ +
    This penalty is cumulative with the general penalty for clan parts. +lblUsePlanetaryAcquisitionsVerbose.text=Enable Verbose Reporting \u26A0 +lblUsePlanetaryAcquisitionsVerbose.tooltip=This shows all the details of the planetary searches in\ + \ the daily log.

    This is generally too much information for normal play, but is useful for\ + \ testing.\ +
    \ +
    Warning: This option can lead to a huge daily report, which will create a saved file\ + \ that takes a significant amount of time to load. + +# createModifiersPanel +lblModifiersPanel.text=Modifiers +lblTechLabel.text=Tech +lblTechLabel.tooltip=The planet's tech rating. +lblIndustryLabel.text=Industry +lblIndustryLabel.tooltip=The planet's industrial rating. +lblOutputLabel.text=Output +lblOutputLabel.tooltip=The planet's technological output rating. + +# createTechLimitsTab +lblTechLimitsTab.text=Tech Limit Options +lblLimitByYearBox.text=Limit Tech Purchases by Game Year +lblLimitByYearBox.tooltip=Units and parts will be limited by the current year of the campaign. +lblDisallowExtinctStuffBox.text=Disallow Purchasing of Extinct Tech +lblDisallowExtinctStuffBox.tooltip=Normally extinct parts can still be found with a penalty. This\ + \ option will disallow them completely. +lblAllowClanPurchasesBox.text=Allow Purchase of Clan Tech +lblAllowClanPurchasesBox.tooltip=Determines whether Clan units and parts will not be available for\ + \ purchase. +lblAllowISPurchasesBox.text=Allow Purchase of Inner Sphere Tech +lblAllowISPurchasesBox.tooltip=Determines whether non-Clan units and parts will not be available\ + \ for purchase. +lblAllowCanonOnlyBox.text=Canon Tech Purchases Only +lblAllowCanonOnlyBox.tooltip=Determines whether custom units are available for purchase. +lblAllowCanonRefitOnlyBox.text=Canon Refits Only +lblAllowCanonRefitOnlyBox.tooltip=Determines whether units can be refitted into non-canon variants. +lblChoiceTechLevel.text=Maximum Tech Level +lblChoiceTechLevel.tooltip=Only units and parts at or below, this tech level will appear in the\ + \ campaign. +lblVariableTechLevelBox.text=Vary Introduction Date by Tech Level \u270E +lblVariableTechLevelBox.tooltip=If checked, the tech level for a part or unit will change depending\ + \ on whether it is in the prototype stage (experimental), production (advanced),\ + \ or common (standard). +lblUseAmmoByTypeBox.text=Use Ammo Cross-Weapon Compatibility \u270E +lblUseAmmoByTypeBox.tooltip=In the warehouse, LRM, SRM, MRM, and MML launchers can mix ammo between\ + \ sizes. For example, an LRM-10 can use LRM-20 ammo. + +## Human Resources Tab +personnelContentTabs.title=Personnel + +# Personnel Tab +personnelGeneralTab.title=General +personnelGeneralTab.border="The merc's worst enemy is usually the employer's High Command."\ +
    Colonel Jaime Wolf\ +
    Commander of Wolf's Dragoons
    + +personnelInformationTab.title=Personnel Information +personnelInformationTab.border="Why continue a wasteful campaign from an untenable position?"\ +
    From a letter written by Takashi Kurita to his son Theodore, Prince of Luthien\ +
    Part of the Military Precepts of House Kurita\ +
    Printed and circulated privately by the Prince in 3020 A.D.
    + +awardsTab.title=Awards +awardsTab.border="They'll get you somehow. One way or another, there'll be an employer who teaches\ + \ you the true meaning of paranoia..."\ +
    Major Fran Delmare\ +
    12th Star Guards
    + +prisonersAndDependentsTab.title=Prisoners \u0026 Dependents +prisonersAndDependentsTab.border="One thing\u2014whose side are we on this week?"\ +
    From the comedy holoplay Minor Major\ +
    Andurien Broadcasting Corporation
    + +medicalTab.title=Medical +medicalTab.border="Justice? Justice died with Richard Cameron. The best thing we can ask for is an\ + \ occasional lapse in injustice."\ +
    Katrina Steiner's address to the Estates General + +salariesTab.title=Salaries +salariesTab.border="No job too small...no fee too high."\ +
    Unknown Mercenacy\ +
    Terran Alliance
    + +# createGeneralTab +lblPersonnelGeneralTab.text=General Options + +lblUseTactics.text=Use Commander Initiative Bonus \u270E \u26A0 +lblUseTactics.tooltip=All initiative rolls are modified by the commander's Tactics skill.\ +
    \ +
    Warning: Princess' units do not currently benefit from this modifier unless it is\ + \ manually applied in MegaMek.\ +
    \ +
    Warning: Should not be combined with 'Use Individual Initiative Bonus.'\ +
    \ +
    Requirement: The commander initiative option must be enabled in MegaMek. +lblUseInitiativeBonus.text=Use Individual Initiative Bonus \u270E \u26A0 +lblUseInitiativeBonus.tooltip=Each unit has their initiative rolls modified by their Tactics skill.\ +
    \ +
    Warning: Princess' units do not currently benefit from this modifier unless it is\ + \ manually applied in MegaMek.\ +
    \ +
    Warning: Should not be combined with '=Use Commander Initiative Bonus.' +lblUseToughness.text=Use Toughness \u270E +lblUseToughness.tooltip=A character's Toughness score acts as a modifier to all consciousness checks. +lblUseRandomToughness.text=Randomize Toughness \u270E +lblUseRandomToughness.tooltip=Personnel have a chance to be recruited with a +1 or -1 to Toughness. +lblUseArtillery.text=Use Artillery Skill +lblUseArtillery.tooltip=Characters can possess the Artillery skill which is used instead of Gunnery\ + \ for specific weapons. +lblUseAbilities.text=Use Special Pilot Abilities (SPAs) +lblUseAbilities.tooltip=Characters can purchase and benefit from SPAs. +lblUseEdge.text=Use Edge +lblUseEdge.tooltip=Characters can purchase and benefit from Edge, which allows them to re-roll\ + \ certain undesirable results. +lblUseSupportEdge.text=Support Personnel Use Edge +lblUseSupportEdge.tooltip=Allows non-combat roles to benefit from Edge re-rolls, although with a\ + \ shorter list of triggers. +lblUseImplants.text=Use Implants +lblUseImplants.tooltip=Allows personnel to have implants (e.g., Manei Domini) +lblUseAlternativeQualityAveraging.text=Use Higher-Precision Skill Averaging \u270E +lblUseAlternativeQualityAveraging.tooltip=Where two skills determine experience level, attempt to\ + \ average by number (e.g., 8/4), rather than product (e.g., Regular/Green), for greater precision. + +# createPersonnelCleanUpPanel +lblPersonnelCleanUpPanel.text=Personnel Cleanup \u270E +lblUsePersonnelRemoval.text=Enable Personnel Cleanup +lblUsePersonnelRemoval.tooltip=If enabled, personnel data will be deleted on the last day of each\ + \ month for any personnel who been left from the unit for over a month. +lblUseRemovalExemptCemetery.text=Exempt Dead Personnel +lblUseRemovalExemptCemetery.tooltip=Data from dead personnel is exempt from being cleaned up. +lblUseRemovalExemptRetirees.text=Exempt Retirees +lblUseRemovalExemptRetirees.tooltip=Data from retired personnel is exempt from being cleaned up. + +# createPersonnelLogsTab +lblPersonnelLogsPanel.text=Personnel Logs +lblUseTransfers.text=Use Reassign for Logging \u2714 +lblUseTransfers.tooltip=This saves space in the personnel log by logging a single reassignment\ + \ instead of a removal and assignment.\ +
    \ +
    Recommended: enabled, especially for larger or longer campaigns. +lblUseExtendedTOEForceName.text=Use Extended TO&E Names in Log +lblUseExtendedTOEForceName.tooltip=This options changes the force name used in the personnel\ + \ reassignment log entry from the specific force (i.e., 'Lance A') to include all parent\ + \ forces (i.e., Lance A/Company A/Burger's Burgles) +lblPersonnelLogSkillGain.text=Log Skill Gains +lblPersonnelLogSkillGain.tooltip=Add log entries to the personnel log when increasing a skill. It\ + \ doesn't include changes made in the customize person dialog nor any made before hiring a person. +lblPersonnelLogAbilityGain.text=Log SPA Gains +lblPersonnelLogAbilityGain.tooltip=Add log entries to the personnel log when gaining a special\ + \ ability. It doesn't include changes made in the customize person dialog nor any made before\ + \ hiring a person. +lblPersonnelLogEdgeGain.text=Log Edge Gains +lblPersonnelLogEdgeGain.tooltip=Add log entries to the personnel log when gaining or changing Edge.\ + \ It doesn't include changes made in the customize person dialog nor any made before hiring a\ + \ person. +lblDisplayPersonnelLog.text=Expand Personal Log by Default +lblDisplayPersonnelLog.tooltip=Determines whether the personnel log will be expanded by default +lblDisplayScenarioLog.text=Expand Scenario Log by Default +lblDisplayScenarioLog.tooltip=Determines whether the scenario log will be expanded by default +lblDisplayKillRecord.text=Expand Kill Log by Default +lblDisplayKillRecord.tooltip=Determines whether the kill log will be expanded by default + +# createPersonnelInformationTab +lblPersonnelInformation.text=Personnel Information Options \u270E +lblUseTimeInService.text=Track Time in Service +lblUseTimeInService.tooltip=Track the amount of time a person has been an active employee of the\ + \ force.\ +
    \ +
    Breaks in service are considered to be the end of their previous service and the start of a\ + \ new service duration. +lblTimeInServiceDisplayFormat.text=Display Time in Service +lblTimeInServiceDisplayFormat.tooltip=This is the format used to display the length of a person's\ + \ service. +lblUseTimeInRank.text=Track Time in Rank +lblUseTimeInRank.tooltip=Track the amount of time an active person has had their current rank in\ + \ the force. +lblTimeInRankDisplayFormat.text=Display Time in Rank +lblTimeInRankDisplayFormat.tooltip=This is the format used to display the length the person has\ + \ spent in their current rank. +lblTrackTotalEarnings.text=Track Total Earnings +lblTrackTotalEarnings.tooltip=This tracks the total amount earned by personnel. The tracking is\ + \ only done when this option is enabled, and it is not backfilled. +lblTrackTotalXPEarnings.text=Display Total Earnings +lblTrackTotalXPEarnings.tooltip=This tracks the total amount of experience earned by personnel. The\ + \ tracking is only done when this option is enabled, and it is not backfilled. +lblShowOriginFaction.text=Display Origin Faction +lblShowOriginFaction.tooltip=Display the origin faction of a person in the personnel table. + +# createAdministratorsTab +lblAdministratorsPanel.text=Administrators \u270E +lblAdminsHaveNegotiation.text=Admins Have Negotiation +lblAdminsHaveNegotiation.tooltip=Generate all admin personnel with ranks in the Negotiation skill. +lblAdminExperienceLevelIncludeNegotiation.text=Negotiation Factored into Experience Level +lblAdminExperienceLevelIncludeNegotiation.tooltip=All admin personnel will factor the Negotiation\ + \ skill into their experience level calculations (green, regular, etc.). +lblAdminsHaveScrounge.text=Admins Have Scrounge +lblAdminsHaveScrounge.tooltip=Generate all admin personnel with ranks in the Scrounge skill. +lblAdminExperienceLevelIncludeScrounge.text=Scrounge Factored into Experience Level +lblAdminExperienceLevelIncludeScrounge.tooltip=All admin personnel will factor the Scrounge skill\ + \ into their experience level calculations (green, regular, etc.). + +# createAwardsTab +lblAwardsTab.text=Awards Options \u270E \u2318 + +lblAwardBonusStyle.text=Bonuses +lblAwardBonusStyle.tooltip=Toggle XP and/or Edge bonuses from awards. +lblAwardTierSize.text=Tier Size +lblAwardTierSize.tooltip=How many times must an award be issued before using the image of the next\ + \ tier? +lblEnableAutoAwards.text=Enable autoAwards +lblEnableAutoAwards.tooltip=Enable the automatic tracking of award eligibility. +lblIssuePosthumousAwards.text=Issue Posthumous Awards +lblIssuePosthumousAwards.tooltip=Enabling this setting will qualify personnel for awards even if\ + \ they're dead (excludes formation-based kill awards). +lblIssueBestAwardOnly.text=Issue Best Award Only +lblIssueBestAwardOnly.tooltip=Activating this setting will disqualify personnel from receiving an\ + \ award if they qualify for a higher-tier award of the same type. +lblIgnoreStandardSet.text=Ignore the Standard Set +lblIgnoreStandardSet.tooltip=Ignore the default set of awards +lblAwardsTabBottom.text=Award Set Filter \u26A0 +lblAwardSetFilterList.tooltip=Include the name of any award sets you don't want managed by\ + \ autoAwards.\ +
    \ +
    Warning: The spelling and capitalization must be exact, and each award set must be\ + \ separated by a comma. While spaces can appear in the award set name, the comma shouldn't be\ + \ followed by a space. + +# createAutoAwardsFilterPanel +lblAutoAwardsFilterPanel.text=Award Tracking +lblEnableContractAwards.text=Contract +lblEnableContractAwards.tooltip=These awards are granted upon the completion of a contract if the\ + \ contract was of a specific type or duration. +lblEnableFactionHunterAwards.text=Faction Hunter \u26A0 +lblEnableFactionHunterAwards.tooltip=Faction Hunter Awards are granted for completing AtB (or\ + \ StratCon) contracts against specific Factions.\ +
    \ +
    Warning: Due to how Garrison Contracts are handled, autoAwards can only detect the\ + \ faction being faced at the time of the contract's conclusion. +lblEnableInjuryAwards.text=Injury \u26A0 +lblEnableInjuryAwards.tooltip=Injury Awards are granted at the conclusion of a scenario when a\ + \ person has sustained enough Hits during that scenario.\ +
    \ +
    Warning: The source of Hits is not tracked, so it's not possible to differentiate\ + \ between hits from enemy fire and those that are self-inflicted. +lblEnableIndividualKillAwards.text=Kill (Individual) +lblEnableIndividualKillAwards.tooltip=This covers awards earned by individual characters when\ + \ scoring confirmed kills on enemy units. +lblEnableFormationKillAwards.text=Kill (Formation) +lblEnableFormationKillAwards.tooltip=This covers awards granted through confirmed kills earned by\ + \ the entire formation (lance, star, company, etc). +lblEnableRankAwards.text=Rank +lblEnableRankAwards.tooltip=These awards are bestowed for achieving specific ranks. +lblEnableScenarioAwards.text=Scenario +lblEnableScenarioAwards.tooltip=These awards are granted for completing a certain number of scenarios. +lblEnableSkillAwards.text=Skill +lblEnableSkillAwards.tooltip=These awards are presented for reaching specific skill levels. +lblEnableTheatreOfWarAwards.text=Theatre of War +lblEnableTheatreOfWarAwards.tooltip=These awards are granted for accepting contracts from\ + \ belligerents during a time of war. +lblEnableTimeAwards.text=Time \u26A0 +lblEnableTimeAwards.tooltip=These awards are granted for serving in a unit for a specific duration,\ + \ such as 'serving 8 years without disciplinary action.'\ +
    \ +
    Warning: While autoAwards can determine when personnel might be eligible for such an\ + \ award, additional details, such as any disciplinary actions, are not tracked. +lblEnableTrainingAwards.text=Training \u26A0 +lblEnableTrainingAwards.tooltip=These are awards issued for graduating from academies.\ +
    \ +
    Requirement: the Education module must also be enabled for this option to function. +lblEnableMiscAwards.text=Misc +lblEnableMiscAwards.tooltip=These awards aren't covered by the other categories. + +# createPrisonersAndDependentsTab +lblPrisonersAndDependentsTab.text=Dependent & Prisoner Options \u270E \u2318 + +# createPrisonersPanel +lblPrisonersPanel.text=Prisoners +lblPrisonerCaptureStyle.text=Capture Style +lblPrisonerCaptureStyle.tooltip=This is the style of logic to use when determining if a person has\ + \ been captured by the force following a scenario. +lblPrisonerStatus.text=Default Status +lblPrisonerStatus.tooltip=This is the default prisoner status assigned when a person is captured.\ + \ Defectors are prisoners that are willing to defect to your forces. +lblPrisonerBabyStatus.text=Prisoner Babies Share Mother's Status +lblPrisonerBabyStatus.tooltip=Babies born to prisoners will share their mother's prisoner status.\ + \ Otherwise, they will instead be added to your campaign as a dependent. +lblAtBPrisonerDefection.text=Enable Defection +lblAtBPrisonerDefection.tooltip=This adds a random roll to determine if a prisoner is willing to\ + \ defect.\ +
    \ +
    This option will be ignored if 'Prisoner Defector' is selected for the default prisoner status. +lblAtBPrisonerRansom.text=Enable Prisoner Ransoms +lblAtBPrisonerRansom.tooltip=Prisoners can be ransomed back to their previous faction. + +# createDependentsPanel +lblDependentsPanel.text=Dependents +lblUseRandomDependentAddition.text=Random Addition +lblUseRandomDependentAddition.tooltip=This enables the monthly addition of random dependents to the\ + \ campaign. +lblUseRandomDependentRemoval.text=Random Removal +lblUseRandomDependentRemoval.tooltip=This enables the monthly removal of random dependents from the\ + \ campaign. + +# createMedicalTab +lblMedicalTab.text=Medical Options +lblUseAdvancedMedical.text=Use Advanced Medical \u270E \u2318 +lblUseAdvancedMedical.tooltip=Use the Advanced Medical Rules. +lblHealWaitingPeriod.text=Days Between Healing Checks +lblHealWaitingPeriod.tooltip=This is the number of days between each time a doctor makes a healing\ + \ check for a wounded person. +lblNaturalHealWaitingPeriod.text=Days Between Natural Healing Checks +lblNaturalHealWaitingPeriod.tooltip=This is the number of days between each time a person naturally\ + \ heals when not being tended to by a doctor. +lblMinimumHitsForVehicles.text=Minimum Number of Hits for Vehicle Crew +lblMinimumHitsForVehicles.tooltip=This is the minimum number of hits received by wounded crews and\ + \ infantry during scenario resolution after being wounded in a scenario. +lblUseRandomHitsForVehicles.text=Randomized Vehicle Crew Hits +lblUseRandomHitsForVehicles.tooltip=The number of hits that crews and infantry receive will be\ + \ randomly selected between the minimum value and five, during scenario resolution. +lblUseTougherHealing.text=Use Tougher Healing \u270E +lblUseTougherHealing.tooltip=The healing check will be penalized for every additional hit above 2. +lblMaximumPatients.text=Beds per Doctor +lblMaximumPatients.tooltip=This is the maximum number of patients that can be treated per doctor. + +# createSalariesTab +lblSalariesTab.text=Salary Options +lblDisableSecondaryRoleSalary.text=Disable Secondary Role Salaries \u270E +lblDisableSecondaryRoleSalary.tooltip=Determines whether personnel will have their salaries\ + \ increased by their secondary role (if any). + +# createSalaryMultipliersPanel +lblSalaryMultipliersPanel.text=Role Multipliers +lblAntiMekSalary.text=Anti-Mek +lblAntiMekSalary.tooltip=Anti-Mek trained soldiers and battle armor/elemental personnel have their\ + \ salaries multiplied by this. +lblSpecialistInfantrySalary.text=Specialist Infantry +lblSpecialistInfantrySalary.tooltip=Soldiers assigned to specialist infantry units have their\ + \ salaries multiplied by this. + +# createExperienceMultipliersPanel +lblExperienceMultipliersPanel.text=Experience Multipliers +lblSkillLevelNone.text=None +lblSkillLevelMultiplier.tooltip=The experience multiplier is multiplicatively applied to the base\ + \ salary as part of determining the final salary. +lblSkillLevelUltra-Green.text=Ultra-Green +lblSkillLevelGreen.text=Green +lblSkillLevelRegular.text=Regular +lblSkillLevelVeteran.text=Veteran +lblSkillLevelElite.text=Elite +lblSkillLevelHeroic.text=Heroic +lblSkillLevelLegendary.text=Legendary + +# createBaseSalariesPanel +lblBaseSalariesPanel.text=Base Salaries + +# Biography Tab +biographyContentTabs.title=Biography + +biographyGeneralTab.title=General +biographyGeneralTab.border="With warfare an accepted part of everyday life and competition rife\ + \ between Great Houses, Minor Houses, Periphery warlords, Bandit Kings, mercantile combines, and\ + \ anyone else with the money and motivation to make a play for resources or power, the profession\ + \ of arms reached new heights of respect and importance. The limited availability of war machines\ + \ and trained men left every interested party in known space scrambling to find capable soldiers\ + \ and 'Mechs. A professional unit with a good reputation was in the enviable situation of the\ + \ proverbial man with a better mousetrap. Was it any wonder that mercenary troops became a\ + \ staple of the Successor States?"\ +
    Major Charlene Fellowes\ +
    From Under Four Flags: My Life as a Mercenary, New Avalon Press, 3023
    + +backgroundsTab.title=Backgrounds +backgroundsTab.border="Now, Krieger and Farber are two of the best Techs in the sphere, and two of\ + \ the best scroungers. They got some facing off a wrecked building to cover us. That stuff works,\ + \ but it needs outer covering, so it was like Christmas to go into a factory and find some truck\ + \ trailers. It must have been a meat packing plant, because the trucks were all painted with\ + \ product logos. Now, I agree with Williams that there must have been enough plain metal to go\ + \ around, but if the kids wanted to have some fun, who am I to argue? Hot mechanics are a rare\ + \ gift, so cut them some slack.\ +
    \ +
    "We left them alone because there weren't enough tools for everyone. And besides, Williams is\ + \ totally inept at repairs, but don't quote me. When we got back, Krieger was gone. Farber was\ + \ there, laughing, and asked how we liked the new design.\ +
    \ +
    "Down my Wolverine's arms, it read 'SpamSpamSpam SpamSpamSpamSpamSpam'. Kilmer's Warhammer\ + \ looked OK until you walked around it, and the whole rear torso, in big yellow letters, screamed\ + \ 'LARD.'"\ +
    \ +
    "We were both laughing, but Williams took one look, grabbed Farber's wrench, and chased her\ + \ until we grabbed him. Up and down both his 'Mech's legs and on its right torso, like a badge,\ + \ it read 'Processed Chicken.'"\ +
    Unknown MechWarrior\ +
    3rd Succession War
    + +deathTab.title=Death +deathTab.border="School didn't teach me how to explode; I just learn fast."\ +
    Sergeant Pete "Wildfire" Watson\ +
    The Shenanigans Squadron
    + +educationTab.title=Education +educationTab.border="It's a sad comment on urban living when you see a Battle Master mug a Phoenix\ + \ Hawk in a blind alley."\ +
    Unknown MechWarrior\ +
    3rd Succession War
    + +nameAndPortraitGenerationTab.title=Name & Portraits +nameAndPortraitGenerationTab.border="Look at the bright side, kid. You get to keep all the money."\ +
    Unknown MechWarrior\ +
    2nd Succession War
    + +rankTab.title=Rank Systems +rankTab.border="Rank isn't about authority \u2014 it's about responsibility. Every step up means you're\ + \ carrying more than your own survival."\ +
    Kommandant Clara "Demo" Bertsimas\ +
    Herc's Jerks
    + +# createGeneralTab +lblBiographyGeneralTab.text=General Options \u270E +lblUseDylansRandomXP.text=Use Random XP +lblUseDylansRandomXP.tooltip=Use Dylan's optional random XP on creation of a new person (20% chance\ + \ each of zero, 1, 2, 3, and randomized between 1 and 8 XP) +lblGender.text=Percent Female +lblGender.tooltip=What percentage of personnel generated should be female +lblNonBinaryDiceSize.text=Non-Binary Personnel Chance: \u26A0 1 in +lblNonBinaryDiceSize.tooltip=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%. +lblFamilyDisplayLevel.text=Family Display Depth \u26A0 +lblFamilyDisplayLevel.tooltip=This setting is the relation to the selected character that MekHQ will\ + \ display up to, including the previous levels.\ +
    \ +
    Warning: Higher levels require more processing when loading a character. + +# createAnniversariesPanel +lblAnniversariesPanel.text=Anniversaries +lblAnnounceBirthdays.text=Announce Birthdays +lblAnnounceBirthdays.tooltip=A daily report notification will occur whenever a character celebrates\ + \ their birthday. +lblAnnounceRecruitmentAnniversaries.text=Announce Recruitment Anniversaries +lblAnnounceRecruitmentAnniversaries.tooltip=A daily report notification will occur whenever a\ + \ character celebrates a recruitment anniversary. +lblAnnounceOfficersOnly.text=Officers Only +lblAnnounceOfficersOnly.tooltip=Only report officer anniversaries. +lblAnnounceChildBirthdays.text=Always Announce 18th Birthdays \u26A0 +lblAnnounceChildBirthdays.tooltip=If enabled, 18th birthdays will always be announced.\ +
    \ +
    Warning: Enabling this option will override any other settings. +# createBackgroundsTab +lblBackgroundsTab.text=Background Options \u270E -### Dialog Properties -CampaignOptionsDialog.title=Campaign Options +# createRandomBackgroundsPanel +lblRandomBackgroundsPanel.text=Random Backgrounds +lblUseRandomPersonalities.text=Assign Random Personalities \u2318 +lblUseRandomPersonalities.tooltip=Personnel are generated with random personality traits, quirks\ + \ and intelligence.\ +
    \ +
    Intelligence affects a character's ability to graduate from education module academies. +lblUseRandomPersonalityReputation.text=Personality Influences Reputation +lblUseRandomPersonalityReputation.tooltip=If enabled, the personality of the campaign commander\ + \ will impact the unit's Reputation. +lblUseIntelligenceXpMultiplier.text=Intelligence Influences XP Costs +lblUseIntelligenceXpMultiplier.tooltip=If enabled, a character's intelligence will influence all XP\ + \ costs. +lblUseSimulatedRelationships.text=Simulate Background Relationships \u26A0 +lblUseSimulatedRelationships.tooltip=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. + +# createRandomOriginOptionsPanel +lblRandomOriginOptionsPanel.text=Origins +lblRandomizeOrigin.text=Randomize Origin +lblRandomizeOrigin.tooltip=This option determines whether new characters should randomize their\ + \ origins based on campaign faction and pla +lblRandomizeDependentsOrigin.text=Randomize Dependent Origins +lblRandomizeDependentsOrigin.tooltip=This generates a random origin faction and planet for\ + \ personnel based on the selected settings +lblRandomizeAroundSpecifiedPlanet.text=Randomize Around Specific Planet +lblRandomizeAroundSpecifiedPlanet.tooltip=Dependents have their origins randomized so that they do\ + \ not come from the current planet and the campaign's faction but instead have origins randomized\ + \ in the same way as standard personnel. +lblSpecifiedSystemFactionSpecific.text=Faction Specific +lblSpecifiedSystemFactionSpecific.tooltip=Limit the system options based on faction. +lblSpecifiedSystem.text=System +lblSpecifiedSystem.tooltip=This is the system from which to select the specified planet around\ + \ which origin planet and faction are randomized. +lblSpecifiedPlanet.text=Planet +lblSpecifiedPlanet.tooltip=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. +lblOriginSearchRadius.text=Random Origin Radius +lblOriginSearchRadius.tooltip=This is the radius in light years from the current planet to search\ + \ for possible origin planets and factions. +lblOriginDistanceScale.text=Distance Scale \u26A0 +lblOriginDistanceScale.tooltip=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. +lblAllowClanOrigins.text=Allow Clan Origins +lblAllowClanOrigins.tooltip=Generate Clan origin factions for factions that aren't tagged as Clan. +lblExtraRandomOrigin.text=Extra Random Origins +lblExtraRandomOrigin.tooltip=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). + +# createDeathTab +lblDeathTab.text=Death Options \u270E +lblKeepMarriedNameUponSpouseDeath.text=Keep Married Name on Spouse Death +lblKeepMarriedNameUponSpouseDeath.tooltip=When a person's spouse dies they will keep their marital\ + \ name instead of returning to their birth name. +lblRandomDeathMethod.text=Random Death Method \u2714 +lblRandomDeathMethod.tooltip=This is the method used to determine if a person dies from random death.\ +
    \ +
    Recommended: Exponential Random Death is the best system as it closely follows a\ + \ realistic death curve. +lblUseRandomClanPersonnelDeath.text=Enable Random Clan Death +lblUseRandomClanPersonnelDeath.tooltip=Allow clan-origin personnel to randomly die. +lblUseRandomPrisonerDeath.text=Enable Random Prisoner Death +lblUseRandomPrisonerDeath.tooltip=Allow prisoners to randomly die. Bondsmen are treated as free\ + \ personnel when it comes to this option, and are thus not affected by it. +lblUseRandomDeathSuicideCause.text=Enable Cause of Death: Suicide +lblUseRandomDeathSuicideCause.tooltip=This includes suicide as a potential cause for a random death. +lblPercentageRandomDeathChance.text=Random Death Percentage \u26A0 +lblPercentageRandomDeathChance.tooltip=This is the percent chance per day that any member of your\ + \ force will randomly die.\ +
    \ +
    Requirement: This option is only relevant if 'Percentage' random death is selected. + +# createDeathAgeGroupsPanel +lblDeathAgeGroupsPanel.text=Death by Age Group + +# createEducationTab +lblEducationTab.text=Education Options \u270E \u2318 +lblUseEducationModule.text=Enable Education Module +lblUseEducationModule.tooltip=Enables the Education Module. +lblCurriculumXpRate.text=Base XP Rate +lblCurriculumXpRate.tooltip=Some curriculum award xp (outside monthly random xp). This setting\ + \ determines how much XP should be gained per education level. +lblMaximumJumpCount.text=Maximum Jump Radius +lblMaximumJumpCount.tooltip=The maximum distance when searching for academies. +lblUseReeducationCamps.text=Enable Faction Changes +lblUseReeducationCamps.tooltip=Should reeducation camps change a students' origin faction to match\ + \ the campaign faction? +lblEnableOverrideRequirements.text=Override Requirements \u26A0 +lblEnableOverrideRequirements.tooltip=Overrides the requirements for attending academies.\ +
    \ +
    Warning: Included for testing purposes and not intended for general play. +lblShowIneligibleAcademies.text=Show Ineligible Academies +lblShowIneligibleAcademies.tooltip=Displays academies a character is ineligible to attend. +lblEntranceExamBaseTargetNumber.text=Entrance Exam Target Number \u26A0 +lblEntranceExamBaseTargetNumber.tooltip=The base target number for Prestigious Academy entrance exams.\ +
    \ +
    The final TN is this value minus Faculty Skill, opposed by 2d6 + Intelligence/4 or just 2d6\ + \ (if personalities are disabled) + +# createEnableStandardSetsPanel +lblEnableStandardSetsPanel.text=Enable Academy Sets +lblEnableLocalAcademies.text=Local Academies +lblEnableLocalAcademies.tooltip=Allows personnel to enroll in local, planet-side academies. +lblEnablePrestigiousAcademies.text=Prestigious Academies +lblEnablePrestigiousAcademies.tooltip=Allows personnel to enroll in named, canonical, academies. +lblEnableUnitEducation.text=Unit Education +lblEnableUnitEducation.tooltip=Allows personnel to enroll in education and training conducted by\ + \ the unit. These are generally inferior to prestigious or local academies. + +# createXpAndSkillBonusesPanel +lblXpAndSkillBonusesPanel.text=Faculty XP & Skill Bonuses +lblEnableBonuses.text=Enable Graduation Bonuses +lblEnableBonuses.tooltip=Enables the chance of gaining a permanent skill bonus when graduating. +lblFacultyXpMultiplier.text=Faculty XP Multiplier +lblFacultyXpMultiplier.tooltip=This value multiplies the number of XP gained when completing (or\ + \ partially completing) a qualification.\ +
    \ +
    Setting this to zero disables faculty XP. -## General Tab -generalPanel.title=General -unitRatingMethodLabel.text=Unit Reputation Method: -manualUnitRatingModifierLabel.text=Manual Reputation Modifier -lblName.text=Name: -lblNameGenerator.text=New Name -lblDate.text=Date: -lblFaction.text=Faction: -lblFactionNames.text=Faction: -lblGender.text=Percent Female: -lblCamo.text=Camo: -lblIcon.text=Unit Icon: -##end General Tab - -## Repair and Maintenance Tab -repairAndMaintenancePanel.title=Repair and Maintenance -lblSubRepair.text=Repair -lblSubMaintenance.text=Maintenance -checkMaintenance.text=Make maintenance checks -checkMaintenance.toolTipText=If checked, each unit will make a parts-specific maintenance check at the end of each maintenance cycle -lblMaintenanceDays.text=Maintenance cycle length in days (combat) -lblMaintenanceDays.toolTipText=This setting determines how frequently maintenance checks are made while on non-Garrison contracts. Peacetime frequency is x4 this value. -lblMaintenanceBonus.text=Maintenance modifier -spnMaintenanceBonus.toolTipText=When checking by parts, Strat Ops applies a -1 bonus to the maintenance check target - you can change this modifier with this value -lblDefaultMaintenanceTime.text=Default Maintenance Time -lblDefaultMaintenanceTime.toolTipText=This setting determines the maintenance time modifier applied to all new units. -useQualityMaintenance.text=Use quality modifiers in maintenance checks -useQualityMaintenance.toolTipText=If checked, quality modifiers will be added to maintenance checks (WARNING: this will lead to unstable quality ratings over time) -reverseQualityNames.text=Reverse quality names (Resets Finance Tab) -reverseQualityNames.toolTipText=If checked, quality name reporting will be reversed so that A is the best and F is the worst.\ -
    \ -
    >Warning: toggling this option will reset all options in the Finances tab to their last saved state. -chkUseRandomUnitQualities.text=Random New Unit Quality -chkUseRandomUnitQualities.toolTipText=If checked, the quality of new units is randomized when they are added to the campaign. Otherwise, quality will always default to D. -chkUsePlanetaryModifiers.text=Use CamOps Planetary Modifiers -chkUsePlanetaryModifiers.toolTipText=If checked, certain planetary conditions will impact maintenance checks when units are stored outside of Maintenance Facilities. -useUnofficialMaintenance.text=Only damage parts that are already at the worst quality (Unofficial) -useUnofficialMaintenance.toolTipText=When this option is checked, and you are using quality maintenance,
    the margin of failure rolls for damaging parts only happen for parts that are already rated at the lowest level. -logMaintenance.text=Log maintenance rolls in log file -logMaintenance.toolTipText=If checked, details of each maintenance check will be added to mekhq.log -useEraModsCheckBox.text=Use era mods for repair rolls -useEraModsCheckBox.toolTipText=Use faction-specific era mods for repair rolls -assignedTechFirstCheckBox.text=Place tech assigned to unit at the top of the list for repairs -assignedTechFirstCheckBox.toolTipText=In the repair bay, if the unit has an assigned technician, that technician is placed at the top of the list of available technicians. -resetToFirstTechCheckBox.text=After repair reset to the technician at the top of the list -resetToFirstTechCheckBox.toolTipText=After any repair/salvage task, always selects the technician at the top of the list
    (Can be combined with option to place tech assigned to unit at top of list) -useDamageMargin.text=Damage/destroy parts by margin of failure -useDamageMargin.toolTipText=Instead of destroying/damaging parts only by elite techs, parts are damaged/destroyed when the tech roll fails by a given margin. -lblDamageMargin.text=Margin: -useAeroSystemHits.text=(Proposed Errata) Damage/destroy Aero system parts by number of hits taken -useAeroSystemHits.toolTipText=Changes repair/replacement time and difficulty of Aero large and small craft systems (Avionics, FCS, etc) -useQuirksBox.text=Use Quirk -useQuirksBox.toolTipText=Allow units to have StratOps quirks -lblDestroyPartTarget.text=Equipment hit in combat survives on a roll of -lblDestroyPartTargetSuffix.text=or better -##end Repair and Maintenance Tab - -## Supplies and Acquisition Tab -suppliesAndAcquisitionsPanel.title=Supplies and Acquisition -lblSubAcquire.text=Acquisition -lblSubDelivery.text=Delivery -lblSubPlanetAcquire.text=Planetary Acquisition -lblAcquireSkill.text=Acquisition Skill: -lblSupportStaffOnly.text=Only support personnel can make acquisition checks -lblWaitingPeriod.text=Waiting period (in days) between acquisition rolls -spnMaxAcquisitions.toolTipText=This number is the maximum number of acquisitions an administrator can attempt each day. Setting this to 0 makes it unlimited. -lblMaxAcquisitions.text=Max Daily Acquisitions Per Admin (0 for unlimited) -lblAcquireClanPenalty.text=Penalty for Clan equipment -lblAcquireIsPenalty.text=Penalty for Inner Sphere equipment -lblTransitTime.text=Delivery Time: -lblMinTransit.text=Minimum Transit Time: -lblMosBonus.text=Reduce delivery time by -lblMosBonusSuffix.text=per margin of success -usePlanetaryAcquisitions.text=Use Planet-based Acquisition System (overrides default delivery times) -usePlanetaryAcquisitions.toolTipText=Supplies will be searched for on specific planets up to a certain jump radius away, starting on current planet.
    To find a given part, an availability roll must first succeed to identify a contact and then parts may be rolled for until a failure.
    Planetary socio-industrial ratings can affect the target roll.
    All delivery times are based on the number of recharges necessary (7 days), the in-system transit time at both points, and 1d6 extra days per jump. -usePlanetaryAcquisitionsVerbose.text=Verbose Planetary Acquisitions Reporting -usePlanetaryAcquisitionsVerbose.toolTipText=This shows all the details of the planetary searches in the daily log.
    This is usually TMI, but is good for testing and for understanding how it works.
    Warning: This option can lead to a huge daily report, which may create a saved campaign file that requires more than the default amount of RAM assigned to MHQ to load, or just takes a significant amount of time to do so. -lblMaxJumpPlanetaryAcquisitions.text=Maximum number of jumps away to search for supplies -lblPlanetaryAcquisitionsFactionLimits.text=Faction supply limitations -disallowPlanetaryAcquisitionClanCrossover.text=No Clan/Inner Sphere supply sharing -disallowPlanetaryAcquisitionClanCrossover.toolTipText=If checked, Inner Sphere factions will not be able to find supplies on Clan-controlled worlds, and vice versa. -disallowClanPartsFromIS.text=Disallow acquiring clan parts from non-Clan factions -disallowClanPartsFromIS.toolTipText=If checked, you will not be able to purchase clan parts from non-Clan factions. The modifier below will be ignored. -spnPenaltyClanPartsFromIS.text=Penalty for acquiring clan parts from non-Clan factions -spnPenaltyClanPartsFromIS.toolTipText=The penalty for acquiring clan parts from non-Clan factions when not disallowed entirely.
    This penalty is cumulative with the general penalty for clan parts. -lblTechBonus.text=Tech -lblIndustryBonus.text=Industry -lblOutputBonus.text=Output -lblSocioIndustrialBonusPanel.text=Planet socio-industrial modifiers -##end Supplies and Acquisition Tab - -## Tech Limits Tab -techLimitsPanel.title=Tech Limits -limitByYearBox.text=Limit Units and Parts by Year -limitByYearBox.toolTipText=Units and parts will be limited by the current year of the campaign -disallowExtinctStuffBox.text=Disallow extinct units and parts -disallowExtinctStuffBox.toolTipText=Normally extinct parts can still be found with a penalty. This option will disallow them completely. -allowClanPurchasesBox.text=Allow the purchasing of Clan units and parts -allowClanPurchasesBox.toolTipText=If unchecked, clan units and parts will not be available for purchase -allowISPurchasesBox.text=Allow the purchasing of Inner Sphere units and parts -allowISPurchasesBox.toolTipText=If unchecked, inner sphere units and parts will not be available for purchase -allowCanonOnlyBox.text=Only allow canon units for purchase -allowCanonOnlyBox.toolTipText=Just what it says -allowCanonRefitOnlyBox.text=Only allow canon variants as refits -allowCanonRefitOnlyBox.toolTipText=Just what it says -variableTechLevelBox.text=Variable tech level -variableTechLevelBox.toolTipText=If checked, the tech level for a part or unit will change depending on whether it is in
    prototype stage (experimental), production (advanced), or common (standard). -factionIntroDateBox.text=Use faction intro dates -factionIntroDateBox.toolTipText=If checked, the campaign faction will be used to determine a part's intro date in cases where it differs.
    If using variable tech levels, the faction is also used to determine the tech level of a part or unit. -useAmmoByTypeBox.text=Use ammo by type (Unofficial) -useAmmoByTypeBox.toolTipText=LRM, SRM, MRM, and MML launchers can mix ammo among sizes. For example, an LRM-10 can use LRM-20 ammo. -lblSkillTarget.text=Target Number: -lblSkillGreen.text=Green Level: -lblSkillRegular.text=Regular Level: -lblSkillVeteran.text=Veteran Level: -lblSkillElite.text=Elite Level: -lblTechLevel.text=Maximum Tech Level: -##end Tech Limits Tab - -## Personnel Tab -personnelPanel.title=Personnel - -## Turnover and Retention Tab -turnoverAndRetentionPanel.title=Turnover and Retention - -## Header -chkUseRandomRetirement.text=Enable Employee Turnover -chkUseRandomRetirement.toolTipText=Determines whether personnel will randomly leave the unit. - -## Settings -turnoverAndRetentionSettingsPanel.title=Settings +# createDropoutChancePanel +lblDropoutChancePanel.text=Weekly Dropout Chances +lblAdultDropoutChance.text=Adult Dropout Chance: 1 in +lblAdultDropoutChance.tooltip=The number of sides on the die used to determine random weekly\ + \ dropouts. A dropout occurs on a roll of 1.\ +
    \ +
    Setting this to 0 disables random dropouts. +lblChildrenDropoutChance.text=Child Dropout Chance: 1 in +lblChildrenDropoutChance.tooltip=The number of sides on the die used to determine random weekly\ + \ dropouts. A dropout occurs on a roll of 1.\ +
    \ +
    Setting this to 0 disables random dropouts. + +# createAccidentsAndEventsPanel +lblAccidentsAndEventsPanel.text=Weekly Accidents & Events +lblAllAges.text=All Ages Affected +lblAllAges.tooltip=If enabled, children will not always survive training accidents\ + \ or negative events. +lblMilitaryAcademyAccidents.text=Military Accidents Chance: 1 in +lblMilitaryAcademyAccidents.tooltip=The number of sides on the die used to determine random weekly\ + \ (potentially fatal) accidents. An accident occurs on a roll of 1.\ +
    \ +
    Setting this to 0 disables random accidents. + +# createNameAndPortraitGenerationTab +lblNameAndPortraitGenerationTab.text=Name and Portrait Generation Options \u270E \u2318 +lblUseOriginFactionForNames.text=Assign Names Based on Origin Faction +lblUseOriginFactionForNames.tooltip=The random name generator uses the person's origin faction to\ + \ assign their name. +lblFactionNames.text=Fixed Faction +lblFactionNames.tooltip=All names will be generated based on the selected faction. +lblAssignPortraitOnRoleChange.text=Reassign Portrait on Role Change +lblAssignPortraitOnRoleChange.tooltip=With this enabled, a person without a portrait will\ + \ automatically gain a random portrait when their primary role is changed switched. + +# createRandomPortraitPanel +lblRandomPortraitPanel.text=Portrait Assignment +lblEnableAllPortraits.text=Enable All +lblEnableAllPortraits.tooltip=Enable all portrait assignment options +lblDisableAllPortraits.text=Disable All +lblDisableAllPortraits.tooltip=Disable all portrait assignment options + +# createRankTab +lblRankTab.text=Rank Systems \u2318 +lblRankTabBody.text=

    You can use the table below to assign ranks for your campaign. Choose one of\ + \ the preset rank systems from the dropdown menu, or create your own by designing a custom rank\ + \ system.

    \ + \ +
      \ +
    • A single rank system can be saved as part of the campaign.
    • \ +
    • Additional custom rank systems will be stored in the user data file. Any extra rank\ + \ systems specific to the campaign will be deleted.
    • \ +
    \ + \ +

    You can also assign custom salary multipliers. These multipliers are independent of the officer\ + \ multiplier, which is addressed elsewhere.

    \ + \
    \ +

    Important Notes:

    \ + \ +
      \ +
    1. \ + Custom Rank System Deletion:\ +
      This dialog doesn't provide a warning about the deletion of campaign-specific custom\ + \ rank systems that aren't selected for the current campaign.\ +
    2. \ +
    3. \ + Rank Revalidation:\ +
      When changes are made, all personnel ranks will be revalidated to align with the new\ + \ rank system.\ +
    4. \ +
    5. \ + Data Validation:\ +
      This dialog doesn't validate the data, so:\ +
        \ +
      • Avoid circular logic.
      • \ +
      • Ensure you have a valid E0 rank:\ +
          \ +
        • At least one E0 rank must exist, with a name like "None" or "Grunt."
        • \ +
        • The "None" rank is handled in the code to display as a blank string when\ + \ the rank name is shown.
        • \ +
        \ +
      • \ +
      \ +
    6. \ +
    + + +# Turnover and Rention Tab +turnoverAndRetentionContentTabs.title=Turnover & Retention + +turnoverTab.title=Turnover +turnoverTab.border="For a MekWarrior, retirement is a strange thing. We spend our lives in battle,\ + \ and when the guns go silent, we find out who we are without them."\ +
    Colonel Sofia "Whisper" Talon\ +
    Ark's Bonked Battalion
    + +fatigueTab.title=Fatigue +fatigueTab.border="I say to Hell with Information! Ammunition is Ammunition!"\ +
    Unknown Mercenary\ +
    Clan Invasion
    + +# createTurnoverTab +lblTurnoverTab.text=Turnover Options \u270E \u2318 +lblTurnoverTabBody.text=Turnover is a reasonably complex system. It is highly recommended that you\ + \ read through the documentation: MekHQ/docs/Personnel Modules/Turnover and Retention.pdf +lblUseRandomRetirement.text=Enable Random Turnover +lblUseRandomRetirement.tooltip=Determines whether personnel will randomly leave the unit. + +# createSettingsPanel +lblSettingsPanel.text=Settings lblTurnoverFixedTargetNumber.text=Target Number -lblTurnoverFixedTargetNumber.toolTipText=The base target number for turnover checks. -lblTurnoverFrequency.text=Turnover Frequency -lblTurnoverFrequency.toolTipText=How frequently should Turnover checks be prompted? -chkUseContractCompletionRandomRetirement.text=Use Contract Completion Turnover Rolls -chkUseContractCompletionRandomRetirement.toolTipText=Make a Turnover check for all active personnel at the end of a contract. -chkUseRandomFounderTurnover.text=Use Random Founder Turnover -chkUseRandomFounderTurnover.toolTipText=Allow Founders to randomly leave the unit (founders can still retire). -chkUseFounderRetirement.text=Use Random Founder Retirement -chkUseFounderRetirement.toolTipText=Allow Founders to randomly retire from the unit once they are aged 50 or older. -chkUseSubContractSoldiers.text=Soldiers Use Commander's Turnover Roll -chkUseSubContractSoldiers.toolTipText=Infantry commanders make Turnover rolls for all soldiers in their unit. This means they will defect, resign, and retire as a unit. +lblTurnoverFixedTargetNumber.tooltip=The base target number for turnover checks. +lblTurnoverFrequency.text=Frequency \u2714 +lblTurnoverFrequency.tooltip=How frequently should Turnover checks be prompted?\ +
    \ +
    Recommended: Turnover is an important balancing mechanic designed to slow power\ + \ progression. It is recommended to use the default value of 'monthly.' +lblUseContractCompletionRandomRetirement.text=Enable End of Contract Turnover +lblUseContractCompletionRandomRetirement.tooltip=Make a Turnover check for all active personnel at\ + \ the end of a contract. +lblUseRandomFounderTurnover.text=Enable Founder Turnover +lblUseRandomFounderTurnover.tooltip=Allow Founders to randomly leave the unit (founders can still\ + \ retire). +lblTrackOriginalUnit.text=Track Original Unit +lblTrackOriginalUnit.tooltip=Personnel, who have their own unit when recruited, will take the same\ + \ unit when they go if it is still available. +lblAeroRecruitsHaveUnits.text=AeroTek Recruits Have Units +lblAeroRecruitsHaveUnits.tooltip=AeroSpace pilot recruits can have fighters, and those that do will\ + \ take a fighter with them when leaving the unit. +lblUseSubContractSoldiers.text=Sub-Contract Soldiers +lblUseSubContractSoldiers.tooltip=Infantry commanders make Turnover rolls for all soldiers in their\ + \ unit. This means they will defect, resign, and retire as a unit. lblServiceContractDuration.text=Service Contract Duration -lblServiceContractDuration.toolTipText=Once recruited, this is the minimum amount of months personnel will remain with the unit (set to 0 to disable service contracts). +lblServiceContractDuration.tooltip=Once recruited, this is the minimum amount of months personnel\ + \ will remain with the unit (set to 0 to disable service contracts). lblServiceContractModifier.text=Service Contract Modifier -lblServiceContractModifier.toolTipText=While personnel are under contract, this value reduces their Turnover target number. -chkPayBonusDefault.text=Automate Retention Bonuses -chkPayBonusDefault.toolTipText=Personnel with a turnover target number equal (or exceeding) the below threshold will have their retention bonus paid by default. +lblServiceContractModifier.tooltip=While personnel are under contract, this value reduces their\ + \ Turnover target number. +lblPayBonusDefault.text=Automate Retention Bonuses +lblPayBonusDefault.tooltip=Personnel with a turnover target number equal (or exceeding) the below\ + \ threshold will have their retention bonus paid by default. lblPayBonusDefaultThreshold.text=Bonus Threshold -lblPayBonusDefaultThreshold.toolTipText=This option sets the threshold for default bonus payouts (if the above option is enabled). - -## Modifiers -turnoverAndRetentionModifiersPanel.title=Modifiers -chkUseCustomRetirementModifiers.text=Custom -chkUseCustomRetirementModifiers.toolTipText=Allows you to manually provide additional modifiers to the Turnover check. -chkUseSkillModifiers.text=Desirability -chkUseSkillModifiers.toolTipText=If enabled, better skilled personnel have a higher turnover target number. -chkUseFatigueModifiers.text=Fatigue -chkUseFatigueModifiers.toolTipText=If enabled, fatigued personnel have a higher turnover target number. -chkUseAgeModifiers.text=Age -chkUseAgeModifiers.toolTipText=If enabled, the age of personnel will influence their turnover target number. -chkUseUnitRatingModifiers.text=Unit Rating -chkUseUnitRatingModifiers.toolTipText=If enabled, the rating of the unit will influence the turnover target number for all personnel. -chkUseFactionModifiers.text=Faction -chkUseFactionModifiers.toolTipText=If enabled, campaign and personnel factions may influence the turnover target number. -chkUseMissionStatusModifiers.text=Mission Status -chkUseMissionStatusModifiers.toolTipText=Allows mission failure; success; and contract breaches to influence the turnover target number. -chkUseHostileTerritoryModifiers.text=Hostile Territory -chkUseHostileTerritoryModifiers.toolTipText=If StratCon or AtB is enabled, personnel will reduce their turnover target numbers while not on a defensive contract.\ -
    \ -
    If AtB is disabled, personnel will reduce their turnover target number while on any active contract. -chkUseFamilyModifiers.text=Family -chkUseFamilyModifiers.toolTipText=Marriage to active personnel, or having adult children among active personnel, will influence the turnover target number. -chkUseLoyaltyModifiers.text=Use Loyalty -chkUseLoyaltyModifiers.toolTipText=If enabled, personnel have a random loyalty rating ranging between -3 and 3. - -chkUseHideLoyalty.text=Hide Loyalty -chkUseHideLoyalty.toolTipText=If enabled, loyalty modifiers will be hidden. - -## Payouts -turnoverAndRetentionPayoutPanel.title=Payouts -lblPayoutRateOfficer.text=Officer Payout Rate -lblPayoutRateOfficer.toolTipText=The number of months' pay officers get when resigning from the unit. -lblPayoutRateEnlisted.text=Enlisted Payout Rate -lblPayoutRateEnlisted.toolTipText=The number of months' pay enlisted personnel get when resigning from the unit. +lblPayBonusDefaultThreshold.tooltip=This option sets the threshold for default bonus payouts (if\ + \ the above option is enabled). + +# createModifiersPanel +lblTurnoverModifiersPanel.text=Modifiers +lblUseCustomRetirementModifiers.text=Custom +lblUseCustomRetirementModifiers.tooltip=Allows you to manually provide additional modifiers to the\ + \ Turnover check. +lblUseFatigueModifiers.text=Fatigue Modifiers +lblUseFatigueModifiers.tooltip=If enabled, fatigued personnel have a higher turnover target number. +lblUseSkillModifiers.text=Desirability +lblUseSkillModifiers.tooltip=If enabled, better skilled personnel have a higher turnover target\ + \ number. +lblUseAgeModifiers.text=Age +lblUseAgeModifiers.tooltip=If enabled, the age of personnel will influence their turnover target\ + \ number. +lblUseUnitRatingModifiers.text=Unit Reputation +lblUseUnitRatingModifiers.tooltip=If enabled, the reputation of the unit will influence the turnover\ + \ target number for all personnel. +lblUseFactionModifiers.text=Faction +lblUseFactionModifiers.tooltip=If enabled, campaign and personnel factions may influence the\ + \ turnover target number. +lblUseMissionStatusModifiers.text=Mission Status +lblUseMissionStatusModifiers.tooltip=Allows mission failure; success; and contract breaches to\ + \ influence the turnover target number. +lblUseHostileTerritoryModifiers.text=Hostile Territory +lblUseHostileTerritoryModifiers.tooltip=If StratCon or AtB is enabled, personnel will reduce their\ + \ turnover target numbers while not on a defensive contract. +lblUseFamilyModifiers.text=Family +lblUseFamilyModifiers.tooltip=Marriage to active personnel, or having adult children among active\ + \ personnel, will influence the turnover target number. +lblUseLoyaltyModifiers.text=Loyalty +lblUseLoyaltyModifiers.tooltip=If enabled, personnel have a random loyalty rating that influences\ + \ their Turnover target number. +lblUseHideLoyalty.text=Hide Loyalty +lblUseHideLoyalty.tooltip=If enabled, loyalty modifiers will be hidden. + +# createPayoutsPanel +lblPayoutsPanel.text=Payouts +lblPayoutRateOfficer.text=Officer Rate +lblPayoutRateOfficer.tooltip=The number of months' pay officers get when resigning from the unit. +lblPayoutRateEnlisted.text=Enlisted Rate +lblPayoutRateEnlisted.tooltip=The number of months' pay enlisted personnel get when resigning from\ + \ the unit. lblPayoutRetirementMultiplier.text=Retirement Multiplier -lblPayoutRetirementMultiplier.toolTipText=The number of months to multiply payout rate, when personnel retire. -chkUsePayoutServiceBonus.text=Use Service Bonus -chkUsePayoutServiceBonus.toolTipText=If enabled, personnel increase their payouts based on years of service. - +lblPayoutRetirementMultiplier.tooltip=The number of months to multiply payout rate, when personnel\ + \ retire. +lblUsePayoutServiceBonus.text=Enable Service Bonuses +lblUsePayoutServiceBonus.tooltip=If enabled, personnel increase their payouts based on years of\ + \ service. lblPayoutServiceBonusRate.text=Bonus % -lblPayoutServiceBonusRate.toolTipText=This sets the payout percentage increase per year of service, applied when personnel resign or retire. - -## Unit Cohesion -turnoverAndRetentionUnitCohesionPanel.title=Unit Cohesion -chkUseAdministrativeStrain.text=Enable Administrative Strain -chkUseAdministrativeStrain.toolTipText=This option limits the number of personnel that can be in the unit before incurring a Turnover penalty (based on the combined ranks of all Admin/HR personnel). -chkUseManagementSkill.text=Enable Management Skill -chkUseManagementSkill.toolTipText=This option applies a modifier to turnover checks based on the Leadership of commanding personnel. - -### Administrative Strain +lblPayoutServiceBonusRate.tooltip=This sets the payout percentage increase per year of service,\ + \ applied when personnel resign or retire. + +# createUnitCohesionPanel +lblUnitCohesionPanel.text=Unit Cohesion +lblUseAdministrativeStrain.text=Enable Administrative Strain +lblUseAdministrativeStrain.tooltip=This option limits the number of personnel that can be in the\ + \ unit before incurring a Turnover penalty (based on the combined ranks of all Admin/HR personnel). +lblUseManagementSkill.text=Enable Management Skill +lblUseManagementSkill.tooltip=This option applies a modifier to turnover checks based on the\ + \ Leadership of commanding personnel. + +# createAdministrativeStrainPanel +lblAdministrativeStrain.text=Administrative Strain lblAdministrativeCapacity.text=Administrative Capacity -lblAdministrativeCapacity.toolTipText=How many personnel can be supported per combined rank in Administration. -lblMultiCrewStrainDivider.text=Multi-Crew, Civilian, & Prisoner Divider -lblMultiCrewStrainDivider.toolTipText=For multi-crew units divide crew size by this value. This value is doubled for civilians and prisoners. - -### Management Skill -chkUseCommanderLeadershipOnly.text=Only Use Commander's Leadership -chkUseCommanderLeadershipOnly.toolTipText=Personnel only use the Leadership skill of whoever has the overall Commander flag. If disabled, personnel will use the Leadership of their profession group commander. +lblAdministrativeCapacity.tooltip=How many personnel can be supported per combined rank in\ + \ Administration? +lblMultiCrewStrainDivider.text=Multi-Crew Strain Divider +lblMultiCrewStrainDivider.tooltip=For multi-crew units (and ProtoMeks) divide crew size by this\ + \ value. This value is doubled for civilians and prisoners. + +# createManagementSkill +lblManagementSkill.text=Management Skill +lblUseCommanderLeadershipOnly.text=Use Commander's Leadership Only +lblUseCommanderLeadershipOnly.tooltip=Personnel only use the Leadership skill of whoever has the\ + \ overall Commander flag. If disabled, personnel will use the Leadership of their profession\ + \ group commander. lblManagementSkillPenalty.text=Unskilled Penalty -lblManagementSkillPenalty.toolTipText=The unskilled modifier to turnover target numbers. Each rank in Leadership reduces this number by 1. - - - -# General Personnel -chkUseTactics.text=Use Tactics Skill as Commander Initiative Bonus -chkUseTactics.toolTipText=Give each pilot a tactics skill that can be added to initiative using the commander initiative option in MegaMek -chkUseInitiativeBonus.text=Use Individual Initiative Bonus -chkUseInitiativeBonus.toolTipText=Give each pilot an individual initiative bonus -chkUseToughness.text=Use Toughness -chkUseToughness.toolTipText=Give each pilot a toughness bonus to KO checks -chkUseRandomToughness.text=Randomize Toughness -chkUseRandomToughness.toolTipText=Personnel have a chance to be recruited with a +1 or -1 to Toughness. -chkUseArtillery.text=Use Artillery Skill -chkUseArtillery.toolTipText=Give each pilot an artillery skill which is separate from the gunnery skill -chkUseAbilities.text=Allow Special Abilities -chkUseAbilities.toolTipText=Allow personnel to have special abilities -chkUseEdge.text=Use Edge -chkUseEdge.toolTipText=Allow personnel to have edge -chkUseSupportEdge.text=Use Edge for Non-Combat Personnel -chkUseSupportEdge.toolTipText=Allows non-combat personnel to use edge -chkUseImplants.text=Allow Implants -chkUseImplants.toolTipText=Allow personnel to have implants (e.g. Manei Domini) -chkUseAlternativeQualityAveraging.text=Use Higher-Precision Skill Level Averaging -chkUseAlternativeQualityAveraging.toolTipText=Where quality is determined by two skills, attempt to average by number (e.g. 8/4), rather than product (e.g. Regular/Green), for greater precision. - -# Log Panel -logPanel.title=Personnel Logs -chkUseTransfers.text=Use Reassign Instead of Remove/Assign for Logging -chkUseTransfers.toolTipText=This saves space in the personnel log by logging a single reassignment instead of a removal and assignment.
    It is highly recommended that this be kept on, especially for larger or longer campaigns. -chkUseExtendedTOEForceName.text=Use Extended TOE Force Names in Reassignment Log -chkUseExtendedTOEForceName.toolTipText=This options changes the Force Name used in the personnel reassignment log entry from 'Lance A' to 'My Campaign\\Lance A' -chkPersonnelLogSkillGain.text=Log Skill Gain in the Personnel Log -chkPersonnelLogSkillGain.toolTipText=Add log entries to the personnel log when increasing a skill. It does not include changes made in the customize person dialog nor any made before hiring a person. -chkPersonnelLogAbilityGain.text=Log Special Ability Gain in the Personnel Log -chkPersonnelLogAbilityGain.toolTipText=Add log entries to the personnel log when gaining a special ability. It does not include changes made in the customize person dialog nor any made before hiring a person. -chkPersonnelLogEdgeGain.text=Log Edge Gain in the Personnel Log -chkPersonnelLogEdgeGain.toolTipText=Add log entries to the personnel log when gaining or changing edge. It does not include changes made in the customize person dialog nor any made before hiring a person. -chkDisplayPersonnelLog.text=Expand Personnel Log by Default -chkDisplayPersonnelLog.toolTipText=If enabled, the personnel log will be expanded by default -chkDisplayScenarioLog.text=Expand Scenario Log by Default -chkDisplayScenarioLog.toolTipText=If enabled, the scenario log will be expanded by default -chkDisplayKillRecord.text=Expand Kill Record by Default -chkDisplayKillRecord.toolTipText=If enabled, the kill record will be expanded by default - -# Expanded Personnel Information -expandedPersonnelInformationPanel.title=Expanded Personnel Information -chkUseTimeInService.text=Track Time In Service -chkUseTimeInService.toolTipText=Track the amount of time a person has been an active employee of the force.
    Breaks in service are considered to be the end of their previous service and the start of a new service duration. -lblTimeInServiceDisplayFormat.text=Display format for Time in Service -lblTimeInServiceDisplayFormat.toolTipText=This is the format that is used to display the length of a person's service. -chkUseTimeInRank.text=Track Time In Rank -chkUseTimeInRank.toolTipText=Track the amount of time an active person has had their current rank in the force. -lblTimeInRankDisplayFormat.text=Display format for Time in Rank -lblTimeInRankDisplayFormat.toolTipText=This is the format that is used to display the length the person has spent in their current rank. -chkTrackTotalEarnings.text=Track Total Earnings -chkTrackTotalEarnings.toolTipText=This tracks the total amount earned by personnel. The tracking is only done when this option is enabled, and it is not backfilled. -chkTrackTotalXPEarnings.text=Track Total Experience Earnings -chkTrackTotalXPEarnings.toolTipText=This tracks the total amount of experience earned by personnel. The tracking is only done when this option is enabled, and it is not backfilled. -chkShowOriginFaction.text=Show Origin Faction -chkShowOriginFaction.toolTipText=Display the origin faction of a person in the personnel table. - -# Admin -administratorsPanel.title=Administrators -chkAdminsHaveNegotiation.text=Admins Have Negotiation -chkAdminsHaveNegotiation.toolTipText=Generate all admin personnel with random ranks in the Negotiation skill. -chkAdminsHaveScrounge.text=Admins Have Scrounge -chkAdminsHaveScrounge.toolTipText=Generate all admin personnel with random ranks in the Scrounge skill. -chkAdminExperienceLevelIncludeNegotiation.text=Admin Experience Level Includes Negotiation -chkAdminExperienceLevelIncludeNegotiation.toolTipText=All admin personnel will factor the Negotiation skill into their experience level calculations (green, regular, etc.). -chkAdminExperienceLevelIncludeScrounge.text=Admin Experience Level Includes Scrounge -chkAdminExperienceLevelIncludeScrounge.toolTipText=All admin personnel will factor the Scrounge skill into their experience level calculations (green, regular, etc.). - - -# Medical -medicalPanel.title=Medical (Unofficial) -chkUseAdvancedMedical.text=Use Advanced Medical Rules (Unofficial) -chkUseAdvancedMedical.toolTipText=Use the Advanced Medical Rules created by Jayof9s (See Advanced_Medical.doc in the docs folder) -lblHealWaitingPeriod.text=Days to Wait Between Healing Checks by Doctors -lblHealWaitingPeriod.toolTipText=This is the number of days between each time a doctor makes a healing check for a wounded person. -lblNaturalHealWaitingPeriod.text=Days to Wait for Natural Healing -lblNaturalHealWaitingPeriod.toolTipText=This is the number of days between each time a person naturally heals when not being tended to by a doctor. -lblMinimumHitsForVehicles.text=Minimum Number of Hits for Wounded Crews and Infantry -lblMinimumHitsForVehicles.toolTipText=This is the minimum number of hits received by wounded crews and infantry during scenario resolution after being wounded in a scenario. -lblMaximumPatients.text=Maximum Patients per Doctor -lblMaximumPatients.toolTipText=This is the maximum number of patients that can be treated per doctor. -chkUseRandomHitsForVehicles.text=Randomize the Number of Hits Received by Crews and Infantry -chkUseRandomHitsForVehicles.toolTipText=The number of hits that crews and infantry receive will be randomly selected between the minimum value and 5 during scenario resolution. -chkUseTougherHealing.text=Tougher Healing -chkUseTougherHealing.toolTipText=The healing check will be penalized for every additional hit above 2. - -# Prisoner -prisonerPanel.title=Prisoners -lblPrisonerCaptureStyle.text=Prisoner Capture Style -lblPrisonerCaptureStyle.toolTipText=This is the style of logic to use when determining if a person has been captured by the force following a scenario. -lblPrisonerStatus.text=Default Prisoner Status -lblPrisonerStatus.toolTipText=This is the default prisoner status assigned when a person is captured. Defectors are prisoners that are willing to defect to your forces. -chkPrisonerBabyStatus.text=Babies Born to Prisoners Share Mother's Prisoner Status -chkPrisonerBabyStatus.toolTipText=Babies born to prisoners will share their mother's prisoner status. Otherwise, they will instead be added to your force as a baby dependent. -chkAtBPrisonerDefection.text=Random Prisoner Defection -chkAtBPrisonerDefection.toolTipText=This adds a random roll to determine if a prisoner is willing to defect.
    This option will be ignored if Prisoner Defector is selected for the default prisoner status. -chkAtBPrisonerRansom.text=Enable Prisoner Ransom -chkAtBPrisonerRansom.toolTipText=Prisoners can be ransomed back to their previous faction. - -# Fatigue -turnoverAndRetentionFatiguePanel.title=CamOps Fatigue -lblFatigueWarning.text=Client must be reloaded whenever enabling or disabling Fatigue.
    CamOps Fatigue has added functionality if Fatigue is enabled in the MegaMek client settings.
    Non-combat modifiers are not implemented. -chkUseFatigue.text=Enable -chkUseFatigue.toolTipText=If enabled, combat personnel will gain Fatigue whenever they are deployed to a Scenario or entering an unexplored StratCon hex. -lblFatigueRate.text=Fatigue Rate -lblFatigueRate.toolTipText=How many fatigue points should be gained per Scenario or unexplored StratCon hex?
    Forces with the Scout role only count the first unexplored hex. -chkUseInjuryFatigue.text=Injuries Increase Fatigue (unofficial) -chkUseInjuryFatigue.toolTipText=If enabled, personnel gain fatigue whenever they suffer injuries during a scenario. +lblManagementSkillPenalty.tooltip=The unskilled modifier to turnover target numbers. Each rank in\ + \ Leadership reduces this number by 1. + +# createFatigueTab +lblFatigueTab.text=Fatigue Options +lblFatigueTabBody.text=The rules for Fatigue can be found in Campaign Operations. With our\ + \ implementation covered in the Turnover and Retention documentation. +lblUseFatigue.text=Enable Fatigue +lblUseFatigue.tooltip=If enabled, combat personnel will gain Fatigue whenever they're deployed to\ + \ a Scenario or entering an unexplored StratCon hex. +lblFatigueRate.text=Fatigue Rate \u26A0 +lblFatigueRate.tooltip=How many fatigue points should be gained per Scenario or unexplored StratCon\ + \ hex?\ +
    \ +
    Forces with the Patrol role only count the first unexplored hex when deploying. +lblUseInjuryFatigue.text=Injuries Increase Fatigue \u270E +lblUseInjuryFatigue.tooltip=If enabled, personnel gain fatigue whenever they suffer injuries during\ + \ a scenario. lblFieldKitchenCapacity.text=Field Kitchen Capacity -lblFieldKitchenCapacity.toolTipText=How many personnel can be served per Field Kitchen? Reduces effective Fatigue by 1. -chkFieldKitchenIgnoreNonCombatants.text=Ignore Non-Combatants -chkFieldKitchenIgnoreNonCombatants.toolTipText=If enabled, non-combatants will not count towards the number of personnel being served by a field kitchen. -lblFatigueLeaveThreshold.text=Automatic Leave Threshold (unofficial) -lblFatigueLeaveThreshold.toolTipText=Automatically assign personnel to Leave status once fatigue (including modifiers) has reached this value.
    Personnel heal fatigue twice as fast while on leave and will automatically return to active duty once their fatigue has returned to 0.
    Set to 0 to disable. - -# Dependent -dependentPanel.title=Dependents (Unofficial) -dependentPanel.toolTipText=These options concern the random addition or removal of dependents from the campaign. -chkUseRandomDependentAddition.text=Use Random Dependent Addition -chkUseRandomDependentAddition.toolTipText=This enables the monthly addition of random dependents to the campaign. -chkUseRandomDependentRemoval.text=Use Random Dependent Removal -chkUseRandomDependentRemoval.toolTipText=This enables the monthly removal of dependents from the campaign. - -# Personnel Removal -personnelRemovalPanel.title=Personnel Clean Up -personnelRemovalPanel.toolTipText=These settings manage the automatic removal of personnel who are no longer serving in the unit. This helps reduce save bloat. -chkUsePersonnelRemoval.text=Enable Personnel Clean Up -chkUsePersonnelRemoval.toolTipText=If enabled, personnel data will be deleted on the last day of each month for any personnel who been departed from the unit for over a month. -chkUseRemovalExemptCemetery.text=Use Cemetery Exemption -chkUseRemovalExemptCemetery.toolTipText=Data from dead personnel is exempt from being cleaned up. -chkUseRemovalExemptRetirees.text=Use Retiree Exemption -chkUseRemovalExemptRetirees.toolTipText=Data from retired personnel is exempt from being cleaned up. - -# Anniversaries -anniversaryPanel.title=Anniversaries -chkAnnounceBirthdays.text=Announce Birthdays -chkAnnounceBirthdays.toolTipText=A daily report notification will occur whenever someone celebrates their birthday. -chkAnnounceRecruitmentAnniversaries.text=Announce Recruitment Anniversaries -chkAnnounceRecruitmentAnniversaries.toolTipText=A daily report notification will occur whenever someone celebrates a recruitment anniversary. -chkAnnounceOfficersOnly.text=Officers Only -chkAnnounceOfficersOnly.toolTipText=Only report officer anniversaries. -chkAnnounceChildBirthdays.text=Coming of Age Override -chkAnnounceChildBirthdays.toolTipText=If enabled, 18th birthdays will always be announced. Enabling this option will override any other settings. - -# Salary -salaryPanel.title=Salary -chkDisableSecondaryRoleSalary.text=Disable Secondary Role Salaries -chkDisableSecondaryRoleSalary.toolTipText=If enabled, personnel will not have their salaries increased by their secondary role (if any). -salaryMultiplierPanel.title=Multipliers -salaryMultiplierPanel.toolTipText=These multipliers are multiplicatively applied to the base salary as part of determining the final salary. -lblAntiMekSalary.text=Anti-Mek -lblAntiMekSalary.toolTipText=Anti-Mek trained soldiers and battle armour/elemental personnel have their salaries multiplied by this. -lblSpecialistInfantrySalary.text=Specialist Infantry -lblSpecialistInfantrySalary.toolTipText=Soldiers assigned to specialist infantry units have their salaries multiplied by this. -salaryExperienceMultiplierPanel.title=Experience Multipliers -salaryExperienceMultiplierPanel.toolTipText=The experience multiplier is multiplicatively applied to the base salary as part of determining the final salary. -lblSalaryExperienceMultiplier.toolTipText=%s personnel have their base pay multiplied by this. -baseSalaryPanel.title=Base Salaries -lblBaseSalary.toolTipText=This is the base salary paid to %ss. - -# Awards -awardsPanel.title=Awards -lblAwardBonusStyle.text=Award Bonuses -lblAwardBonusStyle.toolTipText=Toggle XP and/or Edge bonuses from awards. -chkEnableAutoAwards.text=Track Award Eligibility -chkEnableAutoAwards.toolTipText=Enable the automatic tracking of award eligibility. -chkIssuePosthumousAwards.text=Issue Posthumous Awards (AtB Only) -chkIssuePosthumousAwards.toolTipText=Enabling this setting will qualify personnel for awards even if they are dead (excludes formation-based kill awards). -chkIssueBestAwardOnly.text=Only Issue Best Award -chkIssueBestAwardOnly.toolTipText=Activating this setting will disqualify personnel from receiving an award if they qualify for a higher-tier award of the same type. -chkIgnoreStandardSet.text=Ignore Standard Set -chkIgnoreStandardSet.toolTipText=Ignore the default set of awards -lblAwardTierSize.text=Award Tier Size -lblAwardTierSize.toolTipText=How many times must an award be issued before using the image of the next tier -autoAwardsPanel.title=Award Tracking -chkEnableContractAwards.text=Contract -chkEnableContractAwards.toolTipText=Awards issued for completing Contracts. -chkEnableFactionHunterAwards.text=Faction Hunter -chkEnableFactionHunterAwards.toolTipText=Awards issued for completing Contracts against specific Factions. -chkEnableInjuryAwards.text=Injury -chkEnableInjuryAwards.toolTipText=Awards issued for suffering Hits during a Scenario. -chkEnableIndividualKillAwards.text=Kill (Individual) -chkEnableIndividualKillAwards.toolTipText=Awards issued for pilot kills in Scenarios or Missions. -chkEnableFormationKillAwards.text=Kill (Formation) -chkEnableFormationKillAwards.toolTipText=Awards issued for formation kills (Lance, Company, etc.) in Missions. -chkEnableRankAwards.text=Rank -chkEnableRankAwards.toolTipText=Awards issued for achieving specific Ranks. -chkEnableScenarioAwards.text=Scenario -chkEnableScenarioAwards.toolTipText=Awards issued for completing a certain number of Scenarios. -chkEnableSkillAwards.text=Skill -chkEnableSkillAwards.toolTipText=Awards issued for reaching certain levels in specific Skills. -chkEnableTheatreOfWarAwards.text=Theatre of War -chkEnableTheatreOfWarAwards.toolTipText=Awards issued for partaking in galactic conflicts. -chkEnableTimeAwards.text=Time -chkEnableTimeAwards.toolTipText=Awards for remaining in the unit for a certain duration. -chkEnableTrainingAwards.text=Training -chkEnableTrainingAwards.toolTipText=Awards for graduating from academies (requires Education to be enabled in the Life Paths tab). -chkEnableMiscAwards.text=Misc -chkEnableMiscAwards.toolTipText=Miscellaneous awards (see documentation). -lblAwardSetFilterList.text=Filter List -lblAwardSetFilterList.toolTipText=Include the name of any award sets you do not want managed by autoAwards. The spelling and capitalization must be exact, and each award set must be separated by a comma. While spaces can appear in the award set name, the comma should not be followed by a space. -##end Personnel Tab - -## Life Paths Tab -lifePathsPanel.title=Life Paths - -# Personnel Randomization -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 -chkUseRandomPersonalities.text=Use Random Personalities -chkUseRandomPersonalities.toolTipText=Personnel are generated with random personality traits, quirks and intelligence.\ -
    \ -
    Intelligence affects a character's ability to graduate from education module academies. -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 -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 (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. -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 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 +lblFieldKitchenCapacity.tooltip=How many personnel can be served per Field Kitchen? Reduces\ + \ effective Fatigue by 1. +lblFieldKitchenIgnoreNonCombatants.text=Ignore Non-Combatants +lblFieldKitchenIgnoreNonCombatants.tooltip=If enabled, non-combatants will not count towards the\ + \ number of personnel being served by a field kitchen. +lblFatigueLeaveThreshold.text=Automatic Leave Threshold \u270E \u26A0 +lblFatigueLeaveThreshold.tooltip=Automatically assign personnel to Leave status once fatigue\ + \ (including modifiers) has reached this value.\ +
    \ +
    Personnel heal fatigue twice as fast while on leave and will automatically return to active\ + \ duty once their fatigue has returned to 0.\ +
    \ +
    Set to 0 to disable. + +## Relationships Tab +relationshipsContentTabs.title=Relationships + +marriageTab.title=Marriage +marriageTab.border="My love, I give you the Capellan Confederation!"\ +
    Hanse Davion to Melissa Steiner-Davion at their wedding reception on Terra\ +
    August 20, 3028
    + +divorceTab.title=Divorce +divorceTab.border="Whoever said 'never shoot a man in the back' never saw the front a Hunchback."\ +
    Unknown Mercenary\ +
    First Succession War
    + +procreationTab.title=Procreation +procreationTab.border="They've requested LRMs. I'm pretty sure they mean Living Room Modules."\ +
    Unknown Logistics Tech + +# createMarriageTab +lblMarriageTab.text=Marriage Options \u270E +lblUseManualMarriages.text=Enable Manual Marriages +lblUseManualMarriages.tooltip=This allows the player to disable the Choose Spouse (Mate) option in\ + \ the personnel table mouse adapter. +lblUseClanPersonnelMarriages.text=Enable Clan Marriages +lblUseClanPersonnelMarriages.tooltip=Allow clan-origin personnel to marry other personnel. +lblUsePrisonerMarriages.text=Enable Prisoner Marriages +lblUsePrisonerMarriages.tooltip=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. +lblNoInterestInMarriageDiceSize.text=No Interest in Marriage Chance: 1 in +lblNoInterestInMarriageDiceSize.tooltip=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 Ancestor Depth +lblCheckMutualAncestorsDepth.tooltip=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. +lblLogMarriageNameChanges.text=Log Name Changes +lblLogMarriageNameChanges.tooltip=This enables the addition of a personnel log entry whenever a\ + \ name is changed during marriage. + +# createRandomMarriagePanel +lblRandomMarriages.text=Random Marriages lblRandomMarriageMethod.text=Random Marriage Method -lblRandomMarriageMethod.toolTipText=This is the method used to determine if an eligible person randomly marries. -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 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 to 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 (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 -chkUseClanPersonnelDivorce.toolTipText=Allow clan-origin personnel to divorce, whether as the origin or the spouse. -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 +lblRandomMarriageMethod.tooltip=This is the method used to determine if an eligible person randomly\ + \ marries. +lblUseRandomClanPersonnelMarriages.text=Enable Random Clan Marriages +lblUseRandomClanPersonnelMarriages.tooltip=Allow clan-origin personnel to randomly marry other personnel. +lblUseRandomPrisonerMarriages.text=Enable Random Prisoner Marriages +lblUseRandomPrisonerMarriages.tooltip=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. +lblRandomMarriageAgeRange.text=Random Marriage Age Band +lblRandomMarriageAgeRange.tooltip=This plus/minus age forms the possible range of ages for spouses\ + \ in the forming of a random marriage. + +# createPercentageRandomMarriagePanel +lblPercentageRandomMarriagePanel.text=Marriage Dice +lblRandomMarriageOppositeSexDiceSize.text=Opposite Sex Marriage Chance: 1 in +lblRandomMarriageOppositeSexDiceSize.tooltip=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 Marriage Chance: 1 in +lblRandomSameSexMarriageDiceSize.tooltip=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. +lblRandomNewDependentMarriage.text=Inter-Unit Marriage Chance: 1 in +lblRandomNewDependentMarriage.tooltip=This determines the number of sides on the die rolled to\ + \ determine whether a marriage is to 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. + +# createDivorceTab +lblDivorceTab.text=Divorce Options \u270E +lblUseManualDivorce.text=Enable Manual Divorce +lblUseManualDivorce.tooltip=This allows you to disable the Remove Spouse option in the\ + \ personnel table mouse adapter. +lblUseClanPersonnelDivorce.text=Enable Manual Clan Divorce +lblUseClanPersonnelDivorce.tooltip=Allow clan-origin personnel to divorce, whether as the origin or the spouse. +lblUsePrisonerDivorce.text=Enable Manual Prisoner Divorce +lblUsePrisonerDivorce.tooltip=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. + +# createRandomDivorcePanel +lblRandomDivorcePanel.text=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 -chkUseRandomOppositeSexDivorce.toolTipText=This enables random divorce for members of your force with opposite sex spouses. -chkUseRandomSameSexDivorce.text=Use Random Same Sex Divorce -chkUseRandomSameSexDivorce.toolTipText=This enables random divorce for members of your force with same sex spouses. -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=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) -chkUseManualProcreation.text=Use Manual Procreation -chkUseManualProcreation.toolTipText=This allows the player to disable the Add/Remove Pregnancy options in the personnel table mouse adapter. -chkUseClanPersonnelProcreation.text=Use Clan Personnel Procreation -chkUseClanPersonnelProcreation.toolTipText=Allow clan-origin personnel to procreate. -chkUsePrisonerProcreation.text=Use Prisoner Procreation -chkUsePrisonerProcreation.toolTipText=Allow prisoners to procreate. Bondsmen are treated as free personnel when it comes to this option, and are thus not affected by it. -lblMultiplePregnancyOccurrences.text=Multiple Pregnancies Occur 1 in -lblMultiplePregnancyOccurrences.toolTipText=Multiple Pregnancies (like twins, sextuplets, decuplets) occur 1 in X pregnancies, with each additional child after the first being a 1 in X check, up to a maximum of 10 children per pregnancy.
    Hellin's law says it is 1 in 89 births. However, the default is 1 in 50 births to not make it appear too seldom. -lblMultiplePregnancyOccurrencesEnd.text=Pregnancies +lblRandomDivorceMethod.tooltip=This is the method used to determine if an eligible person randomly\ + \ divorces. +lblUseRandomOppositeSexDivorce.text=Use Random Opposite Sex Divorce +lblUseRandomOppositeSexDivorce.tooltip=This enables random divorce for members of your force with\ + \ opposite sex spouses. +lblUseRandomSameSexDivorce.text=Use Random Same Sex Divorce +lblUseRandomSameSexDivorce.tooltip=This enables random divorce for members of your force with same\ + \ sex spouses. +lblUseRandomClanPersonnelDivorce.text=Use Random Clan Divorce +lblUseRandomClanPersonnelDivorce.tooltip=Allow clan-origin personnel to randomly divorce, whether\ + \ as the origin or the spouse. +lblUseRandomPrisonerDivorce.text=Use Random Prisoner Divorce +lblUseRandomPrisonerDivorce.tooltip=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. +lblRandomDivorceDiceSize.text=Random Divorce Chance: 1 in +lblRandomDivorceDiceSize.tooltip=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. + +# createProcreationTab +lblProcreationTab.text=Procreation Options \u270E + +# createProcreationGeneralOptionsPanel +lblUseManualProcreation.text=Enable Manual Procreation +lblUseManualProcreation.tooltip=This allows you to disable the Add/Remove Pregnancy options in the\ + \ personnel table mouse adapter. +lblUseClanPersonnelProcreation.text=Enable Clan Procreation +lblUseClanPersonnelProcreation.tooltip=Allow clan-origin personnel to procreate. +lblUsePrisonerProcreation.text=Enable Prisoner Procreation +lblUsePrisonerProcreation.tooltip=Allow prisoners to procreate. Bondsmen are treated as free\ + \ personnel when it comes to this option, and are thus not affected by it. +lblMultiplePregnancyOccurrences.text=Multiple Pregnancy Chance: 1 in +lblMultiplePregnancyOccurrences.tooltip=Multiple Pregnancies (like twins, sextuplets, decuplets)\ + \ occur 1 in X pregnancies, with each additional child after the first being a 1 in X check,\ + \ up to a maximum of 10 children per pregnancy.\ +
    \ +
    Hellin's Law says it is 1 in 89 births. However, the default is 1 in 50 births to not make it\ + \ appear too seldom. lblBabySurnameStyle.text=Baby Surname Style -lblBabySurnameStyle.toolTipText=This is the style for how a baby's surname will be selected. -chkAssignNonPrisonerBabiesFounderTag.text=Assign Non-Prisoner Babies Founder Tag -chkAssignNonPrisonerBabiesFounderTag.toolTipText=Automatically have the Founder tag assigned to all babies that are not prisoners.
    Bondsmen are treated as free members of the force when it comes to this option, and thus their children will be assigned the founder flag if this is enabled. -chkAssignChildrenOfFoundersFounderTag.text=Assign Children of Founders Founder Tag -chkAssignChildrenOfFoundersFounderTag.toolTipText=Automatically have the Founder tag assigned to all babies that have at least one parent who is a founder.
    This check will occur even if the baby is born a prisoner. -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 +lblBabySurnameStyle.tooltip=This is the style for how a baby's surname will be selected. +lblAssignNonPrisonerBabiesFounderTag.text=Non-Prisoner Babies are Founders +lblAssignNonPrisonerBabiesFounderTag.tooltip=Automatically have the Founder tag assigned to al\ + l babies that aren't prisoners. Bondsmen are treated as free members of the force when it comes\ + \ to this option, and thus their children will be assigned the founder flag if this is enabled. +lblAssignChildrenOfFoundersFounderTag.text=Children of Founders are Founders +lblAssignChildrenOfFoundersFounderTag.tooltip=Automatically have the Founder tag assigned to all\ + \ babies that have at least one parent who is a founder.\ +
    \ +
    This check will occur even if the baby is born a prisoner. +lblDetermineFatherAtBirth.text=Determine Father at Birth +lblDetermineFatherAtBirth.tooltip=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. +lblDisplayTrueDueDate.text=Display True Due Date +lblDisplayTrueDueDate.tooltip=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 Chance: 1 in +lblNoInterestInChildrenDiceSize.tooltip=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. +lblUseMaternityLeave.text=Enable Automatic Maternity Leave +lblUseMaternityLeave.tooltip=If enabled, pregnant personnel will be placed on maternity leave 20\ + \ weeks before they give birth and will not return to active duty until after they have given\ + \ birth. +lblLogProcreation.text=Log Procreation +lblLogProcreation.tooltip=This enables logging the date of conception and birth in a person's logs. + +# createRandomProcreationPanel +lblRandomProcreationPanel.text=Random Procreation lblRandomProcreationMethod.text=Random Procreation Method -lblRandomProcreationMethod.toolTipText=This is the method used to determine if an eligible person randomly procreates. -chkUseRelationshiplessRandomProcreation.text=Use Relationshipless Random Procreation -chkUseRelationshiplessRandomProcreation.toolTipText=This enables random procreation for female members of your force not in a relationship. -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=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 -chkKeepMarriedNameUponSpouseDeath.text=Keep Marital Name Upon Spouse Death -chkKeepMarriedNameUponSpouseDeath.toolTipText=When a person's spouse dies they will keep their marital name instead of returning to their maiden name. -randomDeathPanel.title=Random Death (Unofficial) -lblRandomDeathMethod.text=Random Death Method -lblRandomDeathMethod.toolTipText=This is the method used to determine if a person dies from random death.
    Exponential Random Death is the best system as it closely follows a realistic death curve.
    The other systems are provided for individual customization. -enabledRandomDeathAgeGroupsPanel.title=Enabled Age Groups -enabledRandomDeathAgeGroupsPanel.toolTipText=This allows you to determine which age groups random death is enabled for, with a default setup of Teenage, Adult, and Elder being enabled. -chkUseRandomClanPersonnelDeath.text=Use Random Clan Personnel Death -chkUseRandomClanPersonnelDeath.toolTipText=Allow clan-origin personnel to randomly die. -chkUseRandomPrisonerDeath.text=Use Random Prisoner Death -chkUseRandomPrisonerDeath.toolTipText=Allow prisoners to randomly die. Bondsmen are treated as free personnel when it comes to this option, and are thus not affected by it. -chkUseRandomDeathSuicideCause.text=Use Suicide Cause -chkUseRandomDeathSuicideCause.toolTipText=This includes suicide as a potential cause for a random death. -percentageRandomDeathPanel.title=Percentage Random Death -lblPercentageRandomDeathChance.text=Death Chance -lblPercentageRandomDeathChance.toolTipText=This is the percent chance per day that any member of your force will randomly die. -exponentialRandomDeathPanel.title=Exponential Random Death -lblExponentialRandomDeathAge.text=* age) -ageRangeRandomDeathPanel.title=Age Range Random Death -lblAgeRange.text=Age Range - -# Education -educationPanel.title=Education - -chkUseEducationModule.text=Enable Education Module -chkUseEducationModule.toolTip=Enables the Education Module. - -lblMaximumJumpCount.text=Maximum Jump Radius -lblMaximumJumpCount.toolTip=The maximum distance when searching for academies. -lblCurriculumXpRate.text=XP Rate -lblCurriculumXpRate.toolTip=Some curriculum award xp (outside monthly random xp). This setting determines how much XP should be gained per education level. -chkUseReeducationCamps.text=Enable Reeducation Camp Faction Change -chkUseReeducationCamps.toolTip=Should reeducation camps change a students' origin faction to match the campaign faction? -chkShowIneligibleAcademies.text=Show Ineligible Academies -chkShowIneligibleAcademies.toolTip=Displays academies a character is ineligible to attend. -chkEnableOverrideRequirements.text=Override Requirements -chkEnableOverrideRequirements.toolTip=Overrides the requirements for attending academies. Included for testing purposes and not intended for general play. -lblEntranceExamBaseTargetNumber.text=Entrance Exam Target Number -lblEntranceExamBaseTargetNumber.toolTip=The base target number for Prestigious Academy entrance exams. The final TN is this value minus Faculty Skill, opposed by 2d6 + Intelligence/4 or just 2d6 (if personalities are disabled) - -standardSets.title=Standard Sets -chkEnableLocalAcademies.text=Enable Local Academies -chkEnableLocalAcademies.toolTip=Allows personnel to enroll in local, planet-side, academies. -chkEnablePrestigiousAcademies.text=Enable Prestigious Academies -chkEnablePrestigiousAcademies.toolTip=Allows personnel to enroll in named, canonical, academies. -chkEnableUnitEducation.text=Enable Unit Education -chkEnableUnitEducation.toolTip=Allows personnel to enroll in education and training conducted by the unit. - -xpAndSkillBonuses.title=Faculty XP and Skill Bonuses -lblFacultyXPMultiplier.text=Faculty XP Multiplier -lblFacultyXPMultiplier.toolTip=This value multiplies the number of XP gained when completing (or partially completing) a qualification. Setting this to 0 disabled faculty XP. -chkEnableBonuses.text=Graduation Bonuses -chkEnableBonuses.toolTip=Enables the chance of gaining a permanent skill bonus when graduating. - -dropoutChance.title=Weekly Dropout & Washout Dice -lblAdultDropoutChance.text=Adult Dropout Die Sides -lblAdultDropoutChance.toolTip=The number of sides on the die used to determine random weekly dropouts. A dropout occurs on a roll of 1. Setting this to 0 disables random dropouts. -lblChildrenDropoutChance.text=Child Dropout Die Sides -lblChildrenDropoutChance.toolTip=The number of sides on the die used to determine random weekly dropouts. A dropout occurs on a roll of 1. Setting this to 0 disables random dropouts. - -accidentsAndEvents.title=Accidents & Events -chkAllAges.text=All Ages Affected -chkAllAges.toolTip=Children do not always survive training accidents or negative events. -lblMilitaryAcademyAccidents.text=Military Academy Training Accidents Die Sides -lblMilitaryAcademyAccidents.toolTip=The number of sides on the die used to determine random weekly (potentially fatal) accidents. An accident occurs on a roll of 1. Setting this to 0 disables random accidents. -## End Life Paths Tab - -## Finances Tab -financesPanel.title=Finances -payForPartsBox.text=Pay For Parts -payForPartsBox.toolTipText=Pay the C-Bill cost of any replacement parts -payForRepairsBox.text=Pay For Repairs -payForRepairsBox.toolTipText=CamOps p46: Repairs cost 20% of a part's list price. This is for equipment repairs only and does not count armor repairs.\ -
    This is reimbursed by Battle Loss Compensation. -payForUnitsBox.text=Pay For New Units -payForUnitsBox.toolTipText=Pay the C-Bill cost for any new units (Meks, vees, etc.) -payForSalariesBox.text=Pay for personnel salaries -payForSalariesBox.toolTipText=Pay the monthly salaries of all personnel -payForRecruitmentBox.text=Pay for personnel recruitment -payForRecruitmentBox.toolTipText=Pay a two-month salary to recruit personnel (FMMr) -useLoanLimitsBox.text=Limit loan parameters by unit rating -useLoanLimitsBox.toolTipText=Put limits on interest, collateral, and length as per IntOps Beta -payForOverheadBox.text=Pay for overhead expenses -payForOverheadBox.toolTipText=Pay overhead expenses as per FM: Mercs Revised (5% of monthly personnel salaries) -payForMaintainBox.text=Pay for unit maintenance -payForMaintainBox.toolTipText=Pay weekly unit maintenance costs as per FM: Mercs Revised -payForTransportBox.text=Pay for Transportation -payForTransportBox.toolTipText=Pay for excess transportation needs (costs are a total hack at the moment, FYI) -usePeacetimeCostBox.text=Use Campaign Operations Peacetime Operating Costs -usePeacetimeCostBox.toolTipText=Includes salaries, spare parts, fuel, and peacetime ammo consumption. 75% added to base contract amount when using CamOps rating. -useExtendedPartsModifierBox.text=Use the expanded spare parts modifiers (CO pg. 14). -showPeacetimeCostBox.text=Breakdown peacetime costs to core costs. -showPeacetimeCostBox.toolTipText=Displays a breakdown of peacetime costs in the daily log. -financialYearDuration.text=Financial Term Duration -financialYearDuration.toolTipText=This changes the Financial Term, which is when the finance table resets. Any settings longer than biennially are not recommended. -newFinancialYearFinancesToCSVExportBox.text=Finance Table CSV Export on New Financial Term -newFinancialYearFinancesToCSVExportBox.toolTipText=This writes the finance table to a CSV file on the first day of a new financial term, right before the table is carried over to the next period. -sellUnitsBox.text=Allow selling of units -sellUnitsBox.toolTipText=Units can be sold for C-bills -sellPartsBox.text=Allow selling of parts -sellPartsBox.toolTipText=Parts can be sold for C-bills -usePercentageMaintenanceBox.text=Use percentage-based maintenance costs (Unofficial) -usePercentageMaintenanceBox.toolTipText=Maintenance costs based upon the value of the unit instead of the type of unit. This actually makes maintenance costs important. -infantryDontCount.text=Infantry Don't Count for Contract Pay -infantryDontCount.toolTipText=When this option is ticked, infantry will not count for contract pay - -# Price Multipliers -priceMultipliersPanel.title=Price Multipliers -lblCommonPartPriceMultiplier.text=Common Tech Part Price Multiplier -lblCommonPartPriceMultiplier.toolTipText=Multiplies the buy/sell price of common tech parts by the specified number. -lblInnerSphereUnitPriceMultiplier.text=Inner Sphere Tech Unit Price Multiplier -lblInnerSphereUnitPriceMultiplier.toolTipText=Multiplies the buy/sell price of Inner Sphere tech units by the specified number. -lblInnerSpherePartPriceMultiplier.text=Inner Sphere Tech Part Price Multiplier -lblInnerSpherePartPriceMultiplier.toolTipText=Multiplies the buy/sell price of Inner Sphere tech parts by the specified number. -lblClanUnitPriceMultiplier.text=Clan Tech Unit Price Multiplier -lblClanUnitPriceMultiplier.toolTipText=Multiplies the buy/sell price of Clan tech units by the specified number. -lblClanPartPriceMultiplier.text=Clan Tech Part Price Multiplier -lblClanPartPriceMultiplier.toolTipText=Multiplies the buy/sell price of Clan tech parts by the specified number. -lblMixedTechUnitPriceMultiplier.text=Mixed Tech Unit Price Multiplier -lblMixedTechUnitPriceMultiplier.toolTipText=Multiplies the buy/sell price of Mixed Tech units by the specified number. -usedPartsValueMultipliersPanel.title=Used Parts Value Multipliers -lblUsedPartPriceMultiplier.toolTipText=Multiplies the value and thus the sell price of used parts by the specified number. -lblDamagedPartsValueMultiplier.text=Damaged Parts Value Multiplier -lblDamagedPartsValueMultiplier.toolTipText=Multiplies the value and thus the sell price of damaged parts by the specified number. -lblUnrepairablePartsValueMultiplier.text=Unrepairable Damaged Parts Value Multiplier -lblUnrepairablePartsValueMultiplier.toolTipText=Multiplies the value and thus the sell price of unrepairable damaged parts by the specified number. -lblCancelledOrderRefundMultiplier.text=Canceled Order Refund Percentage -lblCancelledOrderRefundMultiplier.toolTipText=The decimal percentage of the original purchase price that is refunded when an order is cancelled. - -# Taxes -taxesPanel.title=Taxes -chkUseTaxes.text=Enable Taxes -chkUseTaxes.toolTipText=If enabled, taxes will be paid at the end of each financial term.\ - \ Tax amount is based on profits recorded at the end of the financial term.\ - \ Profits are calculated by comparing current funds against those from the prior financial term.\ - \ Starting Capital and End of Term Carryover sums are excluded from profit calculations. +lblRandomProcreationMethod.tooltip=This is the method used to determine if an eligible person\ + \ randomly procreates. +lblUseRelationshiplessRandomProcreation.text=Enable Random Relationshipless Procreation +lblUseRelationshiplessRandomProcreation.tooltip=This enables random procreation for female members\ + \ of your force not in a relationship. +lblUseRandomClanPersonnelProcreation.text=Enable Random Clan Procreation +lblUseRandomClanPersonnelProcreation.tooltip=Allow clan-origin personnel to randomly procreate. +lblUseRandomPrisonerProcreation.text=Enable Random Prisoner Procreation +lblUseRandomPrisonerProcreation.tooltip=Allow prisoners to randomly procreate. Bondsmen are treated\ + \ as free personnel when it comes to this option, and are thus not affected by it. +lblRandomProcreationRelationshipDiceSize.text=Normal Procreation Chance: 1 in +lblRandomProcreationRelationshipDiceSize.tooltip=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 Procreation Chance: 1 in +lblRandomProcreationRelationshiplessDiceSize.tooltip=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. + +# Finances Tab +financesContentTabs.title=Finances + +financesGeneralTab.title=General +financesGeneralTab.border="War is expensive. If the enemy doesn't kill you, the repair bills might."\ +
    Sergeant Tara "Grease" Hall\ +
    Vogel's Fumble Force + +priceMultipliersTab.title=Price Multipliers +priceMultipliersTab.border="The used parts market is where dreams are built \u2014 and nightmares are patched\ + \ together. Just make sure the part you're buying doesn't come with someone else's curse."\ +
    Captain Nia "Jackal" Thorne\ +
    The Stormbringer J�gers
    + +# createFinancesGeneralOptionsTab +lblFinancesGeneralTab.text=General Options + +# createPaymentsPanel +lblPaymentsPanel.text=Payments +lblPayForPartsBox.text=Pay For Parts +lblPayForPartsBox.tooltip=Pay the C-Bill cost of any replacement parts +lblPayForRepairsBox.text=Pay For Repairs +lblPayForRepairsBox.tooltip=Repairs cost 20% of a part's list price. This is for equipment repairs\ + \ only and doesn't count armor repairs.\ +
    \ +
    This is reimbursed by Battle Loss Compensation. +lblPayForUnitsBox.text=Pay For Units +lblPayForUnitsBox.tooltip=Pay the C-Bill cost for any new units. +lblPayForSalariesBox.text=Pay For Salaries +lblPayForSalariesBox.tooltip=Pay the monthly salaries of all personnel. +lblPayForOverheadBox.text=Pay For Overhead (FM:Mr) +lblPayForOverheadBox.tooltip=Pay 5% of monthly personnel salaries. +lblPayForMaintainBox.text=Pay For Maintenance (FM:Mr) +lblPayForMaintainBox.tooltip=Pay weekly unit maintenance costs. +lblPayForTransportBox.text=Transport +lblPayForTransportBox.tooltip=Pay for excess transportation needs. +lblPayForRecruitmentBox.text=Recruitment (FM:Mr) +lblPayForRecruitmentBox.tooltip=Pay a two-month salary to new recruits. + +# createGeneralOptionsPanel +lblUseLoanLimitsBox.text=Available Loans Based on Unit Reputation \u270E +lblUseLoanLimitsBox.tooltip=Put limits on interest, collateral, and length. +lblUsePercentageMaintenanceBox.text=Enable Percentage-Based Maintenance Costs \u270E +lblUsePercentageMaintenanceBox.tooltip=Maintenance costs based upon the value of the unit instead\ + \ of the unit type. This makes maintenance costs more impactful. +lblUseExtendedPartsModifierBox.text=Enabled Extended Spare Parts Modifiers +lblUseExtendedPartsModifierBox.tooltip=Use the Campaign Operations expanded spare parts modifiers. +lblUsePeacetimeCostBox.text=Enable Peacetime Operating Costs +lblUsePeacetimeCostBox.tooltip=Includes salaries, spare parts, fuel, and peacetime ammo consumption.\ + \ 75% is added to base contract amount when using Campaign Operations reputation rating. +lblShowPeacetimeCostBox.text=Breakdown Operating Costs +lblShowPeacetimeCostBox.tooltip=Displays a breakdown of peacetime costs in the daily log. +lblFinancialYearDuration.text=Financial Year Duration \u26A0 +lblFinancialYearDuration.tooltip=This changes the Financial Term, which is when the finance table\ + \ resets.\ +
    \ +
    Warning: Any settings longer than biennially are not recommended. +lblNewFinancialYearFinancesToCSVExportBox.text=Export Finances as CSV Table on Term End +lblNewFinancialYearFinancesToCSVExportBox.tooltip=This writes the finance table to a CSV file on\ + \ the first day of a new financial term, right before the table is carried over to the next period. + +# createSalesPanel +lblSalesPanel.text=Sales +lblSellUnitsBox.text=Enable the Sale of Units +lblSellUnitsBox.tooltip=Units can be sold for C-bills. +lblSellPartsBox.text=Enable the Sale of Parts +lblSellPartsBox.tooltip=Parts can be sold for C-bills. + +# createTaxesPanel +lblTaxesPanel.text=Taxes \u270E +lblUseTaxesBox.text=Enable Taxes +lblUseTaxesBox.tooltip=If enabled, taxes will be paid at the end of each financial term.\ +
    \ +
    Tax amount is based on profits recorded at the end of the financial term. Profits are\ + \ calculated by comparing current funds against those from the prior financial term.\ +
    \ + The percentage of profits paid as taxes.\ - \ Tax amount is based on profits recorded at the end of the financial term.\ - \ Profits are calculated by comparing current funds against those from the prior financial term (or company starting funds).\ - \ Starting Capital and End of Term Carryover sums are excluded from profit calculations. - -# Shares -sharesPanel.title=Shares System -chkUseShareSystem.text=Use Shares -chkUseShareSystem.toolTipText=Gives personnel a stake in the unit. This system lowers profits but can increase retention. -chkSharesForAll.text=All Personnel Have Shares -chkSharesForAll.toolTipText=All combat and support personnel have shares rather than just MekWarriors -##end Finances Tab - -## Mercenary Tab -mercenaryPanel.title=Mercenary -lblEquipPercent.text=Combat Percent: -chkEquipContractSaleValue.text=Base on equipment sale value -lblDropShipPercent.text=DropShip Percent: -lblJumpShipPercent.text=JumpShip Percent: -lblWarShipPercent.text=WarShip Percent: -panMercenary.IntOpsPayment.title=Base contract payment on percentage of TO&E unit value (Campaign Operations) -panMercenary.IntOpsPayment.tooltip=Base contract payment on percentage of TO&E unit value (Campaign Operations) -panMercenary.FMMRPayment.title=Base contract payment on personnel payroll (FM:Mr) -panMercenary.FMMRPayment.tooltip=Base contract payment on personnel payroll (FM:Mr) -lblBLCSaleValue.text=Base battle loss compensation on equipment sale value -chkOverageRepaymentInFinalPayment.text=Repay Salvage Overages in Final Payment (Unofficial) -chkOverageRepaymentInFinalPayment.toolTipText=This is an unofficial addition that has you repay any overages from your salvage percent as part of the final payment, which may take that into a debit. -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 -lblXPForEvery.text=XP for every -lblKills.text=kills -lblTasks.text=successful tasks -lblSuccessXP.text=XP for each 12 rolled on a successful task -lblMistakeXP.text=XP for each 2 rolled on an unsuccessful task -lblTargetIdleXP.text=active month(s) on a 2d6 roll of greater than or equal to -lblContractNegotiationXP.text=XP awarded to the selected negotiator for a new contract -lblAdminWeeklyXP.text=XP awarded to each administrator every Monday for the work of the previous -lblAdminWeeklyXPPeriod.text=week(s) -spnMissionXpFail.text=Mission Failure XP -spnMissionXpSuccess.text=Mission Success XP -spnMissionXpOutstandingSuccess.text=Mission Outstanding Success XP (StratCon Only: awarded for finishing a mission with 3+ victory points) -lblEdgeCost.text=XP Cost for 1 Edge Point -txtInstructionsXP.title=Customizing Skill Costs -txtInstructionsXP.text=You can adjust the numbers in the table below to customize the XP costs for each skill at each level. \nThe given level listed on the row is the level being purchased, so the numbers in the +2 column are the costs of moving from level 1 to level 2. \nTo determine the BattleTech target number for each skill level, subtract the given level from each skill's target number, given in the Skills tab. \nEnter a -1 for skill levels that should be unattainable, and zero for skill levels that should be skipped. -##end Mercenary Tab - -## Experience Tab -experiencePanel.title=Experience -##end Experience Tab - -## Skills Tab -skillsPanel.title=Skills -##end Skills Tab - -## Special Abilities Tab -specialAbilitiesPanel.title=SPAs -##end Special Abilities Tab - -## Skill Randomization Tab -skillRandomizationPanel.title=Skill Randomization -lblOverallRecruitBonus.text=Overall Recruitment Bonus -panTypeRecruitBonus.title=Personnel Type Recruitment Bonus -chkExtraRandom.text=Extra Randomness -chkExtraRandom.toolTipText=If checked, an additional 1d6 will be rolled. On a 1, the skill will be lowered, and on a 6 the skill will be raised. -lblProbAntiMek.text=Probability of anti-mek skill for conventional infantry -spnOverallRecruitBonus.toolTipText=Add this number to the roll for primary skill levels -spnArtyProb.toolTipText=Probability that MekWarriors, vehicle crews, and infantry have artillery skill. Only applies if separate artillery skill is in use. -spnSecondProb.toolTipText=Probability that person will have a randomly chosen secondary skill. -spnTacticsGreen.toolTipText=Modifier for tactics skill for person with green primary skills. Only applies if tactics skill is in use. -spnTacticsReg.toolTipText=Modifier for tactics skill for person with regular primary skills. Only applies if tactics skill is in use. -spnTacticsVet.toolTipText=Modifier for tactics skill for person with veteran primary skills. Only applies if tactics skill is in use. -spnTacticsElite.toolTipText=Modifier for tactics skill for person with elite primary skills. Only applies if tactics skill is in use. -spnCombatSA.toolTipText=Modifier for Small Arms skill for all non-infantry combat personnel. -spnSupportSA.toolTipText=Modifier for Small Arms skill for all support personnel. -spnAbilityGreen.toolTipText=Modifier for the number of special abilities for person with green primary skills. Only applies if special abilities in use. -spnAbilityReg.toolTipText=Modifier for the number of special abilities for person with green primary skills. Only applies if special abilities in use. -spnAbilityVet.toolTipText=Modifier for the number of special abilities for person with green primary skills. Only applies if special abilities in use. -spnAbilityElite.toolTipText=Modifier for the number of special abilities for person with green primary skills. Only applies if special abilities in use. -lblUltraGreen.toolTipText=Ultra-Green for primary skills and no skill for other skills. -lblPhenotypesPanel.text=Trueborn Phenotype Probabilities -lblArtillerySkill.text=Artillery Skill -lblSecondarySkills.text=Secondary Skills -lblTacticsSkill.text=Tactics Skill -lblSupportPersonnel.text=Support Personnel -lblCombatPersonnel.text=Combat Personnel -lblSmallArmsSkill.text=Small Arms Skill -lblSpecialAbilities.text=Special Abilities -##end Skill Randomization Tab - -## Rank Systems Tab -rankSystemsPanel.title=Rank Systems -##end Rank Systems Tab - -## Name and Portrait Generation Tab -nameAndPortraitGenerationPanel.title=Name and Portrait Generation -chkUseOriginFactionForNames.text=Use origin faction for assigned names -chkUseOriginFactionForNames.toolTipText=The random name generator uses the person's origin faction to assign their name. -chkAssignPortraitOnRoleChange.text=Automatically assign portrait on role change -chkAssignPortraitOnRoleChange.toolTipText=With this enabled, a person without a portrait will automatically gain a random portrait when their primary role is switched to one of those selected below -panUsePortrait.all.text=All Roles -panUsePortrait.no.text=No Roles -txtPortraitInst.text=Checked personnel types will have a random portrait selected for them upon creation. \nThis setup is explained in the guide located at /docs/Windchilds Guides/Windchilds_Guide_to_MekHQ_Portrait_Generation.txt. -panRandomPortrait.title=Portrait Generation -##end Name and Portrait Generation Tab - -## Markets Tab -marketsPanel.title=Markets - -# Personnel Market -personnelMarketPanel.title=Personnel Market -lblPersonnelMarket.text=Personnel Market Type -lblPersonnelMarketType.toolTipText=This is the type of personnel market used to generate and remove new personnel for the campaign. -chkPersonnelMarketReportRefresh.text=Personnel Market Refresh Report -chkPersonnelMarketReportRefresh.toolTipText=Adds a report to the daily log when the personnel market refreshes. -personnelMarketRandomRemovalTargetsPanel.title=Random & Dylan's Removal Targets -personnelMarketRandomRemovalTargetsPanel.toolTipText=An individual daily removal roll that is beneath the relevant target number will cause an appropriately skilled person to leave the personnel market. -lblPersonnelMarketDylansWeight.text=Dylan's Method Common Unit Type Weight -lblPersonnelMarketDylansWeight.toolTipText=This is the weight between 0 and 1 used by Dylan's Method to create a person with a primary role based on the most common unit type within the force's hangar,
    instead of creating a person with a randomly determined role. -chkUsePersonnelHireHiringHallOnly.text=Hiring Halls & Capitals Only -chkUsePersonnelHireHiringHallOnly.toolTipText=Enabling this option disables the personnel market outside of hiring halls or capital planets. - -# Unit Market -unitMarketPanel.title=Unit Market -lblUnitMarketMethod.text=Unit Market Method -lblUnitMarketMethod.toolTipText=This is the method of unit market used to generate new units for the market. -chkUnitMarketRegionalMekVariations.text=Regional BattleMek Weight Variations -chkUnitMarketRegionalMekVariations.toolTipText=This adds some regional variation to the weight generated for BattleMeks based on the generating faction. +lblTaxesPercentage.tooltip=The percentage of profits paid as taxes. + +# createSharesPanel +lblSharesPanel.text=Shares \u270E +lblUseShareSystem.text=Enable Shares +lblUseShareSystem.tooltip=Gives personnel a stake in the unit. This system lowers profits but can\ + \ increase retention. +lblSharesForAll.text=All Personnel Have Shares +lblSharesForAll.tooltip=All combat and support personnel have shares rather than just MekWarriors. + +# createFinancesGeneralOptionsTab +lblPriceMultipliersTab.text=Price Multiplier Options +lblPriceMultipliersTabBody.text=It's important to remember that the qualities (A-F) under Used\ + \ Parts do not update based on whether you have Reverse Quality Names enabled.\ +
    \ +
    This means A is the worst quality and F is the best. + +# createGeneralMultipliersPanel +lblGeneralMultipliersPanel.text=General +lblCommonPartPriceMultiplier.text=Common Parts +lblCommonPartPriceMultiplier.tooltip=Multiplies the buy/sell price of common tech parts by the\ + \ specified value. +lblInnerSphereUnitPriceMultiplier.text=Inner Sphere Units +lblInnerSphereUnitPriceMultiplier.tooltip=Multiplies the buy/sell price of Inner Sphere tech units\ + \ by the specified value. +lblInnerSpherePartPriceMultiplier.text=Inner Sphere Parts +lblInnerSpherePartPriceMultiplier.tooltip=Multiplies the buy/sell price of Inner Sphere tech parts\ + \ by the specified value. +lblClanUnitPriceMultiplier.text=Clan Units +lblClanUnitPriceMultiplier.tooltip=Multiplies the buy/sell price of Clan tech units by the\ + \ specified value. +lblClanPartPriceMultiplier.text=Clan Parts +lblClanPartPriceMultiplier.tooltip=Multiplies the buy/sell price of Clan tech parts by the\ + \ specified value. +lblMixedTechUnitPriceMultiplier.text=Mixed Units +lblMixedTechUnitPriceMultiplier.tooltip=Multiplies the buy/sell price of Mixed Tech units by the\ + \ specified value. + +# createUsedPartsMultiplierPanel +lblUsedPartsMultiplierPanel.text=Used Parts + +# createOtherMultipliersPanel +lblOtherMultipliersPanel.text=Other +lblDamagedPartsValueMultiplier.text=Damaged Parts +lblDamagedPartsValueMultiplier.tooltip=Multiplies the value and thus the sell price of damaged\ + \ parts by the specified value. +lblUnrepairablePartsValueMultiplier.text=Irreparable Parts +lblUnrepairablePartsValueMultiplier.tooltip=Multiplies the value and thus the sell price of\ + \ unrepairable damaged parts by the specified value. +lblCancelledOrderRefundMultiplier.text=Cancelled Order Refund +lblCancelledOrderRefundMultiplier.tooltip=The decimal percentage of the original purchase price\ + \ that is refunded when an order is canceled. + +# Markets Tab +marketsContentTabs.title=Markets + +personnelMarketTab.title=Personnel +personnelMarketTab.border=\u2014 Toilet Paper 22 Tons\ +
    \u2014 Soap 1.2 Tons\ +
    \u2014 Dress Uniform Gloves (white, left) 150\ +
    \u2014 Tires (reconditioned) 2,000\ +
    \u2014 Food (12-31-3015) 96 Tons\ +
    \u2014 Entrenching Tools 100\ +
    \u2014 Ammunition (.22 cal air gun pellets) 15 Tons\ +
    \u2014 Medical Supplies (Prozac, hand directly to Chancellor) 1 case\ +
    \u2014 Morale Package (Hunky Hanse and Bellissima Melissa Dolls) 1 crate\ +
    Unknown Resupply Shipment\ +
    Fourth Succession War
    + +unitMarketTab.title=Units +unitMarketTab.border="Buying a new 'Mek is exciting \u2014 right up until you see the repair bill after\ + \ your first mission. Then it's just another hole in your wallet."\ +
    Sergeant Kyle "Scrapheap" Carter\ +
    The Blacksmiths
    + +contractMarketTab.title=Contracts +contractMarketTab.border="So there I was, between rock and a hard place, when suddenly I thought,\ + \ 'What am I doing on this side of the rock?'"\ +
    Star Commander Karra\ +
    Clan Ghost Bear, Constance, Apr 3050
    + +# createPersonnelMarketTab +lblPersonnelMarketTab.text=Personnel Market Options + +# createPersonnelMarketGeneralOptionsPanel +lblPersonnelMarketType.text=Market Method +lblPersonnelMarketType.tooltip=This is the type of personnel market used to generate and remove new\ + \ personnel for the campaign. +lblPersonnelMarketDylansWeight.text=Common Unit Type Weight +lblPersonnelMarketDylansWeight.tooltip=This is the weight between 0 and 1 used by Dylan's Method to\ + \ create a person with a primary role based on the most common unit type within the force's\ + \ hangar, instead of creating a person with a randomly determined role. +lblPersonnelMarketReportRefresh.text=Post Report on Market Refresh +lblPersonnelMarketReportRefresh.tooltip=Adds a report to the daily log when the personnel market\ + \ refreshes. +lblUsePersonnelHireHiringHallOnly.text=Hiring Halls & Capitals Only \u270E +lblUsePersonnelHireHiringHallOnly.tooltip=Enabling this option disables the personnel market\ + \ outside of hiring halls or capital planets. + +# createPersonnelMarketRemovalOptionsPanel +lblPersonnelMarketRemovalOptionsPanel.text=Removal Target Numbers + +# createUnitMarketTab +lblUnitMarketTab.text=Unit Market Options \u270E +lblUnitMarketMethod.text=Market Method +lblUnitMarketMethod.tooltip=This is the method of unit market used to generate new units for the\ + \ market. +lblUnitMarketRegionalMekVariations.text=Enable Faction 'Mek Weight Variance +lblUnitMarketRegionalMekVariations.tooltip=This adds some regional variation to the weight\ + \ generated for BattleMeks based on the generating faction. lblUnitMarketSpecialUnitChance.text=Special Vehicle Chance: 1 in -lblUnitMarketSpecialUnitChance.toolTipText=This setting determines the probability of artillery or support vehicles appearing in the market. The chance is calculated for each vehicle added to the market. Set this to 0 to disable the function. -lblUnitMarketRarityModifier.text=Unit Market Rarity Modifier -lblUnitMarketRarityModifier.toolTipText=This setting increases or decreases the rolls made to determine how many units appear on the unit market. -chkInstantUnitMarketDelivery.text=Instant Unit Market Delivery -chkInstantUnitMarketDelivery.toolTipText=Units bought from the unit market appear in the hangar immediately rather than having to wait for delivery. -chkUnitMarketReportRefresh.text=Unit Market Refresh Report -chkUnitMarketReportRefresh.toolTipText=Adds a report to the daily log when the unit market refreshes. - -# Contract Market -contractMarketPanel.title=Contract Market (StratCon or AtB Only) -lblContractMarketMethod.text=Contract Market Method -lblContractMarketMethod.toolTipText=This is the method of contract market used to generate new contracts to select. -lblContractSearchRadius.text=Contract Search Radius (Light Years): -lblContractSearchRadius.toolTipText=Limits the location of contract offers to systems which are within this radius of the current system. -chkVariableContractLength.text=Variable Contract Length -chkVariableContractLength.toolTipText=The length of the contract has a duration variance instead of just using the constant base length for the mission type. -chkContractMarketReportRefresh.text=Contract Market Refresh Report -chkContractMarketReportRefresh.toolTipText=Adds a report to the daily log when the contract market refreshes. -lblContractMaxSalvagePercentage.text=Max salvage percentage -lblContractMaxSalvagePercentage.toolTipText=Used to limit the salvage roll when creating a new contract -lblDropShipBonusPercentage.text=DropShip Bounty -lblDropShipBonusPercentage.toolTipText=It is not possible to salvage DropShips from ground maps. Setting this option above 0 will instead pay a percentage of the DropShip's value as a 'bounty.' -##end Markets Tab - -## RATs Tab -ratPanel.title=Random Assignment Tables -btnUseRATGenerator.text=Use RAT Generator (Recommended) -btnUseRATGenerator.tooltip=The unit market's units, GM Tools random units, AtB/StratCon Enemy forces, and all personnel market attached units will be generated using MegaMek's RAT Generator.
    This is recommended because it handles all time periods and factions while being more lore accurate for the changes over the years and has a generation fallback to ensure units are generated when possible. -btnUseStaticRATs.text=Use Traditional RATs -btnUseStaticRATs.tooltip=The unit market's units, GM Tools random units, AtB/StratCon Enemy forces, and all personnel market attached units will be generated from the list of Random Assignment Tables indicated below. -traditionalRATPanel.title=Traditional RATs -txtRATInstructions.text=Enemy forces, the unit market, random units, and recruits will check the following random assignment tables (RATs) in the designated order until one is found that includes the unit type appropriate to the era. Note that unless at least one of the chosen RATs has an entry for general use, MekHQ will not be able to generate units for any faction not explicitly listed. -lblAvailableRATs.text=Available RATs -btnAddRAT.text=Add -btnAddRAT.toolTipText=Add the selected RAT to the list of chosen RATs. -btnRemoveRAT.text=Remove -btnRemoveRAT.toolTipText=Remove the selected RAT from the list of chosen RATs. -btnMoveRATUp.text=Move Up -btnMoveRATUp.toolTipText=Increase the priority of selected RAT. -btnMoveRATDown.text=Move Down -btnMoveRATDown.toolTipText=Decrease the priority of selected RAT. -lblChosenRATs.text=Chosen RATs -chkIgnoreRATEra.text=Allow RATs later than campaign year. -chkIgnoreRATEra.toolTipText=If none of the chosen collections has an appropriate RAT for the era, use the earliest that matches. -##end RATs Tab - -## Against the Bot Tab -againstTheBotPanel.title=StratCon & Against the Bot -lblSubAtbAdmin.text=Unit Administration -lblSubAtBContract.text=Contract Operations -lblSubAtBScenario.text=Scenarios -chkUseAtB.text=Use Against the Bot campaign rules (required for StratCon) -chkUseAtB.toolTipText=Against the Bot is a set of campaign rules developed by users of the BattleTech forums. StratCon is a newer system built on top of AtB and is recommended. -chkUseStratCon.text=Use StratCon campaign rules -chkUseStratCon.toolTipText=An update of the AtB ruleset. -lblSkillLevel.text=Skill Level -lblSkillLevel.toolTipText=This is the difficulty level for generated scenarios.
    Values above Elite are not recommended. -lblScenarioMod.text=Random Scenario Modifiers -lblScenarioModMax.text=Maximum: -lblScenarioModMax.toolTipText=This is the maximum number of random scenario mods that can spawn on for a single Scenario. Excludes StratCon facility modifiers. +lblUnitMarketSpecialUnitChance.tooltip=This setting determines the probability of artillery,\ + \ civilian, cargo, or support vehicles appearing in the market. The chance is calculated for each\ + \ vehicle added to the market.\ +
    \ +
    Set this to 0 to prevent special units from appearing in the unit market. +lblUnitMarketRarityModifier.text=Unit Rarity Modifier +lblUnitMarketRarityModifier.tooltip=This setting increases or decreases the rolls made to determine\ + \ how many units appear on the unit market. +lblInstantUnitMarketDelivery.text=Enable Instant Deliveries +lblInstantUnitMarketDelivery.tooltip=Units bought from the unit market appear in the hangar\ + \ immediately rather than having to wait for delivery. +lblUnitMarketReportRefresh.text=Post Report on Market Refresh +lblUnitMarketReportRefresh.tooltip=Adds a report to the daily log when the unit market refreshes. + +# createContractMarketTab +lblContractMarketTab.text=Contract Market Options + +# createContractMarketGeneralOptionsPanel +lblContractMarketMethod.text=Market Method +lblContractMarketMethod.tooltip=This is the method of contract market used to generate new\ + \ contracts to select. +lblContractSearchRadius.text=Search Radius +lblContractSearchRadius.tooltip=Limits the location of contract offers to systems which are within\ + \ this radius of the current system.\ +
    \ +
    Warning: the larger this number, the more time spent in transit between contracts. +lblVariableContractLength.text=Vary Contract Lengths +lblVariableContractLength.tooltip=The length of the contract has a duration variance instead of\ + \ just using the constant base length for the mission type. +lblContractMarketReportRefresh.text=Post Report on Market Refresh +lblContractMarketReportRefresh.tooltip=Adds a report to the daily log when the contract market\ + \ refreshes. +lblCoontractMaxSalvagePercentage.text=Max Salvage Percent +lblCoontractMaxSalvagePercentage.tooltip=Used to limit the salvage roll when creating a new contract. +lblDropShipBonusPercentage.text=DropShip Bonus Percent +lblDropShipBonusPercentage.tooltip=It is not possible to salvage DropShips from ground maps.\ + \ Setting this option above 0 will instead pay a percentage of the DropShip's value as a 'bounty.' + +# createMercenaryPanel +lblContractPayPanel.text=Contract Pay +lblContractEquipment.text=TO&E Value Influences Pay (CamOps) +lblContractEquipment.tooltip=Contract pay is based on the total value of units in the TO&E. +lblEquipContractSaleValue.text=Base on Sale Value +lblEquipContractSaleValue.tooltip=Use the unit's sale value instead of its buy value when \ + determining the value of the unit for contract pay purposes. +lblEquipPercent.text=Combat Unit % +lblEquipPercent.tooltip=What percentage of combat unit value should contract pay be based on. +lblDropShipPercent.text=DropShip % +lblDropShipPercent.tooltip=What percentage of DropShip value should contract pay be based on. +lblJumpShipPercent.text=JumpShip Percent +lblJumpShipPercent.tooltip=What percentage of JumpShip value should contract pay be based on. +lblWarShipPercent.text=WarShip Percent +lblWarShipPercent.tooltip=What percentage of WarShip value should contract pay be based on. +lblContractPersonnel.text=Payroll Influences Pay (FM:Mr) +lblContractPersonnel.tooltip=Base contract pay is derived from the salaries of all employed personnel. +lblBLCSaleValue.text=Battle Loss Compensation uses Sale Value +lblBLCSaleValue.tooltip=When determining Battle Loss Compensation, base the value on the unit's\ + \ Sale value, instead of its buy value. +lblUseInfantryDoseNotCountBox.text=Infantry Not Counted Towards Contract Pay +lblUseInfantryDoseNotCountBox.tooltip=When this option is selected, infantry will not count for\ + \ contract pay +lblMercSizeLimited.text=Mercenary Campaign Size Impacts Pay +lblMercSizeLimited.tooltip=Mercenary units that exceed a certain size relative to unit rating\ + \ suffer penalties to contract clause rolls. +lblOverageRepaymentInFinalPayment.text=Repay Salvage Overages on Contract End +lblOverageRepaymentInFinalPayment.tooltip=This is an unofficial addition that has you repay any\ + \ overages from your salvage percent as part of the final payment, which may take that into a\ + \ debit. + +# Markets Tab +rulesetsContentTabs.title=Custom Rulesets + +stratConGeneralTab.title=General +stratConGeneralTab.border="Strategy is just a fancy way of saying, 'I'm going to send you in first\ + \ and see what happens.'"\ +
    Sergeant Milo "Wildcard" Trent\ +
    The Tridents
    + +legacyTab.title=Legacy Options +legacyTab.border="You know, for 5-tons, I'd expect more than 256 colors!"\ +
    Colonel Elias "Warhound" Drake\ +
    Impetus Alliance\ +
    Comments made following a Targeting Computer retrofit
    + +# substantializeUniversalOptions +lblSkillLevel.text=Difficulty \u2714 +lblSkillLevel.tooltip=This is the difficulty level for generated scenarios.\ +
    \ +
    Recommended: New players are recommended to start with Green or Ultra-Green. +lblOpForLanceTypeMeks.text=Meks +lblOpForLanceTypeMeks.tooltip=What ratio of enemy forces should be Mek forces? +lblOpForLanceTypeMixed.text=Mixed +lblOpForLanceTypeMixed.tooltip=What ratio of enemy forces should be forces that include both Meks\ + \ and vehicles? +lblOpForLanceTypeVehicle.text=Vehicles +lblOpForLanceTypeVehicle.tooltip=What ratio of enemy forces should be vehicle forces? +lblUseDropShips.text=Use Player DropShips +lblUseDropShips.tooltip=Some scenarios may require the player to deploy with a combat drop. If the\ + \ player doesn't have a DropShip, the employer will provide one. +lblOpForUsesVTOLs.text=Enable OpFor VTOLs +lblOpForUsesVTOLs.tooltip=The Princess may use VTOLS in its vehicle forces. +lblClanVehicles.text=Enable Clan Vehicles +lblClanVehicles.tooltip=Clan opposing forces have a small chance of fielding vehicle Stars. The\ + \ lower the unit rating, the higher the chance of encountering vehicles. +lblRegionalMekVariations.text=Faction Influences Mek Weights +lblRegionalMekVariations.tooltip=Use alternate weight class distributions for some factions. +lblAttachedPlayerCamouflage.text=Attached Units use Campaign Camouflage +lblAttachedPlayerCamouflage.tooltip=Any attached units will have their camouflage changed to match\ + \ the scheme used by your campaign. +lblPlayerControlsAttachedUnits.text=Player Controls Attached Units +lblPlayerControlsAttachedUnits.tooltip=All attached units are placed under your command for the\ + \ duration of the scenario. +lblSPAUpgradeIntensity.text=SPA Chance +lblSPAUpgradeIntensity.tooltip=How likely it is that regular+ OpFor pilots will receive SPAs. -1\ + \ indicates never, 3 indicates always. +lblAutoConfigMunitions.text=OpFor Equips Special Munitions \u270E \u2318 +lblAutoConfigMunitions.tooltip=Use semi-intelligent configuration of allied and enemy munitions,\ + \ tailored to each scenario. +lblScenarioModMax.text=Maximum Count +lblScenarioModMax.tooltip=This is the maximum number of random scenario mods that can spawn on for\ + \ a single scenario. Excludes StratCon facility modifiers. lblScenarioModChance.text=Chance -lblScenarioModChance.toolTipText=This is the percentage chance for a random scenario mod to appear for a Scenario. Excludes StratCon facility modifiers. -lblScenarioModBV.text=BV Cap -lblScenarioModBV.toolTipText=This is the percentage of total BV that can be used for each scenario mods. This affects StratCon facility modifiers, but can be overridden by certain modifiers. -chkTrackOriginalUnit.text=Track Original Unit -chkTrackOriginalUnit.toolTipText=MekWarriors, who have their own unit when recruited, will take the same unit when they go if it is still available. -chkLimitLanceWeight.text=Limit lance deployment by weight -chkLimitLanceWeight.toolTipText=Lances which exceed the maximum weight for an Assault lance (390 tons for IS) cannot be deployed. -chkLimitLanceNumUnits.text=Limit lance deployment by size -chkLimitLanceNumUnits.toolTipText=Lances which do not fall between the minimum and maximum sizes (3-6 for IS) cannot be deployed. -lblLanceStructure.text=Unit organization: -chkUseStrategy.text=Use commander strategy -chkUseStrategy.toolTipText=The maximum number of lances that can be assigned roles is limited by the commander's strategy skill. -lblBaseStrategyDeployment.text=Base number of lances: -spnBaseStrategyDeployment.toolTipText=The number of lances that can be deployed with a commander strategy of zero. -chkAdjustPaymentForStrategy.text=Adjust contract payment for deployment limits -chkAdjustPaymentForStrategy.toolTipText=If the number of lances required to be deployed exceeds the current commander's strategy skill,
    reduce the number when calculating new contracts and reduce the payment proportionally. -lblAdditionalStrategyDeployment.text=Per rank of strategy: -spnAdditionalStrategyDeployment.toolTipText=The number of additional lances that can be deployed for each increase in strategy skill. -chkUseGenericBattleValue.text=Use Force Generation 3 -chkUseGenericBattleValue.toolTipText=Bot forces are balanced used Generic Battle Value, an estimation of the average battle value for a unit of that type and weight.\ - \ This ignores pilot skill, meaning contracts against Green and Elite OpFors should feel fundamentally different.\ - \ Similarly, OpFors with higher or lower than average equipment (such as Clans or Pirates) will present higher or lower difficulty scenarios. -chkUseVerboseBidding.text=Use Verbose Bidding -chkUseVerboseBidding.toolTipText=When Generic BV is in use, Clan OpFors will engage in bidding prior to the scenario.\ - \ If this option is enabled, a list of all units bid away will be provided. -chkUseVehicles.text=Use vehicles -chkUseVehicles.toolTipText=Enemy forces can include vehicles. -chkClanVehicles.text=Clan OpFors use vehicles -chkClanVehicles.toolTipText=Clan opposing forces have a small chance of fielding vehicle Stars. The lower the unit rating, the higher the chance of encountering vehicles. -chkDoubleVehicles.text=Double enemy vehicles -chkDoubleVehicles.toolTipText=Enemy vehicle or combined arms units include twice as many vehicles as they would Meks. -chkAutoConfigMunitions.text=Auto-configure Bot Munitions -chkAutoConfigMunitions.toolTipText=Use semi-intelligent configuration of allied and enemy munitions, tailored to each scenario. -lblOpForLanceType.text=OpFor lance type ratios: -lblOpForLanceType.toolTipText=The relative chance of Inner Sphere opponents deploying Mek or vehicle lances.
    The default values of 1/2/3 result in mixed lances occurring twice as often as Meks lances and vehicles three times as likely. -lblOpForLanceTypeMek.text=Meks: -lblOpForLanceTypeMixed.text=Mixed: -lblOpForLanceTypeVehicle.text=Vehicles: -chkOpForUsesVTOLs.text=Allow enemy VTOLs -chkOpForUsesVTOLs.toolTipText=The bot may use VTOLS in its vehicle forces. -chkOpForUsesAero.text=Allow enemy Aeros -chkOpForUsesAero.toolTipText=The bot may reinforce its units with aircraft. -lblOpForAeroLikelihood.text=/6 Chance -lblOpForAeroLikelihood.toolTipText=The chance of an OpFor being reinforced with aircraft. -chkOpForUsesLocalForces.text=Allow enemy turrets/infantry -chkOpForUsesLocalForces.toolTipText=The bot may introduce turrets, infantry and battle armor in defensive scenarios with appropriate terrain. -lblOpForLocalForceLikelihood.text=/6 Chance -lblOpForLocalForceLikelihood.toolTipText=The chance of an OpFor being reinforced with turrets, infantry or battle armor in appropriate conditions. -chkAdjustPlayerVehicles.text=Adjust lance weight for player vehicles -chkAdjustPlayerVehicles.toolTipText=Vehicles deployed by the player only count half their weight to the lance weight class to compensate for enemy force generation designed to oppose player Meks. -chkUseAero.text=Use Aerospace -chkUseAero.toolTipText=Aerospace pilots and techs appear as recruits in the personnel market. -chkUseDropShips.text=Use DropShips -chkUseDropShips.toolTipText=Some scenarios may require the player to deploy with a combat drop. If the player does not have a DropShip, the employer will provide one. -chkMercSizeLimited.text=Merc company size limits -chkMercSizeLimited.toolTipText=Mercenary units that exceed a certain size relative to unit rating suffer penalties to contract clause rolls. -lblIntensity.text=Intensity: -spnIntensity.toolTipText=Adjust frequency of standard battles. -lblBattleFrequency.text=Chance of battle by role: -btnIntensityUpdate.text=Update Intensity From Battle Roles -chkGenerateChases.text=Generate Chase Scenarios -chkGenerateChases.toolTipText=If selected, AtB chase scenarios will be generated. Otherwise, they will be replaced with other scenarios formats depending on the lance role in question. -chkRestrictPartsByMission.text=Restrict parts by mission -chkRestrictPartsByMission.toolTipText=The availability of parts is limited based on the mission type of the current contract. -lblBonusPartExchangeValue.text=Bonus Part Exchange Value -lblBonusPartExchangeValue.toolTipText=At the end of a StratCon or AtB Contract, any remaining Bonus Parts are converted into c-bills. This setting sets the value of each Bonus Part. Set to 0 to disable Bonus Part exchange. -lblBonusPartMaxExchangeCount.text=Bonus Part Maximum Exchange Count -lblBonusPartMaxExchangeCount.toolTipText=What is the maximum number of Bonus Parts that can be exchanged for c-bills at the end of a StratCon or AtB Contract? Any Bonus Parts beyond this are lost. Set to 0 to apply no limit. -chkRegionalMekVariations.text=Varied weight distributions by faction -chkRegionalMekVariations.toolTipText=Use alternate weight class distributions for some factions. -chkAttachedPlayerCamouflage.text=Player-controlled allied units use player's camouflage -chkPlayerControlsAttachedUnits.text=All attached allied units are player controlled -chkUseWeatherConditions.text=Use weather conditions -chkUseWeatherConditions.toolTipText=Generate weather conditions when generating a scenario. -chkUseLightConditions.text=Use light conditions -chkUseLightConditions.toolTipText=Determine light conditions when generating a scenario. -chkUsePlanetaryConditions.text=Use planetary conditions -chkUsePlanetaryConditions.toolTipText=Set gravity and atmosphere based on the contract location. -lblFixedMapChance.text=Fixed Map Chance -lblFixedMapChance.toolTipText=The likelihood, in percent, that a fixed user-made map will be used in place of a generated map. -lblSPAUpgradeIntensity.text=SPA Upgrade Intensity -lblSPAUpgradeIntensity.toolTipText=How likely it is that regular+ OpFor pilots will receive SPAs. -1 indicates never, 3 indicates always. -chkAeroRecruitsHaveUnits.text=Pilots Have Units -chkAeroRecruitsHaveUnits.toolTipText=Aerospace pilot recruits can have fighters, and those that do will take a fighter with them when leaving the unit. -##end Against the Bot Tab - -## Button Panel -btnOkay.text=Confirm -btnSavePreset.text=Confirm and Save as Preset -btnSavePreset.toolTipText=Confirm the changes in the dialog and save it as a Campaign Preset. -btnLoadPreset.text=Load a Preset -btnLoadPreset.toolTipText=Load a Campaign Preset and apply it to the current dialog. If it is during startup, it will apply all values outside of starting planet, contract count, and game options. Otherwise, it will only apply continuous preset values outside of game options. -##end Button Panel +lblScenarioModChance.tooltip=This is the percentage chance for a random scenario mod to appear for\ + \ a scenario. Excludes StratCon facility modifiers. +lblScenarioModBV.text=Battle Value Percent +lblScenarioModBV.tooltip=This is the percentage of total BV that can be used for each scenario mods.\ + \ This affects StratCon facility modifiers, but can be overridden by certain modifiers. +lblUseWeatherConditions.text=Enable Weather Conditions +lblUseWeatherConditions.tooltip=Generate weather conditions when generating a scenario. +lblUseLightConditions.text=Enable Light Conditions +lblUseLightConditions.tooltip=Determine light conditions when generating a scenario. +lblUsePlanetaryConditions.text=Enable Planetary Conditions +lblUsePlanetaryConditions.tooltip=Set gravity and atmosphere based on the contract location. +lblFixedMapChance.text=User-Made Map Chance +lblFixedMapChance.tooltip=The likelihood, in percent, that a fixed user-made map will be used in\ + \ place of a generated map. +lblRestrictPartsByMission.text=Missions Influence Availability +lblRestrictPartsByMission.tooltip=The availability of parts is limited based on the mission type of\ + \ the current contract. +lblLimitLanceWeight.text=Limit Player Drop Weights (Deprecated) +lblLimitLanceWeight.tooltip=Lances which exceed the maximum weight for an Assault lance (390 tons\ + \ for IS) can't be deployed. +lblLimitLanceNumUnits.text=Limit Player Drop Sizes (Deprecated) +lblLimitLanceNumUnits.tooltip=Lances which don't fall between the minimum and maximum sizes (3-6\ + \ for IS) can't be deployed. +lblUseStrategy.text=Commander Strategy Influences Combat Team Counts +lblUseStrategy.tooltip=The maximum number of combat Teams that can be assigned roles is limited by\ + \ the commander's strategy skill. +lblBaseStrategyDeployment.text=Base Force Count +lblBaseStrategyDeployment.tooltip=The number of lances that can be deployed with a commander\ + \ strategy of zero. +lblAdditionalStrategyDeployment.text=Per Rank in Strategy +lblAdditionalStrategyDeployment.tooltip=The number of additional lances that can be deployed for\ + \ each increase in strategy skill. +lblAdjustPaymentForStrategy.text=Adjust Contract Pay by Maximum Force Count +lblAdjustPaymentForStrategy.tooltip=If the number of lances required to be deployed exceeds the\ + \ current commander's strategy skill, reduce the number when calculating new contracts and reduce\ + \ the payment proportionally. + +# createUniversalScenarioGenerationPanel +lblUniversalScenarioGenerationPanel.text=Scenario Generation + +# createUniversalUnitRatioPanel +lblUniversalUnitRatioPanel.text=Unit Ratios + +# createUniversalModifiersPanel +lblUniversalModifiersPanel.text=Random Modifiers + +# createUniversalMapGenerationPanel +lblUniversalMapGenerationPanel.text=Map Generation + +# createUniversalPartsPanel +lblUniversalPartsPanel.text=Parts Availability + +# createUniversalLancePanel +lblUniversalLancePanel.text=Force Organization + +# createStratConTab +lblStratConTab.text=General Options \u270E +lblUseStratCon.text=Enable StratCon (Digital GM) \u2318 \u26A0 +lblUseStratCon.tooltip=StratCon, short for Strategic Context, is a ruleset that emphasizes\ + \ managing and deploying military units across large-scale sectors, giving context to the weekly\ + \ scenarios that occur while on a contract. StratCon was introduced in 2021 and officially\ + \ replaced Legacy AtB in 2024, alongside the release of v50.02.\ +
    \ +
    Warning:If StratCon is enabled, all Legacy options are ignored. +lblUseGenericBattleValue.text=Enable Force Generation 3 +lblUseGenericBattleValue.tooltip=Bot forces are balanced used Generic Battle Value, an estimation\ + \ of the average battle value for a unit of that type and weight. This ignores pilot skill,\ + \ meaning contracts against Green and Elite OpFors should feel fundamentally different. Similarly,\ + \ OpFors with higher or lower than average equipment (such as Clans or Pirates) will present\ + \ higher or lower difficulty scenarios. +lblUseVerboseBidding.text=Enable Verbose Clan Bidding +lblUseVerboseBidding.tooltip=When Generic BV is in use, Clan OpFors will engage in bidding prior\ + \ to the scenario. If this option is enabled, a list of all units bid away will be provided. + +# initializeLegacyTab +lblLegacyTab.text=Legacy Options \u270E +lblLegacyTabBody.text=This tab includes options for Legacy AtB: Against the Bot. All options\ + \ included here should be considered Deprecated and are not used by StratCon. +lblUseAtB.text=Enable Legacy AtB: Against the Bot (Digital GM) \u2318 \u26A0 +lblUseAtB.tooltip=AtB was MekHQ's first attempt at a Digital GM. It creates randomized contracts\ + \ and scenarios, allowing you to experience being a mercenary commander in the Inner Sphere.\ + \ Support for Legacy AtB ended in 2025 with v50.02.\ +
    \ +
    Warning:If StratCon is enabled, all Legacy options are ignored. + +# createAutoResolvePanel +lblAutoResolvePanel.text=AutoResolve \u270E \u2318 +lblAutoResolveMethod.text=AutoResolve Method \u26A0 +lblAutoResolveMethod.tooltip=Princess \u2014 Princess plays the scenario on your behalf.\ +
    ACAR: Abstract Combat Auto Resolution \u2014 A complex simulation that completes the entire\ + \ scenario in seconds. +lblAutoResolveNumberOfScenarios.text=Prediction Accuracy \u26A0 +lblAutoResolveNumberOfScenarios.tooltip=The number of times ACAR will run when determining the\ + \ chance of victory. Increases how long ACAR will take to make its prediction, but will increase\ + \ accuracy.\ +
    \ +
    Requirement: Requires ACAR. +lblAutoResolveVictoryChanceEnabled.text=Display Chance of Victory \u26A0 +lblAutoResolveVictoryChanceEnabled.tooltip=Determines whether MekHQ should tell you your chance of\ + \ success before resolving the scenario.\ +
    \ +
    Requirement: Requires ACAR. +lblAutoResolveExperimentalPacarGuiEnabled.text=Experimental Commander's Interface for PACAR \u26A0 +lblAutoResolveExperimentalPacarGuiEnabled.tooltip=A lightweight graphical interface for PACAR.\ + \ Warning: This is an experimental feature and may not work as expected. It's intended for solo play only. + +lblMinimapTheme.text=Strategic View Theme \u26A0 +lblMinimapTheme.tooltip=Select a theme for the strategic view screen on the Commander's Interface + +# createLegacyOpForGenerationPanel +lblLegacyOpForGenerationPanel.text=Force Generation +lblUseVehicles.text=Enable NPC Vehicles +lblUseVehicles.tooltip=Enemy forces can include vehicles. +lblDoubleVehicles.text=Double NPC Vehicles +lblDoubleVehicles.tooltip=Clan opposing forces have a small chance of fielding vehicle Stars. The\ + \ lower the unit rating, the higher the chance of encountering vehicles. +lblOpForUsesAero.text=Enable NPC Aerospace Fighters +lblOpForUsesAero.tooltip=The bot may reinforce its units with aircraft. +lblOpForAeroChance.text=Aerospace Chance +lblOpForAeroChance.tooltip=The chance of an OpFor being reinforced with aircraft, determined with a\ + \ 1d6 roll. +lblOpForUsesLocalForces.text=Enable NPC Infantry & Turrets +lblOpForUsesLocalForces.tooltip=The bot may introduce turrets, infantry and battle armor in\ + \ defensive scenarios with appropriate terrain. +lblAdjustPlayerVehicles.text=Treat Player Vehicles as Half Weight +lblAdjustPlayerVehicles.tooltip=Vehicles deployed by the player only count half their weight to the\ + \ lance weight class to compensate for enemy force generation designed to oppose player Meks. + +# createLegacyScenarioGenerationPanel +lblLegacyScenarioGenerationPanel.text=Scenario Generation +lblGenerateChases.text=Generate Chase Scenarios +lblGenerateChases.tooltip=If selected, AtB chase scenarios will be generated. Otherwise, they will\ + \ be replaced with other scenario formats depending on the lance role in question. +lblAtBBattleIntensity.text=Battle Frequency +lblAtBBattleIntensity.tooltip=How intense should contracts be? Lower these values for easier contracts. +lblIntensityUpdate.text=Update Intensity +lblIntensityUpdate.tooltip=Update intensity based on the current battle chance values. + +## Advancement Tab +awardsAndRandomizationContentTabs.title=Awards & Randomization + +xpAwardsTab.title=Experience Awards +xpAwardsTab.border="It is possible to commit no mistakes and still lose. That is not a weakness;\ + \ that is life."\ +
    Captain Jean Stuart, The Voyagers\ +
    Date Unknown
    + +randomizationTab.title=Randomization +randomizationTab.border="Every skill has its moment. What seems trivial today may save lives tomorrow,\ + \ because out in the stars, survival favors the adaptable."\ +
    Colonel Drake "Ironjaw" Valen\ +
    The Missed Connection Militia
    + +# xpAwardsTab +lblXpAwardsTab.text=Experience Award Options +lblXpCostMultiplier.text=Advancement Multiplier \u26A0 +lblXpCostMultiplier.tooltip=This value multiplies the XP costs of all SPAs, Edge purchases, and\ + \ Skill Levels.\ +
    \ +
    Warning: This option updates costs while you play and not the options shown here. + +# createTasksPanel +lblTasksPanel.text=Tasks (Deprecated) +lblTaskXP.text=XP per +lblTaskXP.tooltip=How much experience should be awarded every time a task milestone is met?\ +
    \ +
    Warning: This option disproportionately benefits characters that make a lot of skill\ + \ checks. Usually Admin characters, or those making acquisition checks.\ +
    \ +
    Recommended: Keep this disabled +lblNTasksXP.text=Successful Tasks \u270E \u26A0 \u2714 +lblNTasksXP.tooltip=How many successful tasks need to be completed before experience is awarded?\ +
    \ +
    Warning: This option disproportionately benefits characters that make a lot of skill\ + \ checks. Usually Admin characters, or those making acquisition checks.\ +
    \ +
    Recommended: Keep this disabled +lblSuccessXP.text=XP per Successful Natural 12 \u26A0 \u2714 +lblSuccessXP.tooltip=How much experience should be awarded whenever an unmodified 12 is rolled for\ + \ a skill check?\ +
    \ +
    Warning: This option disproportionately benefits characters that make a lot of skill\ + \ checks. Usually Admin characters, or those making acquisition checks.\ +
    \ +
    Recommended: Keep this disabled +lblMistakeXP.text=XP per Unsuccessful Natural 2 \u26A0 \u2714 +lblMistakeXP.tooltip=How much experience should be awarded whenever an unsuccessful, unmodified 2\ + \ is rolled for a skill check?\ +
    \ +
    Warning: This option disproportionately benefits characters that make a lot of skill\ + \ checks. Usually Admin characters, or those making acquisition checks.\ +
    \ +
    Recommended: Keep this disabled. + +# createScenariosPanel +lblScenariosPanel.text=Scenarios \u270E +lblScenarioXP.text=XP per Scenario \u26A0 \u2714 +lblScenarioXP.tooltip=How much experience should be awarded per scenario?\ +
    \ +
    Warning: This option creates a situation where higher skilled characters get more\ + \ experience than lower skilled characters, as they are more likely to be dispatched to a\ + \ scenario. It also disproportionately punishes support characters, who don't go on scenarios.\ +
    \ +
    Recommended: If this is enabled, it should only aware one or two experience. +lblKillXP.text=XP per +lblKillXP.tooltip=How much experience should be awarded per kill?\ +
    \ +
    Warning: This option creates a situation where higher skilled characters get more\ + \ experience than lower skilled characters, as they are more likely to score 'kills.' It also\ + \ disproportionately benefits characters using weaponry that allows them to score multiple kills\ + \ at once, such as bomb equipped aircraft pilots.\ +
    \ +
    Recommended: Keep this disabled. +lblKills.text=Kills (Deprecated) \u26A0 \u2714 +lblKills.tooltip=How many kills need to be scored before experience is awarded?\ +
    \ +
    Warning: This option creates a situation where higher skilled characters get more\ + \ experience than lower skilled characters, as they are more likely to score 'kills.' It also\ + \ disproportionately benefits characters using weaponry that allows them to score multiple kills\ + \ at once, such as bomb equipped aircraft pilots.\ +
    \ +
    Recommended: Keep this disabled. + +# createMissionsPanel +lblMissionsPanel.text=Missions +lblVocationalXP.text=Vocational XP per \u26A0 +lblVocationalXP.tooltip=How much experience should be awarded per vocational experience check? This\ + \ value is doubled while on any contract not classified as 'Garrison Type' (any contract that\ + \ ends in 'Duty'). +lblVocationalXPFrequency.text=Months +lblVocationalXPFrequency.tooltip=How many months occur between vocational experience checks?\ + \ Characters will make a vocational experience check every time this many months has passed. +lblVocationalXPTargetNumber.text=Vocational XP Target Number +lblVocationalXPTargetNumber.tooltip=What is the 2d6 target number a character must beat to be\ + \ awarded vocational experience? +lblMissionXpFail.text=Failure +lblMissionXpFail.tooltip=How much experience is awarded for a failed contract? This is granted to\ + \ all active personnel. +lblMissionXpSuccess.text=Success +lblMissionXpSuccess.tooltip=How much experience is awarded for a successful contract? This is\ + \ granted to all active personnel. +lblMissionXpOutstandingSuccess.text=Outstanding Success (StratCon Only) +lblMissionXpOutstandingSuccess.tooltip=Experience points awarded when successfully concluding a\ + \ mission with 3+ campaign victory points. + +# createAdministratorsPanel +lblAdministratorsXpPanel.text=Administrators (Deprecated) \u270E \u26A0 \u2714 +lblAdminWeeklyXP.text=XP per +lblAdminWeeklyXP.tooltip=How much experience is awarded to Admin personnel every period?\ +
    \ +
    Warning: This option is responsible for significantly increasing the experience\ + \ gains of admin personnel.\ +
    \ +
    Recommended: Keep this disabled. +lblAdminWeeklyXPPeriod.text=Weeks \u26A0 \u2714 +lblAdminWeeklyXPPeriod.tooltip=How much time needs to pass between admin experience being awarded?\ +
    \ +
    Warning: This option is responsible for significantly increasing the experience\ + \ gains of admin personnel.\ +
    \ +
    Recommended: Keep this disabled. +lblContractNegotiationXP.text=XP to Negotiator \u26A0 \u2714 +lblContractNegotiationXP.tooltip=How much experience does a contract negotiator receive for each\ + \ negotiated contract?\ +
    \ +
    Warning: This option is responsible for significantly increasing the experience\ + \ gains of admin personnel.\ +
    \ +
    Recommended: Keep this disabled. + +# skillRandomizationTab +lblSkillRandomizationTab.text=Skill Randomization Options \u270E +lblExtraRandomness.text=Extra Randomness \u26A0 +lblExtraRandomness.tooltip=If checked, an additional 1d6 will be rolled per skill possessed by a\ + \ newly created character. On a 1, the skill will be lowered, and on a 6 the skill will be raised.\ +
    \ +
    Warning: Due to the way experience levels are calculated, enabling this option will\ + \ more frequently have characters created with slightly lower than normal experience levels. + +lblPhenotypesPanel.text=Clan Trueborn Percentages +lblMekWarrior.text=MekWarrior +lblMekWarrior.tooltip=What percentage of Clan MekWarriors should have a Trueborn phenotype? +lblElemental.text=Elemental +lblElemental.tooltip=What percentage of Clan Battle Armor should have a Trueborn phenotype? +lblAerospace.text=Aerospace +lblAerospace.tooltip=What percentage of Clan Aerospace pilots should have a Trueborn phenotype? +lblVehicle.text=Vehicle +lblVehicle.tooltip=What percentage of Vehicle Crew should have a Trueborn phenotype? +lblProtoMek.text=ProtoMek +lblProtoMek.tooltip=What percentage of ProtoMek pilots should have a Trueborn phenotype? +lblNaval.text=Naval +lblNaval.tooltip=What percentage of Naval crew should have a Trueborn phenotype? + +lblAbilityPanel.text=Random SPA Chances +lblAbilityGreen.text=Green \u26A0 +lblAbilityGreen.tooltip=Modifier to the 2d6 roll used to determine whether a character is created\ + \ already possessing SPAs. Only relevant if SPAs are enabled.\ +
    \ +
    <9: 0 SPAs\ +
    10-11: 1 SPA\ +
    12+: 2 SPAs +lblAbilityRegular.text=Regular \u26A0 +lblAbilityRegular.tooltip=Modifier to the 2d6 roll used to determine whether a character is created\ + \ already possessing SPAs. Only relevant if SPAs are enabled.\ +
    \ +
    <9: 0 SPAs\ +
    10-11: 1 SPA\ +
    12+: 2 SPAs +lblAbilityVeteran.text=Veteran \u26A0 +lblAbilityVeteran.tooltip=Modifier to the 2d6 roll used to determine whether a character is created\ + \ already possessing SPAs. Only relevant if SPAs are enabled.\ +
    \ +
    <9: 0 SPAs\ +
    10-11: 1 SPA\ +
    12+: 2 SPAs +lblAbilityElite.text=Elite \u26A0 +lblAbilityElite.tooltip=Modifier to the 2d6 roll used to determine whether a character is created\ + \ already possessing SPAs. Only relevant if SPAs are enabled.\ +
    \ +
    <9: 0 SPAs\ +
    10-11: 1 SPA\ +
    12+: 2 SPAs + +lblTacticsPanel.text=Tactics +lblTacticsGreen.text=Green \u26A0 +lblTacticsGreen.tooltip=Modifier made to the 2d6 roll used to determine whether a character is created\ + \ with points in the Tactics skill.\ +
    \ +
    <2: Ultra-Green Tactics\ +
    2-5: Green Tactics\ +
    6-9: Regular Tactics\ +
    10-11: Veteran Tactics\ +
    12+: Elite Tactics +lblTacticsRegular.text=Regular \u26A0 +lblTacticsRegular.tooltip=Modifier made to the 2d6 roll used to determine whether a character is created\ + \ with points in the Tactics skill.\ +
    \ +
    <2: Ultra-Green Tactics\ +
    2-5: Green Tactics\ +
    6-9: Regular Tactics\ +
    10-11: Veteran Tactics\ +
    12+: Elite Tactics +lblTacticsVeteran.text=Veteran \u26A0 +lblTacticsVeteran.tooltip=Modifier made to the 2d6 roll used to determine whether a character is created\ + \ with points in the Tactics skill.\ +
    \ +
    <2: Ultra-Green Tactics\ +
    2-5: Green Tactics\ +
    6-9: Regular Tactics\ +
    10-11: Veteran Tactics\ +
    12+: Elite Tactics +lblTacticsElite.text=Elite \u26A0 +lblTacticsElite.tooltip=Modifier made to the 2d6 roll used to determine whether a character is created\ + \ with points in the Tactics skill.\ +
    \ +
    <2: Ultra-Green Tactics\ +
    2-5: Green Tactics\ +
    6-9: Regular Tactics\ +
    10-11: Veteran Tactics\ +
    12+: Elite Tactics + +lblSmallArmsPanel.text=Small Arms +lblCombatSmallArms.text=Combatants +lblCombatSmallArms.tooltip=The skill level modifier applied to the Small Arms skill of\ + \ newly created characters with a combat role. Excludes infantry. +lblNonCombatSmallArms.text=Non-Combatants +lblNonCombatSmallArms.tooltip=The skill level modifier applied to the Small Arms skill of\ + \ newly created characters with a non-combat role. + +lblArtilleryPanel.text=Artillery +lblArtilleryChance.text=Percent +lblArtilleryChance.tooltip=The percentage chance a MekWarrior, Vehicle Crew, or Conventional\ + \ Infantry character has of being created with the Artillery skill (if that option is enabled). +lblArtilleryBonus.text=Modifier +lblArtilleryBonus.tooltip=The skill level modifier applied to the skill (if present). + +lblSecondarySkillPanel.text=Secondary Skills +lblAntiMekChance.text=Anti-Mek Percent +lblAntiMekChance.tooltip=The percentage chance a conventional infantry character has of being\ + \ created with the Anti-Mek skill. +lblSecondarySkillChance.text=Secondary Skill Percent +lblSecondarySkillChance.tooltip=The percentage chance a conventional infantry character has of being\ + \ created with a random secondary skill. +lblSecondarySkillBonus.text=Modifier +lblSecondarySkillBonus.tooltip=The skill level modifier applied to the skill (if present). + +## Advancement Tab +skillsContentTabs.title=Skills + +combatSkillsTab.title=Combat Skills +combatSkillsTab.border="Every skill you master is another weapon in your arsenal. Out here, the\ + \ better you fight, the longer you live."\ +
    Sergeant Mick "Crash" Lannister\ +
    The Enforcers
    + +supportSkillsTab.title=Support Skills +supportSkillsTab.border="Support staff are like the coolant system in a 'Mek \u2014 nobody notices them\ + \ until they're gone, and then everyone's screaming."\ +
    Sergeant Leo "Gears" Malone\ +
    The Panther Corsairs
    + +btnToggle.text=Toggle Advanced Options +btnHideAll.text=Hide All Advanced Options +btnDisplayAll.text=Display All Advanced Options +btnCopy.text=Copy +btnPaste.text=Paste + +lblSkillPanelTargetNumber.text=Base Target Number +lblSkillPanelTargetNumber.tooltip=The base target number used by this skill. All target numbers,\ + \ for uses of this skill are derived from this value. + +lblSkillLevel0.text=0 +lblSkillLevel1.text=1 +lblSkillLevel2.text=2 +lblSkillLevel3.text=3 +lblSkillLevel4.text=4 +lblSkillLevel5.text=5 +lblSkillLevel6.text=6 +lblSkillLevel7.text=7 +lblSkillLevel8.text=8 +lblSkillLevel9.text=9 +lblSkillLevel10.text=10 + +# Combat Skills Tab +lblCombatSkillsTab.text=Combat Skill Options + +lblSkillPanelPiloting/Mek.text=Piloting/Mek +lblSkillPanelPiloting/Aerospace.text=Piloting/Aerospace +lblSkillPanelPiloting/Aircraft.text=Piloting/Aircraft +lblSkillPanelPiloting/GroundVehicle.text=Piloting/Ground Vehicle +lblSkillPanelPiloting/VTOL.text=Piloting/VTOL +lblSkillPanelPiloting/Naval.text=Piloting/Naval +lblSkillPanelPiloting/Spacecraft.text=Piloting/Spacecraft +lblSkillPanelGunnery/Mek.text=Gunnery/Mek +lblSkillPanelGunnery/Aerospace.text=Gunnery/Aerospace +lblSkillPanelGunnery/Aircraft.text=Gunnery/Aircraft +lblSkillPanelGunnery/Vehicle.text=Gunnery/Vehicle +lblSkillPanelGunnery/Spacecraft.text=Gunnery/Spacecraft +lblSkillPanelGunnery/BattleArmor.text=Gunnery/BattleArmor +lblSkillPanelGunnery/ProtoMek.text=Gunnery/ProtoMek +lblSkillPanelArtillery.text=Artillery +lblSkillPanelSmallArms.text=Small Arms +lblSkillPanelAnti-Mek.text=Anti-Mek + +lblSupportSkillsTab.text=Support Skill Options + +lblSkillPanelTech/Mek.text=Tech/Mek +lblSkillPanelTech/Mechanic.text=Tech/Mechanic +lblSkillPanelTech/Aero.text=Tech/Aero +lblSkillPanelTech/BattleArmor.text=Tech/BattleArmor +lblSkillPanelTech/Vessel.text=Tech/Vessel +lblSkillPanelAstech.text=Astech +lblSkillPanelDoctor.text=Doctor +lblSkillPanelMedTech.text=MedTech +lblSkillPanelHyperspaceNavigation.text=Hyperspace Navigation +lblSkillPanelAdministration.text=Administration +lblSkillPanelNegotiation.text=Negotiation +lblSkillPanelLeadership.text=Leadership +lblSkillPanelScrounge.text=Scrounge +lblSkillPanelStrategy.text=Strategy +lblSkillPanelTactics.text=Tactics + +## Advancement Tab +abilityContentTabs.title=Abilities + +combatAbilitiesTab.title=Combat Abilities +combatAbilitiesTab.border="Abilities only matter if you know how to use them. A sharp blade is\ + \ useless in the hands of someone who doesn't know how to wield it."\ +
    Captain Elena "Ironshadow" Kane\ +
    Problem, Meet Solution
    + +maneuveringAbilitiesTab.title=Maneuvering Abilities +maneuveringAbilitiesTab.border="Talent without discipline is like a fusion engine without a 'Mek\ + \ \u2014 powerful, but going nowhere."\ +
    Colonel Liana "Shadow" Voss\ +
    Axis Innovations
    + +utilityAbilitiesTab.title=Utility Abilities +utilityAbilitiesTab.border="Unique abilities? Yeah, I've got one: breaking everything I touch."\ +
    Technician Cole "Scrapheap" Drayton\ +
    The Bulwarks
    + +lblCombatAbilitiesTab.text=Combat Ability Options \u2318 + +lblManeuveringAbilitiesTab.text=Maneuvering Ability Options \u2318 + +lblUtilityAbilitiesTab.text=Utility Ability Options \u2318 +lblEdgeCostPanel.text=Edge Cost +lblEdgeCost.text=Cost +lblEdgeCost.tooltip=The cost per rank of Edge. +lblAddAll.text=Enable All +lblAddAll.tooltip=Enable all SPA. +lblRemoveAll.text=Disable All +lblRemoveAll.tooltip=Disable all SPA. +abilityEnable.text=Enable Ability +abilityCost.text=XP Cost: %s +prerequisites.text=Prerequisites
    +incompatible.text=Incompatible
    +removes.text=Removes
    +lblCustomizeAbility.text=Customize Ability +lblCustomizeAbility.tooltip=Launch the 'customize SPA' dialog, allowing you to change cost,\ + \ requirements, and other options. ## StratCon Notice -stratConPromo.title=+++ INCOMING TRANSMISSION +++ -stratConPromo.message=
    Welcome to StratCon, Commander!
    \ -
    This isn't just about firing a few lasers; it's about controlling the entire battlefield. Deploy your units wisely across diverse terrains - icy wastelands, scorching deserts, you name it. Use the hex grid to outmaneuver and out think the enemy.\ +stratConPromo.title=++INCOMING TRANSMISSION++ +stratConPromo.message=
    \ + Welcome to StratCon, Commander!
    \ +
    This isn't just about firing a few lasers; it's about controlling the entire battlefield.\ + \ Deploy your units wisely across diverse terrains \u2014 icy wastelands, scorching deserts, you name\ + \ it. Use the hex grid to outmaneuver and out think the enemy.\
    \
    Key Features:\ -
    Unit Management:
    Keep your lances in top fighting shape. Deploy smartly, handle repairs, and ensure your warriors are ready for the next engagement. A well-prepared lance is a winning lance.\ +
    Unit Management:
    Keep your lances in top fighting shape. Deploy smartly, handle repairs,\ + \ and ensure your warriors are ready for the next engagement. A well-prepared lance is a winning\ + \ lance.\
    \ -
    Scouting: Send out scouts to reveal enemy positions and trigger scenarios. Sometimes, the best move is to cut your losses and keep those scouts alive for future intel. Trust me, a live scout today means a victory tomorrow.\ +
    Scouting: Send out scouts to reveal enemy positions and trigger scenarios. Sometimes,\ + \ the best move is to cut your losses and keep those scouts alive for future intel. Trust me, a\ + \ live scout today means a victory tomorrow.\
    \ -
    Strategic Command: Deploy your forces across the map to capture key objectives and manage multiple tracks. Keep your personnel and machines combat-ready at all times. Remember, deployed units take time to return-plan your deployments carefully to avoid being caught off-guard.\ +
    Strategic Command: Deploy your forces across the map to capture key objectives and\ + \ manage multiple tracks. Keep your personnel and machines combat-ready at all times. Remember,\ + \ deployed units take time to return-plan your deployments carefully to avoid being caught off-guard.\
    \ -
    Dynamic Scenarios: You'll face everything from recon missions to base defenses and full-blown assaults. Expect to encounter Meks, tanks, DropShips, and even aerospace fighters. Be prepared for anything.\ +
    Dynamic Scenarios: You'll face everything from recon missions to base defenses and\ + \ full-blown assaults. Expect to encounter Meks, tanks, DropShips, and even aerospace fighters.\ + \ Be prepared for anything.\
    \ -
    Managing Victory Points: It's not just about winning battles; it's about winning the war. Focus on your Contract Victory Points (CVP) to keep your contract score high. These determine if you'll succeed on the contract. Scenario Victory Points (SVP) are only for winning individual battles. Keep your CVP high to ensure ultimate victory, while managing SVPs to succeed in the here and now.\ +
    Managing Victory Points: It's not just about winning battles; it's about winning the\ + \ war. Focus on your Contract Victory Points (CVP) to keep your contract score high. These\ + \ determine if you'll succeed on the contract. Scenario Victory Points (SVP) are only for winning\ + \ individual battles. Keep your CVP high to ensure ultimate victory, while managing SVPs to\ + \ succeed in the here and now.\
    \ -
    Remember, Commander, victory isn't just about firepower; it's about strategy, foresight, and making every decision count. +
    Remember, Commander, victory isn't just about firepower; it's about strategy, foresight, and\ + \ making every decision count. stratConPromo.button=Proceed ##end StratCon Notice - -## Option Validation Warnings -CampaignOptionsPane.EmptyCampaignName.text=You cannot have an empty campaign name. -CampaignOptionsPane.NullFactionSelected.text=You cannot have an empty campaign faction. -CampaignOptionsPane.FactionWithoutFactionCodeSelected.text=You cannot select a faction named %s, as it is missing its faction code. diff --git a/MekHQ/resources/mekhq/resources/ContractAutomation.properties b/MekHQ/resources/mekhq/resources/ContractAutomation.properties index e32466dd96c..e78a8ae9449 100644 --- a/MekHQ/resources/mekhq/resources/ContractAutomation.properties +++ b/MekHQ/resources/mekhq/resources/ContractAutomation.properties @@ -1,5 +1,5 @@ # General -generalTitle.text=++ INCOMING TRANSMISSION ++ +incomingTransmission.title=++INCOMING TRANSMISSION++ generalConfirm.text=Accept generalDecline.text=Decline generalNonClan.text=The %s @@ -18,6 +18,8 @@ mothballDescription.text=%s, our employer has offered to assist our personnel in \ will need to be arranged by right-clicking the unit in the Hangar panel of your command console,\ \ and selecting 'mothball.' Our techs will then take care of it as time becomes available. +mothballDescription.addendum=Mothballing your equipment will temporarily remove it from your TO&E. + transitDescription.text=Our target system is %s. %s has already calculated the best route\ \ for us.\
    \ @@ -27,6 +29,8 @@ transitDescription.text=Our target system is %s. %s has already calculate
    If we decline, you will need to manually order route calculation from the Interstellar Map.\ \ This can be done by selecting the target system in your Briefing Room, followed by 'Calculate\ \ Jump Path' and 'Begin Transit.' +transitDescription.supplemental=Our target system is %s. This journey will take us\ + \ approximately %s days. # Reports mothballingFailed.text=%s was not eligible to be automatically mothballed. Units cannot be\ diff --git a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties index d4fa8f98bd1..f616bba5611 100644 --- a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties +++ b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties @@ -27,13 +27,28 @@ lblDistance.text= Days (Jumps) to Location: lblAllyRating.text=Ally Experience & Equipment: lblEnemyRating.text=Enemy Experience & Equipment: lblStartDate.text=Start Date: -lblLength.text=Contract length (months): +lblLength.text=Contract Length (months): lblOverhead.text=Overhead Compensation: +lblOverhead.tooltip=The amount of overhead compensation for the unit paid by the employer. Possible values are None, Half, and Full. lblCommand.text=Command Rights: +# see data scenario modifier XML files for Command Rights details +lblCommand.tooltip=Independent: You are on your own. No allied units will accompany you.\ +

    \ + Liason: A single allied unit will accompany you. If the allied unit gets destroyed, you take a score penalty.\ +

    \ + House: An allied force of 25% of your Battle Value (BV) will accompany you. The more allied units are destroyed, the greater the score penalty.\ +

    \ + Integrated: An allied force of 50% of your Battle Value (BV) will accompany you. The more allied units are destroyed, the greater the score penalty. lblTransport.text=Transport Terms: +lblTransport.tooltip=The percentage of the transport costs the employer pays for you to get to the mission site. lblSalvageRights.text=Salvage Rights: +lblSalvageRights.tooltip=The percentage of the value of the corresponding units you kill that you get to keep. lblStraightSupport.text=Straight Support: +lblStraightSupport.tooltip=The percentage of salaries the employer pays. lblBattleLossComp.text=Battle Loss Compensation: +lblBattleLossComp.tooltip=If you have a unit destroyed during the contract, the employer pays a percentage of \ + that unit's retail value, directly to the Lance Leader who lost the unit. This covers only loss of the unit, not the cost of \ + destroyed or damaged components. lblRequiredLances.text=Required Combat Forces: lblRenegotiate.text=Renegotiate diff --git a/MekHQ/resources/mekhq/resources/ContractViewPanel.properties b/MekHQ/resources/mekhq/resources/ContractViewPanel.properties index fb1cfdff30e..abd4a5f007e 100644 --- a/MekHQ/resources/mekhq/resources/ContractViewPanel.properties +++ b/MekHQ/resources/mekhq/resources/ContractViewPanel.properties @@ -18,8 +18,11 @@ lblAllyRating.text=Ally Experience & Equipment: lblEnemyRating.text=Enemy Experience & Equipment: lblMorale.text=Enemy Morale: lblScore.text=Contract Score: -lblBonusParts.text=Bonus Parts: lblDistance.text=Days (Jumps) to Location: lblOverhead.text=Overhead Compensation: lblSupport.text=StraightSupport: lblSharePct.text=Shares: +lblCargoRequirement.text=Estimated Cargo Requirement: + +txtGarrisonMoraleRouted.text=Peaceful +txtGarrisonMoraleRouted.tooltip=There is no enemy activity in the Area of Operations. diff --git a/MekHQ/resources/mekhq/resources/DateChooser.properties b/MekHQ/resources/mekhq/resources/DateChooser.properties index cf8523700f3..840f8facfe0 100644 --- a/MekHQ/resources/mekhq/resources/DateChooser.properties +++ b/MekHQ/resources/mekhq/resources/DateChooser.properties @@ -30,8 +30,14 @@ friday.text=Fri saturday.text=Sat sunday.text=Sun dayPicker.tooltip=Click on a day to choose it -dateField.text=Click on the date to edit it +dateField.text=
    Campaigns prior to 2470 are not supported.\ +
    \ +
    Click on the date to edit it.
    confirmDate.text=Confirm Date +invalidDate.title=Invalid Date +invalidDate.body=Try: yyyy-MM-dd\ +
    \ +
    Cannot be before %s or after %s. # createEraButtons eraAgeOfWar.text=Age of War
    @@ -193,8 +199,8 @@ ThirdSuccessionWarEnds.tooltip=The Third Succession War ended in 3025 without a \ other states created the Concord of Kapteyn. Around the same time, the Arano Restoration in the\ \ Aurigan Reach highlighted smaller-scale conflicts, with Kamea Arano reclaiming her throne. These\ \ events paved the way for the Fourth Succession War. -ForthSuccessionWar.text=Forth Succession War -ForthSuccessionWar.tooltip=The Fourth Succession War was a dramatic conflict driven by Hanse Davion's\ +FourthSuccessionWar.text=Fourth Succession War +FourthSuccessionWar.tooltip=The Fourth Succession War was a dramatic conflict driven by Hanse Davion's\ \ surprise alliance with the Lyran Commonwealth against the Capellan Confederation and their allies.\ \ With cunning strategies and bold offensives, Davion forces gained significant territory, especially\ \ from the Capellans. The war reshaped the political landscape of the Inner Sphere, as powerful nations\ diff --git a/MekHQ/resources/mekhq/resources/Education.properties b/MekHQ/resources/mekhq/resources/Education.properties index 98e65006c6e..7ab3c705f1d 100644 --- a/MekHQ/resources/mekhq/resources/Education.properties +++ b/MekHQ/resources/mekhq/resources/Education.properties @@ -3,6 +3,7 @@ curriculum.text=Curriculum: educationLevel.text=Education Level: nothingToLearn.text=nothing to learn tuition.text=Tuition: +entranceExam.text=Entrance Exam: duration.text=Duration: durationDays.text=days durationAge.text=until %s years old @@ -86,6 +87,11 @@ bonusAdded.text=During their education or training, %s discovered a natural tale bonusXp.text=%s learned something new (+%s XP) graduatedChild.text=%s %sgraduated%s. Tomorrow, they will begin their journey back to the unit. +#### Training Combat Teams +notLearningAnything.text=%s has learned all they can from %s and %sshould be reassigned%s. +learnedNewSkill.text=Thanks to the teaching of %s, %s has %simproved%s their %s to\ + \ %s+. + #### Graduation Events (positive) addressEncouragement.text=during which the commencement address included words of encouragement addressFriendship.text=during which the commencement address included reflections on friendship diff --git a/MekHQ/resources/mekhq/resources/GUI.properties b/MekHQ/resources/mekhq/resources/GUI.properties index 155225002aa..b9400436f62 100644 --- a/MekHQ/resources/mekhq/resources/GUI.properties +++ b/MekHQ/resources/mekhq/resources/GUI.properties @@ -259,6 +259,7 @@ removePregnancy.text=Remove Pregnancy removePregnancies.text=Remove Pregnancies regenerateLoyalty.text=Regenerate Loyalty regeneratePersonality.text=Regenerate Personality +addRandomSPA.text=Add Random SPA addMinimumComplement.text=Add minimum complement addMinimumComplementRandom.text=Random addMinimumComplementElite.text=Elite @@ -312,70 +313,115 @@ UnitIconDialog.title=Select Unit Icon 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 -InsufficientAstechsNagDialog.title=Astech Shortage -InsufficientAstechsNagDialog.text=Your Tech Teams require more Astechs. You need %d more Astech(s). Do you wish to proceed? - -### InsufficientAstechTimeNagDialog Class -InsufficientAstechTimeNagDialog.title=Astech Time Shortage -InsufficientAstechTimeNagDialog.text=You need more astech time for maintenance. You need %d more astech(s). Do you wish to proceed? - -### InsufficientMedicsNagDialog Class -InsufficientMedicsNagDialog.title=Medic Shortage -InsufficientMedicsNagDialog.text=Your Medical Teams require more Astechs. You need %d more medic(s). Do you wish to proceed? - -### OutstandingScenariosNagDialog Class -OutstandingScenariosNagDialog.title=Pending Battle -OutstandingScenariosNagDialog.text=You have a pending battle. Failure to deploy will result in a defeat and a minor contract breach. \nDo you really wish to advance the day? - -### ShortDeploymentNagDialog Class -ShortDeploymentNagDialog.title=Unmet Deployment Requirements -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 -UnmaintainedUnitsNagDialog.text=You have unmaintained units. Do you really wish to advance the day? - -### PregnantCombatantNagDialog Class -PregnantCombatantNagDialog.title=Pregnant Combat Personnel -PregnantCombatantNagDialog.text=You have pregnant personnel assigned to your TOE. Do you really wish to advance the day? - -### PrisonersNagDialog Class -PrisonersNagDialog.title=Prisoners of War -PrisonersNagDialog.text=You still have prisoners of war. Do you really wish to advance the day? - -### UntreatedPersonnelNagDialog Class -UntreatedPersonnelNagDialog.title=Untreated Personnel -UntreatedPersonnelNagDialog.text=You have untreated personnel. Do you really wish to advance the day? - -### NoCommanderNagDialog Class -NoCommanderNagDialog.title=No Commander -NoCommanderNagDialog.text=The campaign is missing an assigned commander. Do you really wish to advance the day? - -### EndContractNagDialog Class -EndContractNagDialog.title=Contract Ended -EndContractNagDialog.text=A contract has concluded and needs resolution. Do you really wish to advance the day? - -### UnresolvedStratConContactsNagDialog Class -UnresolvedStratConContactsNagDialog.title=Unresolved StratCon Contacts -UnresolvedStratConContactsNagDialog.text=You have unresolved contacts on the StratCon interface:\n%s\nAdvance day anyway? - -### FactionDestroyedNagDialog Class -InvalidFactionNagDialog.title=Invalid Faction -InvalidFactionNagDialog.text=Your campaign faction is invalid. Either it has not yet been created, or it has been destroyed.\n\nPlease pick a new faction in campaign options.\nAdvance day anyway? - -### UnableToAffordExpensesNagDialog Class -UnableToAffordExpensesNagDialog.title=Unable to Afford Expenses -UnableToAffordExpensesNagDialog.text=Payday is due tomorrow, but you cannot afford your monthly expenses of %s.\n\nAdvance day anyway? - -### UnableToAffordLoanPaymentNagDialog Class -UnableToAffordLoanPaymentNagDialog.title=Unable to Afford Loan Payment -UnableToAffordLoanPaymentNagDialog.text=One or more loans are due tomorrow, but you cannot afford to cover the %s payment.\n\nAdvance day anyway? - - -### UnableToAffordJumpNagDialog Class -UnableToAffordJumpNagDialog.title=Unable to Afford the Next Jump -UnableToAffordJumpNagDialog.text=The next jump is %s which you cannot afford.\n\nAdvance day anyway? +incomingTransmission.title=++INCOMING TRANSMISSION++ +ignoreFutureNags.checkbox=Don't show this again +button.advanceDay=Advance Day +button.cancel=Cancel + +### Nag Messages +NoCommanderNagDialog.text=Please be advised that no commanding officer is currently assigned\ + \ to our unit. This could affect operational outcomes and mission planning. Confirm your intent\ + \ to proceed, or assign a commander to maintain strategic efficiency.\ +
    \ +
    You can assign one of your personnel as the campaign commander by right-clicking on that\ + \ character, navigating to Flags and selecting the Commander flag. +EndContractNagDialog.text=%s, a contract has reached its conclusion and requires your attention for\ + \ final resolution. While you may advance the day, I recommend addressing this to avoid delays in\ + \ resource allocation and payment. Confirm your decision to proceed.\ +
    \ +
    You can resolve a contract by selecting the contract in the Briefing Room, followed by\ + \ Complete Mission. +InsufficientAstechsNagDialog.text=%s, a shortage of %d Astech%s is affecting the productivity of\ + \ your tech teams. For optimal performance, consider recruiting additional personnel. Confirm\ + \ your intent to advance the day under these conditions.\ +
    \ +
    You can resolve this issue by selecting 'Marketplace' in the taskbar, then 'Astech Pool,'\ + \ then selecting the option to bring all teams up to full strength. If this does not resolve the\ + \ issue, you have likely spread your techs too thinly by assigning them to too many units.\ + \ Consider hiring more techs. +InsufficientAstechTimeNagDialog.text=%s, current maintenance requires an additional %d Astech%s.\ + \ Proceeding without resolving this will prevent some units from being maintained. Confirm your\ + \ decision to advance the day.\ +
    \ +
    You can resolve this issue by selecting 'Marketplace' in the taskbar, then 'Astech Pool,'\ + \ then selecting the option to bring all teams up to full strength. If this does not resolve the\ + \ issue, you have likely spread your techs too thinly by assigning them to too many units.\ + \ Consider hiring more techs. Alternatively, you may enable overtime, though this has its own\ + \ downsides. +InsufficientMedicsNagDialog.text=%s, our medical teams are short by %d medic%s. This may lead to\ + \ delays in treatment or personnel remaining untreated. Confirm if you wish to advance the day\ + \ regardless.\ +
    \ +
    You can resolve this issue by selecting 'Marketplace' in the taskbar, then 'Medic Pool,'\ + \ then selecting the option to bring all teams up to full strength. +InvalidFactionNagDialog.text=%s, reports indicate our parent faction is invalid. Possibly\ + \ destroyed. Confirm your intent to advance the day under these circumstances.\ +
    \ +
    You will need to select a new faction in the campaign options. Failure to do will result\ + \ in contracts no longer being generated. +OutstandingScenariosNagDialog.text=%s, our forces are positioned for combat but await your commands to\ + \ proceed. Alternatively, you may order a tactical withdrawal by advancing the day. Do you wish\ + \ to proceed without addressing these engagements?\ +
    \ +
    The following scenarios await resolution:%s%s +OutstandingScenariosNagDialog.stratCon=
    \ +
    We can pay 1 CVP to tactically withdraw from any scenario marked as a Turning Point.\ + \ Withdrawal is free for all other scenarios. +PregnantCombatantNagDialog.text=%s, pregnant personnel have been identified in our TO&E, putting\ + \ them at risk of harm. Reassignment is advised. Do you wish to advance the day without\ + \ addressing this concern? +DeploymentShortfallNagDialog.text=%s, deployment levels fall below requirements for one or more active\ + \ contracts. While advancing the day is an option, it may strain relations with our employer. Do\ + \ you wish to continue without addressing this?\ +
    \ +
    You should head to the Briefing Room and address this shortfall. Remember, Combat Teams\ + \ assigned to the Auxiliary or Reserve roles do not count towards deployment levels. Nor do\ + \ Combat Teams assigned to the Training role, unless the contract is Cadre Duty. +PrisonersNagDialog.text=%s, our forces are still holding prisoners of war. It is important to\ + \ consider their status before advancing the day. Do you wish to continue without resolving this?\ +
    \ +
    You should head to the Personnel tab and set the filter to 'Prisoners'. Right-clicking on\ + \ a prisoner will allow you to free, ransom, or execute them. If you are currently off-world, you\ + \ can choose to jettison your prisoners. +UnableToAffordExpensesNagDialog.text=%s, payday is scheduled for tomorrow, but financial reserves\ + \ are insufficient to cover the monthly expenses of %s. Proceeding without resolution may have\ + \ significant operational consequences. Confirm your intent to advance the day.\ +
    \ +
    Consider selling off unwanted units, or parts. If all else fails you can take out a loan\ + \ in the Finances tab./i> +UnableToAffordJumpNagDialog.text=%s, the next jump requires %s, which is beyond our current budget.\ + \ Consider resolving this deficit to maintain progress. Do you wish to advance the day under\ + \ these circumstances?\ +
    \ +
    Consider selling off unwanted units, or parts. If all else fails you can take out a loan\ + \ in the Finances tab./i> +UnableToAffordLoanPaymentNagDialog.text=%s, loans due tomorrow require a payment of %s, which\ + \ exceeds available funds. Confirm your intent to advance the day without addressing this issue.\ +
    \ +
    Consider selling off unwanted units, or parts. If all else fails you might be able to take\ + \ out another loan in the Finances tab./i> +UnresolvedStratConContactsNagDialog.text=%s, reports confirm enemy activity within the Area of\ + \ Operations. Please review the situation carefully and take any necessary action to ensure\ + \ operational success.%s +UnresolvedStratConContactsNagDialog.stratcon=
    \ +
    We can pay 1 CVP to refuse to engage any contact marked as a Turning Point. It is\ + \ free to refuse non-critical contacts. +UntreatedPersonnelNagDialog.text=%s, there are medically untreated personnel under our command.\ + \ Advancing the day without addressing this may result in worsened conditions or operational\ + \ inefficiencies. Confirm if you wish to proceed regardless.\ +
    \ +
    You should go to the Infirmary tab and assign your wounded personnel to Doctors. If you\ + \ have insufficient Doctors, consider reassigning personnel so that the most critically injured\ + \ or essential personnel are treated first. Alternatively, hire more Doctors. +UnmaintainedUnitsNagDialog.text=%s, we have units that have not been assigned a tech. This will\ + \ result in degradation and increased repairs. Confirm if you wish to advance the day without\ + \ addressing this issue.\ +
    \ +
    Head to the Hangar and right-click on each affected unit, choosing to assign a tech as\ + \ required. If you do not have enough techs, it is possible to assign the same tech to multiple \ + units. However, this will reduce the time that tech has available to conduct any necessary\ + \ repairs. Generally, you want at least one tech per unit. Large vessels and conventional\ + \ infantry are self-maintaining. #### Report Dialogs ### CargoReportDialog Class @@ -548,6 +594,12 @@ DataLoadingDialog.OutOfMemoryError.text=MekHQ ran out of memory attempting to lo DataLoadingDialog.ExecutionException.title=Campaign Loading Error DataLoadingDialog.ExecutionException.text=The campaign file couldn't be loaded. \nPlease check the MekHQ.log file for details. +## Unsupported Units +unsupportedUnits.title=Unsupported Units Detected +unsupportedUnits.body=The following units are not supported by MekHQ and have been sold.\ +
    \ +
    All assigned personnel have been unassigned.

    + ### GMToolsDialog Class GMToolsDialog.title=GM Tools ## General Tab @@ -1322,10 +1374,10 @@ chkExtraRandomOrigin.toolTipText=Random origin is randomized to the planet RandomOriginOptionsPanel.InvalidSpecifiedPlanet.text=You must select a valid specified planet. #### StartupScreenPanel Class -btnNewCampaign.text=Start a New Campaign -btnLoadCampaign.text=Load a Campaign +btnNewCampaign.text=New Campaign +btnLoadCampaign.text=Load Campaign btnLoadLastCampaign.text=Load Last Save -btnLoadStoryArc.text=Load a Story Arc +btnLoadStoryArc.text=Story Arcs ##### Panes @@ -1381,3 +1433,128 @@ UnitMarketPane.UnitDeliveryLength.report=Unit will be delivered in %s days. ### Financial Statements UnitMarketPane.PurchasedUnit.finances=Purchased %s UnitMarketPane.PurchasedUnitBlackMarketSwindled.finances=Purchased %s (lost on black market) + +### AutoResolveDialog +AutoResolveMethod.dialog.name=Auto Resolve Chance Dialog +AutoResolveMethod.dialog.title=Calculating scenario outcome +AutoResolveMethod.text=Simulating combat... + +# Progress gag text +AutoResolveMethod.progress.0=Loading scenario... +AutoResolveMethod.progress.1=Checking mek alignment protocols... +AutoResolveMethod.progress.2=Calculating ballistic trajectories... +AutoResolveMethod.progress.3=Rolling dice for pilot survival chances... +AutoResolveMethod.progress.4=Consulting Solaris VII odds makers... +AutoResolveMethod.progress.5=Loading ammo into the simulation (don't jettison!)... +AutoResolveMethod.progress.6=Determining optimal mek trash-talk... +AutoResolveMethod.progress.7=Setting turrets to 'mildly menacing'... +AutoResolveMethod.progress.8=Calculating heat dissipation coefficients... +AutoResolveMethod.progress.9=Estimating repair bay bills... +AutoResolveMethod.progress.10=Compiling mekwarrior ego damage projections... +AutoResolveMethod.progress.11=Balancing AI's tactical hubris... +AutoResolveMethod.progress.12=Crunching numbers on the 'perfect headshot'... +AutoResolveMethod.progress.13=Establishing drop location drama... +AutoResolveMethod.progress.14=Simulating ammo explosions (wear safety glasses)... +AutoResolveMethod.progress.15=Scouting nearest salvage crews... +AutoResolveMethod.progress.16=Recalculating jump jet failures... +AutoResolveMethod.progress.17=Breaking ties with a coin toss... +AutoResolveMethod.progress.18=Deploying fresh cappuccino for the Command Center... +AutoResolveMethod.progress.19=Stackpoling probability matrix generation... +AutoResolveMethod.progress.20=Mocking clanners in idle animation... +AutoResolveMethod.progress.21=Calculating the odds of friendly fire "accidents"... +AutoResolveMethod.progress.22=Measuring mekwarrior stress levels (hint: high)... +AutoResolveMethod.progress.23=Resolving 'how did that hit the ammo bin?' queries... +AutoResolveMethod.progress.24=Adjusting enemy AI difficulty: TOO HARD vs. UNFAIR... +AutoResolveMethod.progress.25=Consulting Star League-era technical manuals... +AutoResolveMethod.progress.26=Running the "AC/20 hit or miss?" lottery... +AutoResolveMethod.progress.27=We trust Comstar... +AutoResolveMethod.progress.28=Debating the moral implications of using flamers... +AutoResolveMethod.progress.29=Double-checking mekwarrior callsigns for profanity... +AutoResolveMethod.progress.30=Preparing excuses for unlucky dice rolls... +AutoResolveMethod.progress.31=Processing missile swarm hit ratios... +AutoResolveMethod.progress.32=Simulating headshot scenarios... +AutoResolveMethod.progress.33=Determining flamer effectiveness... +AutoResolveMethod.progress.34=Adjusting weapon jam probabilities... +AutoResolveMethod.progress.35=Loading Lance formation data... +AutoResolveMethod.progress.36=Identifying terrain bottlenecks... +AutoResolveMethod.progress.37=Evaluating reinforcement deployment options... +AutoResolveMethod.progress.38=Simulating overheating consequences... +AutoResolveMethod.progress.39=Calibrating long-range sensor arrays... +AutoResolveMethod.progress.40=Calculating probability of friendly fire... +AutoResolveMethod.progress.41=Verifying enemy morale thresholds... +AutoResolveMethod.progress.42=Analyzing debris collision chances... +AutoResolveMethod.progress.43=Running thermal vision overlays... +AutoResolveMethod.progress.44=Analyzing enemy retreat vectors... +AutoResolveMethod.progress.45=Preparing enemy salvage reports... +AutoResolveMethod.progress.46=Compiling pilot performance stats... +AutoResolveMethod.progress.47=Checking mek stability in rough terrain... +AutoResolveMethod.progress.48=Resolving targeting discrepancies... +AutoResolveMethod.progress.49=Applying Star League-era targeting algorithms... +AutoResolveMethod.progress.50=Simulating dust cloud visibility effects... +AutoResolveMethod.progress.51=Evaluating melee weapon readiness... +AutoResolveMethod.progress.52=Scouting salvageable terrain features... +AutoResolveMethod.progress.53=Determining Lance composition effectiveness... +AutoResolveMethod.progress.54=Resolving LRM indirect fire paths... +AutoResolveMethod.progress.55=Adjusting pilot fatigue levels... +AutoResolveMethod.progress.56=Simulating cockpit ejection scenarios... +AutoResolveMethod.progress.57=Compiling mekwarrior callsign database... +AutoResolveMethod.progress.58=Identifying Atlas encounter probabilities... +AutoResolveMethod.progress.59=Modeling heat sink efficiency variance... +AutoResolveMethod.progress.60=Resolving AI tactical inconsistencies... +AutoResolveMethod.progress.61=Evaluating urban combat performance... +AutoResolveMethod.progress.62=Cross-referencing weapon cooldown times... +AutoResolveMethod.progress.63=Crunching missile scatter algorithms... +AutoResolveMethod.progress.64=Resolving LOS obstructions... +AutoResolveMethod.progress.65=Simulating comm interference scenarios... +AutoResolveMethod.progress.66=Compiling engagement range profiles... +AutoResolveMethod.progress.67=Reviewing pilot injury records... +AutoResolveMethod.progress.68=Determining ammo depletion effects... +AutoResolveMethod.progress.69=Configuring Lance AI personalities... +AutoResolveMethod.progress.70=Re-evaluating jump jet burn duration... +AutoResolveMethod.progress.71=Resolving hill advantage simulations... +AutoResolveMethod.progress.72=Simulating stray projectile outcomes... +AutoResolveMethod.progress.73=Preparing pilot bravery stats... +AutoResolveMethod.progress.74=Adjusting simulated time dilation rates... +AutoResolveMethod.progress.75=Modeling cockpit feedback delays... +AutoResolveMethod.progress.76=Resolving urban debris interactions... +AutoResolveMethod.progress.77=Simulating mek power fluctuations... +AutoResolveMethod.progress.78=Reviewing mekwarrior injury survival rates... +AutoResolveMethod.progress.79=Running lance morale simulations... +AutoResolveMethod.progress.80=Performing post-battle repair estimates... +AutoResolveMethod.progress.81=Resolving turret targeting sequences... +AutoResolveMethod.progress.82=Analyzing AC/20 hit probabilities... +AutoResolveMethod.progress.83=Recalibrating PPC hit radii... +AutoResolveMethod.progress.84=Compiling headshot excuses for unlucky pilots... +AutoResolveMethod.progress.85=Mocking Clanner tactics... +AutoResolveMethod.progress.86=Resolving critical hit misfire scenarios... +AutoResolveMethod.progress.87=Reviewing pilot trash-talk logs... +AutoResolveMethod.progress.88=Adjusting for night vision conditions... +AutoResolveMethod.progress.89=Analyzing mek repair queue efficiency... +AutoResolveMethod.progress.90=Calculating salvage crew response times... +AutoResolveMethod.progress.91=Resolving simulated dropship landings... +AutoResolveMethod.progress.92=Processing planetary weather data... +AutoResolveMethod.progress.93=Reviewing combat simulation logs... +AutoResolveMethod.progress.94=Determining ammo dump safety radius... +AutoResolveMethod.progress.95=Simulating AI mutiny risk... +AutoResolveMethod.progress.96=Cross-referencing mek chassis vulnerabilities... +AutoResolveMethod.progress.97=Balancing Lance formations for fairness... +AutoResolveMethod.progress.98=Evaluating retreat probabilities... +AutoResolveMethod.progress.99=Finalizing combat scenario results... + +AutoResolveDialog.title=Auto Resolve Battle +AutoResolveDialog.messageFailedCalc=Commander, we were unable to simulate any combat scenarios. Do you want to proceed? +AutoResolveDialog.messageSimulated=Commander, we ran {0} simulated combat scenarios and our forces came out victorious in {1}, lost {2}, and drew {3} times. This gives us a {4}% chance of victory. Do you want to proceed? +AutoResolveDialog.message.victory=Your forces won the scenario. Did your side control the battlefield at the end of the scenario? +AutoResolveDialog.message.defeat=Your forces lost the scenario. Do you want to declare your side as controlling the battlefield at the end of the scenario? +AutoResolveDialog.victory=Victory! +AutoResolveDialog.defeat=Defeat! +ResolveDialog.control.title=Control of Battlefield? +ResolveDialog.control.message=Did your side control the battlefield at the end of the scenario? + + +AutoResolveMethod.PRINCESS.text=Princess (PACAR) +AutoResolveMethod.PRINCESS.toolTipText=Resolve combats with Princess taking control over your units. +AutoResolveMethod.ABSTRACT_COMBAT.text=Abstract Combat Auto Resolution (ACAR) +AutoResolveMethod.ABSTRACT_COMBAT.toolTipText=ACAR, a fast simulation using a subset of the abstract combat system rules +AutoResolveMethod.promptForAutoResolveMethod.text=Select the method to use for auto resolving the scenario. +AutoResolveMethod.promptForAutoResolveMethod.title=Auto Resolve Method \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/Loot.properties b/MekHQ/resources/mekhq/resources/Loot.properties new file mode 100644 index 00000000000..a9faed75182 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/Loot.properties @@ -0,0 +1,3 @@ +looted.cash=%s was %sLooted%s during the skirmish. +looted.successful.parts=The following items were %sSecured%s following the skirmish: %s +looted.failed.parts=The following items were %sLost%s during the skirmish: %s \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/MekLabTab.properties b/MekHQ/resources/mekhq/resources/MekLabTab.properties new file mode 100644 index 00000000000..8cd6f8eddab --- /dev/null +++ b/MekHQ/resources/mekhq/resources/MekLabTab.properties @@ -0,0 +1,3 @@ +dialog.saveAs.title=Save As +dialog.saveAs.message.format=%s %s saved to %s +dialog.filter.unitFiles=Unit Files (*.mtf; *.blk; *.hmp) \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/Mission.properties b/MekHQ/resources/mekhq/resources/Mission.properties index b1572c3fc6f..a1763aa0b2e 100644 --- a/MekHQ/resources/mekhq/resources/Mission.properties +++ b/MekHQ/resources/mekhq/resources/Mission.properties @@ -29,17 +29,26 @@ AtBContractType.RECON_RAID.toolTipText=The unit is tasked with scouting and gath AtBContractType.EXTRACTION_RAID.text=Extraction Raid AtBContractType.EXTRACTION_RAID.toolTipText=The unit is tasked with raiding an enemy planet to capture a target and return the target to their employer. -# AtBLanceRole Enum -AtBLanceRole.FIGHTING.text=Fight -AtBLanceRole.FIGHTING.toolTipText=The lance has been tasked with performing offensive actions against the enemy. -AtBLanceRole.DEFENCE.text=Defend -AtBLanceRole.DEFENCE.toolTipText=The lance has been tasked with performing defensive actions, protecting designated targets and locations from the enemy. -AtBLanceRole.SCOUTING.text=Scout -AtBLanceRole.SCOUTING.toolTipText=The lance has been tasked with performing reconnaissance actions, scouting the enemy and their positions. -AtBLanceRole.TRAINING.text=Training -AtBLanceRole.TRAINING.toolTipText=The lance has been tasked with trained ultra green and green members of the force under the leadership of a veteran or elite officer. -AtBLanceRole.UNASSIGNED.text=Unassigned -AtBLanceRole.UNASSIGNED.toolTipText=The lance is not currently assigned to combat duties. +# CombatRole Enum +CombatRole.MANEUVER.text=Maneuver +CombatRole.MANEUVER.toolTipText=This force rolls twice when reinforcing, using the best roll. +CombatRole.FRONTLINE.text=Frontline +CombatRole.FRONTLINE.toolTipText=This force gains points based on it's commander's Tactics skill.\ + \ These points can be spent to place mines before a scenario, or to support the force with\ + \ infantry, or battle armor from elsewhere in your TO&E. +CombatRole.PATROL.text=Patrol +CombatRole.PATROL.toolTipText=When deploying this force, all adjacent hexes will be revealed, not\ + \ just the hex the force deploys to. +CombatRole.TRAINING.text=Training +CombatRole.TRAINING.toolTipText=The commander of this force is training their subordinates. Please\ + \ see the supporting documentation. +CombatRole.AUXILIARY.text=Auxiliary +CombatRole.AUXILIARY.toolTipText=This force rolls twice when reinforcing, using the best roll. It\ + \ cannot be directly assigned to scenarios, nor will it be automatically assigned to scenarios\ + \ during an Integrated contract. +CombatRole.RESERVE.text=Reserve +CombatRole.RESERVE.toolTipText=This force cannot be directly assigned to scenarios, nor will it be\ + \ automatically assigned to scenarios during an Integrated contract. # AtBMoraleLevel Enum AtBMoraleLevel.ROUTED.text=Routed @@ -64,14 +73,15 @@ ContractCommandRights.INTEGRATED.toolTipText=-
    - No map scouting (StratCon).\
    \
    Integrated command rights, standard for government forces, streamline command and control, particularly in large-scale operations involving multiple forces, ensuring effective collaboration without inter-service rivalry or confusion over command authority. -ContractCommandRights.INTEGRATED.stratConText=The employer will make Lance assignments.
    Complete required scenarios to fulfill contract conditions. +ContractCommandRights.INTEGRATED.stratConText=The employer will make Lance assignments.
    Complete\ + \ Turning Point scenarios to fulfill contract conditions. ContractCommandRights.HOUSE.text=House ContractCommandRights.HOUSE.toolTipText=- Keep your Campaign Victory Points (CVP) positive. Winning a non-initiated scenario: +1 CVP. Losing a non-initiated scenario: -1 CVP.\
    - You can scout the map (StratCon).\
    \
    House command rights empower noble scions and military leaders with autonomy over allied forces, enabling them to strategize and expand their House's power while balancing loyalty to their lineage and House interests. -ContractCommandRights.HOUSE.stratConText=Complete required scenarios to fulfill contract conditions. +ContractCommandRights.HOUSE.stratConText=Complete Turning Point scenarios to fulfill contract conditions. ContractCommandRights.LIAISON.text=Liaison ContractCommandRights.LIAISON.toolTipText=- Maintain positive Campaign Victory Points (CVP).\ @@ -81,7 +91,8 @@ ContractCommandRights.LIAISON.toolTipText=- Ma
    - Terminate the contract early upon completing all objectives, except for Garrison or Cadre contracts.\
    \
    Liaison command rights empower trusted officers to coordinate cooperation between allied factions, streamlining communication and joint operations on the battlefield. These officers serve as crucial links between factions, enhancing unity and effectiveness against shared adversaries. -ContractCommandRights.LIAISON.stratConText=Complete required scenarios and strategic objectives to fulfill contract conditions. +ContractCommandRights.LIAISON.stratConText=Complete Turning Point scenarios and strategic\ + \ objectives to fulfill contract conditions. ContractCommandRights.INDEPENDENT.text=Independent ContractCommandRights.INDEPENDENT.toolTipText=- Complete strategic objectives; disregard Campaign Victory Points.\ @@ -122,3 +133,5 @@ ScenarioStatus.DEFEAT.text=Defeat ScenarioStatus.DEFEAT.toolTipText=The unit was defeated by their opponent(s).
    This is also referred to as a "Substantial Defeat" in the BattleTech rules. ScenarioStatus.DECISIVE_DEFEAT.text=Decisive Defeat ScenarioStatus.DECISIVE_DEFEAT.toolTipText=The unit was decisively defeated by their opponent(s). +ScenarioStatus.REFUSED_ENGAGEMENT.text=Refused Engagement +ScenarioStatus.REFUSED_ENGAGEMENT.toolTipText=The unit did not participate in the battle diff --git a/MekHQ/resources/mekhq/resources/NEWCampaignOptionsDialog.properties b/MekHQ/resources/mekhq/resources/NEWCampaignOptionsDialog.properties deleted file mode 100644 index 837d87a1dbd..00000000000 --- a/MekHQ/resources/mekhq/resources/NEWCampaignOptionsDialog.properties +++ /dev/null @@ -1,11 +0,0 @@ -## SelectPresetDialog Class -# SelectPresetDialog -presetDialog.title=Select a Campaign Preset -presetDialog.description=Choose a preset to tailor your experience. Each option provides a\ - \ well-balanced challenge, suitable for both newcomers and seasoned players. -presetDialogSelect.name=Confirm -presetDialogSelect.tooltip=Confirm the currently selected preset. -presetDialogCustomize.name=Customize -presetDialogCustomize.tooltip=Select a preset, then open the campaign options menu. -presetDialogCancel.name=Cancel -presetDialogCancel.tooltip=Return to the prior screen. \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/NewsDialog.properties b/MekHQ/resources/mekhq/resources/NewsDialog.properties new file mode 100644 index 00000000000..09dba82dea6 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/NewsDialog.properties @@ -0,0 +1,27 @@ +incomingNews.title=++ACCESSING NEWS INTERFACE++ +newsReport.button=Close + +terranNewsNetwork.network=TNN +terranNewsNetwork.name=Terran News Network +terranNewsNetwork.slogan="The Voice of Terra, the Heart of Humanity" + +hegemonyNewsNetwork.network=HNN +hegemonyNewsNetwork.name=Hegemony News Network +hegemonyNewsNetwork.slogan="Uniting Worlds Through Truth and Vision" + +starlightBroadcasting.network=SLB +starlightBroadcasting.name=Starlight Broadcasting +starlightBroadcasting.slogan="Bringing the Stars Together" + +comStarNewsBureau.network=CSNB +comStarNewsBureau.name=ComStar News Bureau +comStarNewsBureau.slogan="Illuminating Truth Across the Stars" + +interstellarNewsNetwork.network=INN +interstellarNewsNetwork.name=Interstellar News Network +interstellarNewsNetwork.slogan="Always First on the Scene." + +chatterweb.name=Chatterweb +chatterweb.slogan="Strength Through Shared Knowledge" + +affiliateNewsNetworks.network=Affiliated News Networks \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/PartsInUseTableModel.properties b/MekHQ/resources/mekhq/resources/PartsInUseTableModel.properties index 685bc3538b9..55e0a9cb3d7 100644 --- a/MekHQ/resources/mekhq/resources/PartsInUseTableModel.properties +++ b/MekHQ/resources/mekhq/resources/PartsInUseTableModel.properties @@ -10,3 +10,4 @@ ordered.heading=Ordered part.heading=Part stored.heading=Stored storedTonnage.heading=Tonnage +requestedStock.heading=Requested Stock Percent \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/PartsReportDialog.properties b/MekHQ/resources/mekhq/resources/PartsReportDialog.properties index be356fa1327..be59f59ce15 100644 --- a/MekHQ/resources/mekhq/resources/PartsReportDialog.properties +++ b/MekHQ/resources/mekhq/resources/PartsReportDialog.properties @@ -1,4 +1,7 @@ Form.title=Parts in Use btnClose.text=Close chkIgnoreMothballed.text=Ignore Parts on Mothballed Units +chkTopUpWeekly.text=Add Part Orders to Fill Requested Stock Levels Weekly +topUpBtn.text=Order Parts to Fill Requested Stock +topUpGMBtn.text=Add Parts to Fill Requested Stock Levels lblIgnoreSparesUnderQuality.text=Ignore Spare Parts Under Quality \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/PartsStoreDialog.properties b/MekHQ/resources/mekhq/resources/PartsStoreDialog.properties index 709f08ae729..1c3efcf0833 100644 --- a/MekHQ/resources/mekhq/resources/PartsStoreDialog.properties +++ b/MekHQ/resources/mekhq/resources/PartsStoreDialog.properties @@ -9,4 +9,3 @@ btnCancel.text=Cancel lblPartsChoice.text=Type: lblFilter.text=Filter: hideImpossible.text=Hide Impossible Target Parts -useBonusPart.text=Use Bonus Part diff --git a/MekHQ/resources/mekhq/resources/Resupply.properties b/MekHQ/resources/mekhq/resources/Resupply.properties new file mode 100644 index 00000000000..02f48636f43 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/Resupply.properties @@ -0,0 +1,3511 @@ +# General +dialog.title=++INCOMING TRANSMISSION++ +incomingTransmission.title=++INCOMING TRANSMISSION++ +confirmReceipt.text=Confirm Itinerary +confirmAccept.text=Accept +confirmRefuse.text=Refuse +convoyConfirm.text=Understood +commander.text=Commander +dialogBorderTitle.text=%s +dialogBorderConvoySpeakerDefault.text=%s Convoy +static.text=[Static] +smugglerFee.text=Local Resupply + +roleplayItems.prompt=Items in italics are roleplay items and are not tracked by MekHQ. +documentation.prompt=Full documentation can be found in 'MekHQ/docs/Stratcon and Against the Bot' + +convoySuccessful.text=A convoy has %sarrived%s, and its supplies have been distributed by\ + \ your logistics personnel. +convoyUnsuccessful.text=Despite their best efforts, your negotiator was %sunsuccessful%s and\ + \ any available supplies were assigned to other forces. +convoySuccessfulSmuggler.text=The smuggler was true to their word, and the supplies have %sarrived%s. +convoyErrorTemplate.text=%sWe encountered an ERROR when trying to fetch template (Address: %s). Please\ + \ report this error.%s +convoyErrorTracks.text=%sWe encountered an ERROR when trying to fetch a random track. Please\ + \ report this error.%s +convoyInterceptedStratCon.text=A convoy was %sintercepted%s, and must be defended or the\ + \ supplies will be lost. +convoyEscaped.text=Miraculously, the convoy was able to %sescape%s. + +convoyDispatched.text=Convoy `%s` has finished loading their cargo and is en route. +convoyInsufficientSize.text=Despite the crews' best efforts, the following items could not be fit in\ + \ the transports and have to be left behind: + +contractStartMessageGeneric.text=%s, for this contract we have the support of local resupply depots.\ + \ However, we can negotiate for larger resupplies if we provide our own personnel and vehicles. If we do this,\ + \ we will need to designate Resupply Convoys in our TO&E.\ +
    \ +
    Be aware that this can be a risky job and if we fail to defend any intercepted convoys all\ + \ units and personnel will be lost.\ +
    \ +
    This contract currently requires an estimated %s tons of cargo space across all\ + \ convoys. We have a total of %s available space across %s convoy%s. Damaged or\ + \ uncrewed vehicles are not considered available. The exact tonnage required is based on our\ + \ current TO&E and may change. We should make sure to have a surplus, if possible, as sometimes\ + \ convoys can run overweight. + +contractStartMessageIndependent.text=%s, for this contract we won't have the support of local\ + \ resupply convoys. If we want to secure monthly resupplies, we will need to designate Resupply\ + \ Convoys in our TO&E.\ +
    \ +
    Be aware that this can be a risky job and if we fail to defend any intercepted convoys all\ + \ units and personnel will be lost.\ +
    \ +
    This contract requires an estimated %s tons of cargo space across all convoys. We\ + \ currently have a total of %s available space across %s convoy%s. Damaged or\ + \ uncrewed vehicles are not considered available. The exact tonnage required is based on our\ + \ current TO&E and may change. We should make sure to have a surplus, if possible, as sometimes\ + \ convoys can run overweight. + +contractStartMessageGuerrilla.text=%s, for this contract it won't be safe to establish normal supply\ + \ networks. Instead, we will be at the mercy of local smugglers. Our employer has provided\ + \ a list of suitable contacts, however there is no guarantee these contacts can be trusted to\ + \ deliver on their promises. Be careful. + +usePlayerConvoyOptional.text=%s, our employer has a delivery ready for us but is offering to expand\ + \ it if we supply our own transports.\ +
    \ +
    This enhanced delivery requires an estimated %s tons of cargo space across all\ + \ convoys. However, as this is an estimate final tonnage may vary. We currently have a total of\ + \ %s available space across %s convoy%s. Damaged or partially crewed vehicles are\ + \ not considered available.\ +
    \ +
    Be aware that this can be a risky job and if we fail to defend any intercepted convoys all\ + \ units and personnel will be lost.\ +
    \ +
    If we refuse to deploy our own transports the delivery is estimated to be just %s tons.\ + \ Should I tell the team%s to saddle up? + +usePlayerConvoyForced.text=%s, our employer has a delivery ready for us, but we will need to supply\ + \ our own convoys.\ +
    \ +
    This delivery requires an estimated %s tons of cargo space across all convoys. However,\ + \ as this is an estimate final tonnage may vary. We currently have a total of %s available\ + \ space across %s convoy%s. Damaged or partially crewed vehicles are not considered available.\ +
    \ +
    Be aware that this can be a risky job and if we fail to defend any intercepted convoys all\ + \ units and personnel will be lost.\ +
    \ +
    Should I tell the team%s to saddle up? + +# Roleplay items +resourcesRations.text=48-Hour Ration Packs +resourcesMedical.text=Medical Supplies +resourcesRoleplay0.text=Hygiene Kits +resourcesRoleplay1.text=Field Manuals +resourcesRoleplay2.text='Morale Boosters' +resourcesRoleplay3.text=Recreational Gear +resourcesRoleplay4.text=Climate Adaptation Clothing +resourcesRoleplay5.text=Portable Lighting +resourcesRoleplay6.text=Communication Kits +resourcesRoleplay7.text=Field Maintenance Kits +resourcesRoleplay8.text=Energy Supplements +resourcesRoleplay9.text=Camouflage Nets +resourcesRoleplay10.text=Water Purification Tablets +resourcesRoleplay11.text=Sleep Aids +resourcesRoleplay12.text=Sanitation Bags +resourcesRoleplay13.text=Insect Repellent and Netting +resourcesRoleplay14.text=Field Shelter Kits +resourcesRoleplay15.text=Solar Chargers +resourcesRoleplay16.text=Duct Tape and Adhesives +resourcesRoleplay17.text=Water Canteens +resourcesRoleplay18.text=Portable Cooking Stoves +resourcesRoleplay19.text=Emergency Beacons +resourcesRoleplay20.text=Nutrient-Rich Dried Fruits and Snacks +resourcesRoleplay21.text=Replacement Cooling Vests +resourcesRoleplay22.text=DataPads +resourcesRoleplay23.text=Neurohelmet Maintenance Kits +resourcesRoleplay24.text=Synthetic Nutrient Gels +resourcesRoleplay25.text=Portable Audio Players +resourcesRoleplay26.text=Coolant Towels +resourcesRoleplay27.text=BattleMek Identification Flash Cards +resourcesRoleplay28.text=Solar-Powered Water Heaters +resourcesRoleplay29.text=Noise-Canceling Earplugs +resourcesRoleplay30.text=Tactical Map Overlays +resourcesRoleplay31.text=Emergency Breathing Filters +resourcesRoleplay32.text=Sonic Distraction Devices +resourcesRoleplay33.text=Battle Fatigue Supplements +resourcesRoleplay34.text=Holographic Tactical Briefing Projectors +resourcesRoleplay35.text=Technical Readouts and Recognition Guides +resourcesRoleplay36.text=Pheromone-Repelling Wipes +resourcesRoleplay37.text=Data Discs +resourcesRoleplay38.text=Interactive Holo-Games +resourcesRoleplay39.text=Audiobooks +resourcesRoleplay40.text=Anti-Distortion Tactical Goggles +resourcesRoleplay41.text=Handheld Signal Jammers +resourcesRoleplay42.text=Replacement 'Mek Operator Gloves +resourcesRoleplay43.text=Battle Anthem Music Chips +resourcesRoleplay44.text=Updated Battle Computer Voice Chips +resourcesRoleplay45.text=Boots +resourcesRoleplay46.text=Personal Power Generators +resourcesRoleplay47.text=Field Foot-Care Kits +resourcesRoleplay48.text=Digital Language Translators +resourcesRoleplay49.text=MRE Flavor Enhancers + +# createResupplyFocusDialog +optionBalanced.text=Balanced +optionBalanced.tooltip=Divide resupply across parts, ammunition, and armor using a ratio of 2:1:1. +optionAmmo.text=Ammunition +optionAmmo.tooltip=Deliver ammunition only. Overall resupply size reduced by 25%. +optionArmor.text=Armor +optionArmor.tooltip=Deliver armor only. Overall resupply size reduced by 25%. +focusDescription.text=%s, we might be able to lean a little on our contact and ask them to focus on\ + \ specific types of supplies. This will be at the cost of overall supply quantity.\ +
    \ +
    Would you like to pick a focus? + +supplyCostFull.text=

    This resupply will cost %s.\ +

    The value of these supplies is %s. +supplyCostAbridged.text=

    The value of these supplies is %s. + +# getDialogReference (salvage) +salvaged0.text=The salvage team just finished their sweep. Some of the cargo is\ + \ practically brand new, with no visible wear or damage - a rare find in these\ + \ conditions. On the other hand, much of it looks like it's been scavenged\ + \ multiple times before. Even the rougher pieces could still serve us well in a\ + \ pinch, provided we use them wisely. %s +salvaged1.text=We pulled a variety of supplies from the last skirmish. Some items are in\ + \ nearly perfect condition, almost as if they were never used. Others appear\ + \ cobbled together, likely just to stay functional. Still, this batch has clear\ + \ tactical potential, especially once we sift through it to separate the gems\ + \ from the junk. %s +salvaged2.text=This latest batch of captured cargo is better than anticipated. A fair portion\ + \ is fully functional, ready for immediate use. However, some items seem to be\ + \ held together by sheer desperation and duct tape. We'll need to sort through\ + \ everything quickly to identify which assets can be put to use right away. %s +salvaged3.text=We've secured a decent amount of usable material from the enemy's hold. There's\ + \ a mix of pristine equipment and some more battered remnants. The newer gear\ + \ could directly enhance our operations, while the rougher pieces might require\ + \ a few quick repairs before they're combat-ready again. %s +salvaged4.text=Our salvage efforts have yielded a diverse collection of materials. Some of\ + \ it is in excellent condition, suggesting it was either new or barely used.\ + \ However, quite a bit looks like it was hastily patched together. We'll need\ + \ to get creative if we want to make the most out of these less pristine items. %s +salvaged5.text=The latest cache of captured supplies ranges from nearly untouched to barely\ + \ usable. The high-quality finds could improve our tactical operations\ + \ immediately. The more damaged pieces will require some work, but with effort,\ + \ they could still hold significant value for our ongoing needs. %s +salvaged6.text=The latest load presents an interesting mix. Some items are untouched,\ + \ seemingly ready for immediate deployment, while others appear to have been\ + \ salvaged several times over. The less pristine pieces will require some quick\ + \ maintenance, but we can still derive tactical utility from this batch. %s +salvaged7.text=This captured cargo exhibits a wide spectrum of quality. A few items are\ + \ in mint condition, as if they were meant for delivery to a fresh unit.\ + \ However, the rest will need more than a bit of maintenance before they can be\ + \ fielded, though they still have potential value. %s +salvaged8.text=The latest haul includes a surprising number of intact materials, but not\ + \ everything is in such good shape. While many items are ready for immediate\ + \ use, others are clearly not worth adding to the attached itinerary due to\ + \ their poor condition. %s +salvaged9.text=We've managed to pull a wide array of resources from the enemy's stores.\ + \ Some of it is still in original packaging, which is rare given the\ + \ circumstances. The rest ranges from passable to dilapidated, but even the\ + \ worst of it could be refurbished with some effort. %s + +looted0.text=With our departure finally approaching, we managed to secure the most\ + \ valuable supplies we could find. Most of it was in decent condition, but\ + \ we were limited to what we could physically carry. The bulkier items were\ + \ left behind, as we simply didn't have the energy or space to take them along\ + \ on this final stretch. %s +looted1.text=We looted what we could, focusing on the highest-value supplies to maximize\ + \ our haul. Most of the items were ready for transport, but we were exhausted\ + \ by then. We opted to leave behind anything that wasn't essential, or that would\ + \ have required significant effort to move. %s +looted2.text=We managed to grab the most essential supplies, but it felt like the last\ + \ leg of a marathon. While some items were in good shape and easy to carry,\ + \ we simply lacked the energy to deal with anything that required extra\ + \ handling or repair work, so we left those behind. %s +looted3.text=We've secured the best supplies we could find, and now we're finally getting\ + \ out of here! Most of the items we took are in good condition, and we only\ + \ carried what was easily transportable. Leaving behind the bulkier stuff was\ + \ a strategic decision, and it feels like a win in this tough situation. %s +looted4.text=Securing the supplies went smoother than expected, and we're now ready to lift\ + \ off! We focused on grabbing the most critical items, leaving the less essential\ + \ ones behind without hesitation. Knowing that we're done here brings a sense\ + \ of relief, and it feels good to be moving forward. %s +looted5.text=We managed to secure the most useful supplies, and it's finally time to say\ + \ goodbye to this place. We took only the most transportable items, leaving behind\ + \ anything too bulky or cumbersome. The team is in high spirits - just glad to be\ + \ getting off this rock and onto the next phase of our journey. %s +looted6.text=We've gathered the necessary supplies as planned. Most of the items were\ + \ in good condition, and we prioritized those that were easily transportable.\ + \ Unfortunately, due to space constraints, we had to leave the bulkier items\ + \ behind, which was expected but still frustrating. %s +looted7.text=We've grabbed what supplies we could manage, prioritizing items in decent\ + \ condition. We had to leave many things behind, focusing only on the essentials.\ + \ Given our limited capacity, we don't have the time or space to carry more,\ + \ so we're making do with what we have. %s +looted8.text=We've packed up the most valuable supplies we could find, but it's a desperate\ + \ load. We had to leave a lot of potentially useful materials behind due to space\ + \ and time constraints. It feels like we're leaving part of our future here,\ + \ but there was simply no other option. %s +looted9.text=We finished securing the supplies, but it doesn't feel like enough. We\ + \ managed to load the basics, but many vital items had to be left behind. We're\ + \ departing with what we could carry, but it feels like we're barely scraping\ + \ by as we move forward. %s + +routedSupplies0.text=We've processed your supply request. The convoy is currently en route.\ + \ Please complete all standard receiving checks upon delivery to ensure accuracy.\ + \ Let us know promptly if there are any urgent needs or issues so we can assist\ + \ as soon as possible. %s +routedSupplies1.text=Your requested supplies have been approved and dispatched. The shipment is\ + \ currently en route to your location. Please confirm receipt upon delivery and\ + \ notify us of any discrepancies or urgent requirements for follow-up. %s +routedSupplies2.text=The supplies you requested have been dispatched and should arrive soon.\ + \ Please ensure appropriate personnel are ready to receive the shipment and\ + \ verify its contents in accordance with standard protocols. %s +routedSupplies3.text=Your recent supply request has been processed, and the shipment is on its way.\ + \ Please follow standard verification procedures upon receipt, and report any\ + \ issues immediately to avoid disruptions in your operations. %s +routedSupplies4.text=The requested supplies have been packed and shipped. Please ensure that personnel\ + \ are prepared for standard inventory checks upon delivery, so we can maintain\ + \ accurate records and address any shortages promptly. %s +routedSupplies5.text=Your requisitioned supplies have been authorized and are now on their way.\ + \ Upon receipt, please ensure that verification protocols are followed to confirm\ + \ the delivery's accuracy and condition. %s +routedSupplies6.text=Supplies requested by your unit have been shipped and are currently in\ + \ transit. Please make sure to follow standard protocols for receiving and\ + \ inventory verification once the shipment arrives. %s +routedSupplies7.text=We are delivering the requested supplies as per your requisition form. Please\ + \ complete standard verification upon receipt to confirm the shipment's contents,\ + \ and let us know if there are any issues that need to be addressed. %s +routedSupplies8.text=Your supply request has been processed, and the shipment is on its way.\ + \ Ensure that logistics teams are ready for the usual unloading and inventory,\ + \ so the process goes smoothly upon arrival. %s +routedSupplies9.text=Your requested supplies are currently en route. Please confirm delivery\ + \ upon arrival and conduct routine inventory checks to ensure all items are\ + \ accounted for and in good condition. %s +routedSupplies10.text=The supplies you requested have been dispatched. Once they arrive, please\ + \ follow standard receipt and verification procedures to confirm accuracy and\ + \ condition of the shipment. %s +routedSupplies11.text=We have processed your recent supply request, and the shipment is now\ + \ en route. Please confirm delivery upon receipt and report any discrepancies\ + \ immediately, so we can address them promptly. %s +routedSupplies12.text=Your requested supplies have been expedited for faster delivery. We recommend\ + \ having personnel prepared for swift unloading and verification to avoid\ + \ potential delays in processing. %s +routedSupplies13.text=The supplies you requisitioned have been approved and dispatched. Ensure\ + \ a thorough inventory check upon delivery, and let us know if there are any\ + \ concerns. We appreciate your timely response. %s +routedSupplies14.text=Your requested supplies are on schedule for delivery. Please ensure your\ + \ team is prepared for standard receiving procedures, and let us know if there\ + \ are any special handling instructions or concerns. %s +routedSupplies15.text=Your requisition has been processed, and the requested supplies are en\ + \ route. Please ensure prompt unloading and confirmation once received to\ + \ maintain operational continuity. %s +routedSupplies16.text=The supplies requested by your unit have been shipped and are in transit.\ + \ Please ensure all standard protocols for receipt and inventory are followed,\ + \ as we aim to support your operations effectively. %s +routedSupplies17.text=The shipment you requested is on its way. Please confirm the quantity and\ + \ condition of the supplies upon arrival, and let us know if any adjustments\ + \ are needed for future shipments. %s +routedSupplies18.text=Your supply request has been fulfilled, and the shipment is currently en\ + \ route. Have your logistics personnel ready for standard intake and verification\ + \ to ensure a smooth receiving process. %s +routedSupplies19.text=The supplies you requested have been dispatched and are on schedule for\ + \ delivery. Ensure your team is prepared for receipt, and confirm delivery as\ + \ per usual protocols to maintain accurate records. %s + + +criticalSupplies0.text=We've processed your supply request, and the convoy is currently en route.\ + \ With the enemy in full retreat, there's little risk of interference, but please\ + \ complete standard receiving checks upon delivery. Let us know promptly if\ + \ there are any urgent needs or issues so we can assist as soon as possible. %s +criticalSupplies1.text=Your requested supplies have been approved and are en route. Given the\ + \ enemy's retreat, we anticipate a smooth delivery. Please confirm receipt upon\ + \ arrival, but rest easy knowing the path is clear. %s +criticalSupplies2.text=The supplies you requested have been dispatched and should arrive soon.\ + \ Please ensure appropriate personnel are ready to receive the shipment and\ + \ verify its contents in accordance with standard protocols. %s +criticalSupplies3.text=Your recent supply request has been processed, and the shipment is on its\ + \ way. The enemy poses no threat now, so standard verification procedures\ + \ should be routine. Take your time, as there's no rush under current conditions. %s +criticalSupplies4.text=The requested supplies have been packed and shipped. The enemy's withdrawal\ + \ ensures a smooth delivery. Ensure personnel are prepared for standard checks,\ + \ though there's no pressure with the situation well in hand. %s +criticalSupplies5.text=Your requisitioned supplies have been authorized and are now on their way.\ + \ Upon receipt, please ensure that verification protocols are followed to confirm\ + \ the delivery's accuracy and condition. %s +criticalSupplies6.text=Supplies requested by your unit have been shipped, and the enemy's\ + \ disarray makes for an easy delivery. Follow standard protocols for receiving\ + \ and inventory verification, but expect a relaxed process. %s +criticalSupplies7.text=We are delivering the requested supplies as per your requisition.\ + \ Given the enemy's retreat, this should be straightforward. Complete standard\ + \ verification upon receipt, but feel free to take a breath - we're in control. %s +criticalSupplies8.text=Your supply request has been processed, and the shipment is on its way.\ + \ With the enemy scattered; logistics teams can prepare for the usual unloading\ + \ without any urgency. %s +criticalSupplies9.text=Your requested supplies are currently en route. With the enemy retreating,\ + \ you can confirm delivery at your own pace and conduct routine checks without\ + \ any expected interference. %s +criticalSupplies10.text=The supplies you requested have been dispatched. The route is secure,\ + \ so follow standard receipt and verification procedures once they arrive,\ + \ though no rush is needed. %s +criticalSupplies11.text=We have processed your recent supply request, and the shipment is now\ + \ en route. With the enemy in disarray, there's no risk of delays. Confirm\ + \ delivery upon receipt, but feel at ease as things have calmed. %s +criticalSupplies12.text=Your requested supplies have been expedited for quicker delivery.\ + \ Given the enemy's retreat, this should be a smooth handover. Have personnel\ + \ ready for swift unloading, but enjoy the lull. %s +criticalSupplies13.text=The supplies you requisitioned have been approved and dispatched.\ + \ The enemy's disorganized state means a clear path ahead, so ensure a clear\ + \ inventory check upon delivery. %s +criticalSupplies14.text=Your requested supplies are on schedule for delivery. The enemy poses\ + \ no significant threat now, so the receiving process should be simple. Let us\ + \ know if any special instructions are needed, but take your time. %s +criticalSupplies15.text=Your requisition has been processed, and the requested supplies are en\ + \ route. With the enemy scattered, ensure prompt unloading, but you can relax\ + \ knowing there's no immediate danger. %s +criticalSupplies16.text=The supplies requested by your unit have been shipped. With the\ + \ enemy's retreat, we expect an easy delivery. Follow all standard protocols,\ + \ but feel assured of a safe process. %s +criticalSupplies17.text=The shipment you requested is on its way. The enemy's condition is\ + \ dire, so expect an uneventful delivery. Confirm the quantity and condition\ + \ upon arrival, but rest assured, all is secure. %s +criticalSupplies18.text=Your supply request has been fulfilled, and the shipment is currently en\ + \ route. With the enemy scattered, have your logistics team ready, but know there's\ + \ no urgency under the circumstances. %s +criticalSupplies19.text=The supplies you requested have been dispatched and are on schedule\ + \ for delivery. The enemy is in full retreat, so the team can expect a smooth\ + \ receipt and confirmation process. %s + + +weakenedSupplies0.text=We've processed your supply request, and the convoy is en route.\ + \ With the enemy's forces nearly destroyed, we anticipate no issues during delivery.\ + \ Complete standard checks upon arrival, but rest assured that any urgent needs\ + \ can be addressed promptly without interference. %s +weakenedSupplies1.text=Your requested supplies have been approved and dispatched. The shipment\ + \ is on its way, with no expected threats given the enemy's weakened state.\ + \ Confirm receipt upon delivery and let us know if any follow-up is needed. %s +weakenedSupplies2.text=The supplies you requested have been dispatched and should arrive soon.\ + \ The enemy's compromised position ensures minimal risks. Have personnel ready\ + \ to receive and verify the shipment following standard procedures. %s +weakenedSupplies3.text=Your recent supply request has been processed, and the shipment is on its\ + \ way. With the enemy barely holding on, there's little to worry about. Standard\ + \ verification upon receipt should be straightforward. %s +weakenedSupplies4.text=The requested supplies have been packed and shipped. The enemy's\ + \ disorganized state guarantees safe passage, so personnel can conduct routine\ + \ inventory checks without urgency. %s +weakenedSupplies5.text=Your requisitioned supplies have been authorized and are now en route.\ + \ Given the enemy's dire condition, delivery should be smooth. Please ensure\ + \ that verification protocols are followed upon receipt. %s +weakenedSupplies6.text=Supplies requested by your unit have been shipped and are in transit.\ + \ With the enemy incapacitated, expect a simple handover. Follow standard\ + \ receiving and verification procedures once the shipment arrives. %s +weakenedSupplies7.text=We are delivering the requested supplies as per your requisition form.\ + \ The enemy's severely compromised state means no anticipated delays. Complete\ + \ standard verification upon receipt. %s +weakenedSupplies8.text=Your supply request has been processed, and the shipment is on its way.\ + \ With enemy forces in disarray; logistics teams can expect routine unloading\ + \ and inventory procedures. %s +weakenedSupplies9.text=Your requested supplies are currently en route. Given the enemy's near\ + \ collapse, there should be no hindrances. Confirm delivery and conduct standard\ + \ checks at your convenience. %s +weakenedSupplies10.text=The supplies you requested have been dispatched. With enemy morale\ + \ breaking, the delivery route is clear. Please follow standard receipt and\ + \ verification procedures upon arrival. %s +weakenedSupplies11.text=We have processed your recent supply request, and the shipment is now en\ + \ route. It should be an easy run for the convoy with the enemy withdrawing. Confirm delivery upon\ + \ receipt and report any discrepancies as needed. %s +weakenedSupplies12.text=Your requested supplies have been expedited for faster delivery. The\ + \ enemy's shattered state ensures a swift and secure handover. Have personnel\ + \ prepared for unloading and verification. %s +weakenedSupplies13.text=The supplies you requisitioned have been approved and dispatched. With\ + \ enemy forces near defeat, delivery should be seamless. Ensure thorough\ + \ inventory checks upon receipt. %s +weakenedSupplies14.text=Your requested supplies are on schedule for delivery. The enemy's\ + \ inability to mount any resistance means no expected delays. Follow standard\ + \ receiving procedures and let us know of any special handling needs. %s +weakenedSupplies15.text=Your requisition has been processed, and the requested supplies are en\ + \ route. Given the enemy's decimated forces, expect a straightforward unloading\ + \ and confirmation process. %s +weakenedSupplies16.text=The supplies requested by your unit have been shipped and are in transit.\ + \ Limited hostiles in your sector means an easy delivery. Follow all standard protocols\ + \ upon receipt. %s +weakenedSupplies17.text=The shipment you requested is on its way. With the enemy's combat\ + \ effectiveness diminished, confirm the quantity and condition of the supplies\ + \ upon arrival without concern. %s +weakenedSupplies18.text=Your supply request has been fulfilled, and the shipment is currently en\ + \ route. The enemy's state means this should be a smooth run, so logistics\ + \ personnel can follow routine intake and verification. %s +weakenedSupplies19.text=The supplies you requested have been dispatched and are on schedule for\ + \ delivery. With the enemy's forces in disarray, ensure your team is prepared\ + \ for receipt and follow standard confirmation protocols. %s + + +stalemateSupplies0.text=We've processed your supply request. The convoy is currently en route,\ + \ but given the ongoing skirmishes, expect potential delays. Please complete\ + \ all standard receiving checks upon delivery, and let us know promptly if\ + \ there are any urgent needs or issues. %s +stalemateSupplies1.text=Your requested supplies have been approved and dispatched. The shipment\ + \ is currently en route, but with the ongoing conflict, please confirm receipt\ + \ upon delivery and notify us of any discrepancies or urgent requirements as\ + \ soon as possible. %s +stalemateSupplies2.text=The supplies you requested have been dispatched and should arrive soon.\ + \ With both sides still clashing, ensure appropriate personnel are ready to\ + \ receive and verify the shipment according to standard protocols. %s +stalemateSupplies3.text=Your recent supply request has been processed, and the shipment is on its\ + \ way. The conflict continues, so please follow standard verification procedures\ + \ upon receipt and report any issues immediately to avoid operational disruptions. %s +stalemateSupplies4.text=The requested supplies have been packed and shipped. The situation\ + \ remains tense, so ensure that personnel are prepared for standard inventory\ + \ checks upon delivery to maintain accurate records and address any shortages. %s +stalemateSupplies5.text=Your requisitioned supplies have been authorized and are now en route.\ + \ Given the ongoing stalemate, please ensure that verification protocols are\ + \ followed upon receipt to confirm accuracy and condition. %s +stalemateSupplies6.text=Supplies requested by your unit have been shipped and are in transit.\ + \ With both sides evenly matched, standard protocols for receiving and inventory\ + \ verification should be followed, though delays are possible. %s +stalemateSupplies7.text=We are delivering the requested supplies as per your requisition form.\ + \ With skirmishes ongoing, please complete standard verification upon receipt\ + \ to confirm the shipment's contents and let us know if there are any issues. %s +stalemateSupplies8.text=Your supply request has been processed, and the shipment is on its way.\ + \ Prepare logistics teams for the usual unloading and inventory, but be aware\ + \ that the situation remains uncertain. %s +stalemateSupplies9.text=Your requested supplies are currently en route. With both sides locked\ + \ in conflict, confirm delivery upon arrival and conduct routine inventory\ + \ checks to ensure all items are accounted for and in good condition. %s +stalemateSupplies10.text=The supplies you requested have been dispatched. Once they arrive,\ + \ follow standard receipt and verification procedures to confirm accuracy,\ + \ as the ongoing conflict may impact delivery times. %s +stalemateSupplies11.text=We have processed your recent supply request, and the shipment is now\ + \ en route. With battles still unfolding, confirm delivery upon receipt and\ + \ report any discrepancies immediately. %s +stalemateSupplies12.text=Your requested supplies have been expedited for faster delivery, given\ + \ the unpredictable situation. We recommend having personnel prepared for swift\ + \ unloading and verification to avoid delays due to the ongoing fighting. %s +stalemateSupplies13.text=The supplies you requisitioned have been approved and dispatched. With\ + \ the enemy active in your sector, ensure thorough inventory checks upon delivery,\ + \ and notify us of any concerns immediately. %s +stalemateSupplies14.text=Your requested supplies are on schedule for delivery. As the situation\ + \ remains unpredictable, please ensure your team is prepared for standard\ + \ receiving procedures and let us know of any special handling needs. %s +stalemateSupplies15.text=Your requisition has been processed, and the requested supplies are en\ + \ route. With skirmishes ongoing, ensure prompt unloading and confirmation once\ + \ received to maintain operational continuity. %s +stalemateSupplies16.text=The supplies requested by your unit have been shipped and are in transit.\ + \ With both sides still evenly matched, follow all standard protocols for\ + \ receipt and inventory to support your operations effectively. %s +stalemateSupplies17.text=The shipment you requested is on its way. Given ongoing enemy action,\ + \ confirm the quantity and condition of the supplies upon arrival and let us\ + \ know if any adjustments are needed for future shipments. %s +stalemateSupplies18.text=Your supply request has been fulfilled, and the shipment is currently en\ + \ route. Have logistics personnel ready for standard intake and verification,\ + \ but be mindful of potential delays given the ongoing skirmishes. %s +stalemateSupplies19.text=The supplies you requested have been dispatched and are on schedule for\ + \ delivery. Given the activity in your area, ensure your team is prepared for\ + \ receipt and confirm delivery as per usual protocols. %s + + +advancingSupplies0.text=We've processed your supply request, and the convoy is currently en route.\ + \ With the enemy gaining momentum, anticipate potential delays. Complete all\ + \ standard receiving checks upon delivery and inform us of any urgent needs or\ + \ issues so we can assist promptly. %s +advancingSupplies1.text=Your requested supplies have been approved and dispatched. The shipment\ + \ is on its way, but with the enemy pushing forward, confirm receipt upon\ + \ delivery and notify us of any urgent requirements or discrepancies immediately. %s +advancingSupplies2.text=The supplies you requested have been dispatched and should arrive soon.\ + \ With the enemy making coordinated strikes in all sectors, ensure personnel are ready to\ + \ receive the shipment and verify its contents promptly, following standard\ + \ protocols. %s +advancingSupplies3.text=Your recent supply request has been processed, and the shipment is en route.\ + \ As the enemy advances, follow standard verification procedures upon receipt,\ + \ and report any issues swiftly to avoid further disruptions. %s +advancingSupplies4.text=The requested supplies have been packed and shipped. Given the enemy's\ + \ increasing control over key areas, ensure that personnel are prepared for\ + \ inventory checks upon delivery to maintain accurate records. %s +advancingSupplies5.text=Your requisitioned supplies have been authorized and are now on their way.\ + \ With the enemy's momentum increasing, please ensure verification protocols\ + \ are followed upon receipt to confirm accuracy and condition of the shipment. %s +advancingSupplies6.text=Supplies requested by your unit have been shipped and are currently in\ + \ transit. Given the enemy's strong advance, follow standard protocols for\ + \ receiving and inventory verification upon arrival, but be ready for potential\ + \ disruptions. %s +advancingSupplies7.text=We are delivering the requested supplies as per your requisition form.\ + \ With the increased enemy activity in the area, complete standard verification upon\ + \ receipt swiftly, and let us know of any issues requiring immediate attention. %s +advancingSupplies8.text=Your supply request has been processed, and the shipment is on its way.\ + \ With the battlefield situation deteriorating, prepare logistics teams for\ + \ rapid unloading and inventory to avoid unnecessary delays. %s +advancingSupplies9.text=Your requested supplies are currently en route. Given the enemy's\ + \ increasing pressure, confirm delivery upon arrival and conduct routine\ + \ inventory checks promptly to ensure all items are accounted for. %s +advancingSupplies10.text=The supplies you requested have been dispatched. With enemy forces\ + \ gaining ground, follow standard receipt and verification procedures as\ + \ quickly as possible to confirm the shipment's accuracy and condition. %s +advancingSupplies11.text=We have processed your recent supply request, and the shipment is now\ + \ en route. As the enemy's position strengthens, confirm delivery upon receipt\ + \ and report any discrepancies immediately to avoid delays. %s +advancingSupplies12.text=Your requested supplies have been expedited for faster delivery.\ + \ Given the enemy's growing dominance, we recommend having personnel prepared\ + \ for swift unloading and verification to maintain operational readiness. %s +advancingSupplies13.text=The supplies you requisitioned have been approved and dispatched. With\ + \ the enemy pressing hard, ensure a thorough inventory check upon delivery,\ + \ and report any concerns urgently. %s +advancingSupplies14.text=Your requested supplies are on schedule for delivery. With the enemy\ + \ making headway, please ensure your team is prepared for quick receiving\ + \ procedures, and notify us of any special handling needs. %s +advancingSupplies15.text=Your requisition has been processed, and the requested supplies are en\ + \ route. Given the enemy's advances, ensure prompt unloading and confirmation\ + \ upon receipt to maintain supply lines. %s +advancingSupplies16.text=The supplies requested by your unit have been shipped and are in transit.\ + \ As the enemy asserts control, follow all standard protocols for receipt and\ + \ inventory quickly to support your operations effectively. %s +advancingSupplies17.text=The shipment you requested is on its way. With the enemy dominating\ + \ key areas, confirm the quantity and condition of the supplies upon arrival,\ + \ and let us know if adjustments are needed for future shipments. %s +advancingSupplies18.text=Your supply request has been fulfilled, and the shipment is currently\ + \ en route. As the enemy gains momentum, have logistics personnel ready for\ + \ rapid intake and verification. %s +advancingSupplies19.text=The supplies you requested have been dispatched and are on schedule for\ + \ delivery. With the enemy's control tightening, ensure your team is prepared\ + \ for receipt, and confirm delivery as per usual protocols. %s + + +dominatingSupplies0.text=We've processed your supply request, and the convoy is currently en route.\ + \ With the enemy controlling key objectives, delivery may be delayed. Complete\ + \ all standard receiving checks promptly upon arrival, and inform us of any\ + \ urgent needs or issues immediately. %s +dominatingSupplies1.text=Your requested supplies have been approved and dispatched. The shipment\ + \ is en route, but given the dire situation, confirm receipt upon delivery and\ + \ report any urgent discrepancies as soon as possible. %s +dominatingSupplies2.text=The supplies you requested have been dispatched and should arrive soon.\ + \ As the enemy threatens our supply routes, ensure personnel are ready to receive the\ + \ shipment and verify its contents swiftly, following standard protocols. %s +dominatingSupplies3.text=Your recent supply request has been processed, and the shipment is on its way.\ + \ With defeat looming, follow verification procedures urgently upon receipt\ + \ and report any issues immediately to avoid further disruptions. %s +dominatingSupplies4.text=The requested supplies have been packed and shipped. With the enemy\ + \ gaining ground, ensure that personnel conduct immediate inventory checks\ + \ upon delivery to maintain supply accuracy and address any shortages urgently. %s +dominatingSupplies5.text=Your requisitioned supplies have been authorized and are now en route.\ + \ As the situation worsens, follow verification protocols promptly upon receipt\ + \ to confirm accuracy and prevent further setbacks. %s +dominatingSupplies6.text=Supplies requested by your unit have been shipped and are currently in\ + \ transit. With heavy casualties reported, adhere to standard protocols for\ + \ receiving and inventory verification as quickly as possible. %s +dominatingSupplies7.text=We are delivering the requested supplies as per your requisition form.\ + \ Given the enemy's upper hand, complete verification upon receipt urgently,\ + \ and report any issues that need immediate attention. %s +dominatingSupplies8.text=Your supply request has been processed, and the shipment is on its way.\ + \ As the enemy controls critical areas, prepare logistics teams for rapid\ + \ unloading and inventory to prevent further losses. %s +dominatingSupplies9.text=Your requested supplies are currently en route. With mounting pressure\ + \ from the enemy, confirm delivery immediately upon arrival and conduct urgent\ + \ inventory checks to ensure all items are accounted for. %s +dominatingSupplies10.text=The supplies you requested have been dispatched. Despite defeat seeming\ + \ imminent, follow receipt and verification procedures swiftly to confirm\ + \ the shipment's accuracy and condition. %s +dominatingSupplies11.text=We have processed your recent supply request, and the shipment is en route.\ + \ With the enemy dominating the sector, confirm delivery urgently upon receipt and report\ + \ discrepancies immediately. %s +dominatingSupplies12.text=Your requested supplies have been expedited for faster delivery. Given\ + \ the worsening situation, have personnel prepared for immediate unloading\ + \ and verification to prevent further delays. %s +dominatingSupplies13.text=The supplies you requisitioned have been approved and dispatched.\ + \ Our forces are struggling in this sector, ensure a thorough inventory check upon delivery\ + \ and report any concerns urgently. %s +dominatingSupplies14.text=Your requested supplies are on schedule for delivery. As the enemy\ + \ is inflicting heavy casualties, ensure your team is prepared for rapid receiving,\ + \ and notify us of any special handling needs immediately. %s +dominatingSupplies15.text=Your requisition has been processed, and the requested supplies are en\ + \ route. Despite loss of the depot approaching, ensure prompt unloading and confirmation to\ + \ maintain any remaining operational continuity. %s +dominatingSupplies16.text=The supplies requested by your unit have been shipped and are in transit.\ + \ With the enemy controlling this sector, follow all protocols for receipt\ + \ and inventory swiftly to support your forces. %s +dominatingSupplies17.text=The shipment you requested is on its way. As our forces are facing heavy\ + \ casualties, confirm the quantity and condition of the supplies upon arrival\ + \ urgently and notify us of any immediate adjustments needed. %s +dominatingSupplies18.text=Your supply request has been fulfilled, and the shipment is en route.\ + \ With the enemy gaining the upper hand, have logistics personnel ready for\ + \ rapid intake and verification to avoid further setbacks. %s +dominatingSupplies19.text=The supplies you requested have been dispatched and are on schedule for\ + \ delivery. Given the enemy's dominance, ensure your team is prepared for urgent\ + \ receipt and confirm delivery without delay. %s + + +overwhelmingSupplies0.text=We've processed your supply request, and the convoy is en route.\ + \ The enemy is executing a decisive push, so there's no time to waste. Complete\ + \ receiving checks as quickly as possible upon arrival. Report urgent needs\ + \ immediately, as we have limited time to act. %s +overwhelmingSupplies1.text=Your requested supplies have been approved and dispatched. Given the\ + \ enemy's overwhelming push, confirm receipt as soon as the shipment arrives,\ + \ and notify us of any urgent discrepancies without delay. %s +overwhelmingSupplies2.text=The supplies you requested have been dispatched and should arrive soon.\ + \ Ensure that personnel are on high alert to receive and verify the shipment,\ + \ as the situation is critical. %s +overwhelmingSupplies3.text=Your recent supply request has been processed, and the shipment is on its\ + \ way. Despite our forces facing collapse, follow verification procedures immediately\ + \ upon receipt and report any issues urgently to prevent further setbacks. %s +overwhelmingSupplies4.text=The requested supplies have been packed and shipped. With the enemy's\ + \ overwhelming advance, personnel must conduct immediate inventory checks upon\ + \ delivery to secure any vital materials before it's too late. %s +overwhelmingSupplies5.text=Your requisitioned supplies have been authorized and are now en route.\ + \ With defeat imminent, verification protocols must be followed swiftly upon\ + \ receipt to confirm accuracy and condition. %s +overwhelmingSupplies6.text=Supplies requested by your unit have been shipped and are currently in\ + \ transit. Given the extreme circumstances, follow standard protocols for\ + \ receiving and verification as quickly as possible upon arrival. %s +overwhelmingSupplies7.text=We are delivering the requested supplies as per your requisition form.\ + \ The enemy is pressing for total victory, so complete verification immediately\ + \ upon receipt and report any critical issues. %s +overwhelmingSupplies8.text=Your supply request has been processed, and the shipment is en route.\ + \ The situation is desperate - ensure logistics teams are ready for rapid unloading\ + \ and inventory upon arrival. %s +overwhelmingSupplies9.text=Your requested supplies are currently en route. With your forces on the\ + \ verge of collapse, confirm delivery urgently upon arrival and conduct immediate\ + \ inventory checks to secure what you can. %s +overwhelmingSupplies10.text=The supplies you requested have been dispatched. Given the dire situation,\ + \ follow receipt and verification procedures immediately to confirm the shipment's\ + \ contents and condition. %s +overwhelmingSupplies11.text=We have processed your recent supply request, and the shipment is now\ + \ en route. As we prepare to withdraw from this sector, confirm delivery upon receipt and report any\ + \ discrepancies urgently. %s +overwhelmingSupplies12.text=Your requested supplies have been expedited for immediate delivery.\ + \ Have personnel prepared for fast unloading and verification to secure the\ + \ supplies amid the enemy's final push. %s +overwhelmingSupplies13.text=The supplies you requisitioned have been approved and dispatched. Ensure\ + \ a rapid inventory check upon delivery, and escalate any critical concerns\ + \ immediately given the deteriorating situation. %s +overwhelmingSupplies14.text=Your requested supplies are on schedule for delivery. With the enemy\ + \ pushing for total victory, ensure your team is prepared for urgent receiving,\ + \ and report any special handling needs immediately. %s +overwhelmingSupplies15.text=Your requisition has been processed, and the supplies are en\ + \ route. Given the enemy's overwhelming advance, ensure immediate unloading\ + \ and confirmation to maintain any semblance of operational continuity. %s +overwhelmingSupplies16.text=The supplies requested by your unit have been shipped and are in transit.\ + \ As collapse appears imminent, follow all standard protocols for receipt and\ + \ inventory urgently. %s +overwhelmingSupplies17.text=The shipment you requested is on its way. Given the overwhelming enemy\ + \ assault, confirm the quantity and condition of the supplies upon arrival\ + \ urgently, and notify us of any necessary adjustments immediately. %s +overwhelmingSupplies18.text=Your supply request has been fulfilled, and the shipment is currently\ + \ en route. With the enemy pressing for total victory, have logistics personnel\ + \ ready for rapid intake and verification. %s +overwhelmingSupplies19.text=The supplies you requested have been dispatched and are on schedule for\ + \ delivery. As our forces face collapse, ensure your team is prepared for urgent\ + \ receipt, and confirm delivery immediately. %s + +guerrillaSpeaker.text=Smuggler Contact + +guerrillaSupplies0.text=%s, word's out that you're tangling with some rough forces from %s.\ + \ I've got some high-end, hard-to-find tech that could make your life a whole\ + \ lot easier. For %s, I'll have it delivered to your doorstep, no questions asked,\ + \ no strings attached. All you gotta do is give me the green light. %s +guerrillaSupplies1.text=Hey %s! I hear you're up against it with %s breathing down\ + \ your neck. Lucky for you, I've got some spare parts and specialty tech that\ + \ might just tip the odds in your favor. We're talking the kind of equipment\ + \ that could keep your machines and your crew one step ahead. %s,\ + \ and it's yours. Just say the word. %s +guerrillaSupplies2.text=%s, word around the streets is that you're in need of\ + \ a little boost. I've got a stash of rare tech that could help turn the tables\ + \ on %s. Nothing too big, but potent enough to make a difference in your\ + \ campaign. It'll run you %s, and trust me, you won't find this stuff on the open market.\ + \ Think it over, but don't take too long. Gear like this disappears fast. %s +guerrillaSupplies3.text=%s, running a guerrilla war without the right equipment?\ + \ That's just a recipe for headaches. Lucky for you, I've secured some\ + \ exclusive parts and upgrades that %s would hate to see in your hands.\ + \ %s, and it's all yours. I'll even throw in a few 'extra'\ + \ connections if you're interested. Just say the word. %s +guerrillaSupplies4.text=%s, I respect the hell outta what you're doing out there\ + \ against %s. But let's be real - you need the right kind of hardware to\ + \ keep up this fight. I've got access to some high-spec tech and specialized\ + \ parts that'll make life a lot easier for your crew. It's yours for %s.\ + \ A bit steep, maybe, but quality like this doesn't come cheap.\ + \ Let me know if you're in. %s +guerrillaSupplies5.text=%s, I get it - you're up against %s without enough gear to go\ + \ around. I've got some select tech that'll give your forces a sharp edge.\ + \ We're talking black-market grade. It'll cost you %s, but\ + \ it'll be worth every one. No need to thank me - just give the money. %s +guerrillaSupplies6.text=Hey %s, %s folks don't fight fair, so why should you?\ + \ I've got some choice tech that could give you the upper hand, no problem.\ + \ I'll have it on your doorstep for %s. Let me know if you're\ + \ interested - gear like this doesn't wait around. %s +guerrillaSupplies7.text=%s, you're holding your own, but if you want to keep up the\ + \ pressure on %s, you're gonna need more than grit. I've got my hands on\ + \ some discreet tech that could make all the difference. %s, and it's yours.\ + \ You know where to find me. %s +guerrillaSupplies8.text=Word is, %s, you're scraping the bottom of the barrel to keep\ + \ your forces operational. I've got a line on some parts and tech that'll\ + \ keep your machines running smooth even in the thick of it with %s.\ + \ %s, no questions asked. Just say the word, and it's yours. %s +guerrillaSupplies9.text=%s, your people need top-tier support if they're gonna stay\ + \ a step ahead of %s. Lucky for you, I've secured some high-grade components\ + \ that'll keep you in the fight a little longer. I'll let it go for %s.\ + \ Quick and quiet delivery - give the nod, and it's done. %s +guerrillaSupplies10.text=%s, it's tough running a fight like this on scraps, isn't it?\ + \ I've got just the kind of rare gear and parts you need to keep %s forces guessing.\ + \ It'll cost you %s. Smooth delivery, no mess. Just say the word, and it's all yours. %s +guerrillaSupplies11.text=Look, %s, I know the struggle of keeping up with %s while\ + \ strapped for resources. But I've got a stash of quality tech that could\ + \ lighten the load for you. %s, and I'll have it shipped to you clean and under the radar.\ + \ A little investment goes a long way. %s +guerrillaSupplies12.text=%s, the odds are against you out there with %s, but you don't\ + \ have to fight fair. I've secured some specialized parts that could give\ + \ you the edge you're looking for. For %s, I'll get it to you fast and quiet. You know the deal. %s +guerrillaSupplies13.text=%s, this campaign won't last long if your tech can't keep up.\ + \ I've got my hands on some rare parts that'd be a dream for your team,\ + \ especially with %s breathing down your neck. %s.\ + \ Smooth and discreet delivery, just say the word. %s +guerrillaSupplies14.text=%s, I know you're looking to stay ahead of %s, and I can help.\ + \ I've got a line on some hard-to-find tech that'll make a real difference out there.\ + \ %s, and I'll get it to you before your luck runs out. Just let me know. %s +guerrillaSupplies15.text=%s, scrounging for parts in a firefight with %s isn't ideal.\ + \ Lucky for you, I've got access to some hard-to-get components that'll\ + \ keep you in the game. For %s, they're yours - no hassle, no trace. Just say the word. %s +guerrillaSupplies16.text=Hey %s, holding the line against %s isn't easy, especially\ + \ without the right tech. I've got something that could turn things in your favor.\ + \ %s gets it delivered straight to you, smooth as silk. Give me the nod, and it's done. %s +guerrillaSupplies17.text=%s, I hear your crew's running thin on quality parts. I've\ + \ got a shipment ready that could be just what you need to keep %s guessing.\ + \ Price is %s, and I guarantee no one will see it coming. Let me know if you're in. %s +guerrillaSupplies18.text=%s, taking on %s without the best gear? Risky. But I've got\ + \ something that'll help you stay one step ahead. It'll run you %s, but trust me,\ + \ it's worth every one. Just give me the go-ahead. %s +guerrillaSupplies19.text=%s, %s forces are tough, but I've got a few tricks up my sleeve.\ + \ Some prime tech ready to ship your way for %s.\ + \ No strings, no trouble - just tell me where to send it. %s +guerrillaSupplies20.text=%s, I know supplies are tight, and %s isn't making things easier.\ + \ I've got some premium parts lined up for you - no questions asked.\ + \ %s and they're yours, shipped right under their noses. Just say the word. %s +guerrillaSupplies21.text=%s, fighting %s without proper resources is a losing game. Lucky\ + \ for you, I've secured some rare parts and tech that'll keep your edge sharp.\ + \ %s and I'll handle the rest. Just let me know. %s +guerrillaSupplies22.text=%s, looks like %s is putting on the pressure. I've got access\ + \ to some quality tech that could make all the difference for your team.\ + \ For %s, I'll get it to you before they even know it's gone.\ + \ Just give me the signal. %s +guerrillaSupplies23.text=%s, it's not easy keeping up with %s in this sector.\ + \ But with a little help, I think you'll manage just fine.\ + \ I've got some choice tech lined up, %s. Quick, clean delivery.\ + \ Let me know if you're interested. %s +guerrillaSupplies24.text=%s, I've got a shipment ready that could put some serious\ + \ power back in your hands against %s. %s, and it's all yours.\ + \ You know I don't offer this to just anyone.\ + \ Give me the word, and we're in business. %s + + +guerrillaSwindled0.text=%s, you really thought I'd come through, huh? It's almost sweet.\ + \ Those C-Bills are gone, and so am I. While you're stuck out there\ + \ against %s, I'll be living easy. Chalk it up to experience, yeah? Maybe\ + \ next time, keep a tighter grip on your wallet. +guerrillaSwindled1.text=Hey %s, got a question for you: was it worth it? Those precious\ + \ C-Bills of yours sure went fast. I'm off to greener pastures, while\ + \ you're stuck knee-deep in trouble with %s. Keep dreaming of payback. I'm already a ghost. +guerrillaSwindled2.text=Ah, %s, it was fun while it lasted.\ + \ But, as they say, 'business is business', and you were just too easy to take for a ride.\ + \ Enjoy your ongoing battle with %s - it's one you can keep fighting without my help.\ + \ By the time you catch on, I'll be just a distant, expensive memory. +guerrillaSwindled3.text=%s, it's been a pleasure doing business...well, for me at least.\ + \ Those C-Bills have already found a safer home, and trust me, it's nowhere\ + \ you'd ever reach. Enjoy the generous donation, and next time? Maybe\ + \ don't trust strangers with a slick offer. +guerrillaSwindled4.text=%s, here's the hard truth: you were played. Those C-Bills\ + \ are fueling my next great escape, far away from your mess with %s.\ + \ Think of it as a lesson learned, if that helps. I'll be thinking of\ + \ you...while I enjoy every last C-Bill. +guerrillaSwindled5.text=%s, you've gotta admit, this was a classic move. You get stuck\ + \ with %s on your tail, and I get a fresh start funded by your generosity.\ + \ No hard feelings, yeah? Business is business, after all. +guerrillaSwindled6.text=%s, I almost feel bad... almost. Those C-Bills of yours will\ + \ be keeping me comfortable while you're stuck out there grinding against %s.\ + \ Consider it a generous donation. I'll be sure to spend it well. +guerrillaSwindled7.text=Hey %s, when you get a quiet moment, you'll have to laugh\ + \ at how smooth that was. Your C-Bills are already halfway across the galaxy,\ + \ and I'm long gone. Hope the sting doesn't last too long, pal. +guerrillaSwindled8.text=%s, you put too much trust in a friendly smile. Those C-Bills\ + \ are mine now, and you're left with nothing but the dust trail I left behind.\ + \ Say hello to %s for me. I'll be on my way to a very comfortable life. +guerrillaSwindled9.text=%s, consider this your wake-up call. You've got %s to worry\ + \ about, and I've got your money. Fair trade, don't you think? I'd say\ + \ it's been a pleasure, but I'd be lying. +guerrillaSwindled10.text=%s, sometimes you gotta respect the art of a good con.\ + \ Your C-Bills are doing wonders for my retirement plans, while you're\ + \ still out there playing soldier against %s. Think of it as my fee for\ + \ giving you a lesson in trust. +guerrillaSwindled11.text=Hey %s, heard there's a fine line between bravery and\ + \ foolishness - guess you found it. I'll be raising a glass to you from\ + \ somewhere warm, funded by your generosity. Keep it up with %s, and\ + \ don't worry about me. +guerrillaSwindled12.text=%s, all those C-Bills for a ghost and a promise. You've\ + \ made it far, but you slipped up this time. I'll be long gone by the\ + \ time you read this, probably sipping a PPC. Thanks for bankrolling the journey. +guerrillaSwindled13.text=%s, when it comes to playing the odds, sometimes you're\ + \ on the wrong side. Consider this my parting gift - your C-Bills are\ + \ mine, and you're on your own against %s. Don't take it too hard.\ + \ It's just business. +guerrillaSwindled14.text=%s, I was almost tempted to come through. But you made\ + \ the deal way too easy, and where's the fun in that? I'll be putting\ + \ those C-Bills to better use than you ever would've. By the time\ + \ you've pieced this together, I'll be jumps away. Good luck out there with %s. +guerrillaSwindled15.text=%s, you really ought to be more careful who you trust.\ + \ Those C-Bills of yours are already well spent, and you're left holding\ + \ the bag. Hope you and %s have fun without me. +guerrillaSwindled16.text=%s, next time, maybe don't believe everything you hear.\ + \ I'm already gone, and your C-Bills are going to fuel a far more\ + \ comfortable life than the one you're fighting for. Thanks for\ + \ the funding! +guerrillaSwindled17.text=%s, business is all about taking opportunities, wouldn't\ + \ you say? You just happened to be my opportunity this time. Your C-Bills\ + \ are history, and so am I. Good luck keeping up with %s without my help. +guerrillaSwindled18.text=%s, it's impressive how easy it was to slip away with\ + \ those C-Bills. Consider it a compliment to my talents and a lesson\ + \ for you. You're on your own against %s now - I'll be sure to think\ + \ of you...occasionally. +guerrillaSwindled19.text=%s, let's be real - this was a one-sided deal from the start.\ + \ I walk away with your C-Bills, and you're left with nothing but regrets.\ + \ Enjoy the fight with %s; I'll enjoy the spoils. +guerrillaSwindled20.text=%s, I'd say I'm sorry, but I'd be lying. Your C-Bills are\ + \ funding my next venture, far from your troubles with %s. Maybe next time\ + \ you'll think twice before trusting a smooth talker. +guerrillaSwindled21.text=%s, you know, there's an art to taking someone for a ride.\ + \ Thanks for the contribution - I'll put it to good use. Meanwhile, %s is\ + \ still breathing down your neck, and I'm already in the wind. Take care! +guerrillaSwindled22.text=%s, by the time you figure out just how bad I played you,\ + \ I'll be just a regret. Those C-Bills are long gone, just like me.\ + \ Good luck with %s - you'll need it. +guerrillaSwindled23.text=%s, looks like the student got schooled. Your C-Bills are\ + \ fueling my next big adventure, and all you've got is a bitter reminder.\ + \ Give %s my regards. I'll be miles away by now. +guerrillaSwindled24.text=%s, call it a life lesson: trust is expensive. While you're\ + \ out there scraping by against %s, I'll be enjoying every C-Bill you sent\ + \ my way. Don't take it too hard - some of us are just born for this. + + +# createLogisticsMessage +logisticsDestroyed.text=Regrettable +logisticsReceived.text=Message Received + +statusUpdate0.text=Joined by an allied logistics convoy for a quick fuel exchange, %s. The process\ + \ was efficient, each team working methodically to ensure no time was lost. The convoy's pace has\ + \ picked up again, with both units maintaining a strong formation. There's a sense of\ + \ camaraderie, even in these brief encounters - each group focused on their respective tasks but\ + \ united by the same overarching mission. +statusUpdate1.text=We're currently passing through burned-out villages, %s. The destruction is\ + \ having an impact on crew morale. However, discipline remains intact, and the team understands\ + \ the importance of maintaining focus on the mission. The convoy's pace is steady, and we are\ + \ proceeding without deviation from the planned route. All units are maintaining formation, with\ + \ heightened vigilance for potential ambushes or hidden threats. No delays expected; we continue\ + \ to operate at optimal speed. +statusUpdate2.text=Evasive maneuvers drained fuel faster than expected, %s. The crew had to dodge\ + \ incoming missile salvos from enemy 'Meks, which spiked our fuel consumption. We've adjusted\ + \ speed and switched to tighter convoy formations to conserve what's left. Spirits remain good,\ + \ with jokes about how it's just another day dodging SRMs and LRMs. Scouts are looking for\ + \ potential fuel caches, possibly left behind by retreating forces. We're also rerouting to a\ + \ safer path, taking advantage of lower inclines to ease fuel demands. Morale is high, and the\ + \ crew's handling it well - typical day on the job. We're confident about reaching the next\ + \ checkpoint without delays. +statusUpdate3.text=Met an allied logistics convoy running low on fuel and rations, %s. The exchange\ + \ was routine, carried out with the efficiency of those used to survival in harsh conditions.\ + \ The strain was evident in their faces. The crew presses forward. ETA to delivery is 17 hours,\ + \ with steady progress. +statusUpdate4.text=Reached a makeshift checkpoint run by starving civilians, %s. They asked for\ + \ food, but we had none to give. Their desperation was palpable, etched into their gaunt faces\ + \ and hollow eyes. We pushed through without incident, but the scene stays with us, another\ + \ reminder of the misery that defines this war. The crew is quiet, morale low - there's no sense\ + \ of victory in pushing forward, only a grim determination to complete the mission. ETA to\ + \ delivery is 17 hours, but the burden of what we cannot change grows heavier. +statusUpdate5.text=Sorry for the blackout, %s, radio antenna was bent by a low-hanging branch. The\ + \ team just finished a quick field repair, restoring full communication capabilities without any\ + \ additional issues. All units are maintaining contact, with regular check-ins confirmed. The\ + \ convoy is proceeding as planned, with no deviation from the established route. No delays are\ + \ expected at this time. +statusUpdate6.text=Detour forced by fallen trees, %s. The arrangement seems too neat to be mere\ + \ chance - perhaps it's nature, or perhaps it's enemy sappers. Crew members swear they've seen\ + \ movement in the treeline, dark shapes that vanish before they can be confirmed. We're advancing\ + \ cautiously, weapons ready, each rustle of leaves amplified by the silence that follows. There's\ + \ a growing sense that we're being funneled somewhere, like prey unaware of the hunter. Spirits\ + \ are uneasy, but discipline holds for now. ETA still stands at 18 hours, but the sense of dread\ + \ is growing. +statusUpdate7.text=Water levels have reached critical, %s. Hydration rations have been adjusted to\ + \ extend remaining supplies. The crew is feeling the strain, but they understand the necessity.\ + \ We're pushing forward, with all personnel adhering to updated water protocols. An adjusted ETA\ + \ of 17 hours has been communicated to all units. No further complications are anticipated. +statusUpdate8.text=%s, we just left the remains of a bombed out village. We saw children huddled\ + \ under makeshift tents, some looked up as we passed, but most didn't bother. The sight was\ + \ haunting, their small figures barely visible against the backdrop of ruin. The convoy pressed\ + \ on, each driver focused on the road, yet the mood feels hollow - like a mechanical march\ + \ through a world that's lost its meaning. There's no room for compassion here, only survival.\ + \ ETA to delivery is 18 hours, but the emptiness remains. +statusUpdate9.text=Arrived at an allied field camp recently hit by enemy 'Meks, %s. The MedTechs\ + \ were busy with fresh casualties, working swiftly in the smoky air filled with urgency. We\ + \ dropped off our supplies, before pushing ahead. The crew is visibly shaken by what they saw,\ + \ the 'Meks were using Infernos. but their focus is unbroken. We press on. ETA to delivery is 17\ + \ hours. +statusUpdate10.text=A small group of refugees tried to approach, %s, but we had to speed up for\ + \ security reasons. We're not running a charity service, after all. The crew's gone quiet again,\ + \ trying to swallow the bitter truth that survival trumps sympathy out here. The guilt is heavy.\ + \ ETA to delivery is still 18 hours, though each one feels longer than the last. +statusUpdate11.text=Stopped briefly to refuel an allied convoy, %s. The drivers looked tired, but\ + \ the handoff was quick and to the point. The convoy is back in motion, keeping pace toward the\ + \ next checkpoint. ETA to delivery remains 17 hours, with no interruptions expected. +statusUpdate12.text=%s, a woman carrying an infant waved desperately as we approached the last\ + \ village. We had nothing to give, so we kept moving. The crew tried to maintain focus, but\ + \ there's a sense of heaviness that comes with repeated exposure to scenes like this. The reality\ + \ of the situation is clear: we're here to deliver military supplies, not to play saviors. The\ + \ convoy maintains its pace, though there's a weight in my heart. ETA to delivery remains 18\ + \ hours. +statusUpdate13.text=Reached an allied checkpoint, %s. The soldiers took the supplies quietly, their\ + \ tired eyes reflecting the same exhaustion we see in each other. It's a scene we've repeated\ + \ countless times - quick exchanges under grim circumstances. We're moving on, but the mood is\ + \ heavy. The crew feels it, a sense of helplessness that lingers long after we leave each\ + \ checkpoint behind. ETA to delivery is 17 hours. +statusUpdate14.text=Recon vehicle experienced a sudden sensor glitch, %s - a brief but total blind\ + \ spot that left us vulnerable. Cause is unknown, and the crew is on edge, suspecting sabotage.\ + \ The momentary blindness felt like more than just a malfunction; it was a gap, an invitation for\ + \ disaster. The techs patched it up, but the fear of another unexpected failure lingers. We're\ + \ moving carefully, with extra scans and sensors active. ETA remains 18 hours, but the atmosphere\ + \ is heavy with paranoia. +statusUpdate15.text=Coolant levels dropped suddenly, %s, forcing an unexpected halt. Diagnostics\ + \ traced the issue to a minor breach, likely caused by a stray AC/2 round from a previous\ + \ skirmish. Techs patched it up efficiently, using reinforced seals to prevent recurrence. Crew\ + \ is alert but remains relaxed - these sorts of issues are routine in active combat zones. Extra\ + \ coolant has been distributed among vehicles, and sensors are calibrated for closer monitoring.\ + \ There's even some banter about past coolant leaks during more intense battles. Morale is\ + \ strong, and we're pushing forward without any major delays. Crew knows this is all part of the\ + \ convoy grind. +statusUpdate16.text=Fuel's dropping rapidly, %s. Rough terrain caught us off-guard, and retreating\ + \ enemy 'Meks left craters along the roads, damaging key routes. We're making adjustments to\ + \ stretch our reserves, but it's critical now. We've implemented lower-speed settings to maximize\ + \ efficiency, but that won't last if more combat breaks out. Crew's maintaining focus, with\ + \ everyone aware of the urgency. There's some chatter about potential fuel resupply options,\ + \ but we're far from any friendly base. We've sent a recon hovercraft ahead to scout for possible\ + \ emergency caches or uncharted fueling points. No delays projected yet, but the situation is\ + \ precarious. Crew's prepared for defensive actions if we encounter hostiles during fuel foraging. +statusUpdate17.text=Civilians waved us down, as we left through the last checkpoint, %s. They were\ + \ begging for medical supplies, but we didn't stop. Their disappointment was clear, but the crew\ + \ remained resolute - our orders leave no room for deviations. Morale is affected, but the\ + \ mission's priority remains unchanged. ETA to delivery is 18 hours, and the convoy maintains\ + \ operational efficiency. +statusUpdate18.text=An allied logistics unit requested spare parts, %s. The convoy maintained its\ + \ course, adhering strictly to the schedule. The crew knows the importance of keeping pace, and\ + \ there's no room for deviations. ETA to delivery is 17 hours, with no expected delays. +statusUpdate19.text=Missing fuel traced to a punctured tank, %s - likely damage from stray laser\ + \ fire during a previous skirmish. We're redistributing remaining fuel reserves across the convoy\ + \ to maintain movement. The situation is tight, but so far, no delays are projected. Crew's\ + \ prepping for possible emergency refueling if needed. +statusUpdate20.text=Located missing crates near an abandoned checkpoint, %s. Evidence suggests a\ + \ previous supply convoy came under heavy fire and had to abandon cargo during their retreat. We\ + \ secured what we could, including ammunition crates and medical supplies. Techs believe some\ + \ crates contain Class-C Coolant, which could come in useful. The team's moving steadily, though\ + \ the discovery has sparked concerns over enemy activity in the area. We've added patrols to\ + \ cover the rear in case of ambush. We'll assess the contents of the salvaged crates at the next\ + \ checkpoint. For now, we're staying cautious. +statusUpdate21.text=Blocked pass ahead, %s - fallen trees and debris, likely remnants of a recent\ + \ skirmish. Clearing it took longer than expected, with scouts maintaining a constant watch for\ + \ potential ambushes. It's a bleak routine - clearing, advancing, expecting the worst, and\ + \ finding nothing but more debris in a war that never seems to change. ETA is 17 hours, but no\ + \ guarantees at this rate. +statusUpdate22.text=Misjudged water reserves, %s, forcing us to ration supplies more tightly.\ + \ Located additional water canisters at an abandoned outpost, likely left behind during a rapid\ + \ retreat. Crew is grumbling over the reduced rations, but discipline holds. No delays are\ + \ expected for now, but the harsh conditions are affecting efficiency. We're actively searching\ + \ for other possible resupply points. +statusUpdate23.text=Came across an allied patrol running low on fuel, %s. The exchange was handled\ + \ efficiently - just a quick transfer of fuel before we both resumed our respective routes. The\ + \ convoy is back in formation, maintaining a steady pace as we push forward. ETA to delivery is\ + \ 17 hours, expecting no further delays. +statusUpdate24.text=An emergency frequency flared up briefly, %s. It sounded like a weak distress\ + \ call, likely from a downed 'MekWarrior or an allied recon unit caught behind lines. Given the\ + \ risks, we didn't break formation to respond. The crew remains calm - it's not the first time\ + \ we've heard distress signals on this route. We're keeping channels open, just in case it's a\ + \ friendly in need. Morale is stable, with some light banter over the comms to maintain spirits.\ + \ No delays expected as we continue. +statusUpdate25.text=A collapsed bridge forced a route change, %s - possibly the result of sabotage.\ + \ An alternate crossing was identified, and the convoy is back on track. The delay was minimal.\ + \ All units are aware of potential risks associated with the detour, including possible ambush\ + \ points. The crew is maintaining focus. No major delays are expected as we proceed. +statusUpdate26.text=Crossed paths with allied scouts, %s. Their vehicles appeared intact, but the\ + \ scouts themselves were tense, and reported possible activity in this sector. ETA to delivery remains 18\ + \ hours, with no delays expected. +statusUpdate27.text=We linked up with an allied logistics convoy that needed basic repairs, %s. We\ + \ handed off some spare parts and took a break to get them moving again. A few of the crew shared\ + \ small items with them - they seemed to be in pretty rough shape. Each stop on this route\ + \ feels pretty tense; everybody seems to feel like we could get jumped out here.\ + \ ETA to delivery is 17 hours. +statusUpdate28.text=Reached an allied outpost that showed clear signs of a recent skirmish, %s.\ + \ Torn sandbags, bullet holes in the walls, and soldiers with dusty uniforms greeted us. Their\ + \ readiness was high despite the signs of battle still fresh around them. The supply handoff was\ + \ fast before we resumed our route. The crew is staying alert, eyes scanning the horizon for\ + \ potential threats. We maintain speed, knowing that vigilance is as important as delivery. ETA\ + \ to delivery remains 18 hours. +statusUpdate29.text=GPS signal cut out near a known ambush site, %s. This could be a result of\ + \ residual jamming from previous encounters. All units are proceeding with heightened caution,\ + \ maintaining full situational awareness. Sensors have been recalibrated to account for potential\ + \ interference, and the crew is prepared for ambush. No delays are expected, but all personnel\ + \ will remain on alert until we clear the area. +statusUpdate30.text=Civilians just crowded the convoy, %s, desperate for water or some such. We had\ + \ to keep moving, because "rationing" doesn't include charity drops. The crew's trying to stay\ + \ focused, but despair has a way of seeping in when you least expect it. At least we're\ + \ efficient, if not empathetic. ETA to delivery holds at 17 hours. +statusUpdate31.text=Met an allied recon team, %s. They were well-prepared, with gear in order and\ + \ weapons ready, but the fatigue was clear in their eyes. It's a burden we all share, each of us\ + \ carrying the same weariness that has become part of the mission. The encounter was brief, but\ + \ the crew feels the shared weight of it all, but the path ahead demands focus. ETA to delivery\ + \ is 17 hours, with no expected delays. +statusUpdate32.text=Saw children scavenging in the ruins of an old market as we passed, %s. It was\ + \ hard to ignore, but the crew tried their best to focus on the mission. After all, emotional\ + \ detachment is the closest thing we've got to armor these days. Spirits are low, but the convoy\ + \ maintains speed. At least the engine hum drowns out the emptiness. ETA to delivery remains 17 hours. +statusUpdate33.text=Landslides have hit the main cargo road, %s, forcing us to reroute to a path\ + \ closer to contested zones. It's a necessary risk, but one that adds to the sense of futility\ + \ - rerouting, retreating, always adapting, yet never truly advancing. It's as if the road itself\ + \ resists our passage. ETA remains at 18 hours, but the sense of inevitable confrontation looms. +statusUpdate34.text=Intercepted a faint distress signal, %s. It's weak, barely a whisper among the\ + \ static. Could be an ally trapped, or it could be a trap set by the enemy. The tone of the\ + \ signal has an eerie, desperate quality, impossible to ignore. We've opened all channels, hoping\ + \ to verify its origin, but no joy. We're proceeding cautiously, weapons primed. No delays\ + \ expected, but the air is heavy with uncertainty. +statusUpdate35.text=Civilians tried to flag us down, %s, hoping for help. We didn't stop, as the\ + \ risk to the convoy was too great. The crew barely reacted, their expressions indifferent.\ + \ Desperate faces lingered in the rear-cam for a moment before disappearing. It's a familiar\ + \ scene - lots of refugees on this route. It's shaken the crews a bit.\ + \ ETA to delivery holds at 17 hours, with no significant delays expected. +statusUpdate36.text=A woman carrying a sick child approached the convoy, hoping for help, %s. We\ + \ had to keep moving, leaving her behind like so many others. It felt like another failure,\ + \ another weight added to the growing burden of this war. The crew fell silent, the reality of\ + \ our choices weighing heavily on everyone. It's a stark reminder of what we've become in the\ + \ name of duty - just another part of the machine, indifferent to the suffering left in its wake.\ + \ ETA to delivery is 17 hours, but the sense of loss remains. +statusUpdate37.text=Reached an allied checkpoint that had just repelled an attack, %s. The soldiers\ + \ were still on high alert, eyes scanning the surroundings for any lingering threats. We were\ + \ passed through the checkpoint quickly. ETA to delivery remains 18 hours, with all systems\ + \ nominal. +statusUpdate38.text=Dense fog has swallowed the convoy, %s, thick as smoke. Visibility is near\ + \ zero. We've seen this kind of cover before - an ideal screen for enemy ambushes. The convoy\ + \ crawls forward, engines hushed, the only sound a distant metallic clanking that seems to\ + \ come from nowhere and everywhere at once. Crew moves slowly, with every nerve on edge. Delays\ + \ are likely. +statusUpdate39.text=Came across civilians searching through debris for food, %s. They were too\ + \ exhausted to react as we passed, eyes hollow and movements slow. The sight was haunting - a\ + \ reminder of what these endless conflicts have reduced people to. The crew remained silent,\ + \ their expressions matching the somber scene outside. There was nothing we could do for them;\ + \ all we could offer was a fleeting glance of sympathy before moving on. The mood in the convoy\ + \ is heavy, and it's clear that the war is weighing on everyone. No delays expected, but the\ + \ emptiness lingers. +statusUpdate40.text=Reached an allied resupply post, %s. The soldiers were organized, moving\ + \ quickly to handle the exchange. The convoy's speed is steady. ETA to delivery is 18 hours, with\ + \ no expected delays. +statusUpdate41.text=Just passed a makeshift graveyard, %s. Civilians were burying the dead, their\ + \ faces etched with a grim acceptance of the war's toll. The convoy kept moving, the sight met\ + \ with silent indifference from the crew. There's no shock, just a dull recognition that this\ + \ conflict leaves no one untouched. ETA to delivery is 18 hours. +statusUpdate42.text=An allied MedTech unit flagged us for supplies, %s. They accepted the crates\ + \ quietly, their expressions drained but determined. It's clear that the toll of this conflict is\ + \ not just physical but mental as well. The convoy continues forward, each driver resolute,\ + \ knowing that our delivery supports those still fighting. ETA to delivery holds at 18 hours. +statusUpdate43.text=Lead vehicle experienced a sudden loss of steering, %s. Emergency repairs were\ + \ conducted swiftly by mechanics, who suspect worn cables as the root cause. The crew is\ + \ understandably cautious. We've resumed movement, maintaining full convoy integrity. Mechanics\ + \ will perform a more thorough inspection at the next scheduled stop. All personnel have been\ + \ briefed on emergency protocols, and no significant delays are anticipated. +statusUpdate44.text=Food rations came up short, %s. Inventory checks show missing supplies - either\ + \ an error or theft. There's a creeping paranoia among the crew, as if someone within our ranks\ + \ is hiding something. We're rationing what's left, but the reduced meals add a gnawing hunger.\ + \ ETA remains 18 hours, assuming no more surprises. +statusUpdate45.text=Discovered a slow fuel leak during inspection, %s. Likely caused by shrapnel\ + \ from our last skirmish with enemy BattleMeks. Techs performed a swift patch, reinforcing weak\ + \ spots. We're monitoring fuel levels more closely to ensure stability. Crew remains alert but\ + \ isn't worried - this kind of repair is par for the course. Spirits are high, with some crew\ + \ sharing stories of repairs under fire. ETA holds at 18 hours, with no expected delays, so long\ + \ as the repairs hold. +statusUpdate46.text=Cooling system failure reported on one of our cargo trucks, %s - strong\ + \ suspicion of sabotage by enemy operatives. This happened during a planned stop, making it\ + \ likely that infiltrators tampered with our systems overnight. Tech crews executed rapid field\ + \ repairs, but the incident has shaken trust among the team. We've doubled the watch and added\ + \ sensor sweeps for sabotage detection. Tension is high, especially with scout reports of\ + \ possible enemy 'Mek sightings nearby. We're continuing our push, aiming to maintain\ + \ pace, but we're ready for an engagement. ETA to the drop-off remains at 18 hours. +statusUpdate47.text=Medical kits are running low, %s, after treating minor injuries during our last\ + \ retreat. Crew is rationing remaining supplies, focusing on essentials. Despite the shortage,\ + \ there's a sense of camaraderie, with jokes about "field fixes" and toughing it out like the old\ + \ days. Resupply will be necessary soon, but for now, spirits are positive. No delays expected,\ + \ and ETA is steady at 18 hours as we press forward. +statusUpdate48.text=Civilians blocked the road, demanding supplies, %s. They left us no choice - we\ + \ had to open fire. The crew executed my orders without hesitation. There's a sense of\ + \ resignation among the team; it's just another reminder of the harsh rules that govern this war.\ + \ Morale isn't high, but no one expected it to be. The convoy continues at full speed. No delays\ + \ are expected. +statusUpdate49.text=Passed civilians getting water from a muddy stream, %s. We maintained convoy\ + \ speed. There was nothing we could offer anyway, not without compromising our own supplies.\ + \ Each decision to move forward without helping weighs heavily, but operational parameters do not\ + \ allow for deviations based on sympathy. ETA to delivery holds at 17 hours, with no anticipated\ + \ delays. +statusUpdate50.text=A young boy tried to flag us down with a piece of uniform, %s. We couldn't slow\ + \ down, so he eventually dropped his arm and watched us pass. It's the kind of scene that stings,\ + \ but we've learned to keep our eyes on the road - easier to justify when you've got orders to\ + \ follow. No delays expected, just a few more dents in whatever's left of our humanity. +statusUpdate51.text=An allied unit needed a quick resupply, %s. We're moving again. ETA to delivery\ + \ is 17 hours. +statusUpdate52.text=Lead vehicle's brakes failed, %s - quick repairs got us moving again, but the\ + \ terrain remains unforgiving. It's not just the land that wears us down; it's the unending\ + \ struggle against obstacles, both mechanical and mental. Every fix feels temporary, like a\ + \ bandage over a wound that never truly heals. Each mile feels like a testament to perseverance,\ + \ yet the point of it all seems lost in the dust behind us. ETA is 18 hours, and the convoy moves\ + \ on, because it must. +statusUpdate53.text=Children ran alongside the convoy, hoping for handouts, %s. We had nothing to\ + \ spare, so they eventually fell back. It's a hard reality, but one that we've accepted as part\ + \ of this mission. The crew remains professional, aware that stopping could jeopardize our\ + \ schedule. Every decision here is based on efficiency, not emotion. Morale may be low, but the\ + \ convoy's pace is steady. No delays expected. +statusUpdate54.text=Stopped by a group of allied scout 'Meks in need of resupply, %s. They looked\ + \ rough, armor melted to slag. The crew remains alert. ETA to delivery is 18 hours. +statusUpdate55.text=Passed a makeshift hospital, %s. Civilians lay on mats, too weak to move. We\ + \ couldn't stop, of course. The crew knows the drill: save the rations for the ones we're\ + \ supposed to help, not the ones already beyond it. There's no room for sentiment in this convoy.\ + \ ETA to delivery remains 18 hours, with spirits as low as usual. +statusUpdate56.text=Near-miss with a mine due to driver fatigue, %s. Quick reactions prevented\ + \ disaster, but it's a reminder of the toll continuous operations take. We're rotating drivers\ + \ more frequently to maintain sharpness. There's some tension, but morale remains solid -\ + \ everyone knows the stakes. Banter continues on the comms, lightening the mood despite the\ + \ scare. We're holding pace, adjusting ETA to 17 hours, and maintaining vigilance as we navigate\ + \ through the minefield. +statusUpdate57.text=Encountered a blocked pass, %s - defensive rock formations likely placed by\ + \ enemy scouts. These barriers were effectively positioned, suggesting recon units were active\ + \ here recently. We managed to clear the path using explosives, but the delay left us exposed.\ + \ Crew remains alert, scanning for potential sniper or light 'Meks lurking nearby. We're moving\ + \ through the pass slowly, given the likelihood of sensor mines or remote explosives. The\ + \ situation is tense, but we've increased sensor sweeps. Prepared to engage if enemy units\ + \ respond to our presence. +statusUpdate58.text=Civilians gathered by the roadside, %s, hoping for handouts. We moved on\ + \ quickly, because stopping for them is about as likely as finding a peaceful solution to this\ + \ whole mess. The crew's not thrilled, but they've learned that helplessness is part of the job\ + \ description. We just keep moving, like clockwork. No delays expected - just another routine day\ + \ in the logistics grind. +statusUpdate59.text=Convoy was halted by an abandoned vehicle barricade, %s. Clearing it took\ + \ longer than expected. The blockade was a remnant of a past battle, with no immediate threats\ + \ detected. Spirits are good, with jokes about finding "souvenirs" among the wreckage. We're\ + \ moving forward, keeping formation intact. ETA remains at 18 hours, with all systems nominal\ + \ and no expected delays. +statusUpdate60.text=Another sudden storm, %s. The barrage of wind can damage the vehicles, so we were\ + \ forced the crew to take cover. Comms remain active, but there's an unspoken tension - a\ + \ familiar sense of vulnerability that only adds to the weariness of war. The storm hammers the\ + \ metal with a sound that's almost mocking, a reminder of how easily even nature can break us\ + \ down. The storm should pass soon, ETA holds at 17 hours. +statusUpdate61.text=A discrepancy in the cargo manifest required a brief halt, %s. The issue has\ + \ been identified and resolved, but it added to the overall tension among the crew. Personnel\ + \ have been reminded of the importance of accuracy in supply management, and that errors can\ + \ compromise mission success. All units remain alert for any additional issues that could arise\ + \ from logistical errors. No further delays are expected. +statusUpdate62.text=Passed through a bombed-out market, %s. A few civilians were scavenging among\ + \ the rubble, their faces gaunt and eyes empty. The air felt suffocating, filled with the stale\ + \ scent of smoke and decay. Spirits are low, and the reality of the situation weighs heavy on\ + \ the convoy. We're moving forward, but it's hard to escape the sense of futility in all this\ + \ destruction. No delays. +statusUpdate63.text=Reached an allied trench that had recently been raided, %s. The soldiers were\ + \ still recovering, moving slowly as if the attack had stripped them of more than just\ + \ resources. We managed a quick resupply. But as we pushed forward, the lingering unease was hard\ + \ to shake - it's the kind of feeling that settles deep, reminding us of what we've all lost. ETA\ + \ to delivery is 18 hours. +statusUpdate64.text=Caught a loose oil hose just in time, %s. The damage suggests shrapnel, but\ + \ it's unclear when it happened. The crew managed a quick patch-up, but there's a lingering sense\ + \ that the convoy's luck is wearing thin. Every repair feels more temporary, as if the machines\ + \ themselves are giving in to age. We're maintaining speed, but no one is certain how long that\ + \ will last. No delays expected, but the sense of foreboding is tangible, like a storm building\ + \ in the distance. +statusUpdate65.text=GPS led us to a dead-end, %s - an old barricade, rusted and overgrown, yet\ + \ still sturdy enough to block our path. It feels intentional, like someone wanted to trap us\ + \ here. The crew is tense, eyes darting toward the dark woods beyond, half-expecting an attack.\ + \ We're rerouting now, trying to find a way around this obstacle, but the delay adds to the\ + \ growing anxiety. ETA is currently 18 hours, but remains uncertain. +statusUpdate66.text=Just passed some elderly civilians, %s. They watched us pass, but there was no\ + \ movement toward us - likely a sign that they've lost any expectation of help. The crew didn't\ + \ react beyond a few brief glances, understanding that this is just another facet of the war.\ + \ It's a familiar sight, one that no longer surprises. No delays expected. +statusUpdate67.text=Signs of early heat exhaustion among drivers, %s. Hydration protocols are in\ + \ effect, but the relentless engine heat is taking its toll. The crew needs a longer rest soon.\ + \ Each moment under this scorching sun feels like another step deeper into a conflict that never\ + \ ends. ETA remains at 17 hours, but the weight of exhaustion is more evident than ever. +statusUpdate68.text=%s, convoy just left a village our forces cleared out last week. Mother and\ + \ child begged for food as we passed. The crew kept moving without hesitation. This kind of scene\ + \ has become routine - an unpleasant reality we've grown accustomed to. Not everyone can be\ + \ saved, and most of us have accepted that fact. Morale's low, but it's not unexpected. There's\ + \ no room for sentiment here, just the necessity of pressing on toward the next checkpoint. No\ + \ delays expected. +statusUpdate69.text=%s, a sudden dust storm has descended on the convoy, cutting visibility to\ + \ almost nothing. The road we've been following is now obscured, swallowed by the swirling grit.\ + \ The convoy presses on. The crew is tense, scanning for signs of an ambush lurking within the\ + \ storm's cover. We're holding pace for now, but the sense of unseen eyes watching is relentless. +statusUpdate70.text=Reached an allied checkpoint, %s. The weariness in the soldiers eyes was hard\ + \ to miss. They moved with purpose, though, despite the fatigue. We're back on the move,\ + \ maintaining speed and focus. ETA to delivery holds at 18 hours. +statusUpdate71.text=Water supplies are running low faster than expected, %s. The engine heat,\ + \ combined with evasive maneuvers, has taken a toll on hydration needs. We've issued tighter\ + \ rationing protocols, but the crew is managing well, joking about old desert campaigns where\ + \ water was even scarcer. Hydration is still enough for now, but reaching the next checkpoint is\ + \ crucial. No delays are expected as we maintain course. +statusUpdate72.text=Engine overheated in Transport-3, %s. Coolant levels are critical, with\ + \ possible shrapnel damage as the cause. Repairs are underway, but the crew is anxious. They know\ + \ that any further complications could leave us stranded in contested territory. We're adjusting\ + \ the ETA to 17 hours, but it's a fragile estimate. +statusUpdate73.text=Fuel reserves are critically low, %s. The terrain has proven far more\ + \ challenging than estimated, with steep inclines and uneven surfaces. We've initiated stringent\ + \ rationing to ensure progress continues without compromising operational integrity. Drivers have\ + \ been instructed to maintain optimal speed to conserve fuel. We're exploring potential emergency\ + \ resupply points along the route, though options appear limited. No delays are expected for now. +statusUpdate74.text=Reached an allied outpost, %s, where fresh sandbags lined the perimeter. The\ + \ soldiers were on edge, their movements sharp and eyes wary, but the resupply was quick and\ + \ efficient. We're moving again, ETA to delivery is 18 hours. +statusUpdate75.text=Stopped to refuel an allied patrol, %s. The soldiers handled the exchange with\ + \ practiced efficiency. There was no room for conversation, and the crew remains focused, but\ + \ it's clear that the weight of this endless routine has become part of us. We accept it, knowing\ + \ that stopping isn't an option. No delays expected, and the convoy maintains its pace. +statusUpdate76.text=Reached an allied outpost running low on rations, %s. The soldiers looked worn\ + \ out. The convoy is moving steadily, adhering to the planned route. ETA to delivery remains 18\ + \ hours. +statusUpdate77.text=We just passed civilians gathering around a makeshift fire, %s. Fatigue marked\ + \ their faces, and their eyes were hollow. The crew remained silent, eyes focused on the road\ + \ ahead, accustomed to these bleak images that now feel like just another part of the landscape.\ + \ It's clear that repetition has worn down any sense of empathy. No delays expected, and the\ + \ convoy maintains its course. +statusUpdate78.text=Several crew members are showing signs of severe fatigue, %s. The endless\ + \ grind of war takes its toll on even the most experienced. Despite the exhaustion, the convoy\ + \ continues its path. ETA stands at 18 hours, but spirits are undeniably low. +statusUpdate79.text=Regrouped with an allied recon unit that needed water and fuel, %s. The\ + \ exchange was fast, but the strain of constant patrols was written all over their faces. As the\ + \ convoy holds its pace, there's a shared silence among the crew, each person lost in their own\ + \ thoughts. ETA to delivery is 18 hours. +statusUpdate80.text=Brakes failed suddenly on a support vehicle, %s - probable sabotage from enemy\ + \ infiltrators. The failure occurred on a steep incline, posing a serious risk. Fortunately,\ + \ quick repairs by the tech team prevented a larger incident. Security measures have been\ + \ intensified, with additional checks on all vehicles. Crew's been briefed on identifying\ + \ potential signs of tampering and maintaining vigilance. Tension is high, given the proximity\ + \ to enemy-held sectors, but no major delays are expected. +statusUpdate81.text=Encountered flooding on the lower roads, %s. We've had to reroute to higher\ + \ ground, less secure but necessary. There's a sense of resignation as we push forward. ETA\ + \ adjusted to 17 hours. +statusUpdate82.text=Sudden static briefly disrupted communications, %s. The cause of the\ + \ interference is unclear, but deliberate jamming cannot be ruled out. Communication channels\ + \ were quickly restored, with all units maintaining vigilance for further disruptions. Crews have\ + \ been briefed on the E-WAR tactics the enemy has used previously and how to counter them. No\ + \ delays expected at this time. +statusUpdate83.text=%s, we just reached an allied camp that had seen recent fighting. Burned-out\ + \ vehicles were still smoking, and the soldiers looked exhausted. The convoy continues at a\ + \ steady pace. ETA to delivery is 18 hours. +statusUpdate84.text=Sudden static cut communications briefly, %s. Could have been deliberate, but\ + \ there's no way to confirm. The crew is monitoring closely, yet there's a sense of futility in\ + \ the effort. Comms are restored, but it feels like another moment where the enemy is both\ + \ everywhere and nowhere. No delays expected. +statusUpdate85.text=An allied recon team signaled for water and rations, %s. They looked exhausted\ + \ but still determined, taking what was needed before nodding their thanks and moving on. We\ + \ maintain our pace, driven by the knowledge that every supply run matters. ETA to delivery is\ + \ 17 hours. +statusUpdate86.text=Fuel pump failure on a support vehicle, %s. Debris was clogging the lines.\ + \ Techs managed a quick fix. Even after repairs, there's a sense that something unseen lingers,\ + \ waiting for another failure. We're moving again, but the pace is slower. ETA 18 hours. +statusUpdate87.text=A sudden hailstorm caused minor armor damage, %s. Crew took cover under a rocky\ + \ overhang while visibility dropped to nearly zero. We maintained radio silence to avoid\ + \ detection, as enemy skirmishers have been using storms for surprise attacks. We're back on\ + \ track, now, moving at a good pace. ETA is still 18 hours, with no major concerns. +statusUpdate88.text=Heavy winds are disrupting convoy alignment, %s. Debris is blown across the\ + \ road. Progress is slower than planned. ETA 16 hours. +statusUpdate89.text=Passed through a camp of displaced civilians, %s. Makeshift tents lined the\ + \ road, filled with hopeless faces that have seen too much of this war. The crew pressed on,\ + \ maintaining speed without a second glance. No delays are expected. ETA to delivery is 18 hours. +statusUpdate90.text=An allied convoy requested urgent resupply, %s. The allied soldiers were\ + \ prepared but wary. They warned us that enemy light 'Meks have been sweeping this sector. We've\ + \ adjusted our route and are now maintaining steady progress toward the next depot. ETA to\ + \ delivery remains 18 hours. +statusUpdate91.text=The road has been turned into mud pits, by recent engagements, %s. We've had to\ + \ reduce speed to avoid getting bogged down. Escorts have been repositioned to offer better\ + \ defensive coverage during the crawl. So far, no significant delays have occurred, but the crew\ + \ is feeling the strain. +statusUpdate92.text=Driver fatigue is becoming clear, %s. We've rotated drivers to maintain\ + \ operational efficiency, but the relentless stress of each run is wearing down the crew. We've\ + \ allowed brief rest periods, but the constant threat of attacks makes even short breaks risky.\ + \ MedTechs are on standby to address exhaustion. Despite the strain, the convoy's progress\ + \ remains steady, but I'm concerned about the long-term impact if we don't get pulled out of\ + \ rotation soon. ETA to drop-off 12 hours. +statusUpdate93.text=Found a slow fuel leak during inspection, %s. Patched up quickly. Fixing\ + \ leaks and moving forward has become second nature, yet each repair feels more futile than the\ + \ last: it's just going to break again. ETA is 18 hours. +statusUpdate94.text=Comms were briefly disrupted, %s. Possible interference from enemy jamming\ + \ fields, but we're not ruling out environmental factors either. The sudden loss of signals\ + \ heightened the tension among the crew, especially given our position along a known raiding\ + \ route. We've restored channels, but the blackout underscored the vulnerability of our convoys.\ + \ We've shifted to a staggered convoy formation to reduce vulnerability to surprise strikes.\ + \ So far, no delays anticipated. +statusUpdate95.text=An allied patrol needed fuel, %s. We've just got back on course. ETA to\ + \ delivery is 17 hours, with no expected delays. +statusUpdate96.text=Interference spiked again, %s - this time significantly stronger. It's unclear\ + \ whether it's residual signals from previous engagements or lingering enemy jamming. Sensors are\ + \ operating at full capacity, with operators running diagnostics to identify potential sources.\ + \ Communication lines have been reinforced, and the convoy remains on course. We're prepared for\ + \ further interruptions, with no delays expected at this point. +statusUpdate97.text=Failed to establish contact with a nearby support unit, %s - only dead air on\ + \ the comms. We're keeping formations tight, weapons primed, and all sensors sweeping for\ + \ movement. Adjustments are being made to improve signal reception, but for now, we're moving\ + \ steadily toward the next NavPoint. No delays expected. +statusUpdate98.text=Saw a line of civilians along the roadside, %s. They waved weakly as we\ + \ approached, but we had nothing to offer. Some personnel averted their eyes, unable to meet the\ + \ gaze of the figures watching us pass. The truth is harsh: stopping could risk the mission, so\ + \ we keep moving. Morale is low, with the weight of helplessness bearing down on us. It's hard to\ + \ ignore the misery that surrounds us, but the convoy must maintain speed. ETA to delivery\ + \ remains 17 hours. +statusUpdate99.text=Drove through a wrecked town, %s. Civilians crowded near the road, eyes filled\ + \ with hope that we might offer aid. But we couldn't stop - not here, not now. The crew's\ + \ expressions were tight, a mix of frustration and resignation. It's another harsh truth of this\ + \ war: we have to choose between helping a few or completing the mission. No delays expected, but\ + \ the mood is grim as we press forward. + +statusUpdateEnemyCritical0.text=%s, encountering minimal resistance in this sector, as any\ + \ remaining enemy forces are pulling out. Their retreat is chaotic, marked by scattered gunfire\ + \ and abandoned positions. Vigilance is high; the fight isn't over yet. +statusUpdateEnemyCritical1.text=%s, made contact with scattered enemy forces that retreated quickly.\ + \ The engagement was light, and enemy units fell back without further resistance. Convoy moving\ + \ forward with minimal adjustment. +statusUpdateEnemyCritical2.text=%s, we encountered a minimal picket line at the river crossing,\ + \ with most enemy forces withdrawing rapidly from this sector. Enemy presence is sparse, and\ + \ their retreat is uncoordinated. +statusUpdateEnemyCritical3.text=%s, enemy presence nearly absent in this sector. Recon confirms\ + \ only minor traces of recent troop movements, with abandoned gear everywhere. Comms chatter is\ + \ quiet. Convoy remains vigilant, continuing to sweep for any lingering threats. Maintaining\ + \ standard advance speed. +statusUpdateEnemyCritical4.text=%s, light picket defense encountered at the river crossing, with\ + \ enemy units withdrawing almost immediately. The crossing is clear, but the threat of rear\ + \ ambushes remains real. We're pressing ahead, keeping formation tight and weapons ready for\ + \ sudden contact. +statusUpdateEnemyCritical5.text=%s, light presence in this sector as enemy units pull back. The\ + \ retreat appears uncoordinated, but all eyes are on the flanks for potential ambushes. +statusUpdateEnemyCritical6.text=%s, carefully navigating uneven terrain, remaining mindful of\ + \ potential enemy ambush points. Several possible choke points have been identified, prompting\ + \ increased sensor sweeps and vigilance. Recent recon reports suggest scattered enemy scouts may\ + \ be operating in the area, but no direct contact has been made. Convoy movement continues at a\ + \ steady pace, with all vehicles maintaining formation and operational readiness. No delays\ + \ expected. +statusUpdateEnemyCritical7.text=%s, minimal contact made at a narrow pass as withdrawing units\ + \ fired on us sporadically. The shots were ineffective, and convoy elements continued without\ + \ interruption. +statusUpdateEnemyCritical8.text=%s, minimal contact made at a narrow pass as withdrawing enemy\ + \ units fired on our position. Engagement was limited to small arms fire and a few scattered LRM\ + \ launches. Recon data indicates that enemy forces are\ + \ continuing their retreat, with no reinforcements expected. +statusUpdateEnemyCritical9.text=%s, scattered enemy units encountered in this sector, firing while\ + \ retreating. The possibility of hidden threats keeps crews alert. Progress is steady, but\ + \ caution is essential. Sensor sweeps continue nonstop. +statusUpdateEnemyCritical10.text=%s, negligible enemy presence in this sector, with enemy forces in\ + \ full retreat. Scattered wreckage marks their hasty withdrawal; it seems they're destroying\ + \ whatever they can't take with them. +statusUpdateEnemyCritical11.text=%s, enemy forces have crumbled in this sector, offering negligible\ + \ resistance. Crew stays alert, aware that a cornered animal is still dangerous. No delays\ + \ expected, but pace remains cautious. +statusUpdateEnemyCritical12.text=%s, maintaining vigilance while navigating through territory\ + \ previously contested by enemy forces. Old defensive positions and makeshift barricades are\ + \ visible, but no active resistance detected. No interference on comms, and convoy is moving at\ + \ expected pace. +statusUpdateEnemyCritical13.text=%s, encountered minor enemy presence at a narrow pass. Enemy\ + \ remnants made a weak stand, launching a few shoulder-mounted SRMs before retreating into cover.\ + \ Convoy pace maintaining constant sweeps for further ambushes or traps. +statusUpdateEnemyCritical14.text=%s, witnessing a rapid enemy retreat in this sector. The\ + \ withdrawal seems too sudden, leaving my crews tense and expecting an ambush. Convoy maintains\ + \ pace, but vigilance remains high. +statusUpdateEnemyCritical15.text=%s, enemy forces nearly absent in this sector. The sudden lack of\ + \ opposition feels like the calm before a storm. Crews maintain a tense focus, wary of sudden\ + \ ambushes. The situation feels unstable. +statusUpdateEnemyCritical16.text=%s, advancing cautiously along the route. Terrain analysis\ + \ suggests the possibility of concealed minefields or improvised barricades ahead. Recon\ + \ hovercraft have detected minor signs of recent movement, indicating possible enemy scouting\ + \ activity. All convoy units are on high alert, using active sensor sweeps to identify any\ + \ immediate threats. Progress is steady but deliberate. +statusUpdateEnemyCritical17.text=%s, made contact with retreating enemy units at the supply depot,\ + \ with no sustained resistance. The depot remains intact, and enemy forces are pulling back.\ + \ Convoy continues to maintain steady progress. +statusUpdateEnemyCritical18.text=%s, light resistance encountered, with most enemy forces\ + \ neutralized swiftly. Engagements were limited to a small infantry detachment supported by a\ + \ single heavily damaged 'Mek, which was promptly disabled. Tactical scans show no remaining\ + \ enemy presence in the vicinity, and forward elements continue their advance unhindered.\ + \ All systems report green, no delays expected. +statusUpdateEnemyCritical19.text=%s, detecting minimal enemy activity in this sector, with most\ + \ enemy forces withdrawing rapidly. Progress remains steady, with no deviations from the route. +statusUpdateEnemyCritical20.text=%s, long range scans detected enemy forces collapsing near the\ + \ planned route. Hostile cohesion is minimal, with no coordinated response evident. Convoy\ + \ maintains formation, moving steadily past their broken lines. +statusUpdateEnemyCritical21.text=%s, witnessing enemy units in full retreat, with minimal\ + \ resistance expected. Smoke trails mark their withdrawal, and abandoned vehicles are strewn\ + \ across the path. Convoy forces keep pressing, guns hot and crews alert. No sense of victory\ + \ yet, but this feels like a turning point. +statusUpdateEnemyCritical22.text=%s, light resistance encountered, %s, marked by scattered\ + \ autocannon fire from retreating forces. Convoy maintained formation, advancing steadily. +statusUpdateEnemyCritical23.text=%s, enemy forces nearly absent in this sector. The lack\ + \ of opposition is unsettling, raising concerns of a trap ahead. Crews maintaining high\ + \ alert. Sensors continue to sweep for unexpected hostiles. The silence is tense. +statusUpdateEnemyCritical24.text=%s, encountered a rapid enemy retreat in this sector. Enemy\ + \ movements suggest a complete breakdown of command and control. Crew remains alert. +statusUpdateEnemyCritical25.text=%s, enemy resistance breaking down across this sector.\ + \ What was once a stronghold is now a field of wreckage and scattered, fleeing troops.\ + \ Convoy units press forward, ready for any last-ditch ambushes, but the air is heavy with\ + \ the smell of burning armor. +statusUpdateEnemyCritical26.text=%s, no significant resistance encountered in this sector. Recon\ + \ confirms that enemy forces have likely withdrawn, leaving the area mostly uncontested. Sensor\ + \ sweeps show no hidden threats or mines, and forward elements are progressing steadily. Convoy\ + \ pace remains steady. +statusUpdateEnemyCritical27.text=%s, steady progress continues, with sensors primed for\ + \ sudden movements from enemy remnants. The air crackles with tension, as all eyes are\ + \ on the horizon and every shadow could hide danger. My crews are focused and ready to react at a\ + \ moment's notice. +statusUpdateEnemyCritical28.text=%s, proceeding cautiously through the current sector,\ + \ with all units maintaining readiness for sudden enemy attacks from concealed positions.\ + \ Sensor sweeps and infrared scans are being conducted regularly to detect\ + \ possible hostile movements. So far, no significant contact has been made, and convoy\ + \ progression remains on schedule. +statusUpdateEnemyCritical29.text=%s, scattered enemy units encountered in this sector. Engagement\ + \ was brief, with limited pushback offered before they withdrew. Convoy armor sustained light\ + \ scarring, but all vehicles remain fully operational. +statusUpdateEnemyCritical30.text=%s, crew morale is high as we push through abandoned\ + \ enemy positions. These former strongholds show signs of rapid evacuation, with\ + \ supplies and light weaponry left behind. +statusUpdateEnemyCritical31.text=%s, took a few desperate shots from retreating forces at\ + \ the canyon. Hostiles were disorganized, using mostly light weapons and scattered LRM\ + \ volleys. Enemy resistance has dissipated as they pull back further.\ + \ Movement through the canyon continues without further incident. +statusUpdateEnemyCritical32.text=%s, advancing smoothly over what used to be a heavily\ + \ contested battlefield. Scorched craters and abandoned enemy vehicles mark the path\ + \ forward, but no active opposition has been encountered. Crew is maintaining high\ + \ readiness despite the lull, with sensors scanning constantly for possible ambushes.\ + \ All systems remain at full operational capacity. +statusUpdateEnemyCritical33.text=%s, weak resistance encountered - scattered shots as\ + \ enemy forces pulled back quickly. The withdrawal feels too easy. Convoy maintains momentum, but\ + \ vigilance is high as the retreating units could reposition for a counterattack. +statusUpdateEnemyCritical34.text=%s, minimal hostiles encountered at the river crossing, with most\ + \ enemy forces withdrawing rapidly. The swift retreat raises alarms of a possible\ + \ regrouping nearby. Convoy advances, but nerves are stretched tight. No hits sustained. +statusUpdateEnemyCritical35.text=%s, minor resistance at the river crossing, with enemy\ + \ morale breaking completely. Their retreat was chaotic, but it could be a feint. Convoy\ + \ remains on high alert, scanning for signs of a possible regrouping. +statusUpdateEnemyCritical36.text=%s, light resistance faced; most enemy forces were\ + \ neutralized without issue. The engagement was brief but chaotic, adding to the tension.\ + \ Sensors remain active, monitoring all directions. +statusUpdateEnemyCritical37.text=%s, a rapid enemy retreat was observed in this sector,\ + \ with hostile forces offering negligible resistance. Visuals confirm that remaining\ + \ enemy units are moving at full speed toward their fallback positions, abandoning or destroying\ + \ any remaining equipment. Convoy units continue to advance without impediment, maintaining\ + \ formation integrity. Comms chatter suggests minimal enemy coordination, and no\ + \ immediate reinforcements detected. Vehicle systems are operating at full capacity,\ + \ with no logistical delays reported. +statusUpdateEnemyCritical38.text=%s, minor opposition encountered in this sector, with\ + \ occasional shots from withdrawing infantry. The shots were scattered and ineffective,\ + \ doing little to slow our advance. The situation continues to unfold in our favor. +statusUpdateEnemyCritical39.text=%s, desperate shots fired by remnants at a narrow pass,\ + \ but no significant offense was encountered. Enemy presence has thinned, with most units already\ + \ retreating deeper into their territory. +statusUpdateEnemyCritical40.text=%s, enemy units in full retreat across this sector, with minimal\ + \ resistance encountered. The few remaining hostiles are offering little more than a token\ + \ defense. No delays expected. +statusUpdateEnemyCritical41.text=%s, advancing with heightened alertness, expecting\ + \ possible surprises from concealed enemy positions. Terrain ahead offers several\ + \ potential ambush sites. No significant threats have materialized yet, but crews are on edge.\ + \ Progress remains steady, with no disruptions reported. +statusUpdateEnemyCritical42.text=%s, convoy elements are maintaining focus while rapidly\ + \ pushing through abandoned enemy outposts. Recent scans indicate that these outposts\ + \ were vacated in haste, leaving behind limited supplies and non-functional vehicles.\ + \ Comms interference is minimal, allowing uninterrupted coordination among convoy units.\ + \ All vehicles green across the board. +statusUpdateEnemyCritical43.text=%s, the area ahead shows signs of recent skirmishes, with heat\ + \ signatures only now fading. All convoy units are on full alert, weapons hot and ready. We're\ + \ going to slow down, so we can scan for potential rear-guard ambushes or hidden 'Meks. +statusUpdateEnemyCritical44.text=%s, the path is rugged, with debris from past battles\ + \ littering the way. Sensors scanning aggressively for mines and hidden infantry along the route.\ + \ So far, we've only hit abandoned enemy positions, but the tension is palpable. +statusUpdateEnemyCritical45.text=%s, enemy units were encountered in this sector. Resistance\ + \ minimal, with only a few desperate shots from retreating 'Meks and armor. The chaos\ + \ of their withdrawal is evident from the scattered debris and distant smoke plumes.\ + \ Convoy continues its advance. +statusUpdateEnemyCritical46.text=%s, morale remains high as convoy units maintain a\ + \ steady advance across former enemy defensive lines. Most hostile positions appear\ + \ abandoned, with minimal interference encountered. Initial scans show scattered\ + \ remains of defensive installations, primarily unmanned turrets and empty foxholes.\ + \ Sensor data suggests a clear path forward, with enemy presence reduced to scattered,\ + \ disorganized elements well beyond engagement range. +statusUpdateEnemyCritical47.text=%s, barely any enemy forces left at the river crossing\ + \ as hostile units pull out of this sector. The water's edge is littered with\ + \ abandoned gear and hastily discarded weapons. Convoy maintains a firm push, engines\ + \ echoing over the water as crews stay on high alert. +statusUpdateEnemyCritical48.text=%s, minor resistance at the river crossing; enemy morale\ + \ broke rapidly under fire. Hostiles scattered in disarray, abandoning equipment and wounded.\ + \ Infantry support weapons were deployed but proved ineffective against the convoy's advance.\ + \ Crossing has been secured, and convoy progress remains steady. +statusUpdateEnemyCritical49.text=%s, brief skirmish encountered in this sector as\ + \ retreating enemy troops attempted a final defense. Engagement was limited, primarily\ + \ involving small arms fire and shoulder-mounted SRMs. Enemy resistance was quickly broken,\ + \ with their remaining forces withdrawing under cover of smoke. Maintaining current pace. + +statusUpdateEnemyStalemate0.text=%s, despite ongoing artillery strikes, convoy maintains\ + \ momentum. Artillery impacts have been primarily concentrated on rear positions, with limited\ + \ effectiveness. Crews continue operating at full capacity.\ + \ Communication lines remain clear, and sensors detect no additional enemy reinforcements at\ + \ this stage. Current pace remains steady, with navigation adhering to planned routes despite\ + \ ongoing bombardment. +statusUpdateEnemyStalemate1.text=%s, convoy remains intact despite intermittent\ + \ harassment from enemy skirmishers. Scout vehicles primarily employed hit-and-run\ + \ tactics, utilizing light autocannons and machine gun fire. Engagements were brief.\ + \ No breaches recorded, and operational tempo remains consistent. Sensor sweeps confirm\ + \ scouts have retreated to maintain distance. Convoy continues to advance, maintaining\ + \ formation integrity. +statusUpdateEnemyStalemate2.text=%s, encountered heavy fire from enemy positions,\ + \ primarily autocannon and laser barrages. Engagement lasted approximately ten minutes\ + \ before enemy forces initiated a withdrawal. Sensor logs indicate the enemy concentrated\ + \ fire on central convoy elements, aiming to disrupt movement. Defensive maneuvers were\ + \ executed successfully, preventing major casualties. All vehicles remain operational,\ + \ with minor repairs underway. Comms remain functional, and we're resuming our\ + \ advance, maintaining speed and formation. +statusUpdateEnemyStalemate3.text=%s, sustained heavy fire left convoy locked in place\ + \ for several hours. Incoming rounds included autocannon bursts and indirect LRM fire.\ + \ The enemy maintained pressure but retreated following a prolonged exchange. All\ + \ convoy systems are fully operational, and defensive formations remain intact.\ + \ Movement has resumed, albeit cautiously. +statusUpdateEnemyStalemate4.text=%s, convoy engaged in a brief firefight at a bridge.\ + \ Both sides exchanged small arms fire and autocannon bursts before disengaging due to\ + \ incoming artillery. No decisive advantage gained by either side. Damage\ + \ assessment shows superficial impacts to convoy armor, with no breaches.\ + \ Bridge remains structurally sound. Sensors indicate sporadic enemy movement in the\ + \ vicinity, but no immediate pursuit. Convoy remains on course. +statusUpdateEnemyStalemate5.text=%s, series of brief clashes erupted near convoy route,\ + \ with both sides exchanging fire but failing to gain ground. Engagements primarily\ + \ involved small arms fire. Enemy forces retreated to maintain distance, indicating\ + \ no sustained offensive intent. Convoy pace remains steady, with minimal deviation\ + \ from planned route. +statusUpdateEnemyStalemate6.text=%s, convoy executing detours to avoid long-range\ + \ autocannon fire from entrenched positions. All vehicles remain in formation,\ + \ maintaining steady movement forward. Sensor sweeps confirm that enemy fire is\ + \ concentrated on known choke points, necessitating adjusted routes. Crews are\ + \ maintaining high readiness, with comms channels open.\ + \ Progress is steady. +statusUpdateEnemyStalemate7.text=%s, convoy moving steadily while avoiding entrenched\ + \ LRM emplacements and sniper fire. Sensor data confirms enemy presence remains active,\ + \ targeting key routes with precise fire. Convoy continues forward, maintaining formation\ + \ All sensors scanning continuously for concealed threats. +statusUpdateEnemyStalemate8.text=%s, progress is slow but steady as convoy maneuvers\ + \ around minefields and fortified positions. Minesweeper units are active, clearing\ + \ paths to prevent damage. Defensive emplacements have been identified but remain\ + \ unengaged, allowing convoy to maintain its current route. Damage reports indicate\ + \ only minor wear on transports due to terrain conditions. No direct enemy\ + \ engagement recorded. Movement continues, but at a cautious pace to ensure safety and\ + \ operational readiness. +statusUpdateEnemyStalemate9.text=%s, convoy remains operational despite slow progress\ + \ through fields of scattered debris and unspent munitions. Terrain is proving\ + \ challenging, with potential hazards limiting speed. Sensor sweeps detect sporadic\ + \ munitions but no immediate threats from active enemy forces. +statusUpdateEnemyStalemate10.text=%s, a brief firefight erupted, resulting in casualties\ + \ on both sides. Engagement was sharp and intense, with autocannon bursts and missile\ + \ strikes exchanged at close range. After sustaining moderate losses, both sides pulled\ + \ back without pressing further. Convoy elements maintained position and resumed forward\ + \ movement once the area cleared. All vehicles remain operational, and crews are already\ + \ prepared for the next contact. +statusUpdateEnemyStalemate11.text=%s, combat remains active in this sector, with\ + \ autocannons and lasers trading blows. Hostile fire is consistent but not\ + \ overwhelming, indicating an attempt to wear us down rather than achieve a breakthrough.\ + \ No decisive advantage gained on either side so far. Convoy continues advancing under\ + \ covering fire, maintaining formation despite constant pressure. +statusUpdateEnemyStalemate12.text=%s, current standoff persists, with both sides\ + \ entrenched and unable to gain ground. Hostile positions are reinforced, delivering\ + \ steady fire to delay our movement. Convoy maintains readiness, using counter-fire to\ + \ suppress enemy advances. No significant breaches reported on our side, but tension\ + \ remains high as both forces await an opening. Movement is steady, with all systems\ + \ fully operational despite the ongoing stalemate. +statusUpdateEnemyStalemate13.text=%s, sustained heavy fire from enemy positions, narrowly\ + \ avoiding major damage. Engagement involved concentrated laser and missile fire,\ + \ forcing a temporary halt. Enemy units withdrew after a few minutes of intense\ + \ exchange, suggesting an attempt to conserve their forces. All convoy elements report green\ + \ status, and advance has resumed with increased caution. +statusUpdateEnemyStalemate14.text=%s, another skirmish broke out but failed to produce\ + \ a decisive outcome. Both sides engaged in heavy fire, with PPC and laser\ + \ bursts exchanged before withdrawing to previous positions. Enemy fire was accurate but\ + \ lacked sustained pressure. Crews maintain vigilance, expecting additional skirmishes as the\ + \ route continues. +statusUpdateEnemyStalemate15.text=%s, fighting continues to threaten our route, as enemy\ + \ forces maintain sporadic but intense autocannon fire. Convoy is taking evasive\ + \ maneuvers, keeping moving under pressure. Crews are focused, executing\ + \ defensive tactics while maintaining operational speed. Enemy presence suggests\ + \ further attempts to disrupt our advance. +statusUpdateEnemyStalemate16.text=%s, repeated skirmishes encountered in this sector,\ + \ with enemy forces retreating after a few autocannon volleys. Convoy maintained\ + \ defensive positions, exchanging accurate fire to suppress enemy positions. Engagements\ + \ were short-lived, with enemy units choosing to fall back rather than commit. Convoy\ + \ continues forward, maintaining readiness for further contacts. +statusUpdateEnemyStalemate17.text=%s, convoy just faced another skirmish. Enemy withdrew\ + \ after we inflicted light casualties, preferring to conserve forces rather than sustain a\ + \ prolonged engagement. The situation remains tense, as enemy elements may attempt another\ + \ strike further along the route. Crew discipline remains high, with readiness levels maintained. +statusUpdateEnemyStalemate18.text=%s, combat in this sector remains ongoing, marked by\ + \ autocannon and missile exchanges. No decisive outcomes achieved as both sides are\ + \ holding ground. Convoy continues to press forward cautiously, maintaining defensive\ + \ formations. +statusUpdateEnemyStalemate19.text=%s, another clash resulted in a stalemate, with the enemy\ + \ sustaining moderate damage before withdrawing. Engagement was brief but intense,\ + \ featuring coordinated missile strikes and autocannon volleys. Convoy maintained position\ + \ until enemy fire subsided, then resumed its advance. Crews are already preparing for the next\ + \ contact. +statusUpdateEnemyStalemate20.text=%s, another skirmish erupted at the river crossing.\ + \ The exchange was fierce, with lasers lighting up the water's edge.\ + \ Mud and debris sprayed everywhere making traction difficult. Enemy forces tried to\ + \ press, but their advance was stalled by allied counter-fire. Despite the chaos, the enemy failed\ + \ to secure any ground and eventually fell back under sustained pressure. We're still\ + \ moving, but the tension in the air is thick. +statusUpdateEnemyStalemate21.text=%s, our convoy was caught in a brief but intense\ + \ skirmish. Gunfire echoed between the hills, and visibility was low due to smoke and\ + \ stirred-up dust. Enemy forces hit us hard initially, but their line cracked after taking\ + \ light casualties. Things are still tense, and we're anticipating another ambush. +statusUpdateEnemyStalemate22.text=%s, convoy exchanged heavy fire with enemy forces\ + \ along the route. Autocannon bursts and missile trails cut through the air as both\ + \ sides clashed. The roar of battle was overwhelming, with each hit rattling the armor.\ + \ The enemy pulled back before managing to breach our lines, leaving only smoke and\ + \ distant echoes behind. Our forward movement is steady, but the crews are running high on\ + \ adrenaline, eyes scanning for the next ambush. +statusUpdateEnemyStalemate23.text=%s, situation in this sector remains unresolved.\ + \ Constant ambushes are keeping everyones' heads down. The terrain is scorched,\ + \ with impact craters lining the path forward. Every movement is under threat, with SRM\ + \ volleys and autocannon fire coming from unseen positions. The convoy is still enroute,\ + \ maintaining formation under relentless pressure. Crews are running on pure resolve,\ + \ pushing through the exhaustion as we wait for a moment to break through. +statusUpdateEnemyStalemate24.text=%s, a firefight broke out, sudden and violent. The air\ + \ was filled with smoke and shrapnel and the sound of metal on metal, with casualties\ + \ sustained on both sides. After a few minutes of brutal exchange, both forces pulled\ + \ back to regroup. The convoy remains intact, but the wear of constant skirmishes is\ + \ beginning to show. +statusUpdateEnemyStalemate25.text=%s, intense fire at a canyon entrance locked the\ + \ convoy in place. Incoming rounds slammed into the canyon walls, sending debris raining\ + \ down on the vehicles. We almost got trapped in there. The situation was chaotic,\ + \ but defensive fire eventually forced the enemy to ease off. All\ + \ systems remain nominal, and forward movement has resumed. +statusUpdateEnemyStalemate26.text=%s, engaged in a clash at the river crossing. Enemy\ + \ forces, led by a lance of light 'Meks, launched a well-coordinated attack. Laser beams\ + \ and missiles crisscrossed the area, turning the riverbank into a battlefield. Despite\ + \ their aggressive push, allied counter-fire held them back, and their lines broke after\ + \ sustaining moderate damage. We're still moving, but I've told the crews to expect\ + \ further strikes. +statusUpdateEnemyStalemate27.text=%s, the deadlock in this sector persists with frequent skirmishes\ + \ erupting along the route. Autocannon fire and missile bursts continue to rain down as\ + \ the convoy inches forward. We're having to make some tough calls here, finding\ + \ routes around mines and contested sectors, but the air is heavy with smoke, and each turn\ + \ feels like walking into an ambush. Progress is slow. +statusUpdateEnemyStalemate28.text=%s, deadlock continues in this sector, with constant\ + \ autocannon fire pinning both sides down. The sound of rounds hitting metal is relentless,\ + \ and the convoy has struggled to gain ground. Crews are pushing through exhaustion, staying\ + \ focused amid the chaos. It's a real mess out here, shell casings and scorched\ + \ remains, with no clear winner in sight. The convoy presses on. +statusUpdateEnemyStalemate29.text=%s, minor skirmishes broke out along our route, marked\ + \ by sharp exchanges of fire. No decisive gains have been made, but the situation remains\ + \ stable for now. The road ahead is littered with debris, and smoke lingers in the air.\ + \ Crews remain vigilant, knowing that each step forward could be another firefight.\ + \ Despite the uncertainty, we're still enroute. +statusUpdateEnemyStalemate30.text=%s, situation in this sector remains tense, marked by\ + \ regular exchanges of fire. Neither side has managed to gain a decisive advantage.\ + \ Autocannon bursts and missile volleys punctuate the air, but the convoy maintains\ + \ formation and keeps moving forward. While the progress is steady, we're all on\ + \ high alert here. +statusUpdateEnemyStalemate31.text=%s, sustained heavy fire left us locked in place for\ + \ several hours. The engagement was intense, with incoming rounds forcing us to\ + \ go to ground. Despite the pressure, the convoy remains intact, with armor holding against\ + \ repeated impacts. It was a close situation, but the escorts ensured our\ + \ safety. Crews maintained composure throughout, and no critical systems were\ + \ compromised. We're now enroute to nav gamma. +statusUpdateEnemyStalemate32.text=%s, a skirmish erupted at the river crossing, with\ + \ concentrated fire from both sides. The intensity was high, but neither force managed\ + \ to establish clear dominance. Convoy elements maintained defensive positions, absorbing\ + \ impacts and responding in kind. Despite the lack of decisive outcomes, the convoy\ + \ remained undeterred and progress now continues. +statusUpdateEnemyStalemate33.text=%s, we got hit again on our route, but\ + \ we made it through. The exchange of fire was sustained, but the\ + \ convoy's defences held firm. Enemy forces eventually withdrew to their\ + \ positions, unable to break through. Convoy remains intact, and forward\ + \ movement continues. Our crews are focused, displaying calm determination despite the\ + \ ongoing challenges. +statusUpdateEnemyStalemate34.text=%s, the situation in this sector remains unresolved, with\ + \ continuous exchanges keeping both sides pinned down. The battle lines are clear, but\ + \ progress is slow. Convoy units maintain steady fire discipline, holding defensive\ + \ formation under pressure. While no major gains have been made, the convoy remains\ + \ operational, and we're pressing forward on our planned route. +statusUpdateEnemyStalemate35.text=%s, convoy was caught in an ambush near the river\ + \ crossing. The skirmish was brief but intense, marked by rapid exchanges of fire and a\ + \ couple of LRM volleys from the enemy. Despite the surprise attack, we were able to\ + \ maintain formation and repelled the threat. Enemy units withdrew shortly\ + \ after, unable to sustain the engagement. Movement has resumed, with crews maintaining a\ + \ steady focus on the mission. +statusUpdateEnemyStalemate36.text=%s, minor skirmishes occurred at a narrow pass,\ + \ but we got through it okay. The engagement was brief, with scattered\ + \ fire exchanged. Convoy crews continue to adapt to the ongoing challenges, keeping\ + \ formations tight and ready for sudden threats. Despite the constant skirmishes, the\ + \ mission remains on track, with all vehicles reporting green status. +statusUpdateEnemyStalemate37.text=%s, we encountered heavy resistance in this sector,\ + \ mainly sustained LRM and PPC fire at long range. Crews remain composed, and we're\ + \ maintaining movement despite the opposition. Progress is slower than predicted. +statusUpdateEnemyStalemate38.text=%s, heavy combat persists in this sector, with\ + \ entrenched enemy forces holding their ground. The exchange of fire is continuous, but\ + \ convoy elements are maintaining defensive stances and responding effectively. Progress\ + \ is methodical, with crews demonstrating disciplined engagement tactics. The situation\ + \ remains fluid. +statusUpdateEnemyStalemate39.text=%s, frequent skirmishes continue to prevent clear\ + \ advances in this sector. The convoy maintains its position, absorbing enemy fire while\ + \ attempting to press forward. The situation remains challenging, but the overall mission\ + \ objective remains in sight. +statusUpdateEnemyStalemate40.text=%s, brief firefight erupted. An intense exchange, but no ground\ + \ gained. Enemy retreated quickly, but tension is high. Crew remains alert, ready for immediate\ + \ response. Pushing forward now. +statusUpdateEnemyStalemate41.text=%s, minor skirmishes erupted along the route. Rapid\ + \ exchanges of fire ensued, with both sides attempting to gain ground. Enemy resistance\ + \ was consistent but not overwhelming. Movement was slowed as crews adapted to the shifting\ + \ engagement zones. The situation remains stable for now, but vigilance is crucial. Convoy maintains\ + \ its pace, but alertness remains high as skirmishes continue intermittently. +statusUpdateEnemyStalemate42.text=%s, deadlock persists across the battlefield. Constant\ + \ autocannon fire exchanges keep both sides entrenched, with little room for maneuver.\ + \ Convoy is maintaining cover, focusing on suppressive fire to manage enemy pressure.\ + \ Progress is slow, but consistent. Despite the standoff, the convoy pushes forward cautiously\ + \ whenever possible, with defensive formations intact. +statusUpdateEnemyStalemate43.text=%s, clash at the river crossing was intense. The\ + \ enemy's attack was well-coordinated, led by a lance of light 'Meks delivering precise\ + \ autocannon fire. Convoy defensive measures absorbed impacts, maintaining formation under\ + \ heavy fire. Despite heavy resistance, forward movement is steady. +statusUpdateEnemyStalemate44.text=%s, heavy resistance encountered at the canyon.\ + \ Autocannon fire was exchanged heavily, with neither side managing to gain a decisive\ + \ advantage. The convoy pressed forward despite the barrage, maintaining a steady pace\ + \ under pressure. The situation remains tense, but the convoy continues forward with caution,\ + \ ready for further engagements. +statusUpdateEnemyStalemate45.text=%s, situation remains unresolved in this sector. Constant\ + \ exchanges of fire are keeping both sides pinned down. The convoy holds steady,\ + \ with crews scanning for sudden changes in enemy movements. No clear outcome is evident, but the\ + \ convoy continues to advance where opportunities arise. Defensive tactics are in place,\ + \ adapting to ongoing resistance. +statusUpdateEnemyStalemate46.text=%s, combat persists in this sector with continuous autocannon and\ + \ SRM fire. No decisive outcome achieved, as both sides maintain entrenched positions.\ + \ Convoy maintains formation, responding to sustained attacks while avoiding\ + \ major breaches. Progress is gradual. +statusUpdateEnemyStalemate47.text=%s, recent engagement ended inconclusively. Heavy fire was\ + \ exchanged, but the convoy remains intact, advancing slowly despite lingering\ + \ threats. +statusUpdateEnemyStalemate48.text=%s, clashes continue along the route, directly\ + \ threatening convoy movement. Heavy autocannon fire forced evasive maneuvers, but crews\ + \ maintained focus and defensive positions. Progress is steady, though tension remains high\ + \ as the convoy presses forward. The situation remains unstable, but the convoy adapts\ + \ continuously to enemy pressure. +statusUpdateEnemyStalemate49.text=%s, recent skirmish failed to produce clear results.\ + \ Heavy fire was exchanged, but hostiles withdrew after sustaining minor damage. The\ + \ convoy continues forward, maintaining operational status under persistent threats. Crews\ + \ remain alert, anticipating renewed contact. + +statusUpdateEnemyDominating0.text=%s, enemy dominance in this sector is making each\ + \ step forward a struggle. Continuous fire rains down, keeping the convoy pinned and\ + \ forcing frequent stops. Autocannon bursts, missile volleys, and laser fire crisscross\ + \ the route, leaving crews under constant stress. Progress is possible, but no breaks in the\ + \ assault yet. +statusUpdateEnemyDominating1.text=%s, convoy still on course, but enemy sniper fire\ + \ is taking a toll on speed. Snipers are positioned strategically, forcing evasive\ + \ movements and slowing progress. Defensive measures are in place, but the convoy's\ + \ pace is erratic due to frequent stops. No significant losses reported, but conditions\ + \ remain harsh. +statusUpdateEnemyDominating2.text=%s, pressing through risky terrain, trying to evade\ + \ skirmishes while maintaining convoy speed. The ground is unstable, with frequent\ + \ obstacles slowing movement. Enemy units are active in the area, but no direct contact\ + \ has occurred yet. +statusUpdateEnemyDominating3.text=%s, navigating through less-contested terrain. The\ + \ aim is to find a safer path beyond enemy long-range sensor reach. The convoy is making\ + \ steady progress, though the rough terrain presents challenges. Visibility is limited,\ + \ but movement remains steady. +statusUpdateEnemyDominating4.text=%s, enemy dominance in this sector has forced\ + \ longer, more hazardous routes. Progress is both slow and dangerous, with risk\ + \ increasing as we detour into less secure territory. Enemy pressure is constant,\ + \ and maintaining formation under these conditions requires rapid adjustments.\ + \ The situation remains critical. +statusUpdateEnemyDominating5.text=%s, relentless attacks forced a withdrawal to a\ + \ backup route. Enemy pressure was too intense, with LRM and SRM fire was overwhelming\ + \ convoy defenses. We're adapting, but the pressure remains high. +statusUpdateEnemyDominating6.text=%s, enemy forces are deploying significant resources\ + \ into this sector. Allied units have begun a tactical retreat, unable to sustain\ + \ defensive positions under current pressure. Without reinforcements, allied forces will\ + \ not hold for long. Current analysis suggests that maintaining this route is not viable\ + \ for subsequent operations. Alternative routing is advised for future movements. +statusUpdateEnemyDominating7.text=%s, attempted to hold ground at the depot, but enemy\ + \ artillery strikes came down relentlessly. Shells landed close, shaking the vehicles and\ + \ forcing rapid repositioning. Despite strong defensive efforts, allied units were forced\ + \ to abandon the depot and retreat swiftly. +statusUpdateEnemyDominating8.text=%s, rerouting to avoid enemy airstrikes. Assault 'Meks\ + \ advancing rapidly, forcing constant adjustments. Defensive measures are in place, with\ + \ crews focused on evasion. The convoy is maintaining speed, but the threat from air\ + \ support and 'Meks remains immediate. +statusUpdateEnemyDominating9.text=%s, entrenched enemy positions are forcing the convoy\ + \ into longer, riskier routes. Moving through rough terrain under fire is taking a toll on\ + \ both speed and morale. Defensive fire keeps the enemy at bay, but the danger of sudden\ + \ ambushes remains high. +statusUpdateEnemyDominating10.text=%s, heavy enemy momentum at the resupply point left\ + \ no choice but to abandon supplies to maintain speed. Crew focus was on survival,\ + \ making the hard call. We're moving fast, but the loss of resources will hurt. +statusUpdateEnemyDominating11.text=%s, enemy control over this sector has forced a\ + \ tactical retreat. Infantry and 'Meks hold the high ground, using it to deliver sustained\ + \ fire on our position. Defensive measures were enacted, but the enemy's elevated position\ + \ gave them a clear advantage. The convoy remains intact, but forward movement is currently\ + \ impeded. Alternate routes are under consideration to avoid further exposure. +statusUpdateEnemyDominating12.text=%s, enemy control of the canyon has necessitated\ + \ longer detours. Entrenched 'Meks block all main routes, preventing direct movement\ + \ through the area. Convoy integrity is maintained, but progress is slow due to rerouting\ + \ requirements. Tactical evaluations suggest limited options for regaining momentum without\ + \ reinforcements. +statusUpdateEnemyDominating13.text=%s, moving cautiously to avoid ambushes from\ + \ entrenched SRM carriers. Sensor sweeps are frequent, aiming to detect potential threats\ + \ before engagement. The terrain favors ambushes, but convoy formations are tight and\ + \ prepared. +statusUpdateEnemyDominating14.text=%s, enemy advance forced retreat from the checkpoint.\ + \ Precision AC/2 fire created conditions unsuitable for defense, prompting withdrawal.\ + \ The checkpoint remains under enemy control. Convoy operations continue, but route\ + \ adjustments are now essential. +statusUpdateEnemyDominating15.text=%s, navigating hazardous terrain to avoid enemy\ + \ ambushes. The convoy is maintaining speed and momentum, though the path is challenging.\ + \ Rocks and debris complicate movement, but crews remain disciplined and calm. Defensive\ + \ measures are active, scanning for potential threats. +statusUpdateEnemyDominating16.text=%s, coordinated enemy strikes hit the resupply point\ + \ hard. Initial defense was strong, but the sheer volume of fire broke our lines. Rapid\ + \ artillery strikes, backed by precise autocannon volleys, forced a retreat. The convoy\ + \ managed to pull back, but supplies were left behind in the chaos. +statusUpdateEnemyDominating17.text=%s, pressing through rocky terrain to avoid sudden\ + \ skirmishes with enemy light 'Meks. Movement is cautious but continuous, with crews\ + \ remaining vigilant. Scout units are visible on the periphery, but defensive measures are\ + \ holding. +statusUpdateEnemyDominating18.text=%s, using alternate routes to stay ahead of advancing\ + \ forces. The terrain is rough, filled with potential ambush sites. Crews are scanning\ + \ continuously, searching for safe passage, but enemy units are closing in, creating constant\ + \ pressure. The situation remains unstable, with enemy pursuit ongoing. +statusUpdateEnemyDominating19.text=%s, taking risky detours to evade advancing heavy\ + \ 'Mek units. The terrain is rough, but morale is steady. +statusUpdateEnemyDominating20.text=%s, moving swiftly to avoid contact with enemy 'Meks\ + \ fortifying strategic positions. The terrain is manageable, and we're prepared for potential\ + \ encounters, maintaining formation and focus. Enemy presence is noted but distant. +statusUpdateEnemyDominating21.text=%s, progressing quickly through rough terrain. Enemy\ + \ BattleMeks are closing in, slowing our advance, but not halting it. Defensive measures\ + \ are active, keeping enemy fire at bay. +statusUpdateEnemyDominating22.text=%s, barely escaped an ambush at the river crossing.\ + \ Light 'Meks harassed the convoy relentlessly. Defensive responses were rapid,\ + \ keeping convoy intact, but the ambush was well-timed, suggesting they knew\ + \ when would arrive. +statusUpdateEnemyDominating23.text=%s, convoy encountered heavy fire as enemy forces\ + \ secured high ground at a blind canyon. AC/20s were deployed effectively, dominating the\ + \ route and preventing further advance. Defensive measures absorbed some impacts, but\ + \ retreat was necessary to prevent losses. The canyon remains under enemy control, making\ + \ this path untenable for continued movement. Rerouting. +statusUpdateEnemyDominating24.text=%s, relentless enemy strikes made reaching the depot\ + \ nearly impossible. Autocannon volleys were precise, pinning the convoy down repeatedly.\ + \ The intensity of the assault shows no signs of letting up. +statusUpdateEnemyDominating25.text=%s, taking dangerous paths to maintain progress while\ + \ avoiding entrenched autocannon positions. Enemy fire is consistent, with bursts forcing\ + \ rapid shifts in route. The terrain is challenging, slowing forward momentum. +statusUpdateEnemyDominating26.text=%s, convoy nearly surrounded at the river crossing.\ + \ Enemy 'Meks moved fast, cutting off key paths with precision. The convoy struggled\ + \ to maintain formation as debris and splashes from near hits filled the air.\ + \ Crews pushed through under immense pressure, barely managing to find a gap before being\ + \ fully encircled. The situation remains dire, with escape routes still under threat. +statusUpdateEnemyDominating27.text=%s, heavy assault encountered along the current route.\ + \ Enemy SRM Carriers initiated a coordinated push, forcing a tactical retreat. The carriers\ + \ attempted to pin the convoy down with sustained fire. Operational integrity is\ + \ preserved, but enemy presence remains significant. SRM carriers are still in the\ + \ vicinity, indicating a strong enemy foothold in this area. +statusUpdateEnemyDominating28.text=%s, heavy resistance encountered along the route. Enemy forces are\ + \ using entrenched positions to hold ground, deploying continuous fire to halt our advance.\ + \ Convoy managed to push through initial bursts, but sustained attacks are slowing progress.\ + \ Defensive measures are in place, and vehicles remain operational. Moving forward will\ + \ require persistence and increased vigilance. +statusUpdateEnemyDominating29.text=%s, enemy launched a concentrated push at a narrow\ + \ pass. Heavy autocannon fire created a lethal barrage that slowedconvoy movement.\ + \ Defensive formations held firm, but progress was severely impacted.\ + \ Crews remain focused, ready for another potential surge as we regroup. +statusUpdateEnemyDominating30.text=%s, convoy took heavy hits from enemy 'Meks blocking\ + \ our planned route. PPC blasts and autocannon fire rocked the vehicles, forcing immediate\ + \ evasive maneuvers. The path ahead is unclear, with enemy units still holding key choke\ + \ points. Defensive formations remain intact, but the pressure is unrelenting. +statusUpdateEnemyDominating31.text=%s, despite intense pressure, the convoy continues to\ + \ push forward. Enemy fire is sustained, creating a high-risk environment. Crews are\ + \ adapting quickly, using evasive tactics to minimize damage. Speed is maintained where\ + \ possible, but caution is necessary. +statusUpdateEnemyDominating32.text=%s, convoy hit hard at the narrow pass. Artillery fire\ + \ was intense, creating significant delays. Defensive maneuvers kept the convoy intact, but\ + \ forward momentum was severely affected. Progress remains slow, but movement has resumed. +statusUpdateEnemyDominating33.text=%s, navigating hostile terrain occupied by enemy\ + \ infantry. Engagements are frequent, with small arms fire disrupting movement. The convoy\ + \ is keeping pace, but speed is reduced to maintain control over rough ground. Crews are\ + \ on constant alert, responding to sudden attacks. Enemy positions are scattered but\ + \ persistent, requiring continuous adjustments. Progress remains steady, but the pressure\ + \ from enemy infantry is increasing. Evasion tactics are in use. +statusUpdateEnemyDominating34.text=%s, enemy control over key routes is forcing risky\ + \ detours. Formation is hard to maintain as we navigate unfamiliar terrain under constant\ + \ fire. Defensive adjustments are ongoing, but pressure is building. +statusUpdateEnemyDominating35.text=%s, convoy remains operational despite immense pressure\ + \ from enemy skirmishers. Constant attacks slow progress, with frequent stops to avoid\ + \ concentrated fire. No critical damage reported, but the situation remains tense. +statusUpdateEnemyDominating36.text=%s, convoy remains operational. However, scattered\ + \ LRM fire complicates route planning. Incoming rounds are not concentrated but create\ + \ unpredictable threats across the path. Current conditions suggest continued movement will\ + \ be slow, with increased risk from sustained indirect fire. Monitoring of hostile\ + \ positions continues. +statusUpdateEnemyDominating37.text=%s, forced into a hasty retreat at the depot after it\ + \ came under continuous artillery bombardment. Shells landed close, creating chaos and\ + \ forcing immediate withdrawal. Crew maintained discipline, executing the retreat under\ + \ intense pressure. Enemy artillery is still active, making a return unfeasible. +statusUpdateEnemyDominating38.text=%s, enemy pressure is mounting, with heavy LRM fire\ + \ targeting the convoy. Defensive formations are holding strong, absorbing impacts while\ + \ maintaining momentum. Armor is strained but intact. The situation demands patience and resilience,\ + \ both of which remain unwavering among all personnel. +statusUpdateEnemyDominating39.text=%s, sudden strike at the narrow pass has heightened risk\ + \ levels. Enemy fire was concentrated but brief, aimed at disrupting the convoy's pace.\ + \ Forward momentum is being cautiously resumed, though the narrow terrain increases vulnerability\ + \ to repeat attacks. Tactical assessments are underway to determine immediate next steps. +statusUpdateEnemyDominating40.text=%s, coordinated attacks drove the convoy back. Enemy\ + \ forces unleashed a rapid assault under intense PPC fire. Defensive fire was returned,\ + \ but the intensity forced a quick withdrawal. The retreat was hasty, leaving no time for\ + \ regrouping, and we are currently waiting for all units to report in. +statusUpdateEnemyDominating41.text=%s, held position at the checkpoint initially, but enemy\ + \ LRMs launched in coordinated waves. Explosions rattled the convoy, showering the area with\ + \ debris and creating disarray. Forced withdrawal was the only option, as further attempts to\ + \ hold ground would have resulted in severe losses. Morale remains strained, but crews are\ + \ ready to press forward again. +statusUpdateEnemyDominating42.text=%s, we just narrowly escaped a well-laid ambush. Enemy forces hit\ + \ hard, but convoy units rallied quickly, breaking through the attack. Enemy positions remain\ + \ active, suggesting more ambushes ahead. +statusUpdateEnemyDominating43.text=%s, taking every available detour to bypass enemy-\ + \ controlled zones. Enemy fire is heavy, with autocannons and missile volleys creating\ + \ constant hazards. Detours are risky, often passing through contested ground and potential\ + \ ambush sites. Convoy pace is inconsistent, with stops to avoid concentrated fire. Urgency is\ + \ high, as enemy units continue to pursue from multiple directions. +statusUpdateEnemyDominating44.text=%s, situation is dire, with heavy enemy presence ahead.\ + \ Withdrawal is not an option. Crews are aware of the stakes and remain committed to the\ + \ mission. Progress is challenging, but every effort is made to keep moving forward. +statusUpdateEnemyDominating45.text=%s, faced a series of well-timed strikes at the river\ + \ crossing. Enemy assault 'Meks gained control of key paths, deploying heavy fire that forced\ + \ multiple detours. All vehicles remain functional, but the enemy's dominance at the crossing\ + \ poses a significant threat to further movement. Current assessment indicates a high likelihood\ + \ of additional attacks in this sector. +statusUpdateEnemyDominating46.text=%s, convoy remains intact. Enemy fire from elevated\ + \ positions is increasing rapidly, but defensive responses are immediate and effective.\ + \ Progress is slow but steady. +statusUpdateEnemyDominating47.text=%s, navigating now enemy-held territory. Fire from all sides\ + \ is constant, forcing evasive maneuvers. Despite heavy fire, the convoy keeps moving,\ + \ maintaining momentum. No major losses reported, but the situation remains critical. +statusUpdateEnemyDominating48.text=%s, enemy launched a strong push at the river crossing,\ + \ using entrenched positions to disrupt movement. Convoy units maintained defensive positions,\ + \ but forward movement was significantly slowed. All systems remain operational, but enemy\ + \ presence is effectively blocking the route. Rerouting. +statusUpdateEnemyDominating49.text=%s, heavy resistance encountered in this sector. Enemy\ + \ units are entrenched, holding key positions with precision fire. Missile trails and\ + \ mortar bursts are creating a wall of steel that slows every advance.\ + \ The convoy is pushing forward, but each meter is earned under fire. + +statusUpdateEnemyOverwhelming0.text=%s, situation is dire, with heavy enemy\ + \ presence ahead. Defensive measures are active, adapting to enemy positions\ + \ as they are revealed. Progress is challenging, but we're going to keep moving. +statusUpdateEnemyOverwhelming1.text=%s, retreating rapidly, trying to maintain\ + \ momentum as enemy 'Meks close in. The situation is desperate, with heavy\ + \ laser fire threatening convoy integrity. We're taking evasive\ + \ maneuvers under pressure. The convoy is not defeated,\ + \ but the enemy pursuit is relentless. +statusUpdateEnemyOverwhelming2.text=%s, enemy forces closing in rapidly;\ + \ immediate action required to avoid encirclement. Crews are moving fast,\ + \ adjusting routes to escape encirclement. The escorts are\ + \ active, keeping advancing forces at bay. The convoy is pushing forward, aiming\ + \ to break through the tightening perimeter. +statusUpdateEnemyOverwhelming3.text=%s, narrowly escaped an ambush. Enemy\ + \ forces launched a sudden, coordinated attack, targeting vulnerable sections\ + \ of the convoy. Defensive actions were swift, enabling a rapid withdrawal.\ + \ Crews maintained composure under fire, keeping formation intact. Convoy\ + \ remains operational and pressing forward. +statusUpdateEnemyOverwhelming4.text=%s, situation remains desperate, but we\ + \ broke through a wall of SRM Carriers. Crews pushed hard, navigating narrow paths\ + \ amidst heavy fire from all sides. We're struggling here, but the terrain opens up\ + \ ahead - we might get through this. +statusUpdateEnemyOverwhelming5.text=%s, convoy witnessed an allied force\ + \ suffer catastrophic losses at the river crossing checkpoint. Visuals confirm\ + \ heavy enemy fire, including concentrated LRM strikes, overwhelming allied defenses.\ + \ Initial estimates indicate significant casualties among friendly units,\ + \ with remaining forces in disarray. Enemy 'Meks seized control of the crossing within minutes.\ + \ The convoy maintained a safe distance, monitoring the situation without\ + \ engaging. Current assessment shows no viable support options at this time. +statusUpdateEnemyOverwhelming6.text=%s, enemy positions remain strong in this sector,\ + \ forcing rapid adjustments in convoy speed and formation. The situation is critical,\ + \ with continued enemy fire creating constant pressure. Defensive measures are\ + \ focused on minimizing damage, maintaining full operational capacity. +statusUpdateEnemyOverwhelming7.text=%s, moving at full speed, barely holding\ + \ formation as LRMs rain down. Crews are pushing hard, keeping vehicles\ + \ on the road under heavy fire. The situation remains critical. +statusUpdateEnemyOverwhelming8.text=%s, overwhelming enemy pressure forced\ + \ abandonment of resupply attempts. Initial engagement involved concentrated\ + \ artillery and missile fire, rapidly escalating to a full-scale assault on the checkpoint.\ + \ Supplies were left behind to maintain speed and maneuverability. Movement\ + \ continues toward secondary navpoints. +statusUpdateEnemyOverwhelming9.text=%s, enemy launched an ambush at the\ + \ river crossing, using entrenched positions to disrupt movement. Dug-in\ + \ troops provided cover for their forward units, limiting convoy progress.\ + \ Convoy units maintained defensive positions, but forward movement was\ + \ significantly slowed. All systems remain operational, but enemy presence is\ + \ effectively blocking the route. +statusUpdateEnemyOverwhelming10.text=%s, coordinated attacks drove the convoy\ + \ back from the supply depot. Enemy forces unleashed a rapid assault with heavy\ + \ laser fire, scorching the surrounding terrain. Defensive fire was returned,\ + \ but the intensity forced a quick withdrawal. +statusUpdateEnemyOverwhelming11.text=%s, retreating without pause under continuous\ + \ artillery bombardment. We're keeping our heads down and pushing forward\ + \ despite heavy impacts. The mission continues, but each moment feels like it\ + \ could be our last. +statusUpdateEnemyOverwhelming12.text=%s, we watched as the resupply depot\ + \ got hit hard. Explosions ripped through its defenses as\ + \ enemy forces broke through, turning it into a scene of utter devastation.\ + \ Fire and smoke filled the air, masking our withdrawal. The convoy continues its\ + \ advance, but the cost has been high, with allied support largely compromised. +statusUpdateEnemyOverwhelming13.text=%s, the enemy's relentless pressure nearly\ + \ broke the convoy's resolve near the river crossing. Sustained autocannon fire\ + \ forced rapid evasive action. Convoy continues to advance, but the way forward is uncertain. +statusUpdateEnemyOverwhelming14.text=%s, narrowly escaped a well-laid ambush.\ + \ Enemy forces hit hard, but convoy units rallied quickly, breaking through the\ + \ attack. We're getting more sensor pings, suggesting more ambushes ahead. +statusUpdateEnemyOverwhelming15.text=%s, moving rapidly through a storm of SRM\ + \ fire. Enemy salvos are continuous, targeting critical convoy elements. Defensive\ + \ measures are active, but there's no time to recover or regroup. The\ + \ situation is dire, but the mission remains intact against overwhelming odds.\ + \ We're heads-down and pedal to the metal here! +statusUpdateEnemyOverwhelming16.text=%s, convoy's route in this sector was\ + \ nearly overrun during the enemy's final push. Enemy units pressed hard, breaking\ + \ through defensive lines with heavy firepower. Drivers struggled to hold\ + \ formation amid chaotic conditions, executing rapid evasive maneuvers to prevent\ + \ full encirclement. Enemy presence remains significant,\ + \ suggesting possible follow-up attacks. Crews are maintaining focus, with defensive\ + \ measures active. +statusUpdateEnemyOverwhelming17.text=%s, attempted to regroup in this sector,\ + \ but overwhelming enemy firepower forced a rapid retreat. Artillery shells rained\ + \ down, creating craters and debris that blocked potential escape routes. Crews\ + \ struggled to maintain formation as enemy 'Meks pressed hard, their cannons and\ + \ lasers tearing into our position. Defensive measures are active, with crews\ + \ ready to respond to further attacks. +statusUpdateEnemyOverwhelming18.text=%s, convoy hit hard at a narrow pass.\ + \ Enemy fire was intense, targeting key vehicles and breaking formation briefly.\ + \ Morale among crews is declining due to sustained pressure. Defensive measures\ + \ are holding. Movement is slow, with evasive tactics in use to prevent losses. +statusUpdateEnemyOverwhelming19.text=%s, pressing through rocky terrain, aiming\ + \ to avoid sudden skirmishes with enemy light 'Meks. Dust clouds obscure\ + \ visibility, making navigation hazardous. Crews are tense, expecting an ambush\ + \ at any moment. We're still moving though. +statusUpdateEnemyOverwhelming20.text=%s, moving swiftly to avoid encirclement by\ + \ skirmishers. Enemy units are actively attempting to cut off escape routes,\ + \ forcing rapid changes in direction. Vehicles are maneuvering through rough\ + \ terrain, engines straining under the pressure. The convoy keeps pushing\ + \ forward, but it's a race against time here. +statusUpdateEnemyOverwhelming21.text=%s, navigating now enemy-held territory. Fire\ + \ from all sides is constant, forcing evasive maneuvers. Despite heavy fire,\ + \ the convoy keeps moving, maintaining momentum. The situation remains critical. +statusUpdateEnemyOverwhelming22.text=%s, withdrawing rapidly while dodging\ + \ incoming enemy aircraft. The situation remains desperate, with aerial attacks\ + \ disrupting movement. Convoy integrity is maintained, but defensive actions\ + \ are at their limit. +statusUpdateEnemyOverwhelming23.text=%s, navigating through enemy-held territory\ + \ under continuous fire. Crews maintain focus, responding swiftly to sniper and\ + \ autocannon bursts. Progress is slower than anticipated, but all systems are\ + \ operational. The situation is tense, but we're still enroute. +statusUpdateEnemyOverwhelming24.text=%s, convoy remains operational despite heavy\ + \ skirmisher pressure. We're doing our best, but every moment counts. +statusUpdateEnemyOverwhelming25.text=%s, faced a devastating ambush at the river\ + \ crossing. Heavy fire hit the flanks hard, forcing rapid evasive action. Crews\ + \ maintained defensive formations, but the intensity of the assault caused\ + \ significant disruption. We're still enroute, but it's touch and go here. +statusUpdateEnemyOverwhelming26.text=%s, the convoy is intact but was nearly crippled by\ + \ relentless enemy fighter attacks. SRMs and machine gun fire rained down from\ + \ above, keeping crews pinned and unable to respond effectively. Armor is scarred,\ + \ and engines are overheating, but we're still on our way. +statusUpdateEnemyOverwhelming27.text=%s, pulling back rapidly to create distance\ + \ from advancing assault 'Meks. Enemy units are maintaining steady pressure.\ + \ The situation is critical, but convoy movement continues under full alert.\ + \ Crews are executing rapid maneuvers to evade direct confrontation. +statusUpdateEnemyOverwhelming28.text=%s, morale is low, impacted by sustained\ + \ enemy fire. We've still got everyone, but it's tough going.\ + \ Progress is slower than expected, but the convoy remains intact. +statusUpdateEnemyOverwhelming29.text=%s, maintaining formation under heavy fire is\ + \ proving difficult. Enemy units are probing the perimeter, forcing rapid\ + \ defensive shifts. +statusUpdateEnemyOverwhelming30.text=%s, situation is critical; defensive positions\ + \ must be re-established immediately in this sector. The enemy is everywhere,\ + \ and while we've remained unnoticed for now, that won't last.\ + \ I don't know what the situation is like at your end, but it's a real\ + \ rat's nest here! +statusUpdateEnemyOverwhelming31.text=%s, convoy's position in this sector was\ + \ nearly overrun during the latest push. Assault 'Meks broke through\ + \ defensive lines, forcing crews to make split-second decisions under intense\ + \ pressure. Vehicles maneuvered desperately, dodging autocannon bursts and PPC\ + \ volleys, and we were able to break free at the last moment. +statusUpdateEnemyOverwhelming32.text=%s, held position at the checkpoint initially,\ + \ but enemy LRMs launched while we were refueling. Forced withdrawal was the only\ + \ option. +statusUpdateEnemyOverwhelming33.text=%s, attempted to regroup in this sector, but\ + \ enemy fire proved too intense. Initial repositioning efforts were disrupted by\ + \ concentrated autocannon bursts, forcing another rapid retreat. Enemy units\ + \ maintain a strong foothold, leaving limited options for immediate re-engagement.\ + \ Convoy continues to retreat toward secondary routes. +statusUpdateEnemyOverwhelming34.text=%s, despite relentless pressure, the convoy\ + \ continues forward. Enemy fire is concentrated, targeting key vehicles in an\ + \ attempt to disrupt formation. Crews maintain defensive posture, responding\ + \ promptly to incoming attacks. Morale is under strain, but the mission remains\ + \ the top priority. No halts possible. +statusUpdateEnemyOverwhelming35.text=%s, retreating to a safer position under\ + \ sustained fire. Enemy pressure is intense, forcing rapid withdrawals while\ + \ maintaining tight formation. Crews are managing vehicle integrity despite the\ + \ barrage. We're currently regrouping, stabilizing our position before the next\ + \ move forward. +statusUpdateEnemyOverwhelming36.text=%s, the convoy is barely moving, pinned down\ + \ by relentless PPC fire from entrenched enemy positions. Crews are working frantically,\ + \ trying to keep engines running and maintain any semblance of formation. The odds\ + \ are heavily stacked against us, but we're going to make a break for it. +statusUpdateEnemyOverwhelming37.text=%s, retreating at full speed. Enemy 'Meks,\ + \ including heavies and fast scouts, are closing quickly. PPC blasts and missile trails\ + \ are coming at us. We're pushing the vehicles to their absolute limits,\ + \ fighting to maintain control over rough terrain. There's no time to regroup or\ + \ assess damages; it's a desperate race to create distance from relentless pursuit. +statusUpdateEnemyOverwhelming38.text=%s, convoy remains intact. Enemy fire from\ + \ elevated positions is increasing rapidly. Progress is slow but steady. +statusUpdateEnemyOverwhelming39.text=%s, taking detours to bypass enemy-controlled zones.\ + \ Enemy units are harassing us from multiple directions,\ + \ and we're struggling to keep moving. +statusUpdateEnemyOverwhelming40.text=%s, we're pulling back, no time to\ + \ regroup. Enemy pressure remains high, with advancing 'Meks closing in rapidly.\ + \ Convoy speed is critical, with evasive tactics prioritized to maintain distance.\ + \ Situation remains unstable, but the convoy is still together. +statusUpdateEnemyOverwhelming41.text=%s, enemy's relentless assault at the river\ + \ crossing nearly broke our formation. Heavy fire targeted the front and rear\ + \ elements, forcing rapid adjustments. +statusUpdateEnemyOverwhelming42.text=%s, drivers are pushing hard, doing their best\ + \ to maintain movement. LRMs and autocannon fire create constant hazards, making\ + \ every moment a struggle. We're doing what we can. +statusUpdateEnemyOverwhelming43.text=%s, the convoy was nearly overrun just moments ago.\ + \ Enemy forces launched a coordinated assault, closing in rapidly from multiple directions.\ + \ Defensive measures were stretched to their limits, but crews managed to repel\ + \ the initial wave. Convoy remains intact, pushing forward under heavy fire. +statusUpdateEnemyOverwhelming44.text=%s, the convoy narrowly escaped a massive\ + \ assault. Initial contact involved heavy SRM fire, forcing evasive maneuvers.\ + \ Enemy 'Meks, primarily heavies, attempted encirclement but failed due to a\ + \ rapid breakout. Enemy forces are regrouping, but convoy continues\ + \ to advance toward safer ground. +statusUpdateEnemyOverwhelming45.text=[Heavy static is audible, punctuated with\ + \ yells and the sound of battle] %s, we're on the move. Enemy VTOLs are on us,\ + \ we're going to try and shake them in the canyon. This is going to be close,\ + \ but I think we can make it. +statusUpdateEnemyOverwhelming46.text=%s, the last enemy assault in this sector was\ + \ devastating, inflicting heavy casualties among allied forces. Enemy units,\ + \ primarily heavy 'Meks and infantry support, launched a coordinated attack,\ + \ rapidly breaking through defensive positions. Allied losses are substantial,\ + \ with multiple vehicles and personnel incapacitated. Enemy fire remains\ + \ concentrated, limiting movement options for remaining allied units. The convoy\ + \ is maintaining distance, operating under full alert. +statusUpdateEnemyOverwhelming47.text=%s, enemy's assault at the depot has\ + \ inflicted heavy losses among allied forces. Enemy fire was precise,\ + \ targeting supply lines and command vehicles. Convoy units maintained a defensive\ + \ posture but could not prevent significant allied casualties. We're on the move,\ + \ and maintaining speed despite the loss of allied support. +statusUpdateEnemyOverwhelming48.text=%s, rerouting immediately to avoid incoming\ + \ airstrikes. Assault 'Meks advancing rapidly, forcing fast adjustments. Crews\ + \ are moving with urgency, trying to maintain formation while navigating rugged\ + \ terrain. Defensive measures are fully active, with sensors sweeping for\ + \ incoming threats. Speed is critical; the convoy is pushing forward. +statusUpdateEnemyOverwhelming49.text=%s, we faced a series of well-timed strikes at\ + \ the last checkpoint. Enemy assault 'Meks gained control of key routes.\ + \ The convoy was able to break through, but I think the checkpoint has been lost.\ + \ Current assessment indicates a high likelihood of additional attacks in this sector. + +statusUpdateIntercepted.boilerplate=%s, hostile interception underway. Incoming contacts unrelenting.\ + \ Reinforcements needed urgently. They're closing in fast.%s +statusUpdateIntercepted0.text=%s, rear assault underway. Hostile 'Meks penetrating our lines.\ + \ Transports are fully exposed; formation in disarray. Incoming contacts on sensors.\ + \ Reinforcements needed urgently. Attempting to fall back, but we're in serious trouble. They're closing in\ + \ fast - holding this position much longer isn't viable, we're going to try regrouping.%s +statusUpdateIntercepted1.text=%s, losing ground rapidly. Encircled by fast-moving 'Meks. Regroup\ + \ attempt failed; transports are under siege. Last call for reinforcements - our line is\ + \ collapsing, and command structure is breaking down.%s +statusUpdateIntercepted2.text=%s, convoy disintegrating under heavy fire. Hostile armor units\ + \ advancing at high speed. Reinforcements required immediately - total defeat imminent. Enemy\ + \ overwhelming all attempts to break free. We're going to make one final attempt to withdraw.%s +statusUpdateIntercepted3.text=%s, we're surrounded. Our perimeter has collapsed. Crew making a final\ + \ stand, barely holding. Supplies are exposed, no fallback route. Immediate reinforcements needed\ + \ to prevent total loss.%s +statusUpdateIntercepted4.text=%s, perimeter has been breached by enemy 'Meks. Convoy overrun, cargo\ + \ being seized. If reinforcements are inbound, they must arrive now. Regroup efforts faltering\ + \ under intense fire.%s +statusUpdateIntercepted5.text=%s, checkpoint lost. Final defenses shattered by artillery. Command\ + \ and control severely compromised. Immediate support required to hold the line, but position is\ + \ barely stable. We're going to try and withdraw.%s +statusUpdateIntercepted6.text=%s, convoy breaking apart. Heavy fire shredding our defenses. Supplies\ + \ falling into enemy hands. Defensive positions have been compromised.\ + \ Urgent need for reinforcements to make a last stand.%s +statusUpdateIntercepted7.text=%s, we're at breaking point. Hostiles control the main supply route,\ + \ regroup attempts have failed. Supplies dwindling, morale fading. Reinforcements must arrive now,\ + \ or total defeat is inevitable.%s +statusUpdateIntercepted8.text=%s, surrounded on all sides. Hostile forces closing in rapidly.\ + \ Immediate reinforcements essential - without them, complete collapse is certain. Situation\ + \ critical.%s +statusUpdateIntercepted9.text=%s, convoy devastated. Sustained bombardment by enemy 'Meks. Supplies\ + \ fully exposed - crew attempting to destroy remaining stock. Reinforcements required urgently;\ + \ convoy is coming apart.%s +statusUpdateIntercepted10.text=%s, convoy in disarray. Defensive lines breached in all sectors. Crew\ + \ falling back, supplies being seized. Reinforcements requested urgently.\ + \ Situation dire.%s +statusUpdateIntercepted11.text=%s, critical state. Hostiles advancing quickly. Supplies\ + \ compromised. Reinforcements essential - total defeat imminent without immediate support.%s +statusUpdateIntercepted12.text=%s, surrounded with no escape. Crew attempting fallback\ + \ but with no clear route available. Immediate support required to avoid total annihilation.%s +statusUpdateIntercepted13.text=%s, overwhelmed on all sides. Final line breached.\ + \ Immediate support required, but time is running out.%s +statusUpdateIntercepted14.text=%s, we're at the edge. Convoy in ruins, crew desperately holding\ + \ ground. Reinforcements needed urgently, or this is the end.%s +statusUpdateIntercepted15.text=%s, defenses completely breached. Convoy collapsing under enemy\ + \ pressure. Reinforcements urgently needed to have any chance of survival.%s +statusUpdateIntercepted16.text=%s, enemy closing in, convoy overrun. Regroup efforts have failed.\ + \ Reinforcements required urgently to prevent complete collapse.%s +statusUpdateIntercepted17.text=%s, enemy advancing uncontested. Supplies exposed and\ + \ unable to secure or destroy. Reinforcements required urgently - time is critical.%s +statusUpdateIntercepted18.text=%s, under relentless fire. Supplies compromised, crew taking heavy\ + \ losses. Reinforcements needed immediately - situation critical.%s +statusUpdateIntercepted19.text=%s, situation hopeless. Last line of defense lost. Convoy collapsing,\ + \ all units breaking down. Reinforcements needed urgently.%s + +interceptionInstructions.text=

    A special scenario has been generated. Failure to complete\ + \ this scenario will result in the loss of all units, personnel, and cargo. + + + + +statusUpdateAbandoned0.text=%s, we're being overwhelmed! Enemy 'Meks are breaching our lines, and\ + \ half the transports are lost! Supplies are about to be captured, and the crew\ + \ is down. No reinforcements in sight! They're breaking through the barricade -\ + \ damn it, they're inside the vehicle! We need immediate - [unintelligible shouting] - I\ + \ don't think we can hold any - [burst of static, then silence]. +statusUpdateAbandoned1.text=%s, the convoy is in disarray! Heavy autocannon fire has cut off our\ + \ last escape route! The crew is making a last stand, but morale is\ + \ gone, and supplies are nearly lost! I can't believe this is the end! They're\ + \ breaching - [explosion drowns out transmission] - if anyone's there, we need\ + \ help - [static]. +statusUpdateAbandoned2.text=%s, we're pinned down! Hostiles everywhere, and no chance to regroup!\ + \ Supplies are exposed, and ammo is gone! The crew is making a final stand, but\ + \ it won't last! Reinforcements were supposed to save us, but - [sudden gunfire\ + \ and shouting] - casualties are mounting! They're at the doors! This is -\ + \ [abrupt cut-off]. +statusUpdateAbandoned3.text=%s, we're under relentless assault! The enemy has broken every\ + \ defensive line, and the crew is scattered! Supplies are in enemy hands, and\ + \ there's no time left! Tried to rally for a last stand, but it's hopeless!\ + \ We're - [screams in the background] - I don't know how much longer we can -\ + \ [gunfire, then abrupt silence]. +statusUpdateAbandoned4.text=%s, there's no escape! The convoy is collapsing, and supplies are\ + \ lost! Fighting for every inch, but the enemy is inside the perimeter! How\ + \ could you leave us here?! Crew is using knives and makeshift weapons to hold the last\ + \ positions. If we don't get - [loud explosion] - I'm not sure anyone will make it out of -\ + \ [static, then silence]. +statusUpdateAbandoned5.text=%s, we're barely holding! The central line is breached, and we're\ + \ down to our last shots! Supplies are almost gone, and we can't hold them off!\ + \ Without reinforcements, it's a massacre! I don't think - [explosions and\ + \ gunfire] - we're not going to last much longer! They're closing in fast! We\ + \ need - [static, then silence]. +statusUpdateAbandoned6.text=%s, completely pinned down! The enemy is relentless, and supplies are\ + \ exposed! The crew is fighting desperately, but we're overwhelmed from all\ + \ sides! Reinforcements are nowhere, and we're - [loud gunfire and shouting] -\ + \ morale is broken, and we're losing men fast! They've broken through! We're -\ + \ [abrupt silence]. +statusUpdateAbandoned7.text=%s, chaos everywhere! Supplies exposed, and the crew is struggling to\ + \ hold ground! Facing overwhelming firepower with no backup! The enemy is\ + \ breaching the final defenses! Damn it, we need help! Crew is - [static,\ + \ followed by screams] - they're inside the vehicles! We can't - [cut-off\ + \ abruptly]. +statusUpdateAbandoned8.text=%s, this is it! Enemy forces have broken through everywhere, and the\ + \ convoy is collapsing! Crew is down to the last weapons, trying to protect\ + \ supplies, but it's hopeless! We're being torn apart, and - [explosions shake transmission] -\ + \ without help soon, we're - [gunfire interrupts]. +statusUpdateAbandoned9.text=%s, we're out of time! Enemy has breached our position, and we're\ + \ losing everything! Supplies are being seized, and there's no stopping it\ + \ without support! Morale is shattered, and everyone is just fighting to\ + \ survive! If you hear this, we're not going to last - [sudden gunfire and\ + \ shouting] - they've breached the main cabin! We're - [silence]. +statusUpdateAbandoned10.text=%s, surrounded and taking heavy fire! Most of the convoy is lost,\ + \ and crew is trying to destroy supplies before capture! Down to hand-to-hand combat!\ + \ Supplies are gone, and - [gunfire drowns out voice] - if anyone's listening,\ + \ we need - [static] - this is it! +statusUpdateAbandoned11.text=%s, we're beyond desperate! We're completely cut off, and enemy forces are\ + \ seizing the last of our supplies! We're out of ammo, and crew is using\ + \ anything they can find! Morale is gone, and we can't hold much longer! The\ + \ convoy is - [loud explosion] - reinforcements aren't coming, are they?! We're\ + \ finished here! They're breaching - [cut-off abruptly]. +statusUpdateAbandoned12.text=%s, breaking point reached! Supplies are gone, and enemy 'Meks are\ + \ pushing through! We tried to make a stand, but it's impossible without\ + \ support! The crew is scattered, and we're out of time! We're - [shouting and\ + \ gunfire] - they're taking everything! If you hear this, send help or -\ + \ [static, then silence]. +statusUpdateAbandoned13.text=%s, down to the last man! Enemy is everywhere, and supplies are\ + \ lost! Wounded everywhere, and no more ammo! Crew is fighting to the end, but\ + \ it's hopeless! We never had a chance without reinforcements! If you hear this,\ + \ know that we - [gunfire and screams] - this is the end! We're not going to -\ + \ [cut-off]. +statusUpdateAbandoned14.text=%s, convoy is in ruins! Supplies in enemy hands, and all vehicles are\ + \ lost! Crew is fighting to survive, but without reinforcements, it's over!\ + \ Trying to regroup, but the enemy is too strong! They've broken through -\ + \ [explosions and gunfire] - We're - [silence]. +statusUpdateAbandoned15.text=%s, enemy breached every line! Down to the last few men, and\ + \ supplies are being seized! No backup, no options left! Morale is broken, and\ + \ crew is fighting with desperation! If anyone is out there, we need -\ + \ [explosion] - they're closing in! We can't hold - [cut-off]. +statusUpdateAbandoned16.text=%s, fire from all directions! Supplies nearly captured, and crew is\ + \ falling back! No reinforcements, no retreat possible! Situation critical, and\ + \ we're - [gunfire drowns out voice] - there's no more time! We're -\ + \ [message ends]. +statusUpdateAbandoned17.text=%s, chaos everywhere! Enemy breached final defenses, and crew is\ + \ barely holding! Supplies lost, and we're out of ammo! Convoy is collapsing, and - [gunfire\ + \ and shouting] - they're taking everything! This is - [ends abruptly]. +statusUpdateAbandoned18.text=%s, this is the end! The enemy has full control, and convoy is falling\ + \ apart! Supplies exposed, and crews are surrendering! Morale is\ + \ gone, and we've got wounded everywhere! We're - [explosion] - if anyone can\ + \ hear this, we need - [ends]. +statusUpdateAbandoned19.text=%s, surrounded, and convoy is collapsing! Supplies gone, and crew\ + \ retreating under fire! No more options! Critical situation, and we've got -\ + \ [gunfire] - this is our last stand! We need - [static, then silence]. + + +# dudDialog +confirmDud.text=Understood, Return to Base + +# getCacheDescriptionDud +dudGeneric0.text=%s, we've scoured the valley below %s for hours now. The canyon walls are steep,\ + \ littered with fallen debris and old scorch marks - possibly from a %s skirmish long before our time.\ + \ Low visibility's a problem; dense fog keeps sensors from getting clean reads. We picked up faint\ + \ energy spikes three times, but each time it flickered out, almost as if it's designed to lure us\ + \ deeper. I've got a bad feeling about this place. The team's morale is dipping; some swear they\ + \ saw movement in the shadows, but I've found no tracks or heat signatures. Continuing the sweep,\ + \ but so far, no sign of the %s depot. +dudGeneric1.text=%s, we're in the eastern ridgeline now, along the edge of %s. Rain is coming down hard,\ + \ turning the ground into mud and gumming up our treads. Signal interference is off the charts -\ + \ comms are breaking up, and sensors are giving ghost returns, as if something from the %s\ + \ is jamming us. The depot's supposed coordinates have been checked twice, but all we've found are\ + \ old bunkers, stripped bare. I'm not sure if we're missing something or if the intel's bad,\ + \ but nothing here feels right. We'll push a little further before pulling back to base. +dudGeneric2.text=%s, we're at the foothills in %s, and this place is like a graveyard. Twisted metal and\ + \ wreckage from old %s 'Meks litter the ground, half-buried in snowdrifts. There's a chill in the\ + \ air that's not just the cold - the men are restless. Sensors keep pinging with minor seismic\ + \ tremors, but no sign of structures beneath the surface. I ordered a manual check, but all\ + \ we're digging up are shattered armor plates and frozen corpses. We're running low on supplies,\ + \ but I'll give it one more pass before retreating. The depot remains elusive. +dudGeneric3.text=%s, we've reached the marshlands near %s. The terrain's worse than expected;\ + \ it's a mire of sucking mud and stagnant pools, hard on the legs and the 'Meks' joints. Visibility\ + \ is near-zero - constant mist and roiling fog make thermal scans unreliable. We picked up what\ + \ might have been a burst transmission earlier, but it cut out before we could trace it back to a\ + \ possible source. The team is growing wary; strange sounds are coming from deeper in the swamp,\ + \ but no visual or sensor confirmation. It could be wildlife... or something else. No sign of the %s\ + \ cache yet. +dudGeneric4.text=%s, we've crossed the plateau and are closing in on the last set of coordinates in %s.\ + \ The high winds up here are tearing at our comms, and static is becoming a constant companion.\ + \ I've seen strange lights on the horizon - some kind of electrical discharge, maybe from old tech,\ + \ but the sensors can't make heads or tails of it. We've found more abandoned gear, some bearing\ + \ faint %s insignia, but no sign of anything functional. Morale is low, and the crew's starting to\ + \ think this depot might be nothing more than a rumor. Requesting permission to withdraw soon. +dudGeneric5.text=%s, we're moving through the deep woods in %s. Thick underbrush is slowing\ + \ our advance, and sensors are picking up strange heat anomalies, but they vanish just as quickly\ + \ as they appear. We found remnants of a resupply - old crates, half-buried in moss - but no\ + \ signs of anything more significant. The atmosphere here is heavy, and some of the crew report\ + \ hearing distorted voices over the comms, but no clear signals from a %s frequency. Still no sight\ + \ of the depot. +dudGeneric6.text=%s, we've reached the desert flats near %s. Sandstorms are fierce, creating\ + \ interference across all sensor bands. We've come across what looks like the ruins of a %s outpost,\ + \ walls partially buried in dunes, but no clear leads on the depot's location. The wind carries\ + \ a strange whistling sound, almost like a distant scream, but no signatures detected. The team\ + \ is growing weary; supplies are running thin, but we'll press on a bit longer. +dudGeneric7.text=%s, we've pushed into the rocky canyons near %s. The cliffs are steep, and\ + \ strange markings along the rock face hint at a possible local presence. However, every cave we've\ + \ searched so far has been empty, save for a few damaged scout cars with %s insignia. Scans suggest\ + \ faint energy trails deeper in, but they're erratic, almost like decoys. It's as if the depot\ + \ was never here - or has been moved elsewhere entirely. +dudGeneric8.text=%s, the swamp here in %s is a nightmare. Deep, murky waters make it nearly\ + \ impossible to maneuver, and sensors are plagued by thick bio-signatures. We found a rusted %s\ + \ DropShip hull partially submerged, but it's long dead. No signs of recent activity, no depot.\ + \ Strange lights dance across the surface of the water, but readings suggest they're natural\ + \ phenomena. The crew's resolve is wavering. +dudGeneric9.text=%s, we're in the abandoned urban sector at %s. These ruins bear %s \ + \ markings, likely from the last conflict here. Most buildings are gutted, with walls blackened by\ + \ fires. We found a few leftover data terminals, but they're too damaged to provide intel on the\ + \ depot. Sporadic sensor pings suggest faint power sources, but they're too weak to be the target.\ + \ We'll continue the sweep. +dudGeneric10.text=%s, we've arrived at the ice fields near %s. Blizzards are brutal, and the\ + \ cold is affecting our equipment. We found a partially collapsed %s bunker under the ice, but it's\ + \ been abandoned for years. A few sensor ghosts suggested movement beneath the ice sheets, but we\ + \ can't confirm if it's related to the depot. The search is feeling more futile with every passing\ + \ hour. +dudGeneric11.text=%s, we've entered the gorge in %s. It's dark and narrow, with jagged rocks\ + \ making navigation difficult. The echoes here play tricks on the sensors - everything feels off.\ + \ We located a rusted %s APC, but it's been stripped clean. Still no sign of the depot, just a lot\ + \ of eerie silence. Morale is dipping, but we'll keep moving. +dudGeneric12.text=%s, we're in the dense fog zone around %s. Visibility is nearly zero, and\ + \ our thermal scans are useless. There are remnants of a communication array here, but it's been\ + \ inactive for ages. We attempted a few broadcasts on %s frequencies, hoping for a response, but\ + \ nothing came back. The depot's location remains elusive. +dudGeneric13.text=%s, we've reached the river crossing at %s. The water is fast and deep, with\ + \ fallen %s equipment littering the banks. We tried scanning for possible underwater installations,\ + \ but the currents make it too risky for a deeper search. No depot detected. The crew is exhausted,\ + \ and the constant roar of the river is fraying nerves. +dudGeneric14.text=%s, we're combing through the craters at %s. The ground is pockmarked from past\ + \ bombardments, making traversal treacherous. We found a cache of old %s munitions, but they're\ + \ degraded beyond use. No power sources, no shelter, and certainly no depot. It's starting to feel\ + \ like a wild goose chase, but we'll give it one last push before calling it off. +dudGeneric15.text=%s, we're at the edge of the wasteland in %s. The ground here is blackened,\ + \ possibly from an earlier scorched-earth tactic. Radiation spikes occasionally flare up, throwing\ + \ off our sensors. We found scattered remains of %s recon vehicles, but the depot itself is nowhere\ + \ to be found. The deeper we go, the more desolate it becomes. +dudGeneric16.text=%s, we've entered a cavern network at %s. It's a twisting maze down here, full\ + \ of stale air and lingering darkness. We found a few %s insignias etched into the rock, but no signs\ + \ of recent activity. Sensors keep picking up false positives, likely echoes from the rocks. No depot,\ + \ no progress. +dudGeneric17.text=%s, we're deep in the ravine at %s. The ground is unstable, and minor landslides\ + \ have slowed us down. We found an old %s MekBay, buried under debris, but it's been picked clean.\ + \ No signs of life, no depot, and only the occasional creak of shifting rocks to accompany us. We're\ + \ preparing to exit the area. +dudGeneric18.text=%s, we're scanning the old mining site at %s. Rusted machinery and broken %s\ + \ loaders dot the landscape, but the depot remains elusive. The tunnels are partially collapsed,\ + \ making it too risky to venture deeper. We'll mark this location as a potential future salvage site,\ + \ but as for the %s depot, there's no trace of it here. +dudGeneric19.text=%s, we've reached the frozen ridge at %s. Snow drifts are high, and visibility\ + \ is minimal. We detected what appeared to be a %s signal burst, but it was gone before we could\ + \ triangulate its source. No depot, just more barren snowfields. The team is growing weary, and\ + \ supplies are dwindling. Requesting permission to retreat. +dudGeneric20.text=%s, we're backtracking through %s. The ground is scarred by ancient shell\ + \ craters, now filled with stagnant pools of water. It's strange - seeing the relics of battles\ + \ fought long ago, with no one left to remember what it was even for. The %s depot, if it ever existed,\ + \ is lost to time, much like the warriors who once bled for it. I fear we're searching for ghosts. +dudGeneric21.text=%s, we've reached the abandoned fortress at %s. %s emblems still adorn the\ + \ crumbling walls, faded and weathered by time. It feels more like a mausoleum than a depot now.\ + \ The corridors echo with emptiness, reminding us of those who must have once called this home. No\ + \ sign of life or supplies - only dust and the lingering sorrow of battles lost. +dudGeneric22.text=%s, we're in the overgrown ruins at %s. Nature has reclaimed the old outpost\ + \ here, vines wrapping around shattered gun emplacements and shattered buildings. We found rusted\ + \ ammo crates with %s insignia, untouched for decades. It's like searching through the remains of\ + \ a forgotten dream. I wonder if anyone even remembers why we're still looking. +dudGeneric23.text=%s, we've combed through the wasteland at %s. The ground is cracked and barren,\ + \ scorched by a %s retreat that left nothing but ash. It's as if the land itself is trying to forget\ + \ what happened here. The depot is nowhere to be found, and the crew can't shake the feeling that\ + \ our efforts mean little in the grand scheme of endless war. +dudGeneric24.text=%s, we've reached the mountain pass in %s. Snow covers the remnants of a %s\ + \ base, the white blanket making everything feel eerily peaceful, despite the chaos that must have\ + \ raged here once. No depot, only shattered bunkers and the hollow remnants of a cause that seems\ + \ distant now, even as we pursue it. +dudGeneric25.text=%s, we're in the canyon at %s. Faded %s banners still hang from some of the\ + \ rocky outcroppings, flapping gently in the wind. We found a few empty crates and a rusted comm\ + \ relay, both long since abandoned. It's clear that whatever purpose this depot once served has\ + \ been abandoned, much like the ideals that built it. +dudGeneric26.text=%s, we've reached the outskirts of an old %s encampment at %s. Everything here\ + \ feels forgotten, like a chapter left unfinished in a book no one reads anymore. There's no depot,\ + \ only the scattered remains of tents and supply crates. Sometimes I wonder if our search is driven\ + \ more by stubbornness than reason. +dudGeneric27.text=%s, we're deep in the forest at %s. The trees are thick, their branches heavy\ + \ with a quiet sadness, as if they've seen too much. We found a rusted %s Scout VTOL, fallen among\ + \ the roots. No sign of the depot, just the faint memory of battles that seemed important once,\ + \ but now feel pointless. +dudGeneric28.text=%s, we're navigating the swamps of %s. This place is a graveyard of old %s\ + \ vehicles, slowly being consumed by the mud. It's hard to say if this depot was ever real, or\ + \ just another mirage in a war full of them. The men seem tired, not just from the search, but from\ + \ the futility of finding meaning in any of this. +dudGeneric29.text=%s, we've arrived at the abandoned %s airfield in %s. The runways are cracked,\ + \ and the hangars are empty. It's a scene of a past defeat, frozen in time. We found nothing but\ + \ broken Meks and hollow shells. No depot, just the echoes of strategies that failed and lives that\ + \ were lost chasing glory. +dudGeneric30.text=%s, we've entered the trench network at %s. These %s trenches are deep, filled\ + \ with rusted weapons and faded uniforms, but no depot. The mud here is heavy, as if it's trying to\ + \ pull us down into the past with every step. It's hard not to feel like we're just retracing old\ + \ failures, destined to repeat them. +dudGeneric31.text=%s, we're among the ruins of a city in %s. The buildings are skeletal,\ + \ shadows of a once-thriving population now reduced to rubble. No %s depot, only the sadness of a city\ + \ that dreamed of prosperity but fell under the weight of war. We're finding nothing of value here,\ + \ only a lingering sense of loss. +dudGeneric32.text=%s, we've reached the fields of %s. It's a plain covered in overgrown grass\ + \ and rusting %s weapon emplacements. There's a haunting calm here, as if even nature has grown\ + \ tired of conflict. No sign of the depot, just remnants of another battle that history has all\ + \ but forgotten. +dudGeneric33.text=%s, we've arrived at the edge of a %s battlefield at %s. Scorched Meks and\ + \ shattered tanks are all that remain, their wreckage a testament to how quickly war can erase\ + \ everything. No depot in sight, just a grim reminder of how futile it all seems when the metal\ + \ stops moving. +dudGeneric34.text=%s, we've reached the lakeshore at %s. The water is still, reflecting the\ + \ gray sky above. We found a few %s ration crates near the bank, but they're long expired, much\ + \ like the hopes that once fueled this depot's creation. We're not finding anything of use, just\ + \ reminders of a war that seems determined to outlast us all. +dudGeneric35.text=%s, we're at the border of the %s encampment in %s. The camp is deserted, its\ + \ tents torn and its fires cold. I can't help but feel a deep sadness as we sift through the remnants\ + \ - once part of a grand strategy, now just meaningless fragments. No depot, only shadows of what was. +dudGeneric36.text=%s, we've reached the foothills of Sector 29-R. The landscape here is scarred by %s\ + \ artillery strikes, and the air is thick with melancholy. We found an old command post, but it's\ + \ stripped bare, like the hollow remains of hope itself. No sign of the depot, just the sense that\ + \ we're chasing something that's long since slipped away. +dudGeneric37.text=%s, we're moving through the plains at %s. We came across a %s outpost, little\ + \ more than ruins now. The wind carries a sad, empty sound, as if the land itself is mourning what\ + \ was lost here. No depot, just more evidence futility of this endless conflict. +dudGeneric38.text=%s, we're in the desert at %s. The ground is hard, and the air is heavy with\ + \ the weight of old battles. We found rusted %s Meks buried in the sand, their pilots long gone.\ + \ The depot is nowhere to be found - only silence and the feeling that every step forward is a step\ + \ deeper into the past. +dudGeneric39.text=%s, we've reached the ruins of a %s HQ at %s. The walls are covered in bullet\ + \ holes, and the floor is littered with spent casings. It's as if time itself has moved on, leaving\ + \ this place behind. No depot here, just the quiet futility of trying to hold on to something that\ + \ war has already claimed. +dudGeneric40.text=%s, we're back at %s, scanning for the fifth time. There's nothing but sand\ + \ and rocks - again. No sign of a %s depot, just the same dusty horizon we've been staring at for\ + \ hours. The crew's getting restless, and honestly, so am I. It's hard to keep pushing when it feels\ + \ like we're chasing our own shadows. +dudGeneric41.text=%s, we've entered the marshlands around %s. The mud is thick, the humidity\ + \ is unbearable, and we haven't found so much as a single %s crate. Sensors keep glitching, but\ + \ we're almost certain it's just the local wildlife messing with the readings. The men are\ + \ complaining about everything - from the mosquitoes to the mission itself. I can't blame them. +dudGeneric42.text=%s, we're trudging through the forest at %s. Every step feels the same -\ + \ trees, mud, more trees. We found an old %s sensor tower, but it's useless now, just like the\ + \ past eight hours of searching. The crew is barely responding to orders; it's like their minds\ + \ are already back at base. +dudGeneric43.text=%s, we're in %s, checking yet another empty canyon. No %s signals, no depot,\ + \ no point. I don't know what the intel team was thinking sending us here. This place is just\ + \ another dead end, and we're starting to wonder if the depot even exists. +dudGeneric44.text=%s, we're skirting the edge of an old battlefield at %s. Just more %s wreckage,\ + \ more silence, and more of the same stale air. No depot in sight, not even a hint of recent activity.\ + \ Morale is as low as I've ever seen it; half the team isn't even pretending to look anymore. +dudGeneric45.text=%s, we've reached the plains at %s. It's flat, empty, and utterly\ + \ unremarkable. We found some %s equipment scattered around, but it's just rusted junk -\ + \ nothing worth hauling back. I think we've reached the point where everyone's just going through\ + \ the motions. +dudGeneric46.text=%s, we're at the river in %s. It's too wide to cross, and the water looks as\ + \ brown as our chances of finding anything %s-related here. We've been staring at our maps for\ + \ hours, but every potential lead just seems to be another wild goose chase. No depot, just a\ + \ bunch of wet boots and wasted time. +dudGeneric47.text=%s, we're stuck in %s, waiting for the fog to clear. We've been sitting here\ + \ so long that the crew has started playing cards just to pass the time. No signals, no %s depot,\ + \ just the same old haze. I think some of the team are starting to wonder why we're even bothering. +dudGeneric48.text=%s, we're combing through the hills at %s. Nothing but rocks and more rocks.\ + \ We found a half-buried %s vehicle, but it's been picked clean - just like every other so-called\ + \ lead. The crew's too frustrated to even argue about it anymore; they just shrug and move on. +dudGeneric49.text=%s, we've entered the swamp at %s. It's all mud and stale water, and our\ + \ sensors are useless here. The %s depot was supposed to be nearby, but I'm starting to think\ + \ this whole mission is just someone's idea of a cruel joke. Morale is at rock bottom. +dudGeneric50.text=%s, we're trudging through %s again. This is our third sweep, and we've\ + \ found exactly nothing %s-related - unless you count a few rusted ammo casings. The men are\ + \ grumbling constantly, and frankly, I'm just as tired of this as they are. It feels like a waste\ + \ of time. +dudGeneric51.text=%s, we're back at %s. There's no sign of a %s depot, not even a false-positive\ + \ on the sensors. I think the crew are starting to doubt the existence of this depot entirely.\ + \ They're moving slower with each passing hour, as if the weight of futility is sinking in. +dudGeneric52.text=%s, we're in the ruins of an old %s outpost at %s. It's just more of the\ + \ same - empty shells, rusted parts, and nothing of value. The crew's too bored to even feign\ + \ interest at this point. I'm not sure how much longer we can keep up this charade. +dudGeneric53.text=%s, we're in the desert of %s. It's hot, dry, and utterly devoid of anything\ + \ useful. We've searched every likely spot for a %s depot, but it's just sand, sand, and more sand.\ + \ The team's growing tired of my orders, and I can't say I blame them. +dudGeneric54.text=%s, we're at the cliffs in %s. The terrain is rough, but what's rougher is\ + \ the lack of results. We've been looking for this %s depot for hours, and all we've got to show for\ + \ it is a few bruised egos. Morale is dragging, and so is this mission. +dudGeneric55.text=%s, we've reached %s. There's nothing here - just more empty plains and more\ + \ disappointment. The crew isn't even trying to hide their frustration anymore. This %s depot, if\ + \ it ever existed, must be laughing at us from the shadows. +dudGeneric56.text=%s, we're checking the caves at %s. These %s tunnels are as empty as our\ + \ hopes of finding anything useful. The crew's getting sick of crawling through the dirt for no\ + \ reason. At this point, I think even I'd settle for a solid lead on a canteen. +dudGeneric57.text=%s, we've reached the abandoned %s camp at %s. It's the same story as always\ + \ - empty crates, broken weapons, and nothing else. The men have stopped asking questions; they\ + \ just keep moving with a blank look in their eyes. I think this mission is breaking us slowly. +dudGeneric58.text=%s, we're back at the ridge in %s. The view's nice, but that's about it. No\ + \ depot, no %s signals, just another false lead in a series of false leads. I think I've seen this\ + \ ridge so many times, I could draw it from memory. The crew isn't even pretending to care anymore. +dudGeneric59.text=%s, we're in the marshes of %s. We've slogged through knee-deep mud for hours\ + \ with nothing to show for it. The %s depot is still just a rumor, and the crew's patience ran out\ + \ three grids ago. At this point, it feels like the mud is dragging us down more than the mission. +dudGeneric60.text=%s, we're back at %s, staring at the same rocks we saw earlier. I'm starting\ + \ to think these %s folks buried their depot in a parallel dimension. I'll check under one more\ + \ suspicious-looking pebble before we move on. Spirits are surprisingly high, though; I think the\ + \ crew's betting on who'll find the most useless piece of scrap today. +dudGeneric61.text=%s, we've hit the swamp at %s. The mud here is so thick, I think it's trying\ + \ to steal our boots. No %s depot yet, but we did find a very angry-looking toad. The crew named\ + \ it "General Sludge," and it's currently our most promising lead. Morale's good - though I think\ + \ they like the challenge of the swamp more than the mission. +dudGeneric62.text=%s, we're in the forest at %s. It's so dense that the trees must have grown to\ + \ hide something, right? So far, no luck on the %s depot, but we did find a tree with some suspicious\ + \ bark. The crew's decided to start a "Most Bizarre Discovery" contest, and the current leader is\ + \ a rock that vaguely resembles a 'Mek pilot flipping us off. +dudGeneric63.text=%s, we're in %s, still searching for that mythical %s depot. All we've found\ + \ is an ancient can of rations. The label reads "Best Before 2550," but Private Randall says\ + \ he's willing to give it a try if we get desperate. Spirits are weirdly high; it seems the absurdity\ + \ of it all is keeping everyone entertained. +dudGeneric64.text=%s, we're at %s, and it's official - this is the most boring stretch of land\ + \ in the Inner Sphere. No depot, no %s activity, just miles of nothing. The crew's taken to naming\ + \ the rocks we pass. We've got "Old Grumpy," "The Jagged Lady," and "Sir Pebbleton III" so far. At\ + \ least they're keeping busy. +dudGeneric65.text=%s, we've reached %s. We haven't found a %s depot, but we did find a very\ + \ determined squirrel trying to break into one of our ration packs. I think it might be our most\ + \ formidable opponent yet. The crew's morale is good - turns out, nothing bonds a team like trying\ + \ to outsmart wildlife. +dudGeneric66.text=%s, we're at the river in %s. Still no %s depot, but we did manage to have a\ + \ nice little "who can skim the most rocks" competition. The winner got to wear an improvised crown\ + \ made from twigs. If only %s tech was as easy to find as things to make fun of. +dudGeneric67.text=%s, we're stuck in %s again. The fog's so thick that I think I just tried to\ + \ have a conversation with a tree. We're laughing about it, though - it's not every day you mistake\ + \ a pine for your comms officer. Still no %s depot, unless it's disguised as a very stealthy squirrel. +dudGeneric68.text=%s, we're at the hills in %s. The only %s-related thing we've found is an\ + \ old helmet with a dent so big it could serve as a soup bowl. The crew's making jokes about how\ + \ the depot's probably cloaked with "super-stealth tech," a.k.a. "just plain missing." +dudGeneric69.text=%s, we've entered the swamp at %s. It's like nature's version of a bad\ + \ joke: every step is squishy, and every scan is a false alarm. No %s depot, but we did discover\ + \ that we have an impressive talent for swamp-related limericks. Spirits are oddly high,\ + \ probably because it's impossible to be sad while rhyming 'slog' with 'bog.' +dudGeneric70.text=%s, we're back at %s. I think I saw the same bush three times today - it's\ + \ either following us, or we're lost. No %s depot yet, but we think we've found "the meaning\ + \ of life" in the shape of a weirdly shaped tree root. Morale's still solid - probably because\ + \ we've all lost our minds a little bit. +dudGeneric71.text=%s, we're in the ruins at %s. Found some old %s propaganda posters that are\ + \ now being repurposed as a dartboard. No depot, but the crew's having a good laugh at the slogans\ + \ - turns out "Victory Through Perseverance" is ironically hilarious when you're lost in the middle\ + \ of nowhere. +dudGeneric72.text=%s, we're at %s in the desert. It's so hot here that we've taken to telling\ + \ "dry" jokes. You know the type: "Why don't %s depots like the desert? Because they're afraid of\ + \ being found." Spirits are surprisingly up, mostly thanks to our terrible sense of humor. +dudGeneric73.text=%s, we're at the cliffs in %s. It's rocky, dangerous, and completely devoid\ + \ of anything remotely %s-related. On the bright side, the crew discovered that yelling "Echo!"\ + \ off the cliffs is an excellent way to pass the time. No depot, but plenty of echoes to keep us\ + \ entertained. +dudGeneric74.text=%s, we've reached %s. This empty plain has become our least favorite spot,\ + \ but we did manage to build a pretty impressive sandcastle in the downtime. The crew's named it\ + \ "Fort %s," in honor of the depot we'll never find. Morale's surprisingly high - who knew building\ + \ sandcastles was so therapeutic? +dudGeneric75.text=%s, we're in the tunnels at %s. Found some %s graffiti that reads "Kilroy Was\ + \ Here." No depot, but it's nice to know we're not the first to get stuck in this mess. The crew's\ + \ taken to singing campfire songs, even without a fire. Gallows humor is the only thing keeping us\ + \ sane. +dudGeneric76.text=%s, we're at the %s camp ruins in %s. There's nothing here but busted tents\ + \ and empty cans, but the crew's decided to reenact a dramatic play about "The Depot That Never\ + \ Was." If you hear loud applause over the comms, don't be alarmed - it's just the sound of our\ + \ own despair. +dudGeneric77.text=%s, we're at the ridge in %s. I'm starting to think this whole %s depot\ + \ hunt is a cosmic joke, and we're the punchline. The crew's betting on who can find the most\ + \ absurd object out here. So far, the front-runner is a rock shaped like a duck. Spirits are still\ + \ high, probably due to the absurdity of it all. +dudGeneric78.text=%s, we're back in the marshes of %s. The mud here is trying to steal our sanity,\ + \ one boot at a time. Still no %s depot, but we did manage to build a raft from some fallen branches.\ + \ We're calling it the "WarShip Lost Cause." Morale remains shockingly good, considering the circumstances. +dudGeneric79.text=%s, we're in the ice fields at %s. We found nothing %s-related, but the crew\ + \ did discover that sliding down icy slopes is a surprisingly fun way to kill time. No depot, but\ + \ plenty of laughter. I suppose if we're going to be lost, we might as well enjoy the ride. +dudGeneric80.text=%s, we're deep in %s, surrounded by fog so thick it feels like it's alive.\ + \ The sensors keep flickering - false signals that seem to move closer, then vanish. No sign of the\ + \ %s depot, but I can't shake the feeling we're being watched. The crew is jumpy; even the slightest\ + \ sound is making us reach for weapons. +dudGeneric81.text=%s, we're in the marshlands of %s. Strange lights hover just beyond our\ + \ vision - flickering, almost playful, but gone the moment we try to focus. No %s depot, only a\ + \ growing sense of unease. It's not just the swamp; it's like the air itself is charged with\ + \ something hostile, something old. +dudGeneric82.text=%s, we've reached the forest in %s. The trees here are unnaturally silent\ + \ - no wind, no birds, not even the rustle of leaves. We found a rusted %s 'Mek half-buried in\ + \ the ground, its cockpit open and empty. The crew swears they heard whispers coming from inside,\ + \ but the scanners showed nothing. +dudGeneric83.text=%s, we're at %s, exploring what seems to be a forgotten %s bunker. The walls\ + \ are covered in strange, indecipherable marks, as if scratched by claws. We detected faint heat\ + \ signatures earlier, but they disappeared without a trace. The men are scared - some say they've\ + \ seen shadows that move when no one else does. +dudGeneric84.text=%s, we're back at %s. We found a clearing littered with %s remains - helmets,\ + \ scattered bones, and rusted weapons. The strange thing is, there's no record of a battle here. No\ + \ depot, but an unsettling feeling that we're standing in a place where something awful happened,\ + \ something not recorded in the history books. +dudGeneric85.text=%s, we're in the plains of %s. There's a strange hum in the air - low,\ + \ constant, and impossible to trace. It's unsettling, like the sound is coming from the ground\ + \ itself. No sign of a %s depot, but the crew's starting to act strangely. Some are whispering to\ + \ themselves, claiming they hear voices beneath the hum. +dudGeneric86.text=%s, we're at the river in %s. The water's surface is black and still, reflecting\ + \ only darkness. We saw strange ripples earlier, like something large moving beneath, but no signs\ + \ of life or %s tech. The crew is spooked - one of the men claims he saw eyes staring up from the\ + \ depths, but they were gone in an instant. +dudGeneric87.text=%s, we're stuck in %s, waiting for the fog to lift. But this isn't normal fog\ + \ - it's cold, dense, and seems to pulse as if breathing. No %s depot, just a chilling sense that\ + \ something here doesn't want us to leave. Even the comms are full of static, but every now and\ + \ then, there's a faint voice in the interference. +dudGeneric88.text=%s, we're in the hills of %s. We came across a %s campsite that looks recent,\ + \ but scans indicate it's been abandoned for decades. There's dried blood on the ground, but no bodies.\ + \ The men are nervous, saying it feels like we've stepped into a place trapped between times. +dudGeneric89.text=%s, we're in the swamp at %s. There's an eerie stillness here, like the world\ + \ is holding its breath. Strange shapes drift in and out of the mist, but disappear when we approach.\ + \ No %s depot, just an overwhelming sense that we're trespassing somewhere we shouldn't be. +dudGeneric90.text=%s, we're pushing through %s, and the fog here is unnaturally dense,\ + \ almost suffocating. Sensors are erratic, picking up strange energy spikes - like a signature, then\ + \ gone. No sign of the %s depot, but there's been talk among the crew about a shape in the mist.\ + \ They say it looked like a Marauder, pitch-black, watching from the edge of visibility before\ + \ vanishing into the haze. The men are spooked. Even the air feels colder, like a warning. We're\ + \ staying alert, but it feels like we're not alone in this cursed place. +dudGeneric91.text=%s, we're in the ruins of %s. The buildings are hollowed out, but there's\ + \ a persistent sound of distant footsteps echoing from the empty corridors. We've searched thoroughly\ + \ - no %s depot, but it feels like we're being stalked by something unseen, something that knows\ + \ this place better than we do. +dudGeneric92.text=%s, we're in the desert at %s. It's as silent as a tomb out here, save for\ + \ the occasional gust that sounds like a faint whisper. No %s depot, but we keep hearing strange\ + \ noises - like metal grinding, far off but distinct. The crew's on edge; even the bravest are\ + \ keeping their weapons drawn. +dudGeneric93.text=%s, we're at the cliffs in %s. We found a %s observation post built into\ + \ the rock, but it's abandoned and smells of decay. There's a weird marking on the wall - a symbol\ + \ we don't recognize, as if it was burned into the stone itself. No depot, just a sense that we're\ + \ being watched from the shadows. +dudGeneric94.text=%s, we've reached %s. The plain stretches endlessly, but we found a single %s\ + \ helmet lying in the grass. It's polished, unnaturally clean, almost as if it was left here recently.\ + \ The crew is spooked; some say it's bait, others think it's a warning. The air feels thick with\ + \ something unseen. +dudGeneric95.text=%s, we're in the tunnels at %s. They're dark and damp, but it's the sounds\ + \ that are the worst - scraping, scratching, and the occasional echo of something that doesn't\ + \ belong. We've found no signs of a %s depot, only unsettling noises and shadows that seem to move\ + \ on their own. +dudGeneric96.text=%s, we're at the ruins of the %s camp in %s. It's silent, too silent, like\ + \ everything here was drained of life. The crew claims to see figures at the edges of their vision\ + \ - always gone when they turn. No depot, just the overwhelming feeling that something dark lingers\ + \ here, unseen but present. +dudGeneric97.text=%s, we're back at the ridge in %s. We found a small %s structure, barely\ + \ standing, with a door that opened on its own. No one can explain it, and no one wants to go inside.\ + \ The crew's getting paranoid; some are talking about "the spirits of the lost," and I can't even\ + \ laugh it off. +dudGeneric98.text=%s, we're in the marshes at %s. It's dark now, and there are strange, distant\ + \ lights floating over the water. We've tried to approach, but they vanish before we can get close.\ + \ No %s depot, just a feeling of dread that's settled over the team. The silence is broken only by\ + \ occasional distant cries, almost human. +dudGeneric99.text=%s, we're in the ice fields at %s. It's bitterly cold, but the strangest part\ + \ is the whispers - faint, almost unintelligible, but unmistakable. They seem to come from nowhere\ + \ and everywhere at once. No %s depot, just a haunting reminder that some places weren't meant to\ + \ be found. + +dudStarLeague0.text=%s, we've reached what remains of an old SLDF supply depot at %s. The facility\ + \ is mostly rubble now, scorched during the Amaris Coup. Some of the walls still bear the blast marks\ + \ from Kerensky's siege. Sensors picked up faint power readings - remnants of long-dead systems\ + \ trying to hum back to life. No clear signs of functional tech or the rumored Star League cache,\ + \ just the bitter scent of decay. The air feels heavy, as if the ghosts of those last, desperate\ + \ defenders linger. The crew's nerves are fraying; some say they hear echoes of distant gunfire in\ + \ the wind. We're proceeding with caution. +dudStarLeague1.text=%s, we're at the ruins of a Terran Hegemony installation in %s, buried deep\ + \ in the forest. This base was one of the last to fall during the Amaris Coup. The bunker entrance\ + \ is blocked by debris, likely the result of self-destruct protocols triggered by the SLDF. The air\ + \ inside is stale and cold, and a few of the men claim to have seen faint blue lights flickering in\ + \ the corridors - emergency systems that shouldn't have power after two centuries. No Star League\ + \ artifacts uncovered yet, but something about this place feels alive... and hostile. +dudStarLeague2.text=%s, we've reached a massive, half-collapsed SLDF logistics hub in %s. The\ + \ place is eerie - massive hangars filled with rows of rusted unusable 'Mek parts, abandoned when\ + \ Kerensky ordered the Exodus. Old bloodstains still mark the walls, silent witnesses to the final\ + \ chaos of the Hegemony's collapse. The crew's on edge; sensors are giving off strange interference,\ + \ as if something in the wreckage is trying to communicate. No confirmed depot yet, just a lingering\ + \ feeling of betrayal, as if the past itself resents our intrusion. +dudStarLeague3.text=%s, we're deep inside a crumbling SLDF command center at %s. The central\ + \ hall is filled with the dusty remains of command consoles, likely used during Kerensky's final\ + \ campaigns against Amaris. The walls are covered with screens still showing deployments from the\ + \ last days of the Star League. Scanners picked up encrypted data bursts - ancient transmissions,\ + \ perhaps, looping endlessly since the coup. Some of the crew refuse to enter the deeper halls,\ + \ saying the air is too cold, too full of dread. No depot detected, but this place feels like a tomb. +dudStarLeague4.text=%s, we've reached what seems to be an abandoned SLDF weapons cache in %s.\ + \ The structure is intact, but something feels off. There's a persistent, low hum that's hard to\ + \ pinpoint, as if the building itself is resonating with some hidden energy. The crew's uneasy -\ + \ one of the men swears he saw a figure in an old SLDF uniform, though scans show no life signs.\ + \ This site fell during one of Kerensky's final assaults; it's said the defenders knew they were\ + \ doomed, but fought to the last anyway. No Star League tech uncovered yet, only the eerie sense\ + \ that something from those days never left. +dudStarLeague5.text=%s, we're inside the remains of an old SLDF research outpost at %s. The\ + \ labs are silent, littered with debris and the scent of decay. The facility fell during the Amaris\ + \ Coup, and the walls seem to resonate with the last desperate moments of the defenders. No depot\ + \ found, only the unsettling stillness that makes even the bravest among us hesitate. It's as if\ + \ the ghosts of Star League's failures linger here, watching. +dudStarLeague6.text=%s, we've reached what was once a Terran Hegemony garrison in %s. The\ + \ structures are in ruins, collapsed during Kerensky's final retribution. The crew senses something\ + \ here - a presence, perhaps, or just the oppressive weight of defeat. No sign of a Star League depot,\ + \ only a gnawing feeling of dread. Some of the men are muttering that LosTech sites like these are\ + \ cursed, best left undisturbed. +dudStarLeague7.text=%s, we're standing outside a sealed bunker at %s. The door is massive,\ + \ reinforced, and covered in scorched marks - likely from Amaris's forces trying to breach it.\ + \ It's silent here, deathly so, and the air feels unnaturally cold. We haven't found anything of\ + \ value yet, just a sense that we're trespassing on a battlefield lost long ago. The men say this\ + \ place is cursed, that the tech within is better left untouched. +dudStarLeague8.text=%s, we're in the wreckage of an old SLDF airfield at %s. The winds howl\ + \ through empty hangars, carrying a faint, distant sound that's hard to identify. The site has been\ + \ abandoned since the Exodus, and it feels like the memories of that era still haunt the place. No\ + \ sign of a depot, just whispers in the wind - if one believes in such things. The crew's spirits\ + \ are sinking; they say the place feels cursed, as if the very ground rejects us. +dudStarLeague9.text=%s, we're exploring the charred remains of an SLDF staging ground at %s. The\ + \ buildings are gutted, the walls blackened by fire and riddled with bullet holes. We haven't\ + \ uncovered any signs of a depot, only the remnants of a brutal last stand. Some of the crew believe\ + \ that sites like this one are haunted, cursed by Amaris' hate and by those who fought for a cause\ + \ that ultimately failed. +dudStarLeague10.text=%s, we've reached a ruined SLDF bunker in %s. The entrance is half-collapsed,\ + \ its steel door twisted beyond recognition. The interior is dark, and our lights barely penetrate\ + \ the dense shadows within. We haven't found anything yet, but the feeling of being watched is\ + \ stronger than ever. The men are convinced that the depot, if it exists, is cursed - like all lost\ + \ Star League secrets. +dudStarLeague11.text=%s, we're in a decrepit SLDF command center at %s. The silence here is thick,\ + \ broken only by the occasional drip of water from the ceiling. No depot in sight, just rows of\ + \ dead consoles that once controlled mighty armies. The crew's uneasy - some say this place feels\ + \ alive, like it's waiting for something. The darkness here feels tangible, and the whispers of a\ + \ cursed past are hard to ignore. +dudStarLeague12.text=%s, we've reached a derelict SLDF outpost in %s. The base is eerily quiet,\ + \ save for the creaking of old metal in the wind. We haven't seen any signs of a depot, only the\ + \ oppressive remnants of a battle long lost. The crew is growing paranoid, claiming to hear footsteps\ + \ behind them when no one's there. They say the tech here is cursed, echoing the despair of those\ + \ who never made it out. +dudStarLeague13.text=%s, we're at what appears to be a forgotten SLDF logistics hub at %s. The\ + \ facility is massive, but most of it is inaccessible, sealed off behind collapsed walls and twisted\ + \ beams. No depot yet, only dark hallways that seem to stretch endlessly. The men are on edge, as\ + \ if the very shadows are moving around them. Some whisper that sites like this are haunted by the\ + \ cursed remnants of the past, a warning to those who seek what's been lost. +dudStarLeague14.text=%s, we're inside the crumbling remains of an SLDF stronghold at %s. The\ + \ air here is cold, with a strange metallic tang that clings to the back of the throat. We've\ + \ searched for hours, but no depot has been found. The crew is jumpy, saying that places like this\ + \ are cursed - trapped in time, haunted by the failure of a once-great Star League. The walls\ + \ themselves seem to hum with an eerie energy. +dudStarLeague15.text=%s, we're exploring a massive SLDF command center in %s. The facility is\ + \ vast and filled with empty command consoles that haven't seen use since Kerensky's Exodus. No\ + \ depot has been found, only a suffocating sense of dread. The crew swears they hear faint whispers\ + \ coming from the dark corners of the room, like distant voices that shouldn't exist. It feels like\ + \ the cursed legacy of the Star League is all that remains here. +dudStarLeague16.text=%s, we've reached the ruins of an SLDF fortress at %s. The exterior is\ + \ pitted with craters from heavy bombardment, likely from Amaris's forces. We've found no depot,\ + \ but the place feels heavy with a sense of old anger and betrayal. The men say the darkness here\ + \ is unnatural, that cursed tech lies somewhere deeper within, waiting to ensnare anyone foolish\ + \ enough to seek it. +dudStarLeague17.text=%s, we're inside an abandoned SLDF listening post at %s. It's been dead\ + \ silent, save for occasional pings on our sensors that vanish without explanation. No depot has\ + \ been located yet, but some of the crew claim they've seen shadows moving in the reflection of\ + \ the old radar screens. They say this place is cursed - haunted by the Star League's failures. +dudStarLeague18.text=%s, we're in the depths of a ruined SLDF research lab at %s. The air is\ + \ stale and tinged with a strange, acrid smell that makes breathing difficult. We've found nothing\ + \ resembling a depot, just endless halls filled with eerie quiet. Some of the crew have begun\ + \ muttering about the cursed legacy of LosTech, saying it's tainted by the ghosts of those who\ + \ tried to wield it. +dudStarLeague19.text=%s, we've reached the edge of an SLDF encampment in %s. The camp is empty,\ + \ save for the rusted remains of tents and barricades. No depot in sight, only a pervasive sense of\ + \ doom that seems to cling to everything here. The men are convinced the site is cursed, as if the\ + \ ghosts of the Star League's final defenders are still here, trying to keep us from uncovering their\ + \ secrets. +dudStarLeague20.text=%s, we're inside the ruins of an SLDF testing facility at %s. The labs are\ + \ silent, filled with broken equipment and shattered glass. We haven't found any signs of a depot,\ + \ only a chilling emptiness. The crew is growing uneasy, saying that cursed LosTech haunts this\ + \ place, keeping it lost for a reason. It feels as if the shadows themselves want to swallow us whole. +dudStarLeague21.text=%s, we're at what remains of an SLDF command post in %s. The place is a shell\ + \ of its former glory - crumbling walls covered in faded insignias. No depot in sight, just a sense\ + \ of abandonment. It's hard not to feel the weight of what's been lost here; it's as if the dreams\ + \ of the Star League withered alongside the men who died defending it. We keep moving, but the\ + \ futility of it all is beginning to sink in. +dudStarLeague22.text=%s, we're at the ruins of a once-thriving SLDF logistics hub in %s. Empty\ + \ storage bays stretch for miles, now home only to dust and silence. No depot, only the echo of a\ + \ once-unified Inner Sphere that fell apart under its own ambitions. The crew moves slowly, as if\ + \ the sadness of the place has seeped into their bones. We press on, but it feels like chasing a\ + \ lost cause. +dudStarLeague23.text=%s, we're trudging through an old SLDF garrison at %s. The barracks are\ + \ empty, the bunks still neatly made, as if waiting for soldiers who will never return. No depot\ + \ found, only a profound stillness that feels like grief itself. The men seem quieter here, as\ + \ if even speaking aloud would disturb the lingering sorrow of an era that died here long before us. +dudStarLeague24.text=%s, we've reached a ruined SLDF hospital in %s. The halls are lined with\ + \ empty beds and broken medical equipment, remnants of a time when the Star League tried to heal\ + \ rather than destroy. No depot, just the feeling that those who were abandoned here never truly\ + \ left. The sense of futility is overwhelming - war promises glory, but it seems to leave only ghosts. +dudStarLeague25.text=%s, we're at an SLDF communications center in %s. The consoles are dead,\ + \ covered in layers of dust. We haven't found the depot, just rows of silent screens that once\ + \ coordinated fleets and armies across the stars. Now, they're just relics of ambition gone cold,\ + \ a reminder that even the greatest empires can crumble into irrelevance. +dudStarLeague26.text=%s, we're inside a derelict SLDF armory at %s. The halls are filled with\ + \ empty racks where weapons once stood ready to defend the ideals of the Star League. No depot in\ + \ sight, only a feeling of profound loss. The crew seems weighed down by the futility of finding\ + \ anything of value here - it's like searching through the ashes of hope itself. +dudStarLeague27.text=%s, we're in a deserted SLDF command center at %s. The battle maps still\ + \ cover the walls, detailing campaigns that are now just faded memories. No depot has been found,\ + \ only the haunting realization that the grand strategies of the Star League ultimately meant\ + \ nothing. The crew's morale is sinking; it's hard to stay hopeful when every step seems to echo\ + \ with failure. +dudStarLeague28.text=%s, we're among the ruins of an SLDF outpost in %s. The outpost was likely\ + \ abandoned during Kerensky's final campaigns, its defenders leaving behind only silent halls and\ + \ unanswered questions. No depot found, only a sense of regret that lingers in the air. It's as if\ + \ the ghosts of those who once stood here are still waiting for orders that will never come. +dudStarLeague29.text=%s, we're at the remnants of a once-mighty SLDF fortress in %s. The walls\ + \ are cracked, and the turrets are silent. No depot, just the feeling of a grand dream that was\ + \ shattered by ambition and betrayal. The crew's growing weary; they know that searching here is\ + \ like sifting through the ruins of lost ideals. The hope of finding something meaningful is fading\ + \ fast. +dudStarLeague30.text=%s, we're in the ruins of an SLDF headquarters at %s. The command table\ + \ stands empty, as if waiting for leaders long dead. No depot found, just a sense of lost authority.\ + \ The crew is silent, perhaps out of respect for the failed ambitions that echo through these walls.\ + \ It's hard not to wonder if we're just repeating history - fighting for something that doesn't\ + \ even matter anymore. +dudStarLeague31.text=%s, we're inside an abandoned SLDF research facility at %s. The labs are\ + \ empty, their experiments long forgotten. No depot has been uncovered, only the faded symbols of\ + \ a Star League that once promised unity. The crew's mood is somber; this place feels like a\ + \ graveyard of forgotten dreams, a testament to the futility of trying to hold onto power through\ + \ war. +dudStarLeague32.text=%s, we're at an SLDF barracks in %s. The beds are unmade, as if the\ + \ soldiers left in a hurry, never to return. No depot has been found, only the oppressive silence\ + \ of lives cut short by battles that couldn't be won. The crew is tired; each room we enter feels\ + \ like stepping into another sad story that has no ending. +dudStarLeague33.text=%s, we're exploring a hollow SLDF outpost at %s. The corridors are dark,\ + \ and the only sound is the wind howling through broken windows. No depot located, only a lingering\ + \ sense of despair from those who once thought they were fighting for a better future. The crew's\ + \ morale is fading - it's hard to believe in glory when all you find are ruins. +dudStarLeague34.text=%s, we're at the crumbling remains of an SLDF citadel in %s. The\ + \ structure was once a symbol of Star League power, now reduced to rubble by time and war. No\ + \ depot here, just the melancholy of lost grandeur. The crew is quiet, as if the realization has\ + \ set in that we're searching for something that died long ago. +dudStarLeague35.text=%s, we're inside an abandoned SLDF hangar at %s. The bay doors are rusted\ + \ shut, and the air smells stale. No depot has been found, only the empty spaces where war machines\ + \ once stood ready. The crew seems haunted by the thought that everything we're searching for might\ + \ just be echoes of an era that was doomed from the start. +dudStarLeague36.text=%s, we've reached the remains of an SLDF command hub in %s. The control\ + \ room is filled with shattered monitors and scattered paperwork - records of battles that ended\ + \ in failure. No depot in sight, only the realization that even the most organized plans can end\ + \ in chaos. The men's spirits are low; this place feels like a monument to the futility of trying\ + \ to control the uncontrollable. +dudStarLeague37.text=%s, we're inside an old SLDF depot at %s, but it's long abandoned. The\ + \ crates are empty, and the walls bear the marks of hasty retreat. No depot found, only the\ + \ remnants of what might have been a grand storehouse of power. The crew's mood is bleak; the\ + \ feeling here is one of missed opportunities, of efforts that ultimately amounted to nothing. +dudStarLeague38.text=%s, we're in the hollow halls of an SLDF citadel at %s. The banners of\ + \ the Star League still hang, tattered and faded. No depot has been found, only the sad remnants\ + \ of a once-united Inner Sphere. The crew's morale is dwindling; the more we search, the more it\ + \ feels like we're walking through a graveyard of ideals that were doomed from the start. +dudStarLeague39.text=%s, we're at the edge of an old SLDF supply base in %s. The base is silent,\ + \ its storage facilities empty and forgotten. No depot in sight, only the empty shells of what was\ + \ meant to sustain a glorious future. The crew is despondent; it's clear that what we're chasing\ + \ might never have existed in the first place - just another sad chapter in a history of broken\ + \ promises. +dudStarLeague40.text=%s, we're inside the remains of an old SLDF command bunker at %s. It's pitch\ + \ dark, and our lights don't seem to penetrate the shadows well. There's a strange, rhythmic sound\ + \ - almost like a heartbeat - coming from somewhere deep below. No sign of the depot, but the air\ + \ feels heavy, oppressive. Some of the crew say they can hear faint whispers, but there's no one\ + \ else here. +dudStarLeague41.text=%s, we've reached %s, where the ruins of an SLDF staging post lie in eerie\ + \ silence. The walls are covered in strange markings - perhaps scrawled by soldiers during the final\ + \ days of the Amaris Coup. The symbols make no sense, but they seem... angry. No depot found, just\ + \ a pervasive sense of dread, like the shadows are watching us. +dudStarLeague42.text=%s, we're at an abandoned SLDF fort in %s. The halls echo with distant\ + \ clanging, even though we've confirmed there's no one else here. Some of the crew are getting\ + \ unnerved, saying the echoes sound like old battle cries. We haven't found the depot, just a cold,\ + \ unsettling feeling that something is trying to drive us away. +dudStarLeague43.text=%s, we've entered a collapsed SLDF research base at %s. The air is stale,\ + \ and the walls seem to sweat moisture, despite the dry surroundings outside. We've found no depot,\ + \ but several crew members reported seeing movement at the edge of their vision. It feels like\ + \ we're being watched by unseen eyes in the darkness. +dudStarLeague44.text=%s, we're in the ruins of an SLDF logistics center in %s. The facility is\ + \ massive, but dead quiet - too quiet. The only sound is a faint, repetitive ticking, like a clock\ + \ that stopped ages ago but refuses to die completely. No depot in sight, only a growing sense of\ + \ fear among the crew. Some say it's the spirits of those who never made it out. +dudStarLeague45.text=%s, we're inside an SLDF communications hub at %s. The consoles are dark,\ + \ but the static crackles sporadically, almost like whispers trying to form words. No depot found,\ + \ but the crew's nerves are fraying. The longer we stay, the colder it gets, as if the very walls\ + \ are trying to expel us. +dudStarLeague46.text=%s, we've reached %s, at the remnants of an SLDF barracks. The bunks are\ + \ still made, as if expecting soldiers to return, but there's no sign of life. Some of the crew\ + \ claim to hear footsteps in the halls, heavy and deliberate, but there's no one there. No depot,\ + \ only an overwhelming sense of being unwelcome. +dudStarLeague47.text=%s, we're at an SLDF forward base in %s. The air is thick with a strange\ + \ metallic scent, and the walls are stained with a dark, rusty substance. We haven't found the\ + \ depot, just a disturbing feeling that something terrible happened here. The crew's jumpy, and a\ + \ few refuse to go deeper into the base. +dudStarLeague48.text=%s, we're exploring an SLDF airfield at %s. The hangars are empty, but\ + \ we've been hearing faint, distant cries - like the sound of pilots calling for evac that never\ + \ came. The crew's morale is sinking fast; some of the men believe the airfield is haunted by those\ + \ who died without hope. No depot found, just unsettling echoes that refuse to die. +dudStarLeague49.text=%s, we're in the ruins of an SLDF intelligence center at %s. It's dark\ + \ and cold, colder than it should be. The air seems to carry distant whispers, and the crew reports\ + \ feeling like they're being watched. We haven't found the depot, only an oppressive atmosphere\ + \ that makes every step feel like a mistake. +dudStarLeague50.text=%s, we're at %s, in what was once an SLDF hospital. The walls are smeared\ + \ with faded, bloody handprints, as if patients tried to claw their way out during the chaos of\ + \ the Amaris Coup. No depot here, only the horrifying sense that the souls of the desperate still\ + \ linger, trapped in a place that offered no salvation. +dudStarLeague51.text=%s, we're inside an old SLDF bunker in %s. The main hallway stretches\ + \ into darkness, with old warning signs flickering faintly in red. No depot found, but some of the\ + \ crew say they feel a strange pull deeper into the bunker, as if something is calling to them.\ + \ It's unsettling - like a trap set by the shadows of the past. +dudStarLeague52.text=%s, we're in the remains of an SLDF command outpost at %s. The base is empty,\ + \ save for a faint, eerie hum that seems to vibrate through the floor. We haven't found any trace\ + \ of the depot, but some of the men are complaining of headaches and hearing faint cries for help\ + \ that echo from nowhere. +dudStarLeague53.text=%s, we're inside a collapsed SLDF depot at %s. The walls are covered in\ + \ strange, unintelligible symbols, scratched into the metal with something sharp. The air feels\ + \ thick, like we're breathing the despair of those who never left. No depot found, just a growing\ + \ sense that we're digging up something that was meant to stay buried. +dudStarLeague54.text=%s, we're at an SLDF staging ground in %s. The campfires are long cold, but\ + \ it feels as if someone was here just moments ago. No depot found, only strange shadows that seem\ + \ to shift and move on their own. The crew's morale is shot; they say the place feels cursed, like\ + \ it's trying to drive us mad. +dudStarLeague55.text=%s, we're exploring an SLDF forward command post in %s. The walls are\ + \ adorned with faded battle maps, but the air is filled with a strange, sulfuric smell. No depot\ + \ in sight, but the feeling of dread is palpable. Some of the crew have started praying, saying\ + \ they can feel the despair of the fallen soldiers here. +dudStarLeague56.text=%s, we're in the ruins of an SLDF research lab at %s. The facility is eerily\ + \ intact, almost as if it's been waiting for us. No depot has been located, but the crew reports\ + \ feeling watched, and there's a sense of something ancient and hostile in the air. It's hard to\ + \ shake the feeling that we're intruding where we shouldn't. +dudStarLeague57.text=%s, we're at an SLDF listening post in %s. The base is silent, but the\ + \ sensors occasionally pick up garbled transmissions - like echoes from the past that refuse to\ + \ die. We haven't found the depot, only a sense that this place is haunted by voices that were\ + \ never heard. The men are rattled; they say the whispers are warnings. +dudStarLeague58.text=%s, we're at the edge of an SLDF stronghold in %s. The walls are covered in\ + \ old blast marks, and the floors are littered with empty shell casings. No depot has been found,\ + \ but some of the men claim to hear voices in the darkness, urging us to leave. The darkness here\ + \ feels tangible, as if it's closing in on us. +dudStarLeague59.text=%s, we're in the depths of an SLDF citadel at %s. The corridors are filled\ + \ with an unnatural mist that shouldn't be here. No depot found, but the temperature keeps dropping,\ + \ and some of the crew swear they hear footsteps behind them. It feels like we're being hunted by\ + \ something old, something that wants to keep its secrets hidden. +dudStarLeague60.text=%s, we're inside an SLDF listening post at %s. The facility is dark, but\ + \ our sensors keep picking up faint signals - brief, encrypted bursts that stop when we try to\ + \ trace them. It's as if someone's watching and adjusting to our movements. No depot located, but\ + \ the crew is on edge. We're not alone out here. +dudStarLeague61.text=%s, we're at an abandoned SLDF staging area in %s. The ground is covered\ + \ in scattered debris, but there are fresh tracks in the dust - too fresh for a place supposedly\ + \ untouched for centuries. No sign of the depot, but it's clear someone has been here recently.\ + \ The crew's getting paranoid; it feels like we're being followed. +dudStarLeague62.text=%s, we're exploring an SLDF command center in %s. The terminals are dead,\ + \ but our systems keep detecting incoming pings - like someone's trying to hack our comms. We\ + \ haven't found the depot, but its clear someone is trying to disrupt our search. The crew is\ + \ uneasy; it feels like we're being watched through the very walls. +dudStarLeague63.text=%s, we're inside the ruins of an SLDF logistics hub at %s. The air is still,\ + \ but our sensors detected movement in the upper levels - small, fleeting heat signatures that vanish\ + \ as soon as we focus on them. No depot in sight, just the unsettling feeling that someone - or\ + \ something - is watching us from the shadows. +dudStarLeague64.text=%s, we're in an old SLDF supply depot at %s. The base appears deserted, but\ + \ there's a strange static in our comms that only started when we entered. It's almost like someone's\ + \ trying to jam our signals, just enough to cause confusion. No depot found, but the crew is\ + \ whispering among themselves, convinced we're under surveillance. +dudStarLeague65.text=%s, we're at an SLDF research lab in %s. The facility is dark, but our\ + \ scanners have picked up faint traces of motion - too quick to lock onto. We haven't found the\ + \ depot, but the feeling of being watched is impossible to ignore. It's as if someone is observing\ + \ our every move, waiting for us to make a mistake. +dudStarLeague66.text=%s, we're at the edge of an SLDF airfield in %s. The place is dead quiet,\ + \ but our sensors keep detecting small, flickering signatures at the perimeter. It feels like we're\ + \ being circled, but whoever it is never comes close enough for a clear scan. No depot here, only\ + \ a deepening sense of unease. +dudStarLeague67.text=%s, we're inside an abandoned SLDF barracks at %s. The crew reports seeing\ + \ glints of light at a distance, almost like the reflection from binoculars. No depot in sight, but\ + \ it's clear someone is keeping tabs on us. The air feels charged, and even the shadows seem to\ + \ shift when we're not looking. +dudStarLeague68.text=%s, we're in an SLDF bunker at %s. Our sensors picked up a weak transmission\ + \ on an encrypted frequency - one that stopped the moment we tried to respond. It's as if someone's\ + \ testing us, probing our presence. No depot found, but the feeling of being watched grows stronger\ + \ with each passing moment. +dudStarLeague69.text=%s, we're inside an old SLDF communications hub at %s. The consoles are\ + \ offline, but our systems detected a sudden spike in electronic interference - like someone nearby\ + \ is actively scanning us. No sign of the depot, only the sense that we're not alone. The crew's\ + \ getting anxious; it's like we're being studied. +dudStarLeague70.text=%s, we're at an SLDF forward base in %s. We've seen strange lights flickering\ + \ in the distance, but they vanish whenever we try to get a closer look. It's not just the darkness;\ + \ it feels like someone is intentionally leading us away. No depot here, just a growing sense that\ + \ someone is manipulating us. +dudStarLeague71.text=%s, we're in the remains of an SLDF intelligence post at %s. Our equipment\ + \ keeps malfunctioning - static on the comms, sudden power fluctuations. No depot found, but it's\ + \ clear someone is trying to interfere with our search. The crew's starting to get jittery, convinced\ + \ we're being hunted by an unseen observer. +dudStarLeague72.text=%s, we're inside an SLDF fort at %s. The place is silent, but our scanners\ + \ detected what might be surveillance scans - brief pings that vanish when we try to triangulate\ + \ them. No depot located, but the feeling of being monitored is becoming unbearable. The crew is\ + \ whispering that we've walked into a trap. +dudStarLeague73.text=%s, we're at an old SLDF depot in %s. The facility is mostly rubble, No\ + \ depot in sight, only the unsettling suspicion that someone is documenting our every move. The\ + \ crew's growing paranoid, convinced we're part of someone else's plan. +dudStarLeague74.text=%s, we're in an SLDF command post at %s. The sensors are acting up - picking\ + \ up multiple small heat signatures, but they fade away as soon as we get closer. No depot found,\ + \ just the unsettling feeling that someone is tracking us, adjusting their position to stay just\ + \ out of sight. +dudStarLeague75.text=%s, we're at an SLDF stronghold in %s. The corridors are filled with\ + \ shadows that seem to stretch and twist as we pass. We've seen flashes of movement, but nothing\ + \ shows up on our sensors. No depot here, only a sense of unseen eyes following us, observing our\ + \ every step. +dudStarLeague76.text=%s, we're exploring the ruins of an SLDF depot at %s. The crew reports\ + \ seeing faint lights in the distance, but they're too erratic to be natural. No depot found, but\ + \ it feels like someone's setting up a perimeter around us, watching, waiting. The crew's getting\ + \ restless, convinced that whoever it is wants us to stop searching - for reasons unknown. +dudStarLeague77.text=%s, we're inside an SLDF logistics hub at %s. We've seen strange flashes,\ + \ like camera flashes, in the darkness. No depot located, only a creeping suspicion that we're being\ + \ recorded - watched like rats in a maze. The crew's morale is slipping; they feel exposed, vulnerable. +dudStarLeague78.text=%s, we're in an abandoned SLDF base at %s. The facility is deserted, but\ + \ we've picked up faint infrared signals from the surrounding hills - like distant observers trying\ + \ to stay hidden. No depot found, just a deepening sense that we're part of someone's game. +dudStarLeague79.text=%s, we're at an SLDF control center in %s. Our comms have been glitching\ + \ non-stop, and there's a strange clicking sound on the encrypted channel. No depot has been located,\ + \ but the crew's sure someone is eavesdropping on us. It feels like every word, every step, is being\ + \ cataloged by unseen eyes. +dudStarLeague80.text=%s, we're inside a ruined SLDF command bunker at %s. The walls still bear\ + \ screens showing flickering maps of Kerensky's last campaigns. It's eerie, as if the General\ + \ himself left these plans behind for us to find. No depot yet, just the feeling that we're walking\ + \ among the echoes of his final orders - a legacy that's long been lost to the stars. +dudStarLeague81.text=%s, we're at an SLDF forward base in %s. There's a worn plaque here,\ + \ commemorating one of Kerensky's pivotal battles during the Hegemony Campaign. It's covered in\ + \ rust, but his name is still visible, a reminder of the ideals he fought for. No depot found,\ + \ just the sense that his presence lingers, urging us forward despite the odds. +dudStarLeague82.text=%s, we're in the ruins of an SLDF supply hub at %s. The crew found an old\ + \ banner bearing Kerensky's name, torn but still recognizable. No sign of the depot, but the place\ + \ feels sacred - like we're treading on ground that once resounded with the General's commands.\ + \ Morale is high, as if the spirit of Kerensky himself is watching over us. +dudStarLeague83.text=%s, we're inside an abandoned SLDF stronghold in %s. The walls are lined\ + \ with crumbling quotes from Kerensky's speeches, still legible despite the decay. The crew is silent,\ + \ moved by his words. No depot found, just a profound sense of honor mixed with sorrow. It's hard\ + \ not to feel like we're living in the shadow of greatness. +dudStarLeague84.text=%s, we're at an SLDF base in %s. The crew found an old comms console engraved\ + \ with a simple line: "For the General." No depot here, just a palpable sense of reverence for the\ + \ man who tried to save the Star League from its own destruction. The team feels a renewed\ + \ determination, driven by Kerensky's spirit to keep searching. +dudStarLeague85.text=%s, we're at an SLDF logistics post at %s. There's a statue here,\ + \ partially collapsed but unmistakably depicting Kerensky. It's covered in grime, but his eyes\ + \ seem to watch us with a mix of hope and disappointment. No depot yet, but the crew is visibly\ + \ humbled by his enduring legacy. +dudStarLeague86.text=%s, we're inside an old SLDF headquarters at %s. The central command table\ + \ still bears the symbol of Kerensky's forces. It feels like a memorial to a lost era, a silent\ + \ tribute to the man who led the Exodus. No depot found, only a sense of duty to honor what he\ + \ stood for - even in failure. +dudStarLeague87.text=%s, we're at an SLDF training camp in %s. The crew stumbled upon an old\ + \ insignia bearing the motto: "Strength through unity." No depot in sight, but the words seem\ + \ to echo through the empty halls, a reminder of the ideals that died when the Star League fell\ + \ apart. +dudStarLeague88.text=%s, we're exploring a remote SLDF bunker at %s. The walls are covered in\ + \ graffiti from soldiers who once served under Kerensky, some of it pleading for his return. No\ + \ depot found, only a lingering sense of loyalty that stretches across the centuries. +dudStarLeague89.text=%s, we're at an SLDF comms station in %s. The crew found a plaque\ + \ commemorating Kerensky's victory over Amaris, now rusted and nearly unreadable. No depot, just\ + \ the remnants of glory that faded with his departure. The men are unusually quiet, as if they can\ + \ feel the weight of Kerensky's unfulfilled dream. +dudStarLeague90.text=%s, we're in an abandoned SLDF outpost at %s. Half written letters to family\ + \ lie scattered, a quick look through them speaks to a time of war, a desire to return to peace,\ + \ and fear of the coming war to retake Terra. No depot, just a haunting sense of apprehension that\ + \ still clings to the air, as if even the General's followers were unsure of their fate. +dudStarLeague91.text=%s, we're in an SLDF barracks at %s. The crew found a dusty old portrait\ + \ of Kerensky, half-covered by fallen debris. His gaze seems to pierce through the centuries, a\ + \ silent witness to the search that continues long after his departure. No depot, just a deep sense\ + \ of loss. +dudStarLeague92.text=%s, we're at an SLDF armory in %s. The walls bear the symbol of Kerensky's\ + \ command - now faded, but still potent. No depot located, but the feeling of walking in the footsteps\ + \ of the General is unmistakable. The crew feels a mix of inspiration and sadness, as if the weight\ + \ of history itself is upon them. +dudStarLeague93.text=%s, we're inside an SLDF depot at %s. The base's main hall has a mural\ + \ dedicated to Kerensky's final speech before the Exodus. It's faded, but the words still carry weight.\ + \ No depot found, only a sense of melancholy as we realize that his dream of unity is more distant\ + \ than ever. +dudStarLeague94.text=%s, we're at an SLDF memorial site in %s. The plaque here reads: "For General\ + \ Kerensky - last hope of the Star League." No depot located, but the crew is unusually somber, as\ + \ if the place itself demands respect. It's a painful reminder that even the greatest leaders\ + \ couldn't prevent the collapse. +dudStarLeague95.text=%s, we're in the ruins of an SLDF command center at %s. The central console\ + \ still bears the symbol of Kerensky's forces, now corroded but visible. No depot here, just the\ + \ feeling that we're walking among the last vestiges of a legacy that ultimately led to self-exile. +dudStarLeague96.text=%s, we're at an SLDF base in %s. The crew found a handwritten note from one\ + \ of Kerensky's officers, addressed to "the General" and left unfinished. No depot found, only the\ + \ lingering sense of promises unfulfilled. The team is subdued, as if they're feeling the weight\ + \ of a history that never had a happy ending. +dudStarLeague97.text=%s, we're inside an old SLDF staging area at %s. The base is filled with\ + \ broken gear, but there's a large mural of Kerensky in the main hall, now barely recognizable. No\ + \ depot located, only a profound sense of reverence for the man who once led these soldiers. +dudStarLeague98.text=%s, we're at an SLDF fort in %s. The crew discovered an old battle standard\ + \ bearing Kerensky's name, untouched for centuries. No depot, but the sense of honor and loss is\ + \ overwhelming. It's hard not to feel like we're chasing the remnants of a dream that died with him. +dudStarLeague99.text=%s, we're in an SLDF depot at %s. There's a statue of Kerensky here, toppled\ + \ and broken. It's a painful sight, a symbol of the Star League's fall and the General's departure.\ + \ No depot found, only the reminder that his vision, though noble, couldn't prevent the inevitable\ + \ collapse. + +# getLosTechCache +transaction.text=Anonymous gift + +# showConfirmationDialog +warning.text=Are you sure? Refusing this offer will have consequences. + +# saleDialog +propositionAccept.text=Accept Transaction +propositionRefuse.text=Enter the Depot +proposition0.text=%s, I understand your team is actively pursuing a Star League depot, a relic of immense\ + \ historical significance. I represent a party with considerable interest in its precise location,\ + \ especially any ties it may have to the SLDF. We are prepared to offer a substantial sum of C-bills\ + \ in exchange for this information. Consider this a rare opportunity to secure significant resources.\ + \ Declining, however, could have unintended consequences - others may not be as patient or generous. +proposition1.text=%s, time is of the essence. The Star League depot is more than just a trove of lost\ + \ technology; it's a piece of history tied directly to the SLDF's final days. My associates are\ + \ eager to secure its exact location and will provide a generous payment in C-bills for this\ + \ information. Refusal, however, could lead to increased interest from other, less diplomatic parties. +proposition2.text=%s, your pursuit of the Star League depot has not gone unnoticed. My principals are\ + \ interested in its coordinates, especially due to its rumored connection to the SLDF's legacy.\ + \ We are prepared to offer an immediate transfer of C-bills in return. Keeping such a discovery\ + \ secret carries its own risks. Accepting our terms ensures both financial security and the avoidance\ + \ of complications. +proposition3.text=%s, I commend your efforts in locating the Star League depot, a site potentially\ + \ linked to the SLDF's lost glory. We propose a straightforward exchange: a significant amount of\ + \ C-bills for the depot's coordinates. Refusing this offer may attract the attention of others who\ + \ are less inclined toward negotiation. Accept the funds, and avoid unnecessary complications. +proposition4.text=%s, consider this a final offer concerning the Star League depot. We can provide a\ + \ substantial amount of C-bills in exchange for its location, particularly given its rumored ties\ + \ to the SLDF. Refusal could invite scrutiny from factions less concerned with diplomacy. A quick,\ + \ discreet transaction benefits both sides. Choose wisely, as time is limited. +proposition5.text=%s, the Star League depot you're pursuing is of significant interest to my associates,\ + \ particularly due to its potential ties to the SLDF's storied history. We are ready to transfer a\ + \ considerable sum of C-bills in exchange for its exact location. This offer is both discreet and\ + \ beneficial. However, failing to respond could attract less favorable attention from other factions. +proposition6.text=%s, I must reiterate the urgency of securing the Star League depot's coordinates. It\ + \ holds immense strategic and historical value, particularly for those who respect the SLDF's legacy.\ + \ My principals are prepared to make a swift, substantial payment in C-bills. Be aware that delaying\ + \ could draw interest from parties who might not negotiate as fairly. +proposition7.text=%s, I represent a group that considers the Star League depot, and any links to the SLDF,\ + \ to be invaluable. We offer a generous sum of C-bills for its coordinates. This is an opportunity\ + \ to secure immediate resources. A refusal could, however, alter the nature of future negotiations\ + \ - possibly not to your advantage. +proposition8.text=%s, we both know the Star League depot has implications beyond mere salvage. It's a\ + \ relic of the SLDF's strength and influence. We are ready to provide a significant transfer of\ + \ C-bills for its location. Consider this a strategic alliance. Declining may invite attention\ + \ from those who prefer force over finance. +proposition9.text=%s, the Star League depot represents more than just lost technology; it's a piece of\ + \ SLDF heritage. My associates wish to acquire its location and will provide immediate compensation\ + \ in C-bills. Refusal to cooperate could have broader implications for your operations, especially\ + \ if others decide to pursue this more aggressively. +proposition10.text=%s, the Star League depot holds more than just salvage - it holds history, specifically\ + \ that of the SLDF. We are prepared to offer a substantial sum of C-bills in exchange for its\ + \ precise location. Declining this offer may lead to less pleasant inquiries from other interested\ + \ factions. The choice is yours, but time is running short. +proposition11.text=%s, your pursuit of the Star League depot has reached the attention of those who\ + \ hold the SLDF in high regard. We wish to negotiate the exchange of its coordinates for C-bills.\ + \ The offer is fair, immediate, and advantageous. A refusal could complicate matters with parties\ + \ more focused on the depot's strategic value than on monetary compensation. +proposition12.text=%s, securing the location of the Star League depot is of paramount importance to my\ + \ principals, particularly given its ties to the SLDF's legacy. We offer substantial C-bills for\ + \ this information, with no strings attached. However, withholding the location could lead to\ + \ unexpected confrontations from other interested parties. +proposition13.text=%s, the Star League depot is of immense value to my associates, especially given its\ + \ historical link to the SLDF. We propose a significant sum of C-bills for the coordinates. Be\ + \ aware that delays or refusals could prompt actions from less patient factions. Your cooperation\ + \ ensures both profit and peace. +proposition14.text=%s, the Star League depot's connection to the SLDF is well understood by my principals.\ + \ We offer a substantial transfer of C-bills in exchange for its location. Declining may draw\ + \ interest from other factions with fewer concerns about diplomacy and more about possession. +proposition15.text=%s, the Star League depot you seek is a critical piece of SLDF history. We are\ + \ prepared to offer immediate compensation in C-bills for its coordinates. Be aware that others\ + \ may soon seek it by force, making this offer one of the safer paths forward. +proposition16.text=%s, the Star League depot holds vital historical insights related to the SLDF's past.\ + \ We are interested only in the coordinates and will offer C-bills in return. It's a straightforward\ + \ transaction, but declining it may lead to a more aggressive pursuit from other factions. +proposition17.text=%s, the Star League depot is of significant interest due to its ties to the SLDF's\ + \ final days. We wish to acquire the coordinates and are prepared to offer a considerable sum of\ + \ C-bills. Your cooperation ensures a smooth exchange. Refusal could, however, attract the wrong\ + \ kind of attention. +proposition18.text=%s, I represent a group deeply invested in Star League history, specifically that of\ + \ the SLDF. We propose a discreet exchange of C-bills for the depot's location. Declining this\ + \ offer could complicate your current mission, as others may choose a more direct approach. +proposition19.text=%s, the Star League depot's link to the SLDF makes it uniquely valuable. My principals\ + \ are prepared to offer a generous sum of C-bills for its coordinates. Accepting ensures mutual\ + \ benefit, while refusal could draw the interest of parties less inclined toward peaceful negotiations. +proposition20.text=%s, some things are not meant to be found - yet here you are, on the trail of a Star\ + \ League depot tied to the SLDF. We seek its location and offer C-bills in exchange. This knowledge\ + \ is not without its burdens, but the rewards can be... enlightening. Choose wisely. +proposition21.text=%s, the past holds many secrets, as does the Star League depot you seek. We are\ + \ prepared to transfer C-bills for its coordinates, but understand: knowledge can bring shadows.\ + \ Refuse, and the shadows may grow longer than you expect. +proposition22.text=%s, you are walking in the footsteps of the SLDF, tracing paths they once tread in\ + \ secrecy. We seek the same location as you, and offer C-bills to lighten your burden. Know that\ + \ roads not taken often have watchers. +proposition23.text=%s, the Star League depot's secrets are old, but our interest in them is new. The\ + \ coordinates, exchanged for C-bills, could secure your future - if you choose to share them. Just\ + \ remember, history is often written by those who find it first. +proposition24.text=%s, we sense you are close to the Star League depot, an echo of the SLDF's lost\ + \ legacy. We offer C-bills for its location, and our offer stands for a short time. Remember, eyes\ + \ are always watching those who seek the past. +proposition25.text=%s, the location you seek has seen more than battles; it has seen betrayal and\ + \ ambition. The Star League depot's coordinates are valuable to us. C-bills are offered in exchange.\ + \ Be cautious - persistence can attract the gaze of those who move in silence. +proposition26.text=%s, what you chase is both tangible and elusive. We seek only the coordinates of\ + \ the Star League depot, and we offer C-bills in return. Time is a fickle ally; those who linger\ + \ too long may find themselves pursued. +proposition27.text=%s, the SLDF left many things behind - this depot is just one of them. We are willing\ + \ to compensate you in C-bills for its location. But be aware: old ghosts often find new hunters.\ + \ Delay may not be wise. +proposition28.text=%s, some legacies were never meant to be uncovered, yet here you are. The Star League\ + \ depot holds value, and so do C-bills. We offer them in exchange for the coordinates, but whispers\ + \ travel fast. Silence has a cost. +proposition29.text=%s, you approach a place where the SLDF's intentions are frozen in time. We wish to\ + \ acquire the coordinates, offering C-bills for what you find. But tread lightly - there are those\ + \ who would prefer that such locations remain forgotten. +proposition30.text=%s, the Star League depot is a key - one that unlocks both opportunity and danger.\ + \ We seek its location, offering C-bills for your cooperation. But keys can turn both ways; keep\ + \ that in mind as you consider our offer. +proposition31.text=%s, knowledge of the SLDF's remnants carries weight, as does the Star League depot's\ + \ location. We offer C-bills in exchange, but understand this: you are not the only seeker. The\ + \ quietest hunters are often the deadliest. +proposition32.text=%s, the past calls to those who listen, and it seems you have answered. The Star\ + \ League depot holds truths that should be uncovered, for the right price. We offer C-bills, but\ + \ others may offer something far less forgiving. +proposition33.text=%s, the SLDF once guarded secrets with zeal, and this Star League depot is no\ + \ exception. We seek its coordinates and will provide C-bills in return. Be wary - secrets long\ + \ buried can attract dangerous interest. +proposition34.text=%s, you walk a path few dare to tread. The Star League depot is closer than you\ + \ think. We offer C-bills for its location, but haste is advised. The past has many eyes, and not\ + \ all are friendly. +proposition35.text=%s, the SLDF's legacy is fraught with hidden agendas. The Star League depot is part\ + \ of that tale. We offer C-bills for its coordinates, but know that even shadows have their own\ + \ purposes. +proposition36.text=%s, some say the Star League depot is cursed, others that it is guarded. We are\ + \ interested only in its location, offering C-bills in exchange. But consider this: doors opened\ + \ too soon can let in more than just opportunity. +proposition37.text=%s, you seek what the SLDF could not protect forever. The Star League depot's\ + \ coordinates are what we desire, and C-bills are what we offer. However, hesitation can be\ + \ dangerous when others are also searching. +proposition38.text=%s, the Star League depot you pursue is part of a larger web, one spun long ago.\ + \ We wish to know its location, and C-bills are our currency of exchange. Be warned, however -\ + \ some webs have more than one spider. +proposition39.text=%s, the depot's coordinates are more than just a location; they are a piece of the\ + \ SLDF's forgotten war. We offer C-bills in return, but the clock is ticking. Know that the past\ + \ is a powerful force, and it does not always forgive. +proposition40.text=%s, the Star League depot you seek is a dangerous prize. We demand the coordinates\ + \ in exchange for C-bills. Refuse, and others - far less diplomatic - will come calling. You don't\ + \ want to be in their crosshairs. +proposition41.text=%s, time is not your ally. The SLDF left behind many secrets, and this Star League\ + \ depot is one of the most coveted. Provide the coordinates, accept the C-bills, and avoid\ + \ complications. Refuse, and those complications will find you. +proposition42.text=%s, your pursuit of the Star League depot is attracting attention beyond your control.\ + \ We offer C-bills in exchange for the coordinates, but consider this your final chance. The next\ + \ contact may not be as patient - or as generous. +proposition43.text=%s, we know you're close to the Star League depot. Deliver the coordinates, or face\ + \ the consequences of keeping such knowledge hidden. Our offer of C-bills is the only peaceful\ + \ resolution you'll receive. +proposition44.text=%s, history's secrets are not meant for everyone. The Star League depot is no exception.\ + \ We offer C-bills for its location, but decline at your own peril. Others will be less interested\ + \ in negotiation and more interested in results. +proposition45.text=%s, you may believe you have time, but that is a dangerous illusion. The Star League\ + \ depot's coordinates are needed now. Accept the C-bills offered, or expect a less pleasant approach\ + \ from others already en route. +proposition46.text=%s, this is not merely an offer - it's a warning. The Star League depot has many eyes\ + \ upon it, some less forgiving than others. Provide the coordinates, take the C-bills, and leave\ + \ the shadows behind. Refusal will only invite them closer. +proposition47.text=%s, the SLDF's secrets come with a heavy cost. You stand at a crossroads: accept our\ + \ C-bills for the Star League depot's location, or face the inevitable interest of those who won't\ + \ ask twice. +proposition48.text=%s, we both know the value of the Star League depot. We also know the danger of delay.\ + \ We offer C-bills for its coordinates. Decline, and the next visitor may not be inclined to negotiate. +proposition49.text=%s, we understand the risks you've taken in finding the Star League depot. But risks\ + \ have a way of multiplying when they're ignored. Provide the coordinates and accept our C-bills,\ + \ or suffer the consequences of silence. +proposition50.text=%s, the Star League depot's location is no longer a secret. Others are closing in,\ + \ and their intentions are not as peaceful as ours. This is your final opportunity to exchange it\ + \ for C-bills. Refuse, and you may find yourself hunted. +proposition51.text=%s, you are walking a dangerous path. The SLDF left behind many ghosts, and the Star\ + \ League depot is one of them. Give us the coordinates and take the C-bills, or find yourself\ + \ haunted by more than just history. +proposition52.text=%s, the past can be a cruel teacher. The Star League depot's location is of great\ + \ interest to many, not all of whom care for your well-being. Deliver the coordinates and secure\ + \ your C-bills. The alternative is far less forgiving. +proposition53.text=%s, we are not alone in our pursuit of the Star League depot. Provide the coordinates,\ + \ accept the C-bills, and avoid the chaos that follows. Delay will only attract the wolves. +proposition54.text=%s, you must understand: the Star League depot is a beacon, and you are standing too\ + \ close. Offer the coordinates in exchange for C-bills, or expect the arrival of those who prefer to\ + \ take what they want by force. +proposition55.text=%s, knowledge of the SLDF depot's location is a dangerous thing. We offer C-bills for\ + \ the coordinates, but time is running out. Those who do not act quickly often become collateral. +proposition56.text=%s, we will not wait forever. The Star League depot is too valuable, and your hesitation\ + \ is drawing dangerous attention. Provide the coordinates, take the C-bills, and avoid the fate of\ + \ those who are too slow to act. +proposition57.text=%s, the Star League depot has become a target, and so have you. Deliver the coordinates\ + \ now, receive your C-bills, and walk away. Refuse, and you may find your options rapidly disappearing. +proposition58.text=%s, this is the last offer you'll receive from us regarding the Star League depot.\ + \ Accept the C-bills for its coordinates, or face those who have less patience for negotiation. You\ + \ do not want to test their resolve. +proposition59.text=%s, the SLDF depot is a prize worth fighting for - and dying for. We offer C-bills\ + \ for its location, but be warned: others are not far behind, and they are far less concerned with\ + \ your survival. +proposition60.text=%s, I hear you're getting close to that Star League depot. I've got some serious C-bills\ + \ waiting for you if you're willing to share the location. Let's keep this simple and beneficial for\ + \ both of us. After all, who needs unnecessary complications, right? +proposition61.text=%s, you've done some impressive work tracking that Star League depot! I've got a good\ + \ feeling about this. How about we make a deal? A generous sum of C-bills in exchange for the coordinates\ + \ - no strings attached. Let's make this a win-win, shall we? +proposition62.text=%s, I've always admired your tenacity. Finding a Star League depot is no small feat!\ + \ I'm willing to pay a fair amount of C-bills for the location. No need to make things difficult -\ + \ just a straightforward deal between two professionals. +proposition63.text=%s, it's not every day someone stumbles across a Star League depot. Lucky for you,\ + \ I happen to be in the market for that exact kind of information. I'll make it worth your while -\ + \ C-bills, quick and easy. What do you say? +proposition64.text=%s, I'll be honest - this Star League depot is the stuff of legend. I'm sure you're\ + \ ready for a good payday, so how about we swap the coordinates for a nice pile of C-bills? No fuss,\ + \ no hassle - just a friendly exchange. +proposition65.text=%s, you're on the verge of a big find! I'm talking about that Star League depot, of\ + \ course. I've got a hefty sum of C-bills ready to go, just for the coordinates. Think of it as a\ + \ friendly favor that pays off handsomely. +proposition66.text=%s, it's not every day someone uncovers a piece of SLDF history like this depot. I'd\ + \ love to be a part of your success story. Let's swap the coordinates for a good amount of C-bills,\ + \ and you'll walk away with a heavy wallet. +proposition67.text=%s, I've been following your progress with this Star League depot, and I've got to\ + \ say, I like your style! How about a friendly deal - coordinates for C-bills? It's a simple trade\ + \ that'll put a smile on your face. +proposition68.text=%s, you've done the hard work of finding the Star League depot, and now I'd like to\ + \ help you cash in on it. I've got C-bills with your name on them, ready to be transferred as soon\ + \ as we get the coordinates. Easy money! +proposition69.text=%s, you and I both know how rare it is to find something tied to the SLDF. I've got\ + \ the C-bills to make this worth your while. Let's keep things simple - coordinates for cash. No\ + \ need to make this more complicated than it has to be! +proposition70.text=%s, you've got a nose for finding lost treasures, and that Star League depot is a\ + \ prime example. I'm offering a nice pile of C-bills for the location. Let's make this a friendly,\ + \ profitable exchange. After all, who doesn't like a good payday? +proposition71.text=%s, you're on a hot streak with this Star League depot, and I want to be a part of\ + \ it. I'm offering a generous sum of C-bills for the coordinates - easy money for you, and no strings\ + \ attached. Let's shake hands, so to speak. +proposition72.text=%s, finding a Star League depot is no small feat, but getting paid for it? That's the\ + \ fun part. I've got the C-bills ready for you. All I need is the location, and we both walk away\ + \ happy. What do you say? +proposition73.text=%s, you've got yourself a golden opportunity with that Star League depot. I've got\ + \ C-bills burning a hole in my pocket, and I'd love to share them with you in exchange for the\ + \ coordinates. Let's make this a smooth, friendly deal. +proposition74.text=%s, let's make this a story we can both smile about. You give me the Star League\ + \ depot's coordinates, and I'll make sure you're paid handsomely in C-bills. No drama, no\ + \ complications - just a friendly exchange. +proposition75.text=%s, I'm sure you're used to these kinds of negotiations, so let me be clear: you\ + \ provide the Star League depot's location, and you'll have more C-bills than you know what to do\ + \ with. No hard feelings, just business. +proposition76.text=%s, you've got yourself a fine opportunity with that SLDF depot. But let's be honest\ + \ - turning coordinates into C-bills is even better. I'm offering just that, with no strings\ + \ attached. Friends keep things simple, after all. +proposition77.text=%s, I'm not here to complicate things. I just want the Star League depot's coordinates,\ + \ and I'm more than happy to pay you well for them in C-bills. Let's keep this simple, friend - no\ + \ need to let others get involved +proposition78.text=%s, let's not beat around the bush. The Star League depot you've found is impressive,\ + \ but I'm sure you'd prefer the C-bills I'm offering. A fair trade, wouldn't you say? We can keep\ + \ this all friendly and profitable. +proposition79.text=%s, we both know that finding a Star League depot is no small feat. But selling the\ + \ info? Now that's easy. Let me take it off your hands for a good price in C-bills. We both get\ + \ what we want, and no one gets hurt. +proposition80.text=%s, you're a real pro at this, I can tell. So let's keep it professional, but friendly:\ + \ you hand over the Star League depot's location, and I'll hand over the C-bills. No drama, just a\ + \ clean deal. +proposition81.text=%s, I've heard you're closing in on an SLDF treasure. How about we make a deal? You\ + \ share the coordinates, and I'll make sure you're enjoying a nice payday with a generous amount of\ + \ C-bills. Just good business, friend. +proposition82.text=%s, I know how these things go - sometimes you're the one who finds the Star League\ + \ depot, and sometimes you're the one who buys the info. This time, I'm buying. Let's make a friendly\ + \ exchange for C-bills, shall we? +proposition83.text=%s, you're onto something good with that Star League depot. I can help make it even\ + \ better - with a pile of C-bills, of course. Just share the coordinates, and we'll both walk away\ + \ smiling. +proposition84.text=%s, I admire your tenacity in tracking down that SLDF depot. But you know what's better\ + \ than hard work? Getting paid for it. Give me the location, and you'll have more C-bills than you\ + \ can count. What do you say? +proposition85.text=%s, let's cut to the chase. I want the Star League depot's coordinates, and you want\ + \ C-bills. I'm here to make sure you get exactly what you deserve, with a little extra for your\ + \ troubles. No need to complicate things. +proposition86.text=%s, you've always struck me as someone who knows a good deal when they see one. Here\ + \ it is: you give me the Star League depot's location, and I'll fill your coffers with C-bills.\ + \ Let's make it easy on both of us. +proposition87.text=%s, you've got yourself a real find with that Star League depot. I'd love to take it\ + \ off your hands - just the coordinates, of course. I'll make sure you're rolling in C-bills before\ + \ you know it. Friends help friends, right? +proposition88.text=%s, chasing down a Star League depot isn't easy work. I respect that. But why do it\ + \ the hard way? You share the location, and I'll make sure you're flush with C-bills. We can both\ + \ walk away happy, no strings attached. +proposition89.text=%s, I've got a feeling we're both after the same thing - the Star League depot. But\ + \ hey, I'm offering C-bills for the coordinates, fair and square. Why not cash in now and save\ + \ yourself the trouble of unwanted company? +proposition90.text=%s, it's not every day you get this close to an SLDF depot, is it? I can make it\ + \ worth your while. Just a friendly deal between us: you give me the coordinates, and I give you\ + \ a nice stack of C-bills. No fuss, no muss. +proposition91.text=%s, I hear you're hot on the trail of a Star League depot. Let's not make things\ + \ too complicated: hand over the coordinates, and I'll make sure you're swimming in C-bills before\ + \ the day's out. Just between friends, yeah? +proposition92.text=%s, our terms are clear: C-bills in exchange for the Star League depot's coordinates.\ + \ This transaction is purely business, aimed at delivering prompt compensation for actionable\ + \ intelligence. We anticipate your cooperation. +proposition93.text=%s, as you near the Star League depot, we wish to formalize an agreement. We offer\ + \ C-bills for the coordinates, in full and with immediate payment. We trust that this approach\ + \ aligns with your own professional standards. +proposition94.text=%s, our interest in the Star League depot is strictly professional. We offer a\ + \ significant transfer of C-bills for the location. This is an efficient solution for both sides\ + \ - minimal risk, maximum benefit. +proposition95.text=%s, we propose a clear-cut deal: the coordinates of the Star League depot for a\ + \ substantial amount of C-bills. This is a straightforward exchange, free of any further obligations\ + \ or complications. We await your response. +proposition96.text=%s, we understand the value of the Star League depot you've uncovered. We offer\ + \ C-bills in direct exchange for the location, ensuring prompt payment. This is a clean,\ + \ business-focused transaction that benefits all parties. +proposition97.text=%s, our offer remains firm: a substantial amount of C-bills for the precise\ + \ coordinates of the Star League depot. This is a professional exchange of information for\ + \ compensation, designed to maximize efficiency for both parties involved. +proposition98.text=%s, the Star League depot's coordinates are of high value to our interests. We\ + \ propose a clear transaction: C-bills in exchange for the location. The terms are simple and\ + \ direct, with no additional stipulations. We look forward to a swift resolution. +proposition99.text=%s, you've made significant progress in locating the Star League depot. We are\ + \ prepared to facilitate an immediate transfer of C-bills upon receipt of the coordinates. This\ + \ offer is straightforward and beneficial for both parties. We recommend acting promptly. + +propositionValue.text=We're offering %s for the depot's location, sealed and unsullied. +senderUnknown.text=Sender Unknown \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/ScenarioTableModel.properties b/MekHQ/resources/mekhq/resources/ScenarioTableModel.properties index 66033b477b0..39fffa28331 100644 --- a/MekHQ/resources/mekhq/resources/ScenarioTableModel.properties +++ b/MekHQ/resources/mekhq/resources/ScenarioTableModel.properties @@ -2,5 +2,5 @@ col_name.text=Scenario Name col_status.text=Resolution col_date.text=Date col_assign.text=Units Assigned -col_sector.text=Sector +col_sector.text=Grid Reference col_unknown.text=? diff --git a/MekHQ/resources/mekhq/resources/VocationalExperienceAwardDialog.properties b/MekHQ/resources/mekhq/resources/VocationalExperienceAwardDialog.properties new file mode 100644 index 00000000000..e09a38e8864 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/VocationalExperienceAwardDialog.properties @@ -0,0 +1,10 @@ +confirm.button=Understood +dialog.message=, our line-officers have flagged the following personnel as having made noticeable\ + \ improvement in the past few months.\ +
    \ +
    It might be worth reviewing their records.\ +
    \ +
    +dialog.ooc=Each of the listed characters has gained {0} xp.\ +
    This represents improvements made naturally while performing the duties required by their\ + \ assigned roles. \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/messages.properties b/MekHQ/resources/mekhq/resources/messages.properties new file mode 100644 index 00000000000..31f6dc25cd8 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/messages.properties @@ -0,0 +1,19 @@ +# +# Copyright (c) 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 . +# + diff --git a/MekHQ/src/mekhq/AtBGameThread.java b/MekHQ/src/mekhq/AtBGameThread.java index fad80ca3228..d963f007a23 100644 --- a/MekHQ/src/mekhq/AtBGameThread.java +++ b/MekHQ/src/mekhq/AtBGameThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2011-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -19,7 +19,6 @@ package mekhq; import io.sentry.Sentry; -import megamek.client.AbstractClient; import megamek.client.Client; import megamek.client.bot.BotClient; import megamek.client.bot.princess.BehaviorSettings; @@ -27,14 +26,20 @@ import megamek.client.bot.princess.PrincessException; import megamek.client.generator.RandomCallsignGenerator; import megamek.client.ui.swing.ClientGUI; +import megamek.client.ui.swing.CommanderGUI; +import megamek.client.ui.swing.ILocalBots; import megamek.common.*; import megamek.common.annotations.Nullable; import megamek.common.planetaryconditions.PlanetaryConditions; import megamek.logging.MMLogger; +import mekhq.campaign.enums.CampaignTransportType; import mekhq.campaign.force.Force; import mekhq.campaign.mission.*; import mekhq.campaign.personnel.Person; +import mekhq.campaign.unit.ITransportAssignment; import mekhq.campaign.unit.Unit; +import mekhq.utilities.MHQInternationalization; +import mekhq.utilities.PotentialTransportsMap; import javax.swing.*; import java.io.File; @@ -44,7 +49,9 @@ import java.util.*; import java.util.stream.Collectors; -import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; +import static mekhq.campaign.enums.CampaignTransportType.SHIP_TRANSPORT; +import static mekhq.campaign.enums.CampaignTransportType.TACTICAL_TRANSPORT; +import static mekhq.campaign.force.CombatTeam.getStandardForceSize; /** * Enhanced version of GameThread which imports settings and non-player units @@ -57,7 +64,7 @@ public class AtBGameThread extends GameThread { private final AtBScenario scenario; private final BehaviorSettings autoResolveBehaviorSettings; - + private final boolean minimalGUI; /** * Constructor for AtBGameThread * @@ -76,41 +83,36 @@ public class AtBGameThread extends GameThread { */ public AtBGameThread(String name, String password, Client client, MekHQ app, List units, AtBScenario scenario, @Nullable BehaviorSettings autoResolveBehaviorSettings) { - this(name, password, client, app, units, scenario, autoResolveBehaviorSettings, true); + this(name, password, client, app, units, scenario, autoResolveBehaviorSettings, false, true); } public AtBGameThread(String name, String password, Client client, MekHQ app, List units, - AtBScenario scenario, @Nullable BehaviorSettings autoResolveBehaviorSettings, boolean started) { + AtBScenario scenario, @Nullable BehaviorSettings autoResolveBehaviorSettings, boolean minimalGUI, boolean started) { super(name, password, client, app, units, scenario, started); this.scenario = Objects.requireNonNull(scenario); this.autoResolveBehaviorSettings = autoResolveBehaviorSettings; + this.minimalGUI = minimalGUI; } - // String tokens for dialog boxes used for transport loading - // FIXME : I'm not localized! - private static final String LOAD_DROPSHIP_DIALOG_TITLE = "Load DropShips onto Transport?"; - private static final String LOAD_DROPSHIP_DIALOG_TEXT = "Would you like the DropShip(s) assigned to %s to deploy loaded into its bays?"; - private static final String LOAD_SMALL_CRAFT_DIALOG_TITLE = "Load Small Craft onto Transport?"; - private static final String LOAD_SMALL_CRAFT_DIALOG_TEXT = "Would you like the small craft assigned to %s to deploy loaded into its bays?"; - private static final String LOAD_FTR_DIALOG_TEXT = "Would you like the fighter(s) assigned to %s to deploy loaded into its bays?"; - private static final String LOAD_FTR_DIALOG_TITLE = "Load Fighters onto Transport?"; - private static final String LOAD_GND_DIALOG_TEXT = "Would you like the ground unit(s) assigned to %s to deploy loaded into its bays?"; - private static final String LOAD_GND_DIALOG_TITLE = "Load Ground Units onto Transport?"; - @Override public void run() { client.addCloseClientListener(this); - if (swingGui != null) { - for (AbstractClient client2 : swingGui.getLocalBots().values()) { - client2.die(); - } - swingGui.getLocalBots().clear(); - } createController(); - swingGui = new ClientGUI(client, controller); + if (autoResolveBehaviorSettings != null && minimalGUI) + { + var acarGui = new CommanderGUI(client, controller); + localBots = acarGui; + swingGui = acarGui; + acarGui.start(); + } else { + var clientGui = new ClientGUI(client, controller); + localBots = clientGui; + swingGui = clientGui; + swingGui.initialize(); + } + controller.clientgui = swingGui; - swingGui.initialize(); try { client.connect(); @@ -218,7 +220,7 @@ public void run() { * delay for slower scout units. */ boolean useDropship = false; - if (scenario.getLanceRole().isScouting()) { + if (scenario.getCombatRole().isPatrol()) { for (Entity en : scenario.getAlliesPlayer()) { if (en.getUnitType() == UnitType.DROPSHIP) { useDropship = true; @@ -235,6 +237,8 @@ public void run() { } } + PotentialTransportsMap potentialTransports = new PotentialTransportsMap(CampaignTransportType.values()); + var entities = new ArrayList(); for (Unit unit : units) { // Get the Entity @@ -243,9 +247,12 @@ public void run() { entity.setExternalIdAsString(unit.getId().toString()); // Set the owner entity.setOwner(client.getLocalPlayer()); - if (unit.hasTransportedUnits()) { + if (unit.hasShipTransportedUnits()) { // Store this unit as a potential transport to load - scenario.getPlayerTransportLinkages().put(unit.getId(), new ArrayList<>()); + potentialTransports.putNewTransport(SHIP_TRANSPORT, unit.getId()); + } + if (unit.hasTacticalTransportedUnits()) { + potentialTransports.putNewTransport(TACTICAL_TRANSPORT, unit.getId()); } // If this unit is a spacecraft, set the crew size and marine size values if (entity.isLargeCraft() || (entity.getUnitType() == UnitType.SMALL_CRAFT)) { @@ -267,9 +274,10 @@ public void run() { // Set scenario type-specific delay deploymentRound = Math.max(entity.getDeployRound(), scenario.getDeploymentDelay() - speed); // Lances deployed in scout roles always deploy units in 6-walking speed turns - if (scenario.getLanceRole().isScouting() && (scenario.getStrategicFormation(campaign) != null) - && (scenario.getStrategicFormation(campaign).getForceId() == scenario.getStrategicFormationId()) - && !useDropship) { + if (scenario.getCombatRole().isPatrol() + && (scenario.getCombatTeamById(campaign) != null) + && (scenario.getCombatTeamById(campaign).getForceId() == scenario.getCombatTeamId()) + && !useDropship) { deploymentRound = Math.max(deploymentRound, 6 - speed); } } @@ -293,28 +301,16 @@ public void run() { } client.sendAddEntity(entities); - // Run through the units again. This time add transported units to the correct - // linkage, + // Run through the units again. This time add + // transported units to the correct linkage, // but only if the transport itself is in the game too. + // Try to load into transportShips first, then tacticalTransports for (Unit unit : units) { - if (unit.hasTransportShipAssignment()) { - Unit transportShip = unit.getTransportShipAssignment().getTransportShip(); - if (scenario.getPlayerTransportLinkages().containsKey(transportShip.getId())) { - scenario.addPlayerTransportRelationship(transportShip.getId(), unit.getId()); - } - } + potentialTransports.tryToAddTransportedUnit(unit); } // Now, clean the list of any transports that don't have deployed units in the // game - Set emptyTransports = new HashSet<>(); - for (UUID id : scenario.getPlayerTransportLinkages().keySet()) { - if (scenario.getPlayerTransportLinkages().get(id).isEmpty()) { - emptyTransports.add(id); - } - } - for (UUID id : emptyTransports) { - scenario.getPlayerTransportLinkages().remove(id); - } + potentialTransports.removeEmptyTransports(); /* Add player-controlled ally units */ entities.clear(); @@ -335,8 +331,10 @@ public void run() { } } deploymentRound = Math.max(entity.getDeployRound(), scenario.getDeploymentDelay() - speed); - if (!useDropship && scenario.getLanceRole().isScouting() - && (scenario.getStrategicFormation(campaign).getForceId() == scenario.getStrategicFormationId())) { + if (!useDropship + && scenario.getCombatRole().isPatrol() + && (scenario.getCombatTeamById(campaign) != null) + && (scenario.getCombatTeamById(campaign).getForceId() == scenario.getCombatTeamId())) { deploymentRound = Math.max(deploymentRound, 6 - speed); } } @@ -346,20 +344,23 @@ public void run() { } client.sendAddEntity(entities); client.sendPlayerInfo(); - + var botClients = new ArrayList(); + var botName = new HashSet(); /* Add bots */ for (int i = 0; i < scenario.getNumBots(); i++) { BotForce bf = scenario.getBotForce(i); String name = bf.getName(); - if (swingGui.getLocalBots().containsKey(name)) { + if (botName.contains(name)) { int append = 2; - while (swingGui.getLocalBots().containsKey(name + append)) { + while (botName.contains(name + append)) { append++; } name += append; } Princess botClient = new Princess(name, client.getHost(), client.getPort()); botClient.setBehaviorSettings(bf.getBehaviorSettings()); + botClients.add(botClient); + botName.add(name); try { botClient.connect(); @@ -369,7 +370,7 @@ public void run() { e); } - swingGui.getLocalBots().put(name, botClient); + getLocalBots().put(name, botClient); // chill out while bot is created and connects to megamek Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); @@ -381,42 +382,44 @@ public void run() { loadTransports(botClient, scenario, bf); } + Set alreadyResetTransport = new HashSet<>(); + // All player and bot units have been added to the lobby - // Prompt the player to autoload units into transports - if (!scenario.getPlayerTransportLinkages().isEmpty()) { - for (UUID id : scenario.getPlayerTransportLinkages().keySet()) { + // Prompt the player to autoload units into transport ships + if (potentialTransports.hasTransports(SHIP_TRANSPORT)) { + for (UUID transportId : potentialTransports.getTransports(SHIP_TRANSPORT)) { boolean loadDropShips = false; boolean loadSmallCraft = false; boolean loadFighters = false; boolean loadGround = false; - Unit transport = campaign.getUnit(id); + Unit transportShip = campaign.getUnit(transportId); Set toLoad = new HashSet<>(); // Let the player choose to load DropShips, Small Craft, fighters, and/or // ground units on each transport - if (transport.getTransportedUnits().stream() + if (transportShip.getShipTransportedUnits().stream() .anyMatch(unit -> unit.getEntity().getUnitType() == UnitType.DROPSHIP)) { loadDropShips = JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null, - String.format(AtBGameThread.LOAD_DROPSHIP_DIALOG_TEXT, transport.getName()), - AtBGameThread.LOAD_DROPSHIP_DIALOG_TITLE, JOptionPane.YES_NO_OPTION); + MHQInternationalization.getFormattedTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.LOAD_DROPSHIP_DIALOG_TEXT.text", transportShip.getName()), + MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.LOAD_DROPSHIP_DIALOG_TITLE.title"), JOptionPane.YES_NO_OPTION); } - if (transport.getTransportedUnits().stream() + if (transportShip.getShipTransportedUnits().stream() .anyMatch(unit -> unit.getEntity().getUnitType() == UnitType.SMALL_CRAFT)) { loadSmallCraft = JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null, - String.format(AtBGameThread.LOAD_SMALL_CRAFT_DIALOG_TEXT, transport.getName()), - AtBGameThread.LOAD_SMALL_CRAFT_DIALOG_TITLE, JOptionPane.YES_NO_OPTION); + MHQInternationalization.getFormattedTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.LOAD_SMALL_CRAFT_DIALOG_TEXT.text", transportShip.getName()), + MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.LOAD_SMALL_CRAFT_DIALOG_TITLE.title"), JOptionPane.YES_NO_OPTION); } - if (transport.isCarryingSmallerAero()) { + if (transportShip.isCarryingSmallerAero()) { loadFighters = JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null, - String.format(AtBGameThread.LOAD_FTR_DIALOG_TEXT, transport.getName()), - AtBGameThread.LOAD_FTR_DIALOG_TITLE, JOptionPane.YES_NO_OPTION); + MHQInternationalization.getFormattedTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.LOAD_FTR_DIALOG_TEXT.text", transportShip.getName()), + MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.LOAD_FTR_DIALOG_TITLE.title"), JOptionPane.YES_NO_OPTION); } - if (transport.isCarryingGround()) { + if (transportShip.isCarryingGround()) { loadGround = (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null, - String.format(AtBGameThread.LOAD_GND_DIALOG_TEXT, transport.getName()), - AtBGameThread.LOAD_GND_DIALOG_TITLE, JOptionPane.YES_NO_OPTION)); + MHQInternationalization.getFormattedTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.LOAD_GND_DIALOG_TEXT.text", transportShip.getName()), + MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.LOAD_GND_DIALOG_TITLE.title"), JOptionPane.YES_NO_OPTION)); } // Now, send the load commands @@ -424,33 +427,101 @@ public void run() { // List of technicians assigned to transported units. Several units can share a // tech. Set cargoTechs = new HashSet<>(); - for (UUID cargoId : scenario.getPlayerTransportLinkages().get(id)) { - Unit unit = campaign.getUnit(cargoId); - if (unit != null) { + for (UUID cargoId : potentialTransports.getTransportedUnits(SHIP_TRANSPORT, transportId)) { + Unit transportedUnit = campaign.getUnit(cargoId); + if (transportedUnit != null) { // Convert the list of Unit UUIDs to MM EntityIds - toLoad.add(unit.getEntity().getId()); - if (unit.getTech() != null) { - cargoTechs.add(unit.getTech()); + toLoad.add(transportedUnit.getEntity().getId()); + if (scenario.getPlayerTransportLinkages().containsKey(transportShip.getId())) { + scenario.addPlayerTransportRelationship(transportShip.getId(), transportedUnit.getId()); + } + if (transportedUnit.getTech() != null) { + cargoTechs.add(transportedUnit.getTech()); + } + } + } + // Update the transport's passenger count with assigned techs + transportShip.getEntity() + .setNPassenger(transportShip.getEntity().getNPassenger() + cargoTechs.size()); + client.sendUpdateEntity(transportShip.getEntity()); + // And now load the units. Unit crews load as passengers here. + Utilities.loadPlayerTransports(transportShip.getEntity().getId(), toLoad, + client, loadDropShips, loadSmallCraft, loadFighters, loadGround); + alreadyResetTransport.add(transportId); + } + } + } + + // Prompt the player to autoload units into tactical transports (lower priority) + if (potentialTransports.hasTransports(TACTICAL_TRANSPORT)) { + for (UUID transportId : potentialTransports.getTransports(TACTICAL_TRANSPORT)) { + boolean loadTactical = false; + Unit transport = campaign.getUnit(transportId); + Map toLoad = new HashMap<>(); + + if (transport.hasTacticalTransportedUnits()) { + loadTactical = (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null, + MHQInternationalization.getFormattedTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.TACTICAL_TRANSPORT.text", transport.getName()), + MHQInternationalization.getFormattedTextAt("mekhq.resources.AssignForceToTransport", "AtBGameThread.loadTransportDialog.TACTICAL_TRANSPORT.title"), JOptionPane.YES_NO_OPTION)); + } + + // Now, send the load commands + if (loadTactical) { + // List of technicians assigned to transported units. Several units can share a + // tech. + Set cargoTechs = new HashSet<>(); + for (UUID cargoId : potentialTransports.getTransportedUnits(TACTICAL_TRANSPORT, transportId)) { + Unit transportedUnit = campaign.getUnit(cargoId); + if (transportedUnit != null && transport.getEntity().canLoad(transportedUnit.getEntity())) { //transportedUnit.getTransportAssignment().getTransporterType()) { + // Convert the list of Unit UUIDs to MM EntityIds + toLoad.put(transportedUnit.getEntity().getId(), transportedUnit.getTacticalTransportAssignment()); + if (scenario.getPlayerTransportLinkages().containsKey(transportId)) { + scenario.addPlayerTransportRelationship(transportId, transportedUnit.getId()); + } + if (transportedUnit.getTech() != null && transportedUnit.hasTransportAssignment(TACTICAL_TRANSPORT) + && transportedUnit.getTransportAssignment(TACTICAL_TRANSPORT).isTransportedInBay()){ + cargoTechs.add(transportedUnit.getTech()); } } } // Update the transport's passenger count with assigned techs transport.getEntity() - .setNPassenger(transport.getEntity().getNPassenger() + cargoTechs.size()); + .setNPassenger(transport.getEntity().getNPassenger() + cargoTechs.size()); client.sendUpdateEntity(transport.getEntity()); - // And now load the units. Unit crews load as passengers here. + // And now load the units. Utilities.loadPlayerTransports(transport.getEntity().getId(), toLoad, - client, loadDropShips, loadSmallCraft, loadFighters, loadGround); + client, loadTactical, alreadyResetTransport.contains(transportId)); + alreadyResetTransport.add(transportId); } } } + if (swingGui instanceof ILocalBots iLocalBots) { + for (var botClient : botClients) { + iLocalBots.getLocalBots().put(botClient.getName(), botClient); + } + } // if AtB was loaded with the auto resolve bot behavior settings then it loads a new bot, // set to the players team // and then moves all the player forces under this new bot if (Objects.nonNull(autoResolveBehaviorSettings)) { - setupPlayerBotForAutoResolve(player); + var bot = setupPlayerBotForAutoResolve(player); + getLocalBots().put(bot.getName(), bot); + botClients.add(bot); + for (var botClient : botClients) { + botClient.sendDone(true); + } + Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); + if (swingGui != null && swingGui instanceof CommanderGUI commanderGUI) { + commanderGUI.enableReady(); + commanderGUI.getLocalBots().put(bot.getName(), bot); + } + if (swingGui instanceof ILocalBots iLocalBots) { + iLocalBots.getLocalBots().put(bot.getName(), bot); + } + client.getLocalPlayer().setDone(true); + client.sendDone(true); } } @@ -461,7 +532,7 @@ public void run() { Sentry.captureException(ex); logger.error("", ex); } finally { - swingGui.setDisconnectQuietly(true); + disconnectGuiSilently(); client.die(); client = null; swingGui = null; @@ -469,8 +540,8 @@ public void run() { } } - private void setupPlayerBotForAutoResolve(Player player) throws InterruptedException, PrincessException { - var botName = player.getName() + ":AI"; + private BotClient setupPlayerBotForAutoResolve(Player player) throws InterruptedException, PrincessException { + var botName = player.getName() + "@AI"; Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); var botClient = new Princess(botName, client.getHost(), client.getPort()); @@ -483,7 +554,6 @@ private void setupPlayerBotForAutoResolve(Player player) throws InterruptedExcep logger.error(String.format("Could not connect with Bot name %s", botName), e); } - swingGui.getLocalBots().put(botName, botClient); var retryCount = MekHQ.getMHQOptions().getStartGameBotClientRetryCount(); while (botClient.getLocalPlayer() == null) { @@ -515,6 +585,7 @@ private void setupPlayerBotForAutoResolve(Player player) throws InterruptedExcep .collect(Collectors.toList()); botClient.sendChangeOwner(playerEntities, botClient.getLocalPlayer().getId()); Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); + return botClient; } private PlanetaryConditions getPlanetaryConditions() { diff --git a/MekHQ/src/mekhq/campaign/CampaignPreset.java b/MekHQ/src/mekhq/CampaignPreset.java similarity index 97% rename from MekHQ/src/mekhq/campaign/CampaignPreset.java rename to MekHQ/src/mekhq/CampaignPreset.java index cf58104fc4e..800469eddc1 100644 --- a/MekHQ/src/mekhq/campaign/CampaignPreset.java +++ b/MekHQ/src/mekhq/CampaignPreset.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. * @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.campaign; +package mekhq; import megamek.Version; import megamek.common.annotations.Nullable; @@ -24,8 +24,9 @@ import megamek.common.util.sorter.NaturalOrderComparator; import megamek.logging.MMLogger; import megamek.utilities.xml.MMXMLUtility; -import mekhq.MHQConstants; -import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.RandomSkillPreferences; import mekhq.campaign.event.OptionsChangedEvent; import mekhq.campaign.personnel.PersonnelOptions; import mekhq.campaign.personnel.SkillType; @@ -64,6 +65,8 @@ * @author Justin "Windchild" Bowen */ public class CampaignPreset { + static final private Version LAST_COMPATIBLE_VERSION = new Version("0.50.03-SNAPSHOT"); + private static final MMLogger logger = MMLogger.create(CampaignPreset.class); // region Variable Declarations @@ -439,6 +442,14 @@ public static List loadCampaignPresetsFromDirectory(final @Nulla return null; } + if (LAST_COMPATIBLE_VERSION.isLowerThan(version)) { + String message = String.format( + "Cannot parse Campaign Preset from %s in newer version %s.", + version.toString(), MHQConstants.VERSION); + logger.error(message); + return null; + } + final CampaignPreset preset = new CampaignPreset(); try { for (int x = 0; x < nl.getLength(); x++) { diff --git a/MekHQ/src/mekhq/GameThread.java b/MekHQ/src/mekhq/GameThread.java index 15dc857b7f4..8ea76557a28 100644 --- a/MekHQ/src/mekhq/GameThread.java +++ b/MekHQ/src/mekhq/GameThread.java @@ -23,9 +23,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; import io.sentry.Sentry; import megamek.client.AbstractClient; @@ -33,8 +31,7 @@ import megamek.client.CloseClientListener; import megamek.client.bot.BotClient; import megamek.client.bot.princess.Princess; -import megamek.client.ui.swing.ClientGUI; -import megamek.client.ui.swing.MegaMekGUI; +import megamek.client.ui.swing.*; import megamek.client.ui.swing.util.MegaMekController; import megamek.common.Entity; import megamek.common.MapSettings; @@ -54,7 +51,8 @@ class GameThread extends Thread implements CloseClientListener { protected String myname; protected String password; protected Client client; - protected ClientGUI swingGui; + protected IClientGUI swingGui; + protected ILocalBots localBots; protected MegaMekController controller; protected MekHQ app; protected Campaign campaign; @@ -137,16 +135,21 @@ public Client getClient() { return client; } + protected Map getLocalBots() { + return localBots.getLocalBots(); + } + @Override public void run() { client.addCloseClientListener(this); - if (swingGui != null) { - for (AbstractClient client2 : swingGui.getLocalBots().values()) { + if (!getLocalBots().isEmpty()) { + for (AbstractClient client2 : getLocalBots().values()) { client2.die(); } - swingGui.getLocalBots().clear(); + getLocalBots().clear(); } + createController(); swingGui = new ClientGUI(client, controller); controller.clientgui = swingGui; @@ -268,9 +271,9 @@ public void run() { for (int i = 0; i < scenario.getNumBots(); i++) { BotForce bf = scenario.getBotForce(i); String name = bf.getName(); - if (swingGui.getLocalBots().containsKey(name)) { + if (getLocalBots().containsKey(name)) { int append = 2; - while (swingGui.getLocalBots().containsKey(name + append)) { + while (getLocalBots().containsKey(name + append)) { append++; } name += append; @@ -284,7 +287,7 @@ public void run() { logger.error( String.format("Could not connect with Bot name %s", bf.getName()), e); } - swingGui.getLocalBots().put(name, botClient); + getLocalBots().put(name, botClient); // chill out while bot is created and connects to megamek Thread.sleep(MekHQ.getMHQOptions().getStartGameDelay()); @@ -299,7 +302,7 @@ public void run() { Sentry.captureException(e); logger.error("", e); } finally { - swingGui.setDisconnectQuietly(true); + disconnectGuiSilently(); client.die(); client = null; swingGui = null; @@ -307,6 +310,12 @@ public void run() { } } + protected void disconnectGuiSilently() { + if (swingGui != null && swingGui instanceof IDisconnectSilently disconnectSilently) { + disconnectSilently.setDisconnectQuietly(true); + } + } + /** * wait for the server to add the bot client, then send starting position, * camo, and entities diff --git a/MekHQ/src/mekhq/IconPackage.java b/MekHQ/src/mekhq/IconPackage.java index 363403c2917..49f03269847 100644 --- a/MekHQ/src/mekhq/IconPackage.java +++ b/MekHQ/src/mekhq/IconPackage.java @@ -24,7 +24,7 @@ /** * This is a convenience class that will keep all the various graphics - * + * * @author Jay Lawson */ public class IconPackage { @@ -53,10 +53,17 @@ public class IconPackage { loadingScreenImages.put(1921, "data/images/misc/MekHQ Load_spooky_uhd.png"); } + private final TreeMap autoResolveScreenImages = new TreeMap<>(Map.of( + 0, "data/images/misc/MekHQ AutoResolve.png")); + public TreeMap getLoadingScreenImages() { return loadingScreenImages; } + public TreeMap getAutoResolveScreenImages() { + return autoResolveScreenImages; + } + public TreeMap getStartupScreenImagesScreenImages() { return startupScreenImages; } diff --git a/MekHQ/src/mekhq/MekHQ.java b/MekHQ/src/mekhq/MekHQ.java index 7689c4b8384..a489a9ac386 100644 --- a/MekHQ/src/mekhq/MekHQ.java +++ b/MekHQ/src/mekhq/MekHQ.java @@ -26,16 +26,22 @@ import megamek.MegaMek; import megamek.SuiteConstants; import megamek.client.Client; +import megamek.client.HeadlessClient; import megamek.client.bot.princess.BehaviorSettings; -import megamek.client.generator.RandomNameGenerator; -import megamek.client.generator.RandomUnitGenerator; +import megamek.client.ui.dialogs.AutoResolveChanceDialog; +import megamek.client.ui.dialogs.AutoResolveProgressDialog; +import megamek.client.ui.dialogs.AutoResolveSimulationLogDialog; import megamek.client.ui.preferences.PreferencesNode; import megamek.client.ui.preferences.SuitePreferences; import megamek.client.ui.swing.GUIPreferences; import megamek.client.ui.swing.gameConnectionDialogs.ConnectDialog; import megamek.client.ui.swing.gameConnectionDialogs.HostDialog; import megamek.client.ui.swing.util.UIUtil; +import megamek.common.Board; import megamek.common.annotations.Nullable; +import megamek.common.autoresolve.acar.SimulatedClient; +import megamek.common.autoresolve.converter.SingletonForces; +import megamek.common.autoresolve.event.AutoResolveConcludedEvent; import megamek.common.event.*; import megamek.common.net.marshalling.SanityInputFilter; import megamek.logging.MMLogger; @@ -44,26 +50,24 @@ import megameklab.MegaMekLab; import mekhq.campaign.Campaign; import mekhq.campaign.CampaignController; -import mekhq.campaign.Kill; import mekhq.campaign.ResolveScenarioTracker; -import mekhq.campaign.ResolveScenarioTracker.PersonStatus; -import mekhq.campaign.event.ScenarioResolvedEvent; +import mekhq.campaign.autoresolve.AtBSetupForces; +import mekhq.campaign.handler.PostScenarioDialogHandler; import mekhq.campaign.handler.XPHandler; import mekhq.campaign.mission.AtBScenario; import mekhq.campaign.mission.Scenario; import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.autoAwards.AutoAwardsController; -import mekhq.campaign.personnel.enums.PersonnelStatus; import mekhq.campaign.stratcon.StratconRulesManager; import mekhq.campaign.unit.Unit; import mekhq.gui.CampaignGUI; +import mekhq.gui.dialog.ChooseMulFilesDialog; import mekhq.gui.dialog.ResolveScenarioWizardDialog; -import mekhq.gui.dialog.RetirementDefectionDialog; import mekhq.gui.panels.StartupScreenPanel; import mekhq.gui.preferences.StringPreference; import mekhq.gui.utilities.ObservableString; import mekhq.service.AutosaveService; import mekhq.service.IAutosaveService; +import mekhq.utilities.MHQInternationalization; import javax.swing.*; import javax.swing.text.DefaultEditorKit; @@ -75,10 +79,11 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.ObjectInputFilter.Config; -import java.util.HashMap; +import java.lang.management.ManagementFactory; import java.util.List; import java.util.UUID; -import java.util.stream.Stream; + +import static megamek.MMConstants.LOCALHOST_IP; /** * The main class of the application. @@ -113,11 +118,12 @@ public class MekHQ implements GameListener { private CampaignController campaignController; private CampaignGUI campaignGUI; - private IconPackage iconPackage = new IconPackage(); + private final IconPackage iconPackage = new IconPackage(); private final IAutosaveService autosaveService; // endregion Variable Declarations private static final SanityInputFilter sanityInputFilter = new SanityInputFilter(); + private static final String defaultTheme = "com.formdev.flatlaf.FlatDarculaLaf"; public static SuitePreferences getMHQPreferences() { return mhqPreferences; @@ -207,7 +213,7 @@ private static void setUserPreferences() { PreferencesNode preferences = MekHQ.getMHQPreferences().forClass(MekHQ.class); // TODO: complete integration of Suite Preferences, including GUIPreferences - selectedTheme = new ObservableString("selectedTheme", GUIPreferences.UI_THEME); + selectedTheme = new ObservableString("selectedTheme", ""); selectedTheme.addPropertyChangeListener(new MekHqPropertyChangedListener()); preferences.manage(new StringPreference(selectedTheme)); @@ -239,14 +245,17 @@ private static void setUserPreferences() { } } - public void exit() { - int savePrompt = JOptionPane.showConfirmDialog(null, "Do you want to save the game before quitting MekHQ?", - "Save First?", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); - if ((savePrompt == JOptionPane.CANCEL_OPTION) || (savePrompt == JOptionPane.CLOSED_OPTION)) { - return; - } else if ((savePrompt == JOptionPane.YES_OPTION) && !getCampaigngui().saveCampaign(null)) { - // When the user did not actually save the game, don't close MHQ - return; + public void exit(boolean includeSavePrompt) { + if (includeSavePrompt) { + int savePrompt = JOptionPane.showConfirmDialog(null, + "Do you want to save the game before quitting MekHQ?", + "Save First?", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); + if ((savePrompt == JOptionPane.CANCEL_OPTION) || (savePrompt == JOptionPane.CLOSED_OPTION)) { + return; + } else if ((savePrompt == JOptionPane.YES_OPTION) && !getCampaigngui().saveCampaign(null)) { + // When the user did not actually save the game, don't close MHQ + return; + } } // Actually close MHQ @@ -303,6 +312,9 @@ public static void main(String... args) { // Finally, let's handle startup SwingUtilities.invokeLater(() -> MekHQ.getInstance().startup()); + + // log jvm parameters + logger.info(ManagementFactory.getRuntimeMXBean().getInputArguments()); } public static void initializeLogging(final String originProject) { @@ -442,15 +454,20 @@ public void startHost(Scenario scenario, boolean loadSavegame, List meks, stopHost(); return; } - - client = new Client(playerName, "127.0.0.1", port); + // Refactor this into a factory + var useExperimentalPacarGui = getCampaign().getCampaignOptions().isAutoResolveExperimentalPacarGuiEnabled(); + if (autoResolveBehaviorSettings != null && useExperimentalPacarGui) { + client = new HeadlessClient(playerName, LOCALHOST_IP, port); + } else { + client = new Client(playerName, LOCALHOST_IP, port); + } client.getGame().addGameListener(this); currentScenario = scenario; - // Start the game thread + // Start the game thread - also refactor this into a factory if (getCampaign().getCampaignOptions().isUseAtB() && (scenario instanceof AtBScenario)) { - gameThread = new AtBGameThread(playerName, password, client, this, meks, (AtBScenario) scenario, autoResolveBehaviorSettings); + gameThread = new AtBGameThread(playerName, password, client, this, meks, (AtBScenario) scenario, autoResolveBehaviorSettings, useExperimentalPacarGui, true); } else { gameThread = new GameThread(playerName, password, client, this, meks, scenario); } @@ -516,141 +533,192 @@ public void gamePhaseChange(GamePhaseChangeEvent e) { // Why Empty? } + /** + * This method is called automatically when the megamek game is over. + * @param gve + */ @Override public void gameVictory(PostGameResolution gve) { // Prevent double run if (gameThread.stopRequested()) { return; } - try { - boolean control = JOptionPane.showConfirmDialog(campaignGUI.getFrame(), - "Did your side control the battlefield at the end of the scenario?", "Control of Battlefield?", - JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION; + boolean control = yourSideControlsTheBattlefieldDialogAsk( + MHQInternationalization.getText("ResolveDialog.control.message"), + MHQInternationalization.getText("ResolveDialog.control.title")); ResolveScenarioTracker tracker = new ResolveScenarioTracker(currentScenario, getCampaign(), control); tracker.setClient(gameThread.getClient()); tracker.setEvent(gve); tracker.processGame(); - ResolveScenarioWizardDialog resolveDialog = new ResolveScenarioWizardDialog(campaignGUI.getFrame(), true, - tracker); + ResolveScenarioWizardDialog resolveDialog = + new ResolveScenarioWizardDialog(campaignGUI.getCampaign(), campaignGUI.getFrame(), + true, tracker); resolveDialog.setVisible(true); - if (!getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()) { - RetirementDefectionDialog rdd = new RetirementDefectionDialog(campaignGUI, - campaignGUI.getCampaign().getMission(currentScenario.getMissionId()), false); - - if (!rdd.wasAborted()) { - getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments()); - } + if (resolveDialog.wasAborted()) { + return; } - if (getCampaign().getCampaignOptions().isEnableAutoAwards()) { - HashMap personnel = new HashMap<>(); - HashMap> scenarioKills = new HashMap<>(); - - for (UUID personId : tracker.getPeopleStatus().keySet()) { - Person person = getCampaign().getPerson(personId); - PersonStatus status = tracker.getPeopleStatus().get(personId); - int injuryCount = 0; + PostScenarioDialogHandler.handle(campaignGUI, getCampaign(), currentScenario, tracker, control); - if (!person.getStatus().isDead() || getCampaign().getCampaignOptions().isIssuePosthumousAwards()) { - if (status.getHits() > person.getHitsPrior()) { - injuryCount = status.getHits() - person.getHitsPrior(); - } - } + } catch (Exception ex) { + logger.error(ex, "gameVictory()"); + } finally { + gameThread.requestStop(); + } + } - personnel.put(personId, injuryCount); - scenarioKills.put(personId, tracker.getPeopleStatus().get(personId).getKills()); - } + /** + * This method is called when player wants to manually resolve the scenario providing MUL files. + */ + public void resolveScenario(Scenario selectedScenario) { + if (null == selectedScenario) { + return; + } + boolean control = yourSideControlsTheBattlefieldDialogAsk( + MHQInternationalization.getText("ResolveDialog.control.message"), + MHQInternationalization.getText("ResolveDialog.control.title")); - boolean isCivilianHelp = false; + ResolveScenarioTracker tracker = new ResolveScenarioTracker(selectedScenario, getCampaign(), control); - if (tracker.getScenario() instanceof AtBScenario) { - isCivilianHelp = ((AtBScenario) tracker.getScenario()) - .getScenarioType() == AtBScenario.CIVILIANHELP; - } + ChooseMulFilesDialog chooseFilesDialog = new ChooseMulFilesDialog(campaignGUI.getFrame(), true, tracker); + chooseFilesDialog.setVisible(true); + if (chooseFilesDialog.wasCancelled()) { + return; + } - AutoAwardsController autoAwardsController = new AutoAwardsController(); - autoAwardsController.PostScenarioController(getCampaign(), personnel, scenarioKills, isCivilianHelp); - } + ResolveScenarioWizardDialog resolveDialog = new ResolveScenarioWizardDialog(getCampaign(), + campaignGUI.getFrame(), true, tracker); + resolveDialog.setVisible(true); - for (UUID personId : tracker.getPeopleStatus().keySet()) { - Person person = getCampaign().getPerson(personId); + if (resolveDialog.wasAborted()) { + return; + } - if (person.getStatus() == PersonnelStatus.MIA && !control) { - person.changeStatus(campaignGUI.getCampaign(), campaignGUI.getCampaign().getLocalDate(), - PersonnelStatus.POW); - } - } + PostScenarioDialogHandler.handle(campaignGUI, getCampaign(), selectedScenario, tracker, control); + } - // we need to trigger ScenarioResolvedEvent before stopping the thread or - // currentScenario may become null - MekHQ.triggerEvent(new ScenarioResolvedEvent(currentScenario)); - gameThread.requestStop(); + private boolean yourSideControlsTheBattlefieldDialogAsk(String message, String title) { + return JOptionPane.showConfirmDialog(campaignGUI.getFrame(), + message, title, + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION; + } - // MegaMek dumps these in the deployment phase to free memory - if (getCampaign().getCampaignOptions().isUseAtB()) { - RandomUnitGenerator.getInstance(); - RandomNameGenerator.getInstance(); - } - // MegaMek creates some temporary files that MHQ needs to remove between runs - final File tempImageDirectory = new File("data/images/temp"); - if (tempImageDirectory.isDirectory()) { - // This can't be null because of the above - Stream.of(tempImageDirectory.listFiles()).filter(file -> file.getName().endsWith(".png")) - .forEach(File::delete); - } - } catch (Exception ex) { - logger.error(ex, "gameVictory()"); - } - } + // region Event Handling Methods that are not implemented + // These methods are here because MekHQ implements GameListener + // but currently only needs to hear the post game resolution event @Override public void gamePlayerChange(GamePlayerChangeEvent e) { - // Why Empty? } @Override public void gamePlayerChat(GamePlayerChatEvent e) { - // Why Empty? } @Override public void gamePlayerConnected(GamePlayerConnectedEvent e) { - // Why Empty? } @Override public void gamePlayerDisconnected(GamePlayerDisconnectedEvent e) { - // Why Empty? } @Override public void gameReport(GameReportEvent e) { - // Why Empty? } @Override public void gameSettingsChange(GameSettingsChangeEvent e) { - // Why Empty? } @Override public void gameTurnChange(GameTurnChangeEvent e) { - // Why Empty? } @Override public void gameClientFeedbackRequest(GameCFREvent e) { - // Why Empty? } + // end region public IconPackage getIconPackage() { return iconPackage; } + /** + * This method is called when the player wants to auto resolve the scenario using ACAR method + * @param units The list of player units involved in the scenario + */ + public void startAutoResolve(AtBScenario scenario, List units) { + + this.autosaveService.requestBeforeMissionAutosave(getCampaign()); + + if (getCampaign().getCampaignOptions().isAutoResolveVictoryChanceEnabled()) { + var proceed = AutoResolveChanceDialog + .showDialog( + campaignGUI.getFrame(), + getCampaign().getCampaignOptions().getAutoResolveNumberOfScenarios(), + Runtime.getRuntime().availableProcessors(), + 1, + new AtBSetupForces(getCampaign(), units, scenario, new SingletonForces()), + new Board(scenario.getBaseMapX(), scenario.getBaseMapY())) == JOptionPane.YES_OPTION; + if (!proceed) { + return; + } + } + + var event = AutoResolveProgressDialog.showDialog( + campaignGUI.getFrame(), + new AtBSetupForces(getCampaign(), units, scenario, new SingletonForces()), + new Board(scenario.getBaseMapX(), scenario.getBaseMapY())); + + var autoResolveBattleReport = new AutoResolveSimulationLogDialog(campaignGUI.getFrame(), event.getLogFile()); + autoResolveBattleReport.setModal(true); + autoResolveBattleReport.setVisible(true); + + autoResolveConcluded(event, scenario); + } + + /** + * This method is called when the auto resolve game is over. + * @param autoResolveConcludedEvent The event that contains the results of the auto resolve game. + */ + public void autoResolveConcluded(AutoResolveConcludedEvent autoResolveConcludedEvent, AtBScenario scenario) { + try { + String message = autoResolveConcludedEvent.controlledScenario() ? + MHQInternationalization.getText("AutoResolveDialog.message.victory") : + MHQInternationalization.getText("AutoResolveDialog.message.defeat"); + String title = autoResolveConcludedEvent.controlledScenario() ? + MHQInternationalization.getText("AutoResolveDialog.victory") : + MHQInternationalization.getText("AutoResolveDialog.defeat"); + boolean control = yourSideControlsTheBattlefieldDialogAsk(message, title); + + ResolveScenarioTracker tracker = new ResolveScenarioTracker(scenario, getCampaign(), control); + tracker.setClient(new SimulatedClient(autoResolveConcludedEvent.getGame())); + tracker.setEvent(autoResolveConcludedEvent); + tracker.processGame(); + + ResolveScenarioWizardDialog resolveDialog = + new ResolveScenarioWizardDialog(getCampaign(), campaignGUI.getFrame(), + true, tracker); + resolveDialog.setVisible(true); + if (resolveDialog.wasAborted()) { + for (UUID personId : tracker.getPeopleStatus().keySet()) { + Person person = getCampaign().getPerson(personId); + person.setHits(person.getHitsPrior()); + } + return; + } + PostScenarioDialogHandler.handle(campaignGUI, getCampaign(), scenario, tracker, + autoResolveConcludedEvent.controlledScenario()); + } catch (Exception ex) { + logger.error("Error during auto resolve concluded", ex); + } + } + /* * Access methods for event bus. */ @@ -678,7 +746,7 @@ private void initEventHandlers() { } private static void setLookAndFeel(String themeName) { - final String theme = themeName.isBlank() ? "com.formdev.flatlaf.FlatDarculaLaf" : themeName; + final String theme = themeName.isBlank() || themeName.equals("UITheme") ? defaultTheme : themeName; Runnable runnable = () -> { try { @@ -692,6 +760,23 @@ private static void setLookAndFeel(String themeName) { addOSXKeyStrokes((InputMap) UIManager.get("TextArea.focusInputMap")); } + UIUtil.updateAfterUiChange(); + return; + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException + | UnsupportedLookAndFeelException e) { + logger.error(e, "setLookAndFeel()"); + } + try { + UIManager.setLookAndFeel(defaultTheme); + if (System.getProperty("os.name", "").startsWith("Mac OS X")) { + // Ensure OSX key bindings are used for copy, paste etc + addOSXKeyStrokes((InputMap) UIManager.get("EditorPane.focusInputMap")); + addOSXKeyStrokes((InputMap) UIManager.get("FormattedTextField.focusInputMap")); + addOSXKeyStrokes((InputMap) UIManager.get("TextField.focusInputMap")); + addOSXKeyStrokes((InputMap) UIManager.get("TextPane.focusInputMap")); + addOSXKeyStrokes((InputMap) UIManager.get("TextArea.focusInputMap")); + } + UIUtil.updateAfterUiChange(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { @@ -703,7 +788,7 @@ private static void setLookAndFeel(String themeName) { public static void updateGuiScaling() { System.setProperty("flatlaf.uiScale", Double.toString(GUIPreferences.getInstance().getGUIScale())); - setLookAndFeel(GUIPreferences.getInstance().getUITheme()); + setLookAndFeel(selectedTheme.getValue()); } private static class MekHqPropertyChangedListener implements PropertyChangeListener { diff --git a/MekHQ/src/mekhq/Utilities.java b/MekHQ/src/mekhq/Utilities.java index 1e4899d9fea..6d57793f697 100644 --- a/MekHQ/src/mekhq/Utilities.java +++ b/MekHQ/src/mekhq/Utilities.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2009 - Jay Lawson (jaylawson39 at yahoo.com). All Rights Reserved. - * Copyright (c) 2019-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2019-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -40,6 +40,7 @@ import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.Phenotype; import mekhq.campaign.unit.CrewType; +import mekhq.campaign.unit.ITransportAssignment; import mekhq.campaign.unit.Unit; import mekhq.campaign.unit.UnitTechProgression; import org.apache.commons.csv.CSVFormat; @@ -58,6 +59,9 @@ import java.util.Map.Entry; import java.util.function.Consumer; +import static java.lang.Math.max; +import static mekhq.campaign.personnel.SkillType.*; + public class Utilities { private static final MMLogger logger = MMLogger.create(Utilities.class); @@ -344,11 +348,11 @@ public static int generateExpLevel(int bonus) { int roll = MathUtility.clamp(Compute.d6(2) + bonus, 1, 12); return switch (roll) { - case 1 -> SkillType.EXP_ULTRA_GREEN; - case 2, 3, 4, 5 -> SkillType.EXP_GREEN; - case 6, 7, 8, 9 -> SkillType.EXP_REGULAR; - case 10, 11 -> SkillType.EXP_VETERAN; - case 12 -> SkillType.EXP_ELITE; + case 1 -> EXP_ULTRA_GREEN; + case 2, 3, 4, 5 -> EXP_GREEN; + case 6, 7, 8, 9 -> EXP_REGULAR; + case 10, 11 -> EXP_VETERAN; + case 12 -> EXP_ELITE; default -> throw new IllegalStateException("Unexpected value in mekhq/Utilities.java/generateExpLevel: " + roll); }; @@ -375,7 +379,7 @@ public static int randomSkillFromTarget(int target) { } else if (dice == 6) { target += 2; } - return Math.max(target, 0); + return max(target, 0); } public static Map> genRandomCrewWithCombinedSkill(Campaign c, @@ -828,42 +832,100 @@ public static boolean rollProbability(int prob) { } /** - * Calculates the age of a character based on their experience level and clan status. + * Calculates the age based on the experience level and clan status. + * + *

    This method computes the age of a character by rolling a given number of exploding + * six-sided dice (d6) depending on the specified experience level. It starts with a base age + * and adds results of the rolls. If the character is part of a Clan, the dice rolls are halved + * (rounded up). Additionally, for all experience levels other than {@code EXP_NONE}, the final + * result is clamped to a minimum of 18.

    + * + *

    An exploding die roll occurs if the roll is 6. In such cases, another die is rolled, and + * the result is added to the previous roll (minus one).

    + * + *

    The calculated average age for each experience level is shown below:

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Average Ages by Experience Level
    Experience LevelNon-Clan Average AgeClan Average Age
    EXP_NONE29.429.4
    EXP_GREEN1818
    EXP_REGULAR24.418.2
    EXP_VETERAN28.620.3
    EXP_ELITE32.822.4
    * - * @param expLevel the experience level of the character represented by an integer value. - * @param clan a boolean value indicating whether the character is a clan character. - * @return the age of the character calculated based on the provided expLevel and clan. + * @param experienceLevel The experience level of the character. Must be one of the constants + * defined in {@code SkillType}. + * @param isClan {@code true} if the character is part of a Clan, in which case dice rolls are + * halved (rounded up), {@code false} otherwise. + * @return The calculated age of the character based on the input parameters. */ - public static int getAgeByExpLevel(int expLevel, boolean clan) { - int baseAge = 18; - - int nDice = switch (expLevel) { - case SkillType.EXP_NONE -> 7; - case SkillType.EXP_GREEN -> 1; - case SkillType.EXP_REGULAR -> 2; - case SkillType.EXP_VETERAN -> 4; - case SkillType.EXP_ELITE -> 8; - default -> 0; - }; + public static int getAgeByExpLevel(int experienceLevel, boolean isClan) { + if (experienceLevel > EXP_ELITE) { + experienceLevel = EXP_ELITE; + } + + int baseAge = 16; + + if (experienceLevel == EXP_NONE) { + baseAge = 0; // only use the result of the dice roll + } int age = baseAge; - for (int i = 0; i < nDice; i++) { + // How many dice to roll + int diceCount = switch (experienceLevel) { + case EXP_NONE -> 7; + case EXP_GREEN -> 1; + case EXP_REGULAR -> 2; + case EXP_VETERAN -> 3; + case EXP_ELITE -> 4; + default -> 0; + }; + + // Handle exploding dice + for (int i = 0; i < diceCount; i++) { int roll = Compute.d6(1); if (roll == 6) { roll += (Compute.d6() - 1); } - if (clan) { + if (isClan && (experienceLevel != EXP_NONE)) { roll = (int) Math.ceil(roll / 2.0); } age += roll; } - if (expLevel == SkillType.EXP_NONE) { - age -= baseAge; // only use the result of the dice roll + // Clamp age, if necessary + if (experienceLevel != EXP_NONE) { + age = max(16, age); } return age; @@ -889,7 +951,7 @@ public static Money[] readMoneyArray(Node node) { public static Money[] readMoneyArray(Node node, int minimumSize) { String[] values = node.getTextContent().split(","); - Money[] result = new Money[Math.max(values.length, minimumSize)]; + Money[] result = new Money[max(values.length, minimumSize)]; for (int i = 0; i < values.length; i++) { result[i] = Money.fromXmlString(values[i]); @@ -1218,6 +1280,89 @@ public static void loadPlayerTransports(int trnId, Set toLoad, Client c } } + /** + * Handles loading a player's transported units onto their transports once a + * megamek scenario has actually started. + * + * + * @param trnId - The MM id of the transport entity we want to load + * @param toLoad - Map of entity ids and transport assignments for the units we want to load + * @param client - the player's Client instance + * @param loadTactical - Should "tactical"-ly transported units be loaded? + * @param isAlreadyReset - transports loaded via "Ship" will have been reset once, don't do it again here + * @see mekhq.campaign.enums.CampaignTransportType#TACTICAL_TRANSPORT + * @see ITransportAssignment + */ + public static void loadPlayerTransports(int trnId, Map toLoad, Client client, + boolean loadTactical, boolean isAlreadyReset) { + Set alreadyTransportedEntities = new HashSet<>(); + + if (!loadTactical) { + // Nothing to do. Get outta here! + return; + } + Entity transport = client.getEntity(trnId); + + if (transport == null) { + return; + } + + // Reset transporter status, as currentSpace might still retain updates from + // when the Unit + // was assigned to the Transport on the TO&E tab + if (!isAlreadyReset) { + transport.resetTransporter(); + } + + for (int id : toLoad.keySet()) { + Entity cargo = client.getEntity(id); + if (cargo == null) { + continue; + } + + ITransportAssignment transportAssignment = toLoad.get(id); + + if (transportAssignment == null) { + continue; + } + + // Find a bay with space in it and update that space so the next unit can + // process, unless the unit isn't being loaded into a bay + if (transportAssignment.getTransportedLocation() instanceof Bay bay) { + cargo.setTargetBay(bay.getBayNumber()); + } else { + if (transportAssignment.isTransportedInBay()) { + cargo.setTargetBay(selectBestBayFor(cargo, transport)); + } + } + } + + // If we reset the transporters for the Ship transport, we'll need to save those units + // before we remove the fake capacity that was removed by selectBestBayFor + if (isAlreadyReset) { + alreadyTransportedEntities.addAll(transport.getLoadedUnits()); + } + + // Reset transporter status again so that sendLoadEntity can process correctly + transport.resetTransporter(); + + //Restore the Ship transported entities + for (Entity alreadyTransportedEntity : alreadyTransportedEntities) { + transport.load(alreadyTransportedEntity, alreadyTransportedEntity.getTargetBay()); + } + for (int id : toLoad.keySet()) { + Entity cargo = client.getEntity(id); + if (!transport.canLoad(cargo, false)) { + continue; + } + + //Transported units should deploy on their transport's turn + cargo.setDeployRound(transport.getDeployRound()); + + sendLoadEntity(client, id, trnId, cargo); + } + } + private static void sendLoadEntity(Client client, int id, int trnId, Entity cargo) { client.sendLoadEntity(id, trnId, cargo.getTargetBay()); // Add a wait to make sure that we don't start processing client.sendLoadEntity @@ -1324,7 +1469,7 @@ public static int selectBestBayFor(Entity cargo, Entity transport) { /** * @param shortNameRaw complete Entity name as returned by getShortNameRaw() * @throws EntityLoadingException - */ + */ public static MekSummary retrieveUnit(String shortNameRaw) throws EntityLoadingException { MekSummary summary = MekSummaryCache.getInstance().getMek(shortNameRaw); diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index a610f94fe1e..a744a0583ff 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -2,7 +2,7 @@ * Campaign.java * * Copyright (c) 2009 - Jay Lawson (jaylawson39 at yahoo.com). All Rights Reserved. - * Copyright (c) 2022 - 2024 The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022 - 2025 The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,6 +21,7 @@ */ package mekhq.campaign; +import megamek.Version; import megamek.client.bot.princess.BehaviorSettings; import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.generator.RandomGenderGenerator; @@ -49,11 +50,12 @@ import mekhq.Utilities; import mekhq.campaign.Quartermaster.PartAcquisitionResult; import mekhq.campaign.againstTheBot.AtBConfiguration; +import mekhq.campaign.enums.CampaignTransportType; import mekhq.campaign.event.*; import mekhq.campaign.finances.*; import mekhq.campaign.finances.enums.TransactionType; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.force.Force; -import mekhq.campaign.force.StrategicFormation; import mekhq.campaign.icons.StandardForceIcon; import mekhq.campaign.icons.UnitIcon; import mekhq.campaign.log.HistoricalLogEntry; @@ -68,15 +70,18 @@ import mekhq.campaign.market.unitMarket.DisabledUnitMarket; import mekhq.campaign.mission.*; import mekhq.campaign.mission.atb.AtBScenarioFactory; -import mekhq.campaign.mission.enums.AtBLanceRole; import mekhq.campaign.mission.enums.AtBMoraleLevel; +import mekhq.campaign.mission.enums.CombatRole; import mekhq.campaign.mission.enums.MissionStatus; import mekhq.campaign.mission.enums.ScenarioStatus; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; +import mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType; import mekhq.campaign.mod.am.InjuryUtil; import mekhq.campaign.parts.*; import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.parts.equipment.EquipmentPart; +import mekhq.campaign.parts.equipment.HeatSink; import mekhq.campaign.parts.equipment.MissingEquipmentPart; import mekhq.campaign.personnel.*; import mekhq.campaign.personnel.autoAwards.AutoAwardsController; @@ -102,11 +107,13 @@ import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.rating.UnitRatingMethod; import mekhq.campaign.storyarc.StoryArc; +import mekhq.campaign.stratcon.StratconCampaignState; import mekhq.campaign.stratcon.StratconContractInitializer; import mekhq.campaign.stratcon.StratconRulesManager; import mekhq.campaign.stratcon.StratconTrackState; import mekhq.campaign.unit.CrewType; import mekhq.campaign.unit.*; +import mekhq.campaign.unit.enums.TransporterType; import mekhq.campaign.universe.*; import mekhq.campaign.universe.Planet.PlanetaryEvent; import mekhq.campaign.universe.PlanetarySystem.PlanetarySystemEvent; @@ -142,15 +149,28 @@ import java.util.Map.Entry; import java.util.stream.Collectors; -import static mekhq.campaign.force.StrategicFormation.recalculateStrategicFormations; +import static java.lang.Math.floor; +import static java.lang.Math.max; +import static java.lang.Math.round; +import static mekhq.campaign.enums.CampaignTransportType.SHIP_TRANSPORT; +import static mekhq.campaign.enums.CampaignTransportType.TACTICAL_TRANSPORT; +import static mekhq.campaign.force.CombatTeam.getStandardForceSize; +import static mekhq.campaign.force.CombatTeam.recalculateCombatTeams; import static mekhq.campaign.market.contractMarket.ContractAutomation.performAutomatedActivation; -import static mekhq.campaign.personnel.SkillType.S_ADMIN; +import static mekhq.campaign.mission.AtBContract.pickRandomCamouflage; +import static mekhq.campaign.mission.resupplyAndCaches.PerformResupply.performResupply; +import static mekhq.campaign.mission.resupplyAndCaches.ResupplyUtilities.processAbandonedConvoy; +import static mekhq.campaign.parts.enums.PartQuality.QUALITY_A; import static mekhq.campaign.personnel.backgrounds.BackgroundsController.randomMercenaryCompanyNameGenerator; import static mekhq.campaign.personnel.education.EducationController.getAcademy; +import static mekhq.campaign.personnel.education.TrainingCombatTeams.processTrainingCombatTeams; import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.Payout.isBreakingContract; +import static mekhq.campaign.stratcon.SupportPointNegotiation.negotiateAdditionalSupportPoints; import static mekhq.campaign.unit.Unit.SITE_FACILITY_BASIC; +import static mekhq.campaign.universe.Factions.getFactionLogo; import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; -import static mekhq.campaign.mission.AtBContract.pickRandomCamouflage; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; + /** * The main campaign class, keeps track of teams and units * @@ -162,6 +182,7 @@ public class Campaign implements ITechManager { public static final String REPORT_LINEBREAK = "

    "; private UUID id; + private Version version; // this is dynamically populated on load and doesn't need to be saved // we have three things to track: (1) teams, (2) units, (3) repair tasks // we will use the same basic system (borrowed from MegaMek) for tracking @@ -170,7 +191,8 @@ public class Campaign implements ITechManager { // and more still - we're tracking DropShips and WarShips in a separate set so // that we can assign units to transports private final Hangar units = new Hangar(); - private final Set transportShips = new HashSet<>(); + CampaignTransporterMap shipTransporters = new CampaignTransporterMap(this, SHIP_TRANSPORT); + CampaignTransporterMap tacticalTransporters = new CampaignTransporterMap(this, TACTICAL_TRANSPORT); private final Map personnel = new LinkedHashMap<>(); private Warehouse parts = new Warehouse(); private final TreeMap forceIds = new TreeMap<>(); @@ -178,6 +200,10 @@ public class Campaign implements ITechManager { private final TreeMap scenarios = new TreeMap<>(); private final Map> kills = new HashMap<>(); + // This maps PartInUse ToString() results to doubles, representing a mapping + // of parts in use to their requested stock percentages to make these values persistent + private Map partsInUseRequestedStockMap = new LinkedHashMap<>(); + private transient final UnitNameTracker unitNameTracker = new UnitNameTracker(); private int astechPool; @@ -203,7 +229,7 @@ public class Campaign implements ITechManager { // hierarchically structured Force object to define TO&E private Force forces; - private Hashtable strategicFormations; // AtB + private Hashtable combatTeams; // AtB private Faction faction; private int techFactionCode; @@ -217,7 +243,8 @@ public class Campaign implements ITechManager { private Boolean fieldKitchenWithinCapacity; - // this is updated and used per gaming session, it is enabled/disabled via the Campaign options + // this is updated and used per gaming session, it is enabled/disabled via the + // Campaign options // we're re-using the LogEntry class that is used to store Personnel entries public LinkedList inMemoryLogHistory = new LinkedList<>(); @@ -253,6 +280,7 @@ public class Campaign implements ITechManager { private transient AbstractDivorce divorce; private transient AbstractMarriage marriage; private transient AbstractProcreation procreation; + private List personnelWhoAdvancedInXP; private RetirementDefectionTracker retirementDefectionTracker; private List turnoverRetirementInformation; @@ -278,6 +306,23 @@ public class Campaign implements ITechManager { private BehaviorSettings autoResolveBehaviorSettings; private List automatedMothballUnits; + //options relating to parts in use and restock + private boolean ignoreMothballed; + private boolean topUpWeekly; + private PartQuality ignoreSparesUnderQuality; + + /** + * Represents the different types of administrative specializations. + * Each specialization corresponds to a distinct administrative role + * within the organization. + * + *

    These specializations are used to determine administrative roles and responsibilities, + * such as by identifying the most senior administrator for a given role.

    + */ + public enum AdministratorSpecialization { + COMMAND, LOGISTICS, TRANSPORT, HR + } + private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Campaign", MekHQ.getMHQOptions().getLocale()); @@ -318,7 +363,7 @@ public Campaign() { setRankSystemDirect(Ranks.getRankSystemFromCode(Ranks.DEFAULT_SYSTEM_CODE)); forces = new Force(name); forceIds.put(0, forces); - strategicFormations = new Hashtable<>(); + combatTeams = new Hashtable<>(); finances = new Finances(); astechPool = 0; medicPool = 0; @@ -337,6 +382,7 @@ public Campaign() { setDivorce(new DisabledRandomDivorce(getCampaignOptions())); setMarriage(new DisabledRandomMarriage(getCampaignOptions())); setProcreation(new DisabledRandomProcreation(getCampaignOptions())); + personnelWhoAdvancedInXP = new ArrayList<>(); retirementDefectionTracker = new RetirementDefectionTracker(); turnoverRetirementInformation = new ArrayList<>(); atbConfig = null; @@ -348,6 +394,10 @@ public Campaign() { fameAndInfamy = new FameAndInfamyController(); autoResolveBehaviorSettings = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; automatedMothballUnits = new ArrayList<>(); + topUpWeekly = false; + ignoreMothballed = false; + ignoreSparesUnderQuality = QUALITY_A; + } /** @@ -394,6 +444,14 @@ public UUID getId() { return id; } + public void setVersion(Version version) { + this.version = version; + } + + public @Nullable Version getVersion() { + return version; + } + public String getName() { return name; } @@ -433,7 +491,8 @@ public PlanetarySystem getCurrentSystem() { } /** - * Returns the Hiring Hall level from the force's current system on the current date. If there + * Returns the Hiring Hall level from the force's current system on the current + * date. If there * is no hiring hall present, the level is HiringHallLevel.NONE. * * @return The Hiring Hall level of the current system at the present date. @@ -459,74 +518,77 @@ public List getAllForces() { } /** - * Adds a {@link StrategicFormation} to the {@code strategicFormations} {@link Hashtable} using + * Adds a {@link CombatTeam} to the {@code combatTeams} {@link Hashtable} using * {@code forceId} as the key. * - * @param strategicFormation the {@link StrategicFormation} to be added to the {@link Hashtable} + * @param combatTeam the {@link CombatTeam} to be added to the {@link Hashtable} */ - public void addStrategicFormation(StrategicFormation strategicFormation) { - strategicFormations.put(strategicFormation.getForceId(), strategicFormation); + public void addCombatTeam(CombatTeam combatTeam) { + combatTeams.put(combatTeam.getForceId(), combatTeam); } /** - * Removes a {@link StrategicFormation} from the {@code strategicFormations} {@link Hashtable} - * using {@code forceId} as they key. + * Removes a {@link CombatTeam} from the {@code combatTeams} {@link Hashtable} using + * {@code forceId} as the key. * - * @param forceId the key of the {@link StrategicFormation} to be removed from the {@link Hashtable} + * @param forceId the key of the {@link CombatTeam} to be removed from the {@link Hashtable} */ - public void removeStrategicFormation(final int forceId) { - this.strategicFormations.remove(forceId); + public void removeCombatTeam(final int forceId) { + this.combatTeams.remove(forceId); } /** - * Returns the {@link Hashtable} containing all the {@link StrategicFormation} objects after + * Returns the {@link Hashtable} using the combatTeam's {@code forceId} as the key + * and containing all the {@link CombatTeam} objects after * removing the ineligible ones. Although sanitization might not be necessary, it ensures that * there is no need for {@code isEligible()} checks when fetching the {@link Hashtable}. * - * @return the sanitized {@link Hashtable} of {@link StrategicFormation} objects stored in the + * @return the sanitized {@link Hashtable} of {@link CombatTeam} objects stored in the * current campaign. */ - public Hashtable getStrategicFormationsTable() { + public Hashtable getCombatTeamsTable() { // Here we sanitize the list, ensuring ineligible formations have been removed before // returning the hashtable. In theory, this shouldn't be necessary, however, having this // sanitizing step should remove the need for isEligible() checks whenever we fetch the // hashtable. - List formationsToSanitize = new ArrayList<>(); - for (StrategicFormation strategicFormation : strategicFormations.values()) { - if (!strategicFormation.isEligible(this)) { - formationsToSanitize.add(strategicFormation.getForceId()); - try { - Force force = getForce(strategicFormation.getForceId()); - force.setStrategicFormation(false); - } catch (Exception ex) { - // We're not too worried if we can't find the associated Force, - // as this just means it has been deleted at some point and not removed correctly. + for (Force force : getAllForces()) { + int forceId = force.getId(); + if (combatTeams.containsKey(forceId)) { + CombatTeam combatTeam = combatTeams.get(forceId); + + if (combatTeam.isEligible(this)) { + continue; + } + } else { + CombatTeam combatTeam = new CombatTeam(forceId, this); + + if (combatTeam.isEligible(this)) { + combatTeams.put(forceId, combatTeam); + continue; } } - } - for (int id : formationsToSanitize) { - strategicFormations.remove(id); + combatTeams.remove(forceId); } - return strategicFormations; + return combatTeams; } /** - * Returns an {@link ArrayList} of all {@link StrategicFormation} objects in the - * {@code strategicFormations} {@link Hashtable}. - * Calls the {@code getStrategicFormationsTable()} method to sanitize the {@link Hashtable} + * Returns an {@link ArrayList} of all {@link CombatTeam} objects in the + * {@code combatTeams} {@link Hashtable}. + * Calls the {@code getCombatTeamsTable()} method to sanitize the {@link Hashtable} * before conversion to {@link ArrayList}. * - * @return an {@link ArrayList} of all the {@link StrategicFormation} objects in the - * {@code strategicFormations} {@link Hashtable} + * @return an {@link ArrayList} of all the {@link CombatTeam} objects in the + * {@code combatTeams} {@link Hashtable} */ - public ArrayList getAllStrategicFormations() { - // This call allows us to utilize the self-sanitizing feature of getStrategicFormationsTable(), + public ArrayList getAllCombatTeams() { + // This call allows us to utilize the self-sanitizing feature of getCombatTeamsTable(), // without needing to directly include the code here, too. - strategicFormations = getStrategicFormationsTable(); + combatTeams = getCombatTeamsTable(); - return strategicFormations.values().stream() + return combatTeams.values().stream() .filter(l -> forceIds.containsKey(l.getForceId())) .collect(Collectors.toCollection(ArrayList::new)); } @@ -607,6 +669,25 @@ public RetirementDefectionTracker getRetirementDefectionTracker() { return retirementDefectionTracker; } + /** + * Sets the list of personnel who have advanced in experience points (XP) via vocational xp. + * + * @param personnelWhoAdvancedInXP a {@link List} of {@link Person} objects representing personnel + * who have gained XP. + */ + public void setPersonnelWhoAdvancedInXP(List personnelWhoAdvancedInXP) { + this.personnelWhoAdvancedInXP = personnelWhoAdvancedInXP; + } + + /** + * Retrieves the list of personnel who have advanced in experience points (XP) via vocational xp. + * + * @return a {@link List} of {@link Person} objects representing personnel who have gained XP. + */ + public List getPersonnelWhoAdvancedInXP() { + return personnelWhoAdvancedInXP; + } + public List getTurnoverRetirementInformation() { return turnoverRetirementInformation; } @@ -975,7 +1056,7 @@ public void addForce(Force force, Force superForce) { force.updateCommander(this); if (campaignOptions.isUseAtB()) { - recalculateStrategicFormations(this); + recalculateCombatTeams(this); } } @@ -1000,7 +1081,7 @@ public void moveForce(Force force, Force superForce) { * @param force */ public void importForce(Force force) { - lastForceId = Math.max(lastForceId, force.getId()); + lastForceId = max(lastForceId, force.getId()); forceIds.put(force.getId(), force); } @@ -1011,7 +1092,7 @@ public void importForce(Force force) { * @param scenario */ public void importScenario(Scenario scenario) { - lastScenarioId = Math.max(lastScenarioId, scenario.getId()); + lastScenarioId = max(lastScenarioId, scenario.getId()); scenarios.put(scenario.getId(), scenario); } @@ -1069,18 +1150,18 @@ public void addUnitToForce(@Nullable Unit u, int id) { } if (campaignOptions.isUseAtB()) { - recalculateStrategicFormations(this); + recalculateCombatTeams(this); } } /** - * Adds force and all its subforces to the AtB lance table + * Adds force and all its subforces to the Combat Teams table */ - private void addAllStrategicFormations(Force force) { - recalculateStrategicFormations(this); + private void addAllCombatTeams(Force force) { + recalculateCombatTeams(this); for (Force subForce : force.getSubForces()) { - addAllStrategicFormations(subForce); + addAllCombatTeams(subForce); } } @@ -1110,7 +1191,7 @@ public void importMission(final Mission mission) { } private void addMissionWithoutId(Mission m) { - lastMissionId = Math.max(lastMissionId, m.getId()); + lastMissionId = max(lastMissionId, m.getId()); missions.put(m.getId(), m); MekHQ.triggerEvent(new MissionNewEvent(m)); } @@ -1169,6 +1250,53 @@ public List getAtBContracts() { .collect(Collectors.toList()); } + /** + * Determines whether there is an active AtB (Against the Bot) contract. + * This method checks if there are contracts currently active. Optionally, + * it can also consider future contracts that have been accepted but have + * not yet started. + * + * @param includeFutureContracts a boolean indicating whether contracts that + * have been accepted but have not yet started + * should also be considered as active. + * @return {@code true} if there is any currently active AtB contract, or if + * {@code includeFutureContracts} is {@code true} and there are future + * contracts starting after the current date. Otherwise, {@code false}. + * @see #hasFutureAtBContract() + */ + public boolean hasActiveAtBContract(boolean includeFutureContracts) { + if (!getActiveAtBContracts().isEmpty()) { + return true; + } + + if (includeFutureContracts) { + return hasFutureAtBContract(); + } + + return false; + } + + /** + * Determines whether there are any future AtB (Against the Bot) contracts. + * A future contract is defined as a contract that has been accepted but + * has a start date later than the current day. + * + * @return true if there is at least one future AtB contract (accepted but + * starting after the current date). Otherwise, false. + */ + public boolean hasFutureAtBContract() { + List contracts = getAtBContracts(); + + for (AtBContract contract : contracts) { + // This catches any contracts that have been accepted, but haven't yet started + if (contract.getStartDate().isAfter(currentDay)) { + return true; + } + } + + return false; + } + public List getActiveAtBContracts() { return getActiveAtBContracts(false); } @@ -1275,56 +1403,79 @@ public CurrentLocation getLocation() { /** * Imports a {@link Unit} into a campaign. * - * @param u A {@link Unit} to import into the campaign. + * @param unit A {@link Unit} to import into the campaign. */ - public void importUnit(Unit u) { - Objects.requireNonNull(u); + public void importUnit(Unit unit) { + Objects.requireNonNull(unit); - logger.debug("Importing unit: ({}): {}", u.getId(), u.getName()); + logger.debug("Importing unit: ({}): {}", unit.getId(), unit.getName()); - getHangar().addUnit(u); + getHangar().addUnit(unit); + + checkDuplicateNamesDuringAdd(unit.getEntity()); - checkDuplicateNamesDuringAdd(u.getEntity()); + unit.initializeAllTransportSpace(); - // If this is a ship, add it to the list of potential transports - if ((u.getEntity() instanceof Dropship) || (u.getEntity() instanceof Jumpship)) { - addTransportShip(u); + if (!unit.isMothballed()) { + for (CampaignTransportType campaignTransportType : CampaignTransportType.values()) { + if (!unit.getTransportCapabilities(campaignTransportType).isEmpty()) { + addCampaignTransport(campaignTransportType, unit); + } + } } // Assign an entity ID to our new unit - if (Entity.NONE == u.getEntity().getId()) { - u.getEntity().setId(game.getNextEntityId()); + if (Entity.NONE == unit.getEntity().getId()) { + unit.getEntity().setId(game.getNextEntityId()); } - game.addEntity(u.getEntity()); + game.addEntity(unit.getEntity()); } + /** - * Adds an entry to the list of transit-capable transport ships. We'll use this - * to look for empty bays that ground units can be assigned to - * - * @param unit - The ship we want to add to this Set + * Adds a transport (Unit) to the list specified transporters map. + * This transporters map is used to store transports, the kinds of + * transporters they have, and their remaining capacity. The + * transporters map is meant to be utilized by the GUI. + * @see CampaignTransporterMap + * @param campaignTransportType Transport Type (enum) we're adding to + * @param unit unit with transport capabilities */ - public void addTransportShip(Unit unit) { - logger.debug("Adding DropShip/WarShip: {}", unit.getId()); - transportShips.add(Objects.requireNonNull(unit)); + public void addCampaignTransport(CampaignTransportType campaignTransportType, Unit unit) { + if (campaignTransportType.isShipTransport()) { + shipTransporters.addTransporter(unit); + } else if (campaignTransportType.isTacticalTransport()) { + tacticalTransporters.addTransporter(unit); + } } /** - * Deletes an entry from the list of transit-capable transport ships. This gets - * updated when - * the ship is removed from the campaign for one reason or another - * + * This will update the transport in the transports list with current capacities. + * When a unit is added or removed from a transport, that information needs updated + * in the campaign transport map. This method takes the CampaignTransportType and + * transport as inputs and updates the map with the current capacities of the + * transport. + * @param campaignTransportType type (Enum) of TransportedUnitsSummary we're interested in + * @param transport Unit + */ + public void updateTransportInTransports(CampaignTransportType campaignTransportType, Unit transport) { + getCampaignTransporterMap(campaignTransportType).updateTransportInTransporterMap(transport); + } + + /** + * Deletes an entry from the list of specified list of transports. This gets + * updated when the transport should no longer be in the CampaignTransporterMap, + * such as when a Transport is mothballed or removed from the campaign. + * @see CampaignTransporterMap + * @param campaignTransportType Transport Type (enum) we're checking * @param unit - The ship we want to remove from this Set */ - public void removeTransportShip(Unit unit) { - // If we remove a transport ship from the campaign, - // we need to remove any transported units from it - if (transportShips.remove(unit) && unit.hasTransportedUnits()) { - List transportedUnits = new ArrayList<>(unit.getTransportedUnits()); - for (Unit transportedUnit : transportedUnits) { - unit.removeTransportedUnit(transportedUnit); - } + public void removeCampaignTransporter(CampaignTransportType campaignTransportType, Unit unit) { + if (campaignTransportType.isShipTransport()) { + shipTransporters.removeTransport(unit); + } else if (campaignTransportType.isTacticalTransport()) { + tacticalTransporters.removeTransport(unit); } } @@ -1347,6 +1498,7 @@ public void addTestUnit(TestUnit tu) { unit.getEntity().setOwner(player); unit.getEntity().setGame(game); unit.getEntity().setExternalIdAsString(unit.getId().toString()); + unit.setMaintenanceMultiplier(getCampaignOptions().getDefaultMaintenanceTime()); // now lets grab the parts from the test unit and set them up with this unit for (Part p : tu.getParts()) { @@ -1407,13 +1559,16 @@ public Unit addNewUnit(Entity en, boolean allowNewPilots, int days, PartQuality en.setGame(game); en.setExternalIdAsString(unit.getId().toString()); - unit.initializeBaySpace(); + unit.initializeAllTransportSpace(); // Added to avoid the 'default force bug' when calculating cargo removeUnitFromForce(unit); - // If this is a ship, add it to the list of potential transports - if ((unit.getEntity() instanceof Dropship) || (unit.getEntity() instanceof Jumpship)) { - addTransportShip(unit); + if (!unit.isMothballed()) { + for (CampaignTransportType campaignTransportType : CampaignTransportType.values()) { + if (!unit.getTransportCapabilities(campaignTransportType).isEmpty()) { + addCampaignTransport(campaignTransportType, unit); + } + } } unit.initializeParts(true); @@ -1502,7 +1657,8 @@ public Unit getUnit(UUID id) { // region Personnel // region Person Creation /** - * Creates a new dependent with given gender. The origin faction and planet are set to null. + * Creates a new dependent with given gender. The origin faction and planet are + * set to null. * * @param gender The {@link Gender} of the new dependent. * @return Return a {@link Person} object representing the new dependent. @@ -1514,20 +1670,24 @@ public Person newDependent(Gender gender) { /** * Creates a new dependent with given gender, origin faction and origin planet. * - * @param gender The {@link Gender} of the new dependent. - * @param originFaction The {@link Faction} that represents the origin faction for the new dependent. - * This can be null, suggesting the faction will be chosen based on campaign options. - * @param originPlanet The {@link Planet} that represents the origin planet for the new dependent. - * This can be null, suggesting the planet will be chosen based on campaign options. + * @param gender The {@link Gender} of the new dependent. + * @param originFaction The {@link Faction} that represents the origin faction + * for the new dependent. + * This can be null, suggesting the faction will be chosen + * based on campaign options. + * @param originPlanet The {@link Planet} that represents the origin planet for + * the new dependent. + * This can be null, suggesting the planet will be chosen + * based on campaign options. * @return Return a {@link Person} object representing the new dependent. */ public Person newDependent(Gender gender, @Nullable Faction originFaction, - @Nullable Planet originPlanet) { + @Nullable Planet originPlanet) { return newPerson(PersonnelRole.DEPENDENT, - PersonnelRole.NONE, - new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), originFaction), - new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions(), originPlanet), - gender); + PersonnelRole.NONE, + new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), originFaction), + new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions(), originPlanet), + gender); } /** @@ -1646,8 +1806,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); @@ -1676,56 +1836,59 @@ public boolean recruitPerson(Person p, PrisonerStatus prisonerStatus) { } /** - * @param p the person being added + * @param person the person being added * @param prisonerStatus the person's prisoner status upon recruitment * @param gmAdd false means that they need to pay to hire this person, * true means it is added without paying * @param log whether or not to write to logs * @return true if the person is hired successfully, otherwise false */ - public boolean recruitPerson(Person p, PrisonerStatus prisonerStatus, boolean gmAdd, boolean log) { - if (p == null) { + public boolean recruitPerson(Person person, PrisonerStatus prisonerStatus, boolean gmAdd, boolean log) { + if (person == null) { return false; } // 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() + if (getCampaignOptions().isPayForRecruitment() && !person.getPrimaryRole().isDependent() && !gmAdd && prisonerStatus.isFree()) { if (!getFinances().debit(TransactionType.RECRUITMENT, getLocalDate(), - p.getSalary(this).multipliedBy(2), "Recruitment of " + p.getFullName())) { - addReport("Insufficient funds to recruit " - + p.getFullName() + ""); + person.getSalary(this).multipliedBy(2), String.format(resources.getString("personnelRecruitmentFinancesReason.text"),person.getFullName()))) { + addReport(String.format(resources.getString("personnelRecruitmentInsufficientFunds.text"), MekHQ.getMHQOptions().getFontColorNegativeHexColor(), person.getFullName())); return false; } } - personnel.put(p.getId(), p); - p.setJoinedCampaign(getLocalDate()); + personnel.put(person.getId(), person); + person.setJoinedCampaign(getLocalDate()); + + + String formerSurname = person.getSurname(); - if (log) { - String add = !prisonerStatus.isFree() ? (prisonerStatus.isBondsman() ? " as a bondsman" : " as a prisoner") - : ""; - addReport(String.format("%s has been added to the personnel roster%s.", p.getHyperlinkedName(), add)); - } - if (p.getPrimaryRole().isAstech()) { + if (person.getPrimaryRole().isAstech()) { astechPoolMinutes += Person.PRIMARY_ROLE_SUPPORT_TIME; astechPoolOvertime += Person.PRIMARY_ROLE_OVERTIME_SUPPORT_TIME; - } else if (p.getSecondaryRole().isAstech()) { + } else if (person.getSecondaryRole().isAstech()) { astechPoolMinutes += Person.SECONDARY_ROLE_SUPPORT_TIME; astechPoolOvertime += Person.SECONDARY_ROLE_OVERTIME_SUPPORT_TIME; } - p.setPrisonerStatus(this, prisonerStatus, log); + person.setPrisonerStatus(this, prisonerStatus, log); if (getCampaignOptions().isUseSimulatedRelationships()) { - if ((prisonerStatus.isFree()) && (!p.getOriginFaction().isClan()) && (!p.getPrimaryRole().isDependent())) { - simulateRelationshipHistory(p); + if ((prisonerStatus.isFree()) && (!person.getOriginFaction().isClan()) && (!person.getPrimaryRole().isDependent())) { + simulateRelationshipHistory(person); } } - MekHQ.triggerEvent(new PersonNewEvent(p)); + if (log) { + formerSurname = person.getSurname().equals(formerSurname) ? "" : ' ' + String.format(resources.getString("personnelRecruitmentFormerSurname.text") + ' ', formerSurname); + String add = !prisonerStatus.isFree() ? (' ' + resources.getString(prisonerStatus.isBondsman() ? "personnelRecruitmentBondsman.text" : "personnelRecruitmentPrisoner.text")) + : ""; + addReport(String.format(resources.getString("personnelRecruitmentAddedToRoster.text"), person.getHyperlinkedName(), formerSurname, add)); + } + + MekHQ.triggerEvent(new PersonNewEvent(person)); return true; } @@ -1755,7 +1918,8 @@ private void simulateRelationshipHistory(Person person) { if (!person.getGenealogy().hasSpouse()) { List toRemove = new ArrayList<>(); - // there is a chance a departing spouse might take some of their children with them + // 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) { @@ -1778,7 +1942,8 @@ private void simulateRelationshipHistory(Person person) { // then we check for children if ((person.getGender().isFemale()) && (!person.isPregnant())) { - getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, true); + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, + true); if (person.isPregnant()) { @@ -1789,7 +1954,8 @@ private void simulateRelationshipHistory(Person person) { } if ((currentSpouse != null) && (currentSpouse.getGender().isFemale()) && (!currentSpouse.isPregnant())) { - getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), currentSpouse, true); + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), + currentSpouse, true); if (currentSpouse.isPregnant()) { @@ -1799,25 +1965,27 @@ private void simulateRelationshipHistory(Person person) { } } - if((person.isPregnant()) && (currentDate.isAfter(person.getDueDate()))) { + if ((person.isPregnant()) && (currentDate.isAfter(person.getDueDate()))) { children.addAll(getProcreation().birthHistoric(this, localDate, person, babysFather)); babysFather = null; } - if((currentSpouse != null) && (currentSpouse.isPregnant()) && (currentDate.isAfter(currentSpouse.getDueDate()))) { + if ((currentSpouse != null) && (currentSpouse.isPregnant()) + && (currentDate.isAfter(currentSpouse.getDueDate()))) { children.addAll(getProcreation().birthHistoric(this, localDate, currentSpouse, spousesBabysFather)); spousesBabysFather = null; } } - // with the simulation concluded, we add the current spouse (if any) and any remaining children to the unit + // 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"))); + currentSpouse.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), + resources.getString("relativeJoinsForceSpouse.text"))); MekHQ.triggerEvent(new PersonChangedEvent(currentSpouse)); } @@ -1862,16 +2030,16 @@ private void simulateRelationshipHistory(Person person) { recruitPerson(child, PrisonerStatus.FREE, true, false); addReport(String.format(resources.getString("relativeJoinsForce.text"), - child.getHyperlinkedFullTitle(), - person.getHyperlinkedFullTitle(), - resources.getString("relativeJoinsForceChild.text"))); + child.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), + resources.getString("relativeJoinsForceChild.text"))); MekHQ.triggerEvent(new PersonChangedEvent(child)); } MekHQ.triggerEvent(new PersonChangedEvent(person)); } - //endregion Personnel Recruitment + // endregion Personnel Recruitment // region Bloodnames /** @@ -1882,7 +2050,8 @@ private void simulateRelationshipHistory(Person person) { * appropriate to the person's phenotype and the player's faction. * * @param person The Bloodname candidate - * @param ignoreDice If true, skips the random roll and assigns a Bloodname automatically + * @param ignoreDice If true, skips the random roll and assigns a Bloodname + * automatically */ public void checkBloodnameAdd(Person person, boolean ignoreDice) { // if person is non-clan or does not have a phenotype @@ -1890,7 +2059,8 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { return; } - // Person already has a bloodname, we open up the dialog to ask if they want to keep the + // Person already has a bloodname, we open up the dialog to ask if they want to + // keep the // current bloodname or assign a new one if (!person.getBloodname().isEmpty()) { int result = JOptionPane.showConfirmDialog(null, @@ -2057,6 +2227,22 @@ public Collection getPersonnel() { return personnel.values(); } + /** + * Retrieves a list of personnel, excluding those whose status indicates they have + * left the unit. + *

    + * This method filters the personnel collection to only include individuals who + * are still part of the unit, as determined by their status. + *

    + * + * @return a {@code List} of {@link Person} objects who have not left the unit + */ + public List getPersonnelFilteringOutDeparted() { + return getPersonnel().stream() + .filter(person -> !person.getStatus().isDepartedUnit()) + .collect(Collectors.toList()); + } + /** * Provides a filtered list of personnel including only active Persons. * @@ -2068,8 +2254,24 @@ public List getActivePersonnel() { .collect(Collectors.toList()); } + /** + * Retrieves a filtered list of personnel who have at least one combat profession. + *

    + * This method filters the list of all personnel to include only those whose primary + * or secondary role is designated as a combat role. + *

    + * + * @return a {@link List} of {@link Person} objects representing combat-capable personnel + */ + public List getActiveCombatPersonnel() { + return getActivePersonnel().stream() + .filter(p -> p.getPrimaryRole().isCombat() || p.getSecondaryRole().isCombat()) + .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() { @@ -2086,8 +2288,8 @@ public List getActiveDependents() { */ public List getCurrentPrisoners() { return getActivePersonnel().stream() - .filter(person -> person.getPrisonerStatus().isCurrentPrisoner()) - .collect(Collectors.toList()); + .filter(person -> person.getPrisonerStatus().isCurrentPrisoner()) + .collect(Collectors.toList()); } /** @@ -2279,35 +2481,75 @@ private int getQuantity(Part p) { return (p.getUnit() != null) ? 1 : p.getQuantity(); } - private PartInUse getPartInUse(Part p) { + private PartInUse getPartInUse(Part part) { // SI isn't a proper "part" - if (p instanceof StructuralIntegrity) { + if (part instanceof StructuralIntegrity) { return null; } // Skip out on "not armor" (as in 0 point armer on men or field guns) - if ((p instanceof Armor) && ((Armor) p).getType() == EquipmentType.T_ARMOR_UNKNOWN) { + if ((part instanceof Armor) && ((Armor) part).getType() == EquipmentType.T_ARMOR_UNKNOWN) { return null; } // Makes no sense buying those separately from the chasis - if ((p instanceof EquipmentPart) - && ((EquipmentPart) p).getType() != null - && (((EquipmentPart) p).getType().hasFlag(MiscType.F_CHASSIS_MODIFICATION))) { + if ((part instanceof EquipmentPart) + && ((EquipmentPart) part).getType() != null + && (((EquipmentPart) part).getType().hasFlag(MiscType.F_CHASSIS_MODIFICATION))) { return null; } // Replace a "missing" part with a corresponding "new" one. - if (p instanceof MissingPart) { - p = ((MissingPart) p).getNewPart(); + if (part instanceof MissingPart) { + part = ((MissingPart) part).getNewPart(); } - PartInUse result = new PartInUse(p); + PartInUse result = new PartInUse(part); + result.setRequestedStock(getDefaultStockPercent(part)); return (null != result.getPartToBuy()) ? result : null; } + /** + * Determines the default stock percentage for a given part type. + * + *

    This method uses the type of the provided {@link Part} to decide which + * default stock percentage to return. The values for each part type are + * retrieved from the campaign options.

    + * + * @param part The {@link Part} for which the default stock percentage is to + * be determined. The part must not be {@code null}. + * @return An {@code int} representing the default stock percentage for the + * given part type, as defined in the campaign options. + */ + private int getDefaultStockPercent(Part part) { + if (part instanceof HeatSink) { + return campaignOptions.getAutoLogisticsHeatSink(); + } else if (part instanceof MekLocation) { + if (((MekLocation) part).getLoc() == Mek.LOC_HEAD) { + return campaignOptions.getAutoLogisticsMekHead(); + } + + if (((MekLocation) part).getLoc() == Mek.LOC_CT) { + return campaignOptions.getAutoLogisticsNonRepairableLocation(); + } + + return campaignOptions.getAutoLogisticsMekLocation(); + } else if (part instanceof TankLocation) { + return campaignOptions.getAutoLogisticsNonRepairableLocation(); + } else if (part instanceof AmmoBin) { + return campaignOptions.getAutoLogisticsAmmunition(); + } else if (part instanceof Armor ) { + return campaignOptions.getAutoLogisticsArmor(); + } + + return campaignOptions.getAutoLogisticsOther(); + } + /** * Add data from an actual part to a PartInUse data element - * @param partInUse part in use record to update - * @param incomingPart new part that needs to be added to this record - * @param ignoreMothballedUnits don't count parts in mothballed units - * @param ignoreSparesUnderQuality don't count spare parts lower than this quality + * + * @param partInUse part in use record to update + * @param incomingPart new part that needs to be added to this + * record + * @param ignoreMothballedUnits don't count parts in mothballed units + * @param ignoreSparesUnderQuality don't count spare parts lower than this + * quality */ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { @@ -2330,9 +2572,11 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, /** * Find all the parts that match this PartInUse and update their data - * @param partInUse part in use record to update - * @param ignoreMothballedUnits don't count parts in mothballed units - * @param ignoreSparesUnderQuality don't count spare parts lower than this quality + * + * @param partInUse part in use record to update + * @param ignoreMothballedUnits don't count parts in mothballed units + * @param ignoreSparesUnderQuality don't count spare parts lower than this + * quality */ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { @@ -2341,27 +2585,29 @@ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, partInUse.setTransferCount(0); partInUse.setPlannedCount(0); getWarehouse().forEachPart(incomingPart -> { - PartInUse newPiu = getPartInUse(incomingPart); - if (partInUse.equals(newPiu)) { + PartInUse newPartInUse = getPartInUse(incomingPart); + if (partInUse.equals(newPartInUse)) { updatePartInUseData(partInUse, incomingPart, - ignoreMothballedUnits, ignoreSparesUnderQuality); + ignoreMothballedUnits, ignoreSparesUnderQuality); } }); for (IAcquisitionWork maybePart : shoppingList.getPartList()) { - PartInUse newPiu = getPartInUse((Part) maybePart); - if (partInUse.equals(newPiu)) { + PartInUse newPartInUse = getPartInUse((Part) maybePart); + if (partInUse.equals(newPartInUse)) { partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? - ((MissingPart) maybePart).getNewPart() : - (Part) maybePart) * maybePart.getQuantity()); + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() + : (Part) maybePart) * maybePart.getQuantity()); } } } /** - * Create a data set detailing all the parts being used (or not) and their warehouse spares - * @param ignoreMothballedUnits don't count parts in mothballed units - * @param ignoreSparesUnderQuality don't count spare parts lower than this quality + * Create a data set detailing all the parts being used (or not) and their + * warehouse spares + * + * @param ignoreMothballedUnits don't count parts in mothballed units + * @param ignoreSparesUnderQuality don't count spare parts lower than this + * quality * @return a Set of PartInUse data for display or inspection */ public Set getPartsInUse(boolean ignoreMothballedUnits, @@ -2377,6 +2623,11 @@ public Set getPartsInUse(boolean ignoreMothballedUnits, if (inUse.containsKey(partInUse)) { partInUse = inUse.get(partInUse); } else { + if (partsInUseRequestedStockMap.containsKey(partInUse.getDescription())) { + partInUse.setRequestedStock(partsInUseRequestedStockMap.get(partInUse.getDescription())); + } else { + partInUse.setRequestedStock(getDefaultStockPercent(incomingPart)); + } inUse.put(partInUse, partInUse); } updatePartInUseData(partInUse, incomingPart, ignoreMothballedUnits, ignoreSparesUnderQuality); @@ -2392,18 +2643,22 @@ public Set getPartsInUse(boolean ignoreMothballedUnits, if (inUse.containsKey(partInUse)) { partInUse = inUse.get(partInUse); } else { + if (partsInUseRequestedStockMap.containsKey(partInUse.getDescription())) { + partInUse.setRequestedStock(partsInUseRequestedStockMap.get(partInUse.getDescription())); + } else { + partInUse.setRequestedStock(getDefaultStockPercent((Part) maybePart)); + } inUse.put(partInUse, partInUse); } partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? - ((MissingPart) maybePart).getNewPart() : - (Part) maybePart) * maybePart.getQuantity()); + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() + : (Part) maybePart) * maybePart.getQuantity()); } return inUse.keySet().stream() - // Hacky but otherwise we end up with zero lines when filtering things out - .filter(p -> p.getUseCount() != 0 || p.getStoreCount() != 0 || p.getPlannedCount() != 0) - .collect(Collectors.toSet()); + // Hacky but otherwise we end up with zero lines when filtering things out + .filter(p -> p.getUseCount() != 0 || p.getStoreCount() != 0 || p.getPlannedCount() != 0) + .collect(Collectors.toSet()); } @Deprecated @@ -2521,14 +2776,16 @@ public List getTechs(final boolean noZeroMinute, final boolean eliteFirs * excluded from the list. * @param eliteFirst If TRUE and sorted also TRUE, then return the list sorted * from best to worst - * @param expanded If TRUE, then include techs with expanded roles (e.g. Tech/Vessel skill) + * @param expanded If TRUE, then include techs with expanded roles (e.g. + * Tech/Vessel skill) * @return The list of active {@link Person}s who qualify as technicians - * ({@link Person#isTech()}), or who qualify as expanded technicians ({@link Person#isTechExpanded()}). + * ({@link Person#isTech()}), or who qualify as expanded technicians + * ({@link Person#isTechExpanded()}). */ public List getTechsExpanded(final boolean noZeroMinute, final boolean eliteFirst, final boolean expanded) { final List techs = getActivePersonnel().stream() .filter(person -> (expanded ? person.isTechExpanded() : person.isTech()) - && (!noZeroMinute || (person.getMinutesLeft() > 0))) + && (!noZeroMinute || (person.getMinutesLeft() > 0))) .collect(Collectors.toList()); // also need to loop through and collect engineers on self-crewed vessels @@ -2723,19 +2980,56 @@ public TargetRoll getTargetFor(Person medWork, Person doctor) { } /** - * This method finds and returns the most senior command administrator. - * It checks for both primary and secondary roles of the administrator. - * In case of multiple administrators with the command role, it uses the - * {@code outRanksUsingSkillTiebreaker} method to decide the seniority. + * Finds and returns the most senior administrator for a specific type of administrative role. + * Seniority is determined using the {@link Person#outRanksUsingSkillTiebreaker} method when + * there are multiple eligible administrators for the specified role. + * + *

    The method evaluates both the primary and secondary roles of each administrator + * against the provided {@link AdministratorSpecialization} type.

    + * + *

    The valid types of administrative roles are represented by the {@link AdministratorSpecialization} enum:

    + *
      + *
    • {@link AdministratorSpecialization#COMMAND} - Command Administrator
    • + *
    • {@link AdministratorSpecialization#LOGISTICS} - Logistics Administrator
    • + *
    • {@link AdministratorSpecialization#TRANSPORT} - Transport Administrator
    • + *
    • {@link AdministratorSpecialization#HR} - HR Administrator
    • + *
    + * + * @param type the {@link AdministratorSpecialization} representing the administrative role to check for. + * Passing a {@code null} type will result in an {@link IllegalStateException}. + * + * @return the most senior {@link Person} with the specified administrative role, or {@code null} + * if no eligible administrator is found. * - * @return the senior administrator with a command role, or {@code null} if no such - * administrator exists. + *

    Behavior:

    + *
      + *
    • The method iterates through all administrators retrieved by {@link #getAdmins()}.
    • + *
    • For each {@link Person}, it checks if their primary or secondary role matches the specified type + * via utility methods like {@code AdministratorRole#isAdministratorCommand}.
    • + *
    • If no eligible administrators exist, the method returns {@code null}.
    • + *
    • If multiple administrators are eligible, the one with the highest seniority is returned.
    • + *
    • Seniority is determined by the {@link Person#outRanksUsingSkillTiebreaker} method, + * which uses a skill-based tiebreaker when necessary.
    • + *
    + * + * @throws IllegalStateException if {@code type} is null or an unsupported value. */ - public @Nullable Person getSeniorAdminCommandPerson() { + public @Nullable Person getSeniorAdminPerson(AdministratorSpecialization type) { Person seniorAdmin = null; for (Person person : getAdmins()) { - if (person.getPrimaryRole().isAdministratorCommand() || person.getSecondaryRole().isAdministratorCommand()) { + boolean isEligible = switch (type) { + case COMMAND -> person.getPrimaryRole().isAdministratorCommand() + || person.getSecondaryRole().isAdministratorCommand(); + case LOGISTICS -> person.getPrimaryRole().isAdministratorLogistics() + || person.getSecondaryRole().isAdministratorLogistics(); + case TRANSPORT -> person.getPrimaryRole().isAdministratorTransport() + || person.getSecondaryRole().isAdministratorTransport(); + case HR -> person.getPrimaryRole().isAdministratorHR() + || person.getSecondaryRole().isAdministratorHR(); + }; + + if (isEligible) { if (seniorAdmin == null) { seniorAdmin = person; continue; @@ -3352,9 +3646,11 @@ public void activate(Unit u) { } public void refit(Refit theRefit) { - Person tech = (theRefit.getUnit().getEngineer() == null) ? theRefit.getTech() : theRefit.getUnit().getEngineer(); + Person tech = (theRefit.getUnit().getEngineer() == null) ? theRefit.getTech() + : theRefit.getUnit().getEngineer(); if (tech == null) { - addReport("No tech is assigned to refit " + theRefit.getOriginalEntity().getShortName() + ". Refit cancelled."); + addReport("No tech is assigned to refit " + theRefit.getOriginalEntity().getShortName() + + ". Refit cancelled."); theRefit.cancel(); return; } @@ -3370,7 +3666,7 @@ public void refit(Refit theRefit) { theRefit.addTimeSpent(tech.getMinutesLeft()); tech.setMinutesLeft(0); report = report + ", " + theRefit.getTimeLeft() + " minutes left. Completion "; - int daysLeft = (int) Math.ceil(theRefit.getTimeLeft() / tech.getDailyAvailableTechTime()) + 1; + int daysLeft = (int) Math.ceil((double) theRefit.getTimeLeft() / (double) tech.getDailyAvailableTechTime()); if (daysLeft == 1) { report += " tomorrow.
    "; } else { @@ -3411,11 +3707,11 @@ public void refit(Refit theRefit) { if (!theRefit.isBeingRefurbished()) { refit(theRefit); report += " Completion "; - int daysLeft = (int) Math.ceil(theRefit.getTimeLeft() / tech.getDailyAvailableTechTime()) + 1; + int daysLeft = (int) Math.ceil((double) theRefit.getTimeLeft() / (double) tech.getDailyAvailableTechTime()); if (daysLeft == 1) { report += " tomorrow.
    "; } else { - report += " in " + daysLeft + " days.
    "; + report += " in " + daysLeft + " days.
    "; } } } @@ -3553,7 +3849,7 @@ public String fixPart(IPartWork partWork, Person tech) { report += " minutes left. Work"; if ((minutesUsed > 0) && (tech.getDailyAvailableTechTime() > 0)) { report += " will be finished "; - int daysLeft = (int) Math.ceil(partWork.getTimeLeft() / tech.getDailyAvailableTechTime()) + 1; + int daysLeft = (int) Math.ceil((double) partWork.getTimeLeft() / (double)tech.getDailyAvailableTechTime()); if (daysLeft == 1) { report += " tomorrow.
    "; } else { @@ -3710,17 +4006,53 @@ public int getDeploymentDeficit(AtBContract contract) { return 0; } - int total = -contract.getRequiredLances(); - int role = -Math.max(1, contract.getRequiredLances() / 2); + int total = -contract.getRequiredCombatTeams(); + int role = -max(1, contract.getRequiredCombatTeams() / 2); + int minimumUnitCount = (int) ((double) getStandardForceSize(faction) / 2); + + final CombatRole requiredLanceRole = contract.getContractType().getRequiredCombatRole(); + for (CombatTeam combatTeam : combatTeams.values()) { + CombatRole combatRole = combatTeam.getRole(); + + Force force = null; + int unitCount = 0; + try { + int forceId = combatTeam.getForceId(); + force = getForce(forceId); - final AtBLanceRole requiredLanceRole = contract.getContractType().getRequiredLanceRole(); - for (StrategicFormation l : strategicFormations.values()) { - if (!l.getRole().isUnassigned() && (l.getMissionId() == contract.getId())) { - total++; - if (l.getRole() == requiredLanceRole) { - role++; + unitCount += force.getAllUnits(true).size(); + } catch (Exception ignored) { + continue; + } + + boolean isUnderStrength = unitCount < minimumUnitCount; + boolean reportUnderStrength = false; + + if (!combatRole.isReserve() && !combatRole.isAuxiliary()) { + if ((combatTeam.getMissionId() == contract.getId())) { + if (!combatRole.isTraining() || contract.getContractType().isCadreDuty()) { + if (isUnderStrength) { + reportUnderStrength = true; + } else { + total++; + } + } + } + + if (combatRole == requiredLanceRole) { + if (isUnderStrength) { + reportUnderStrength = true; + } else { + role++; + } } } + + if (reportUnderStrength) { + addReport(String.format(resources.getString("understrength.text"), + force.getName(), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), + CLOSING_SPAN_TAG, minimumUnitCount)); + } } if (total >= 0 && role >= 0) { @@ -3751,7 +4083,7 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem if (campaignOptions.isUseStratCon() && contract.getMoraleLevel().isRouted()) { LocalDate newRoutEndDate = contract.getStartDate() - .plusMonths(Math.max(1, Compute.d6() - 3)) + .plusMonths(max(1, Compute.d6() - 3)) .minusDays(1); contract.setRoutEndDate(newRoutEndDate); } @@ -3765,7 +4097,16 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem if (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { int deficit = getDeploymentDeficit(contract); - if (deficit > 0) { + StratconCampaignState campaignState = contract.getStratconCampaignState(); + + if (campaignState != null && deficit > 0) { + addReport(String.format(resources.getString("contractBreach.text"), + contract.getName(), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + + campaignState.updateVictoryPoints(-1); + } else if (deficit > 0) { contract.addPlayerMinorBreaches(deficit); addReport("Failure to meet " + contract.getName() + " requirements resulted in " + deficit + ((deficit == 1) ? " minor contract breach" : " minor contract breaches")); @@ -3785,17 +4126,20 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem (AtBDynamicScenario) scenario, contract.getStratconCampaignState()); if (stub) { - scenario.convertToStub(this, ScenarioStatus.DEFEAT); - addReport("Failure to deploy for " + scenario.getName() + " resulted in defeat."); + if (scenario.getStratConScenarioType().isResupply()) { + processAbandonedConvoy(this, contract, (AtBDynamicScenario) scenario); + } + + scenario.convertToStub(this, ScenarioStatus.REFUSED_ENGAGEMENT); } else { scenario.clearAllForcesAndPersonnel(this); } } else { - scenario.convertToStub(this, ScenarioStatus.DEFEAT); + scenario.convertToStub(this, ScenarioStatus.REFUSED_ENGAGEMENT); contract.addPlayerMinorBreach(); addReport("Failure to deploy for " + scenario.getName() - + " resulted in defeat and a minor contract breach."); + + " resulted in a minor contract breach."); } } } @@ -3811,10 +4155,10 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem contract.checkEvents(this); // If there is a standard battle set for today, deploy the lance. - for (final AtBScenario s : contract.getCurrentAtBScenarios()) { - if ((s.getDate() != null) && s.getDate().equals(getLocalDate())) { - int forceId = s.getStrategicFormationId(); - if ((strategicFormations.get(forceId) != null) && !forceIds.get(forceId).isDeployed()) { + for (final AtBScenario atBScenario : contract.getCurrentAtBScenarios()) { + if ((atBScenario.getDate() != null) && atBScenario.getDate().equals(getLocalDate())) { + int forceId = atBScenario.getCombatTeamId(); + if ((combatTeams.get(forceId) != null) && !forceIds.get(forceId).isDeployed()) { // If any unit in the force is under repair, don't deploy the force // Merely removing the unit from deployment would break with user expectation boolean forceUnderRepair = false; @@ -3827,20 +4171,30 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem } if (!forceUnderRepair) { - forceIds.get(forceId).setScenarioId(s.getId(), this); - s.addForces(forceId); + forceIds.get(forceId).setScenarioId(atBScenario.getId(), this); + atBScenario.addForces(forceId); addReport(MessageFormat.format( resources.getString("atbScenarioTodayWithForce.format"), - s.getName(), forceIds.get(forceId).getName())); - MekHQ.triggerEvent(new DeploymentChangedEvent(forceIds.get(forceId), s)); + atBScenario.getName(), forceIds.get(forceId).getName())); + MekHQ.triggerEvent(new DeploymentChangedEvent(forceIds.get(forceId), atBScenario)); } else { - addReport(MessageFormat.format( - resources.getString("atbScenarioToday.format"), s.getName())); + if (atBScenario.getHasTrack()) { + addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), + atBScenario.getName())); + } else { + addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), + atBScenario.getName())); + } } } else { - addReport(MessageFormat.format( - resources.getString("atbScenarioToday.format"), s.getName())); + if (atBScenario.getHasTrack()) { + addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), + atBScenario.getName())); + } else { + addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), + atBScenario.getName())); + } } } } @@ -3853,8 +4207,10 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem * It generates contract offers in the contract market, * updates ship search expiration and results, * processes ship search on Mondays, - * awards training experience to eligible training lances on active contracts on Mondays, - * adds or removes dependents at the start of the year if the options are enabled, + * awards training experience to eligible training lances on active contracts on + * Mondays, + * adds or removes dependents at the start of the year if the options are + * enabled, * rolls for morale at the start of the month, * and processes ATB scenarios. */ @@ -3871,13 +4227,7 @@ private void processNewDayATB() { if (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { processShipSearch(); - - // Training Experience - Award to eligible training Strategic Formations on active contracts - getStrategicFormationsTable().values().stream() - .filter(strategicFormation -> strategicFormation.getRole().isTraining() - && (strategicFormation.getContract(this) != null) && strategicFormation.isEligible(this) - && strategicFormation.getContract(this).isActiveOn(getLocalDate(), true)) - .forEach(this::awardTrainingXP); + processTrainingCombatTeams(this); } if (getLocalDate().getDayOfMonth() == 1) { @@ -3900,17 +4250,27 @@ private void processNewDayATB() { report = resources.getString("garrisonDutyRouted.text"); } else if (oldMorale != newMorale) { report = String.format(resources.getString("contractMoraleReport.text"), - newMorale, contract.getName(), newMorale.getToolTipText()); + newMorale, contract.getName(), newMorale.getToolTipText()); } if (!report.isBlank()) { addReport(report); } + + // Resupply + if (getCampaignOptions().isUseStratCon()) { + boolean inLocation = location.isOnPlanet() + && location.getCurrentSystem().equals(contract.getSystem()); + + if (inLocation) { + processResupply(contract); + } + } } } - if (campaignOptions.isUseStratCon() && (currentDay.getDayOfMonth() == 1)) { - negotiateAdditionalSupportPoints(); + if (campaignOptions.isUseStratCon() && (currentDay.getDayOfWeek() == DayOfWeek.MONDAY)) { + negotiateAdditionalSupportPoints(this); } processNewDayATBScenarios(); @@ -3919,7 +4279,7 @@ private void processNewDayATB() { if (campaignOptions.isUseGenericBattleValue()) { if (contract.getStartDate().equals(getLocalDate())) { if (getCampaignOptions().isUseGenericBattleValue() - && BatchallFactions.usesBatchalls(contract.getEnemyCode())) { + && BatchallFactions.usesBatchalls(contract.getEnemyCode())) { contract.setBatchallAccepted(contract.initiateBatchall(this)); } } @@ -3928,153 +4288,120 @@ private void processNewDayATB() { } /** - * Handles monthly negotiation of additional support points for active AtB Contracts. - * Admin/Transport personnel skill levels and contract start dates are considered during negotiations. - * Side effects include state changes and report generation. + * Processes the resupply operation for a given contract. + *

    + * This method checks if the contract type is not Guerrilla Warfare or if a + * d6 roll is greater than 4. If any of these conditions is met, it calculates the maximum + * resupply size based on the contract's required lances, creates an instance of the + * {@link Resupply} class, and initiates a resupply action. + * + * @param contract The relevant {@link AtBContract} */ - public void negotiateAdditionalSupportPoints() { - // Fetch a list of all Admin/Transport personnel - List adminTransport = new ArrayList<>(); + private void processResupply(AtBContract contract) { + boolean isGuerrilla = contract.getContractType().isGuerrillaWarfare(); - for (Person person : getAdmins()) { - if (person.getPrimaryRole().isAdministratorTransport() - || person.getSecondaryRole().isAdministratorTransport()) { - adminTransport.add(person); - } - } - - // Sort that list based on skill - adminTransport.sort((person1, person2) -> { - Skill person1Skill = person1.getSkill(S_ADMIN); - int person1SkillValue = person1Skill.getLevel() + person1Skill.getBonus(); - - Skill person2Skill = person2.getSkill(S_ADMIN); - int person2SkillValue = person2Skill.getLevel() + person2Skill.getBonus(); - - return Double.compare(person1SkillValue, person2SkillValue); - }); - - // Fetch a list of all active AtB Contracts and sort that list oldest -> newest - List activeContracts = getActiveAtBContracts(); - - List sortedContracts = activeContracts.stream() - .sorted(Comparator.comparing(AtBContract::getStartDate)) - .toList(); - - // Loop through available contracts, rolling for additional Support Points until we run - // out of Admin/Transport personnel, or we run out of active contracts - for (AtBContract contract : sortedContracts) { - int negoatiatedSupportPoints = 0; - int maximumSupportPointsNegotiated = contract.getRequiredLances(); - - if (adminTransport.isEmpty()) { - break; - } - - int availableAdmins = adminTransport.size(); - - for (int i = 0; i < availableAdmins; i++) { - Person assignedAdmin = adminTransport.get(0); - adminTransport.remove(0); - - int targetNumber = assignedAdmin.getSkill(S_ADMIN).getFinalSkillValue(); - int roll = Compute.d6(2); - - if (roll >= targetNumber) { - negoatiatedSupportPoints++; - - int marginOfSuccess = (roll - targetNumber) / 4; - - negoatiatedSupportPoints += marginOfSuccess; - } - - if (negoatiatedSupportPoints >= maximumSupportPointsNegotiated) { - negoatiatedSupportPoints = maximumSupportPointsNegotiated; - break; - } - } - - if (negoatiatedSupportPoints > 0) { - contract.getStratconCampaignState().addSupportPoints(negoatiatedSupportPoints); - - addReport(String.format(resources.getString("stratConWeeklySupportPoints.text"), - ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - negoatiatedSupportPoints, CLOSING_SPAN_TAG, contract.getName())); - } else { - addReport(String.format(resources.getString("stratConWeeklySupportPointsFailed.text"), - ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG, contract.getName())); - } + if (!isGuerrilla || Compute.d6(1) > 4) { + ResupplyType resupplyType = isGuerrilla ? ResupplyType.RESUPPLY_SMUGGLER : ResupplyType.RESUPPLY_NORMAL; + Resupply resupply = new Resupply(this, contract, resupplyType); + performResupply(resupply, contract); } } /** - * Processes the new day for all personnel present in the campaign. + * Processes the daily activities and updates for all personnel that haven't already left the + * campaign. *

    - * This method loops through all personnel present and performs the necessary actions - * for each person for a new day. + * This method iterates through all personnel and performs various daily updates, including + * health checks, status updates, relationship events, and other daily or periodic tasks. *

    * The following tasks are performed for each person: *

      - *
    • Death - If the person has died, skip processing further for the dead person.
    • - *
    • Marriage - Process any marriage-related actions.
    • - *
    • Reset minutes left for the person.
    • - *
    • Reset acquisitions made to 0.
    • - *
    • Healing - If the person needs healing and advanced medical is not used, - * decrement the days to wait for healing and heal naturally or with a doctor.
    • - *
    • Advanced Medical - If advanced medical is used, resolve the daily healing for the person.
    • - *
    • Reset current edge points for support personnel on Mondays.
    • - *
    • Idle XP - If idle XP is enabled and it's the first day of the month, - * check if the person qualifies for idle XP and award them if they do.
    • - *
    • Divorce - Process any divorce-related actions.
    • - *
    • Procreation - Process any procreation-related actions.
    • - *
    • Anniversaries - Check if it's the person's birthday or 18th birthday - * and announce it if needed.
    • - *
    • Auto Awards - If it's the first day of the month, calculate the auto award - * support points based on the person's roles and experience level.
    • + *
    • Death Handling: If the person has died, their processing is skipped for the day.
    • + *
    • Relationship Events: Processes relationship-related events, such as marriage or divorce.
    • + *
    • Reset Actions: Resets the person's minutes left for work and sets acquisitions made to 0.
    • + *
    • Medical Events:
    • + *
    • - If advanced medical care is available, processes the person's daily healing.
    • + *
    • - If advanced medical care is unavailable, decreases the healing wait time and + * applies natural or doctor-assisted healing.
    • + *
    • Weekly Edge Resets: Resets edge points to their purchased value weekly (applies + * to support personnel).
    • + *
    • Vocational XP: Awards monthly vocational experience points to the person where + * applicable.
    • + *
    • Anniversaries: Checks for birthdays or significant anniversaries and announces + * them as needed.
    • + *
    • autoAwards: On the first day of every month, calculates and awards support + * points based on roles and experience levels.
    • *
    *

    - * Note: This method uses several other methods to perform the specific actions for each task. + * Concurrency Note: + * A separate filtered list of personnel is used to avoid concurrent modification issues during iteration. + *

    + * This method relies on several helper methods to perform specific tasks for each person, + * separating the responsibilities for modularity and readability. + * + * @see #getPersonnelFilteringOutDeparted() Filters out departed personnel before daily processing */ public void processNewDayPersonnel() { - List personnelForRelationshipProcessing = new ArrayList<>(); + // This list ensures we don't hit a concurrent modification error + List personnel = getPersonnelFilteringOutDeparted(); + + // Prep some data for vocational xp + int vocationalXpRate = campaignOptions.getVocationalXP(); + if (hasActiveContract) { + if (campaignOptions.isUseAtB()) { + for (AtBContract contract : getActiveAtBContracts()) { + if (!contract.getContractType().isGarrisonType()) { + vocationalXpRate *= 2; + break; + } + } + } else { + vocationalXpRate *= 2; + } + } - for (Person person : getPersonnel()) { + // Process personnel + for (Person person : personnel) { if (person.getStatus().isDepartedUnit()) { continue; } - personnelForRelationshipProcessing.add(person); - - // Death + // Daily events if (getDeath().processNewDay(this, getLocalDate(), person)) { // The person has died, so don't continue to process the dead continue; } person.resetMinutesLeft(); - // Reset acquisitions made to 0 person.setAcquisition(0); processAdvancedMedicalEvents(person); - // Reset edge points to the purchased value each week. This should only - // apply for support personnel - combat troops reset with each new mm game - processWeeklyEdgeResets(person); + processAnniversaries(person); - processMonthlyIdleXP(person); + // Weekly events + if (currentDay.getDayOfWeek() == DayOfWeek.MONDAY) { + processWeeklyRelationshipEvents(person); - // Anniversaries - processAnniversaries(person); + processWeeklyEdgeResets(person); + } + + // Monthly events + if (currentDay.getDayOfMonth() == 1) { + processMonthlyAutoAwards(person); - // autoAwards - processMonthlyAutoAwards(person); + if (vocationalXpRate > 0) { + if (processMonthlyVocationalXp(person, vocationalXpRate)) { + personnelWhoAdvancedInXP.add(person); + } + } + } } - // Divorce, Marriage - // This has to be processed separately to avoid a ConcurrentModificationException - for (Person person : personnelForRelationshipProcessing) { - processWeeklyRelationshipEvents(person); + if (!personnelWhoAdvancedInXP.isEmpty()) { + addReport(String.format(resources.getString("gainedExperience.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + personnelWhoAdvancedInXP.size(), CLOSING_SPAN_TAG)); } } @@ -4115,87 +4442,95 @@ private void processAdvancedMedicalEvents(Person person) { * @param person the person for whom weekly Edge resets will be processed */ private void processWeeklyEdgeResets(Person person) { - if ((person.hasSupportRole(true) || person.isEngineer()) - && (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY)) { + if ((person.hasSupportRole(true) || person.isEngineer())) { person.resetCurrentEdge(); } } /** - * Process monthly idle XP for a given person. - * This method checks if the person is eligible to gain idle XP based on campaign options and current status. - * If the person meets the criteria, they may gain XP and an associated report will be added. + * Processes the monthly vocational experience (XP) gain for a given person based on their + * eligibility and the vocational experience rules defined in campaign options. * - * @param person The person for whom to process monthly idle XP. + *

    Eligibility for receiving vocational XP is determined by checking the following conditions: + *

      + *
    • The person must have an active status (e.g., not retired, deceased, or in education).
    • + *
    • The person must not be a child as of the current date.
    • + *
    • The person must not be categorized as a dependent.
    • + *
    • The person must not have the status of a prisoner.
    • + * Note: Bondsmen are exempt from this restriction and are eligible for vocational XP. + *
    + * + * @param person the {@link Person} whose monthly vocational XP is to be processed + * @param vocationalXpRate the amount of XP awarded on a successful roll + * @return {@code true} if XP was successfully awarded during the process, {@code false} otherwise */ - private void processMonthlyIdleXP(Person person) { + private boolean processMonthlyVocationalXp(Person person, int vocationalXpRate) { if (!person.getStatus().isActive()) { - return; + return false; } - if ((getCampaignOptions().getIdleXP() > 0) && (getLocalDate().getDayOfMonth() == 1) - && !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()) { - person.awardXP(this, getCampaignOptions().getIdleXP()); - addReport(person.getHyperlinkedFullTitle() + " has gained " - + getCampaignOptions().getIdleXP() + " XP"); - } - person.setIdleMonths(0); + if (person.isChild(currentDay)) { + return false; + } + + if (person.isDependent()) { + return false; + } + + if (person.getPrisonerStatus().isCurrentPrisoner()) { + // Prisoners can't gain vocational XP, while Bondsmen can + return false; + } + + int checkFrequency = campaignOptions.getVocationalXPCheckFrequency(); + int targetNumber = campaignOptions.getVocationalXPTargetNumber(); + + person.setVocationalXPTimer(person.getVocationalXPTimer() + 1); + if (person.getVocationalXPTimer() >= checkFrequency) { + if (Compute.d6(2) >= targetNumber) { + person.awardXP(this, vocationalXpRate); + person.setVocationalXPTimer(0); + return true; + } else { + person.setVocationalXPTimer(0); } } + + return false; } /** * Process weekly relationship events for a given {@link Person} on Monday. - * This method triggers specific events related to divorce, marriage, procreation, and maternity leave. + * This method triggers specific events related to divorce, marriage, + * procreation, and maternity leave. * - * @param person The {@link Person} for which to process weekly relationship events + * @param person The {@link Person} for which to process weekly relationship + * events */ private void processWeeklyRelationshipEvents(Person person) { - if (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { + if (currentDay.getDayOfWeek() == DayOfWeek.MONDAY) { getDivorce().processNewWeek(this, getLocalDate(), person, false); - getMarriage().processNewWeek(this, getLocalDate(), person); + getMarriage().processNewWeek(this, getLocalDate(), person, false); 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())) { - LocalDate lastChildBirthDate = getYoungestChildDateOfBirth(children); - - if (currentDay.isAfter(lastChildBirthDate)) { - person.changeStatus(this, getLocalDate(), PersonnelStatus.ACTIVE); - } - } - } - } } } /** - * Process anniversaries for a given person, including birthdays and recruitment anniversaries. + * Process anniversaries for a given person, including birthdays and recruitment + * anniversaries. * * @param person The {@link Person} for whom the anniversaries will be processed */ private void processAnniversaries(Person person) { if ((person.getRank().isOfficer()) || (!getCampaignOptions().isAnnounceOfficersOnly())) { if ((person.getBirthday(getGameYear()).isEqual(getLocalDate())) - && (campaignOptions.isAnnounceBirthdays())) { + && (campaignOptions.isAnnounceBirthdays())) { addReport(String.format(resources.getString("anniversaryBirthday.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - person.getAge(getLocalDate()), - CLOSING_SPAN_TAG)); + person.getHyperlinkedFullTitle(), + ReportingUtilities + .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + person.getAge(getLocalDate()), + CLOSING_SPAN_TAG)); } LocalDate recruitmentDate = person.getRecruitment(); @@ -4204,63 +4539,65 @@ private void processAnniversaries(Person person) { int yearsOfEmployment = (int) ChronoUnit.YEARS.between(recruitmentDate, currentDay); if ((recruitmentAnniversary.isEqual(getLocalDate())) - && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { + && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { addReport(String.format(resources.getString("anniversaryRecruitment.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - yearsOfEmployment, CLOSING_SPAN_TAG, name)); + person.getHyperlinkedFullTitle(), + ReportingUtilities + .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + yearsOfEmployment, CLOSING_SPAN_TAG, name)); } } } else if ((person.getAge(getLocalDate()) == 18) && (campaignOptions.isAnnounceChildBirthdays())) { if (person.getBirthday(getGameYear()).isEqual(getLocalDate())) { addReport(String.format(resources.getString("anniversaryBirthday.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - person.getAge(getLocalDate()), - CLOSING_SPAN_TAG)); + person.getHyperlinkedFullTitle(), + ReportingUtilities + .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + person.getAge(getLocalDate()), + CLOSING_SPAN_TAG)); } } } /** - * Process monthly auto awards for a given person based on their roles and experience level. + * Process monthly auto awards for a given person based on their roles and + * experience level. * * @param person the person for whom the monthly auto awards are being processed */ private void processMonthlyAutoAwards(Person person) { - if (getLocalDate().getDayOfMonth() == 1) { - double multiplier = 0; - - int score = 0; + double multiplier = 0; - if (person.getPrimaryRole().isSupport(true)) { - int dice = person.getExperienceLevel(this, false); + int score = 0; - if (dice > 0) { - score = Compute.d6(dice); - } + if (person.getPrimaryRole().isSupport(true)) { + int dice = person.getExperienceLevel(this, false); - multiplier += 0.5; + if (dice > 0) { + score = Compute.d6(dice); } - if (person.getSecondaryRole().isSupport(true)) { - int dice = person.getExperienceLevel(this, true); + multiplier += 0.5; + } - if (dice > 0) { - score += Compute.d6(dice); - } + if (person.getSecondaryRole().isSupport(true)) { + int dice = person.getExperienceLevel(this, true); - multiplier += 0.5; - } else if (person.getSecondaryRole().isNone()) { - multiplier += 0.5; + if (dice > 0) { + score += Compute.d6(dice); } - person.changeAutoAwardSupportPoints((int) (score * multiplier)); + multiplier += 0.5; + } else if (person.getSecondaryRole().isNone()) { + multiplier += 0.5; } + + person.changeAutoAwardSupportPoints((int) (score * multiplier)); } /** - * Retrieves the date of birth of the youngest child among the provided list of children. + * Retrieves the date of birth of the youngest child among the provided list of + * children. * * @param children the list children * @return the date of birth of the youngest child @@ -4404,7 +4741,7 @@ public void processNewDayUnits() { private void processNewDayForces() { // update formation levels Force.populateFormationLevelsFromOrigin(this); - recalculateStrategicFormations(this); + recalculateCombatTeams(this); // Update the force icons based on the end-of-day unit status if desired if (MekHQ.getMHQOptions().getNewDayForceIconOperationalStatus()) { @@ -4437,23 +4774,24 @@ public boolean newDay() { autosaveService.requestDayAdvanceAutosave(this); // Advance the day by one - final LocalDate yesterday = getLocalDate(); - setLocalDate(getLocalDate().plusDays(1)); + final LocalDate yesterday = currentDay; + currentDay = currentDay.plusDays(1); // Determine if we have an active contract or not, as this can get used // elsewhere before we actually hit the AtB new day (e.g., personnel market) - if (getCampaignOptions().isUseAtB()) { + if (campaignOptions.isUseAtB()) { setHasActiveContract(); } // Clear Reports - getCurrentReport().clear(); - setCurrentReportHTML(""); + currentReport.clear(); + currentReportHTML=""; newReports.clear(); + personnelWhoAdvancedInXP.clear(); beginReport("" + MekHQ.getMHQOptions().getLongDisplayFormattedDate(getLocalDate()) + ""); // New Year Changes - if (getLocalDate().getDayOfYear() == 1) { + if (currentDay.getDayOfYear() == 1) { // News is reloaded reloadNews(); @@ -4463,17 +4801,17 @@ public boolean newDay() { readNews(); - getLocation().newDay(this); + location.newDay(this); // Manage the Markets - getPersonnelMarket().generatePersonnelForDay(this); + personnelMarket.generatePersonnelForDay(this); // TODO : AbstractContractMarket : Uncomment // getContractMarket().processNewDay(this); - getUnitMarket().processNewDay(this); + unitMarket.processNewDay(this); // Process New Day for AtB - if (getCampaignOptions().isUseAtB()) { + if (campaignOptions.isUseAtB()) { processNewDayATB(); } @@ -4489,12 +4827,12 @@ public boolean newDay() { processEducationNewDay(); } - if ((campaignOptions.isEnableAutoAwards()) && (getLocalDate().getDayOfMonth() == 1)) { + if ((campaignOptions.isEnableAutoAwards()) && (currentDay.getDayOfMonth() == 1)) { AutoAwardsController autoAwardsController = new AutoAwardsController(); autoAwardsController.ManualController(this, false); } - if ((getLocation().isOnPlanet()) && (getLocalDate().getDayOfMonth() == 1)) { + if ((location.isOnPlanet()) && (currentDay.getDayOfMonth() == 1)) { processRandomDependents(); } @@ -4507,19 +4845,14 @@ public boolean newDay() { setShoppingList(goShopping(getShoppingList())); // check for anything in finances - getFinances().newDay(this, yesterday, getLocalDate()); + finances.newDay(this, yesterday, getLocalDate()); // process removal of old personnel data on the last day of each month if ((campaignOptions.isUsePersonnelRemoval()) - && (getLocalDate().getMonth().length(false) == getLocalDate().getDayOfMonth())) { + && (currentDay.getMonth().length(false) == currentDay.getDayOfMonth())) { processPersonnelRemoval(); } - if ((campaignOptions.isEnableAutoAwards()) && (getLocalDate().getDayOfMonth() == 1)) { - AutoAwardsController autoAwardsController = new AutoAwardsController(); - autoAwardsController.ManualController(this, false); - } - // this duplicates any turnover information so that it is still available on the // new day. // otherwise, it's only available if the user inspects history records @@ -4529,6 +4862,11 @@ public boolean newDay() { } } + if (topUpWeekly && currentDay.getDayOfWeek() == DayOfWeek.MONDAY) { + int bought = stockUpPartsInUse(getPartsInUse(ignoreMothballed, ignoreSparesUnderQuality)); + addReport(String.format(resources.getString("weeklyStockCheck.text"), bought)); + } + // This must be the last step before returning true MekHQ.triggerEvent(new NewDayEvent(this)); return true; @@ -4551,7 +4889,7 @@ private void processReputationChanges() { if (yearsBetween >= 1) { if (crimePirateModifier < 0) { - remainingCrimeChange = Math.max(0, 2 + crimePirateModifier); + remainingCrimeChange = max(0, 2 + crimePirateModifier); changeCrimePirateModifier(2); // this is the amount of change specified by CamOps } @@ -4606,56 +4944,70 @@ public void setInitiativeMaxBonus(int bonus) { } /** - * 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. + * Processes the random addition and removal of dependents, adjusting the active dependents + * list based on campaign options, active personnel, and unit rating modifiers. + * + *

    This method operates in the following steps: + *

      + *
    1. Filters active personnel into dependents and non-dependents. Dependents without a family + * (i.e., an empty genealogy) are added to the list of active dependents, while eligible non-dependents + * are counted to determine the dependent capacity.
    2. + *
    3. Calculates the dependent capacity as 5% of active non-dependents, with a minimum + * value of 1.
    4. + *
    5. Randomly removes dependents from the active list based on campaign options and a roll + * compared to the unit rating modifier.
    6. + *
    7. Randomly adds new dependents, if allowed, ensuring the total number of dependents does not exceed + * the dependent capacity. The addition is based on a roll relative to the unit rating modifier.
    8. + *
    + * + *

    The method ensures that the number of dependents is managed dynamically and reflects the + * current status of the campaign, its personnel, and relevant modifiers. */ - void processRandomDependents() { - List dependents = getActiveDependents(); - Collections.shuffle(dependents); + private void processRandomDependents() { + int activeNonDependents = 0; + List activeDependents = new ArrayList<>(); - // we use this value a lot, so might as well store it for easier retrieval - LocalDate currentDate = getLocalDate(); + for (Person person : getActivePersonnel()) { + if (!person.getPrisonerStatus().isFreeOrBondsman()) { + continue; + } + if (person.isChild(currentDay)) { + continue; + } + if (person.isDependent()) { + if (person.getGenealogy().familyIsEmpty()) { + activeDependents.add(person); + } + continue; + } - // 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(); + activeNonDependents++; + } - int dependentCapacity = (int) Math.max(1, (activeNonDependents.size() * 0.05)); + // Prepare the data + int dependentCapacity = max(1, (int) round(activeNonDependents * 0.05)); + Collections.shuffle(activeDependents); // roll for random removal - int dependentCount = dependentsRollForRemoval(dependents, currentDate, dependentCapacity); + int dependentCount = dependentsRollForRemoval(activeDependents, dependentCapacity); // then roll for random addition dependentsAddNew(dependentCount, dependentCapacity); } /** - * Randomly removes dependents from the given list if the campaign options allow random + * Randomly removes dependents from the given list if the campaign options allow + * random * dependent removal. * * @param dependents The list of dependents. - * @param currentDate The current date. * @param dependentCapacity The maximum number of dependents allowed. * @return The updated number of dependents. */ - int dependentsRollForRemoval(List dependents, LocalDate currentDate, int dependentCapacity) { + int dependentsRollForRemoval(List dependents, int dependentCapacity) { if (getCampaignOptions().isUseRandomDependentRemoval()) { for (Person dependent : dependents) { - if (!isRemovalEligible(dependent, currentDate)) { + if (!isRemovalEligible(dependent, currentDay)) { continue; } @@ -4680,9 +5032,11 @@ int dependentsRollForRemoval(List dependents, LocalDate currentDate, int } /** - * @return The lower integer value between the given input and the randomly generated integer. + * @return The lower integer value between the given input and the randomly + * generated integer. * - * @param firstRoll The input integer to compare with the randomly generated integer. + * @param firstRoll The input integer to compare with the randomly generated + * integer. */ private int getLowerRandomInt(int firstRoll) { int secondRoll = Compute.randomInt(100); @@ -4692,9 +5046,10 @@ private int getLowerRandomInt(int firstRoll) { /** * Checks if a dependent is eligible for removal. * - * @param dependent the person to check + * @param dependent the person to check * @param currentDate the current date - * @return {@code true} if the person is eligible for removal, {@code false} otherwise + * @return {@code true} if the person is eligible for removal, {@code false} + * otherwise */ boolean isRemovalEligible(Person dependent, LocalDate currentDate) { boolean hasNonAdultChildren = dependent.getGenealogy().hasNonAdultChildren(currentDate); @@ -4707,13 +5062,13 @@ boolean isRemovalEligible(Person dependent, LocalDate currentDate) { /** * Randomly adds new dependents to the campaign. * - * @param dependentCount the current number of dependents + * @param dependentCount the current number of dependents * @param dependentCapacity the maximum capacity for dependents */ void dependentsAddNew(int dependentCount, int dependentCapacity) { if ((getCampaignOptions().isUseRandomDependentAddition()) && (dependentCount < dependentCapacity)) { int availableCapacity = dependentCapacity - dependentCount; - int rollCount = (int) Math.max(1, availableCapacity * 0.2); + int rollCount = (int) max(1, availableCapacity * 0.2); for (int i = 0; i < rollCount; i++) { int roll = Compute.randomInt(100); @@ -4850,15 +5205,12 @@ private void processFatigueNewDay() { int personnelCount; if (campaignOptions.isUseFieldKitchenIgnoreNonCombatants()) { - personnelCount = (int) getActivePersonnel().stream() - .filter(person -> !(person.getPrisonerStatus().isFree() && person.getPrimaryRole().isNone())) - .filter(person -> person.getPrimaryRole().isCombat() || person.getSecondaryRole().isCombat()) - .count(); + personnelCount = getActiveCombatPersonnel().size(); } else { - personnelCount = (int) getActivePersonnel().stream() - .filter(person -> !(person.getPrisonerStatus().isFree() && person.getPrimaryRole().isNone())) - .count(); + personnelCount = getActivePersonnel().size(); } + + personnelCount -= getCurrentPrisoners().size(); fieldKitchenWithinCapacity = personnelCount <= Fatigue.checkFieldKitchenCapacity(this); } else { fieldKitchenWithinCapacity = false; @@ -4916,8 +5268,23 @@ public void removeUnit(UUID id) { // remove unit from any forces removeUnitFromForce(unit); - // If this is a ship, remove it from the list of potential transports - removeTransportShip(unit); + // If this is a transport, remove it from the list of potential transports + for (CampaignTransportType campaignTransportType : CampaignTransportType.values()) { + if (hasTransports(campaignTransportType)) { + removeCampaignTransporter(campaignTransportType, unit); + } + + // If we remove a transport unit from the campaign, + // we need to remove any transported units from it + // and clear the transport assignments for those + // transported units + if (unit.getTransportedUnitsSummary(campaignTransportType).hasTransportedUnits()) { + List transportedUnits = new ArrayList<>(unit.getTransportedUnitsSummary(campaignTransportType).getTransportedUnits()); + for (Unit transportedUnit : transportedUnits) { + transportedUnit.unloadFromTransport(campaignTransportType); + } + } + } // If this unit was assigned to a transport ship, remove it from the transport if (unit.hasTransportShipAssignment()) { @@ -4926,7 +5293,10 @@ public void removeUnit(UUID id) { .unloadFromTransportShip(unit); } - // finally remove the unit + // remove from automatic mothballing + automatedMothballUnits.remove(unit); + + // finally, remove the unit getHangar().removeUnit(unit.getId()); checkDuplicateNamesDuringDelete(unit.getEntity()); @@ -4967,72 +5337,15 @@ public void removePerson(final @Nullable Person person, final boolean log) { // Deal with Astech Pool Minutes if (person.getPrimaryRole().isAstech()) { - astechPoolMinutes = Math.max(0, astechPoolMinutes - Person.PRIMARY_ROLE_SUPPORT_TIME); - astechPoolOvertime = Math.max(0, astechPoolOvertime - Person.PRIMARY_ROLE_OVERTIME_SUPPORT_TIME); + astechPoolMinutes = max(0, astechPoolMinutes - Person.PRIMARY_ROLE_SUPPORT_TIME); + astechPoolOvertime = max(0, astechPoolOvertime - Person.PRIMARY_ROLE_OVERTIME_SUPPORT_TIME); } else if (person.getSecondaryRole().isAstech()) { - astechPoolMinutes = Math.max(0, astechPoolMinutes - Person.SECONDARY_ROLE_SUPPORT_TIME); - astechPoolOvertime = Math.max(0, astechPoolOvertime - Person.SECONDARY_ROLE_OVERTIME_SUPPORT_TIME); + astechPoolMinutes = max(0, astechPoolMinutes - Person.SECONDARY_ROLE_SUPPORT_TIME); + astechPoolOvertime = max(0, astechPoolOvertime - Person.SECONDARY_ROLE_OVERTIME_SUPPORT_TIME); } MekHQ.triggerEvent(new PersonRemovedEvent(person)); } - /** - * Awards XP to the lance based on the maximum experience level of its - * commanding officer and - * the minimum experience level of the unit's members. - * - * @param l The {@link StrategicFormation} to calculate XP to award for training. - */ - private void awardTrainingXP(final StrategicFormation l) { - for (UUID trainerId : forceIds.get(l.getForceId()).getAllUnits(true)) { - Unit trainerUnit = getHangar().getUnit(trainerId); - - // not sure how this occurs, but it probably shouldn't halt processing of a new - // day. - if (trainerUnit == null) { - continue; - } - - Person commander = trainerUnit.getCommander(); - // AtB 2.31: Training lance – needs a officer with Veteran skill levels - // and adds 1xp point to every Green skilled unit. - if (commander != null && commander.getRank().isOfficer()) { - // Take the maximum of the commander's Primary and Secondary Role - // experience to calculate their experience level... - int commanderExperience = Math.max(commander.getExperienceLevel(this, false), - commander.getExperienceLevel(this, true)); - if (commanderExperience > SkillType.EXP_REGULAR) { - // ...and if the commander is better than a veteran, find all of - // the personnel under their command... - for (UUID traineeId : forceIds.get(l.getForceId()).getAllUnits(true)) { - Unit traineeUnit = getHangar().getUnit(traineeId); - - if (traineeUnit == null) { - continue; - } - - for (Person p : traineeUnit.getCrew()) { - if (p.equals(commander)) { - continue; - } - // ...and if their weakest role is Green or Ultra-Green - int experienceLevel = Math.min(p.getExperienceLevel(this, false), - !p.getSecondaryRole().isNone() - ? p.getExperienceLevel(this, true) - : SkillType.EXP_ELITE); - if (experienceLevel >= 0 && experienceLevel < SkillType.EXP_REGULAR) { - // ...add one XP. - p.awardXP(this, 1); - addReport(p.getHyperlinkedName() + " has gained 1 XP from training."); - } - } - } - break; - } - } - } - } - public void removeAllPatientsFor(Person doctor) { for (Person p : getPersonnel()) { if (null != p.getDoctorId() @@ -5120,7 +5433,7 @@ public void removeForce(Force force) { } if (campaignOptions.isUseAtB()) { - recalculateStrategicFormations(this); + recalculateCombatTeams(this); } } @@ -5154,7 +5467,7 @@ public void removeUnitFromForce(Unit u) { } if (campaignOptions.isUseAtB() && force.getUnits().isEmpty()) { - strategicFormations.remove(force.getId()); + combatTeams.remove(force.getId()); } } } @@ -5519,6 +5832,24 @@ public void removeFunds(final TransactionType type, final Money quantity, addReport("Funds removed : " + quantityString + " (" + description + ')'); } + + /** + * Generic method for paying Personnel (Person) in the company. + * Debits money from the campaign and if the campaign tracks + * total earnings it will account for that. + * @param type TransactionType being debited + * @param quantity total money - it's usually displayed outside of this method + * @param description String displayed in the ledger and report + * @param individualPayouts Map of Person to the Money they're owed + */ + public void payPersonnel(TransactionType type, Money quantity, String description, Map individualPayouts) { + getFinances().debit(type, getLocalDate(), quantity, description, + individualPayouts, getCampaignOptions().isTrackTotalEarnings()); + String quantityString = quantity.toAmountAndSymbolString(); + addReport("Funds removed : " + quantityString + " (" + description + ')'); + + } + public CampaignOptions getCampaignOptions() { return campaignOptions; } @@ -5556,10 +5887,28 @@ public FameAndInfamyController getFameAndInfamy() { return fameAndInfamy; } + /** + * Retrieves the list of units that are configured for automated mothballing. + * + *

    Automated mothballing is a mechanism where certain units are automatically + * placed into a mothballed state, reducing their active maintenance costs + * and operational demands over time.

    + * + * @return A {@link List} of {@link Unit} objects that are set for automated + * mothballing. Returns an empty list if no units are configured. + */ public List getAutomatedMothballUnits() { return automatedMothballUnits; } + /** + * Sets the list of units that are configured for automated mothballing. + * + *

    Replaces the current list of units that have undergone automated mothballing.

    + * + * @param automatedMothballUnits A {@link List} of {@link Unit} objects + * to configure for automated mothballing. + */ public void setAutomatedMothballUnits(List automatedMothballUnits) { this.automatedMothballUnits = automatedMothballUnits; } @@ -5730,26 +6079,38 @@ public void writeToXML(final PrintWriter pw) { // CAW: implicit DEPENDS-ON to the node, do not move this above it contractMarket.writeToXML(pw, indent); - if (!strategicFormations.isEmpty()) { - MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "strategicFormations"); - for (StrategicFormation l : strategicFormations.values()) { - if (forceIds.containsKey(l.getForceId())) { - l.writeToXML(pw, indent); + if (!combatTeams.isEmpty()) { + MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "combatTeams"); + for (CombatTeam combatTeam : combatTeams.values()) { + if (forceIds.containsKey(combatTeam.getForceId())) { + combatTeam.writeToXML(pw, indent); } } - MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "strategicFormations"); + MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "combatTeams"); } MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchStart", getShipSearchStart()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchType", shipSearchType); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchResult", shipSearchResult); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchExpiration", getShipSearchExpiration()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveBehaviorSettings", autoResolveBehaviorSettings.getDescription()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveBehaviorSettings", + autoResolveBehaviorSettings.getDescription()); } retirementDefectionTracker.writeToXML(pw, indent); + MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "personnelWhoAdvancedInXP"); + for (Person person : personnelWhoAdvancedInXP) { + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "personWhoAdvancedInXP", person.getId()); + } + MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "personnelWhoAdvancedInXP"); + MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "automatedMothballUnits"); for (Unit unit : automatedMothballUnits) { + if (unit == null) { + // <50.03 compatibility handler + continue; + } + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "mothballedUnit", unit.getId()); } MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "automatedMothballUnits"); @@ -5801,10 +6162,16 @@ public void writeToXML(final PrintWriter pw) { } MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "customPlanetaryEvents"); + MHQXMLUtility.writeSimpleXMLOpenTag(pw, ++indent, "partsInUse"); + writePartInUseToXML(pw, indent); + MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "partsInUse"); + + if (MekHQ.getMHQOptions().getWriteCustomsToXML()) { writeCustoms(pw); } + // Okay, we're done. // Close everything out and be done with it. MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "campaign"); @@ -6120,29 +6487,29 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign double carriedCargo = cargoStats.getCargoTonnage(true, false) + cargoStats.getCargoTonnage(false, true); // calculate the number of units left untransported - int noMek = Math.max(nMek - stats.getOccupiedBays(Entity.ETYPE_MEK), 0); - int noDS = Math.max(nDropship - stats.getOccupiedBays(Entity.ETYPE_DROPSHIP), 0); - int noSC = Math.max(nSC - stats.getOccupiedBays(Entity.ETYPE_SMALL_CRAFT), 0); - int noCF = Math.max(nCF - stats.getOccupiedBays(Entity.ETYPE_CONV_FIGHTER), 0); - int noASF = Math.max(nAero - stats.getOccupiedBays(Entity.ETYPE_AEROSPACEFIGHTER), 0); - int nolv = Math.max(nLVee - stats.getOccupiedBays(Entity.ETYPE_TANK, true), 0); - int nohv = Math.max(nHVee - stats.getOccupiedBays(Entity.ETYPE_TANK), 0); - int noinf = Math.max( + int noMek = max(nMek - stats.getOccupiedBays(Entity.ETYPE_MEK), 0); + int noDS = max(nDropship - stats.getOccupiedBays(Entity.ETYPE_DROPSHIP), 0); + int noSC = max(nSC - stats.getOccupiedBays(Entity.ETYPE_SMALL_CRAFT), 0); + int noCF = max(nCF - stats.getOccupiedBays(Entity.ETYPE_CONV_FIGHTER), 0); + int noASF = max(nAero - stats.getOccupiedBays(Entity.ETYPE_AEROSPACEFIGHTER), 0); + int nolv = max(nLVee - stats.getOccupiedBays(Entity.ETYPE_TANK, true), 0); + int nohv = max(nHVee - stats.getOccupiedBays(Entity.ETYPE_TANK), 0); + int noinf = max( stats.getNumberOfUnitsByType(Entity.ETYPE_INFANTRY) - stats.getOccupiedBays(Entity.ETYPE_INFANTRY), 0); - int noBA = Math.max(nBA - stats.getOccupiedBays(Entity.ETYPE_BATTLEARMOR), 0); - int noProto = Math.max(nProto - stats.getOccupiedBays(Entity.ETYPE_PROTOMEK), 0); - int freehv = Math.max(stats.getTotalHeavyVehicleBays() - stats.getOccupiedBays(Entity.ETYPE_TANK), 0); - int freeinf = Math.max(stats.getTotalInfantryBays() - stats.getOccupiedBays(Entity.ETYPE_INFANTRY), 0); - int freeba = Math.max(stats.getTotalBattleArmorBays() - stats.getOccupiedBays(Entity.ETYPE_BATTLEARMOR), 0); - int freeSC = Math.max(stats.getTotalSmallCraftBays() - stats.getOccupiedBays(Entity.ETYPE_SMALL_CRAFT), 0); - int noCargo = (int) Math.ceil(Math.max(carriedCargo - nCargo, 0)); - - int newNoASF = Math.max(noASF - freeSC, 0); - int placedASF = Math.max(noASF - newNoASF, 0); + int noBA = max(nBA - stats.getOccupiedBays(Entity.ETYPE_BATTLEARMOR), 0); + int noProto = max(nProto - stats.getOccupiedBays(Entity.ETYPE_PROTOMEK), 0); + int freehv = max(stats.getTotalHeavyVehicleBays() - stats.getOccupiedBays(Entity.ETYPE_TANK), 0); + int freeinf = max(stats.getTotalInfantryBays() - stats.getOccupiedBays(Entity.ETYPE_INFANTRY), 0); + int freeba = max(stats.getTotalBattleArmorBays() - stats.getOccupiedBays(Entity.ETYPE_BATTLEARMOR), 0); + int freeSC = max(stats.getTotalSmallCraftBays() - stats.getOccupiedBays(Entity.ETYPE_SMALL_CRAFT), 0); + int noCargo = (int) Math.ceil(max(carriedCargo - nCargo, 0)); + + int newNoASF = max(noASF - freeSC, 0); + int placedASF = max(noASF - newNoASF, 0); freeSC -= placedASF; - int newNolv = Math.max(nolv - freehv, 0); - int placedlv = Math.max(nolv - newNolv, 0); + int newNolv = max(nolv - freehv, 0); + int placedlv = max(nolv - newNolv, 0); freehv -= placedlv; int noVehicles = (nohv + newNolv); @@ -6231,7 +6598,7 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign mekCollars += 1; } - leasedASFCapacity += (int) Math.floor(leasedLargeMekDropships * largeMekDropshipASFCapacity); + leasedASFCapacity += (int) floor(leasedLargeMekDropships * largeMekDropshipASFCapacity); leasedCargoCapacity += largeMekDropshipCargoCapacity; } @@ -6251,8 +6618,8 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign } // Our Union-ish DropShip can carry some ASFs and cargo. - leasedASFCapacity += (int) Math.floor(leasedAverageMekDropships * mekDropshipASFCapacity); - leasedCargoCapacity += (int) Math.floor(leasedAverageMekDropships * mekDropshipCargoCapacity); + leasedASFCapacity += (int) floor(leasedAverageMekDropships * mekDropshipASFCapacity); + leasedCargoCapacity += (int) floor(leasedAverageMekDropships * mekDropshipCargoCapacity); } // Leopard CVs @@ -6274,7 +6641,7 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign } // Our Leopard-ish DropShip can carry some cargo. - leasedCargoCapacity += (int) Math.floor(asfDropshipCargoCapacity * leasedAverageASFDropships); + leasedCargoCapacity += (int) floor(asfDropshipCargoCapacity * leasedAverageASFDropships); } // Triumphs @@ -6289,7 +6656,7 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign vehicleCollars += 1; } - leasedCargoCapacity += (int) Math.floor(leasedLargeVehicleDropships * largeVehicleDropshipCargoCapacity); + leasedCargoCapacity += (int) floor(leasedLargeVehicleDropships * largeVehicleDropshipCargoCapacity); } // Gazelles @@ -6306,7 +6673,7 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign } // Our Gazelle-ish DropShip can carry some cargo. - leasedCargoCapacity += (int) Math.floor(vehicleDropshipCargoCapacity * leasedAverageVehicleDropships); + leasedCargoCapacity += (int) floor(vehicleDropshipCargoCapacity * leasedAverageVehicleDropships); } // Do we have any leftover cargo? @@ -6358,7 +6725,7 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign collarsNeeded += nDropship; // now factor in owned JumpShips - collarsNeeded = Math.max(0, collarsNeeded - nCollars); + collarsNeeded = max(0, collarsNeeded - nCollars); Money totalCost = dropshipCost.plus(collarCost.multipliedBy(collarsNeeded)); @@ -6694,7 +7061,7 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, * non-Clan units */ if (acquisition.getTechBase() == Part.T_CLAN && !getFaction().isClan()) { - partAvailability = Math.max(partAvailability, EquipmentType.RATING_F); + partAvailability = max(partAvailability, EquipmentType.RATING_F); partAvailabilityLog.append("
    [clan part for non clan faction]"); } else if (et != null) { /* @@ -6763,63 +7130,12 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, } public @Nullable AtBContract getAttachedAtBContract(Unit unit) { - if (null != unit && null != strategicFormations.get(unit.getForceId())) { - return strategicFormations.get(unit.getForceId()).getContract(this); + if (null != unit && null != combatTeams.get(unit.getForceId())) { + return combatTeams.get(unit.getForceId()).getContract(this); } return null; } - /** - * AtB: count all available bonus parts - * - * @return the total int number of bonus parts for all active - * contracts - */ - public int totalBonusParts() { - int retVal = 0; - if (hasActiveContract()) { - for (Contract c : getActiveContracts()) { - if (c instanceof AtBContract) { - retVal += ((AtBContract) c).getNumBonusParts(); - } - } - } - return retVal; - } - - public void spendBonusPart(IAcquisitionWork targetWork) { - // Can only spend from active contracts, so if there are none we can't spend a - // bonus part - if (!hasActiveContract()) { - return; - } - - String report = targetWork.find(0); - - if (report.endsWith("0 days.")) { - // First, try to spend from the contact the Acquisition's unit is attached to - AtBContract contract = getAttachedAtBContract(targetWork.getUnit()); - - if (contract == null) { - // Then, just the first free one that is active - for (Contract c : getActiveContracts()) { - if (((AtBContract) c).getNumBonusParts() > 0) { - contract = (AtBContract) c; - break; - } - } - } - - if (contract == null) { - logger.error("AtB: used bonus part but no contract has bonus parts available."); - } else { - addReport( - resources.getString("bonusPartLog.text") + ' ' + targetWork.getAcquisitionPart().getPartName()); - contract.useBonusPart(); - } - } - } - public int findAtBPartsAvailabilityLevel(IAcquisitionWork acquisition, StringBuilder reportBuilder) { AtBContract contract = (acquisition != null) ? getAttachedAtBContract(acquisition.getUnit()) : null; @@ -6942,10 +7258,10 @@ public void fillAstechPool() { } public void decreaseAstechPool(int i) { - astechPool = Math.max(0, astechPool - i); + astechPool = max(0, astechPool - i); // always assume that we fire the ones who have not yet worked - astechPoolMinutes = Math.max(0, astechPoolMinutes - 480 * i); - astechPoolOvertime = Math.max(0, astechPoolOvertime - 240 * i); + astechPoolMinutes = max(0, astechPoolMinutes - 480 * i); + astechPoolOvertime = max(0, astechPoolOvertime - 240 * i); MekHQ.triggerEvent(new AstechPoolChangedEvent(this, -i)); } @@ -6979,7 +7295,7 @@ public int getAvailableAstechs(final int minutes, final boolean alreadyOvertime) return 0; } - int availableHelp = (int) Math.floor(((double) astechPoolMinutes) / minutes); + int availableHelp = (int) floor(((double) astechPoolMinutes) / minutes); if (isOvertimeAllowed() && (availableHelp < MHQConstants.ASTECH_TEAM_SIZE)) { // if we are less than fully staffed, then determine whether // we should dip into overtime or just continue as short-staffed @@ -7075,7 +7391,7 @@ public void fillMedicPool() { } public void decreaseMedicPool(int i) { - medicPool = Math.max(0, medicPool - i); + medicPool = max(0, medicPool - i); MekHQ.triggerEvent(new MedicPoolChangedEvent(this, -i)); } @@ -7251,12 +7567,13 @@ public int getAtBUnitRatingMod() { /** * Returns the Strategy skill of the designated commander in the campaign. * - * @return The value of the commander's strategy skill if a commander exists, otherwise 0. + * @return The value of the commander's strategy skill if a commander exists, + * otherwise 0. */ public int getCommanderStrategy() { int cmdrStrategy = 0; if (getFlaggedCommander() != null && - getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) { + getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) { cmdrStrategy = getFlaggedCommander().getSkill(SkillType.S_STRATEGY).getLevel(); } return cmdrStrategy; @@ -7371,6 +7688,7 @@ public void clearGameData(Entity entity) { entity.resetCoolantFailureAmount(); entity.setConversionMode(0); entity.setDoomed(false); + entity.setDestroyed(false); entity.setHidden(false); entity.clearNarcAndiNarcPods(); entity.setShutDown(false); @@ -7761,10 +8079,15 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { // check for money in escrow // According to FMM(r) pg 179, both failure and breach lead to no // further payment even though this seems foolish - if ((contract.getStatus().isSuccess()) - && (contract.getMonthsLeft(getLocalDate()) > 0)) { + if (contract.getStatus().isSuccess()) { remainingMoney = contract.getMonthlyPayOut() - .multipliedBy(contract.getMonthsLeft(getLocalDate())); + .multipliedBy(contract.getMonthsLeft(getLocalDate())); + + if (contract instanceof AtBContract) { + Money routedPayout = ((AtBContract) contract).getRoutedPayout(); + + remainingMoney = routedPayout == null ? remainingMoney : routedPayout; + } } // If overage repayment is enabled, we first need to check if the salvage @@ -7787,27 +8110,17 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { if (getCampaignOptions().isUseShareSystem()) { ResourceBundle financeResources = ResourceBundle.getBundle("mekhq.resources.Finances", - MekHQ.getMHQOptions().getLocale()); + MekHQ.getMHQOptions().getLocale()); - Money shares = remainingMoney.multipliedBy(contract.getSharesPercent()).dividedBy(100); - remainingMoney = remainingMoney.minus(shares); + if (remainingMoney.isGreaterThan(Money.zero())) { + Money shares = remainingMoney.multipliedBy(contract.getSharesPercent()).dividedBy(100); + remainingMoney = remainingMoney.minus(shares); - if (getFinances().debit(TransactionType.SALARIES, getLocalDate(), shares, + if (getFinances().debit(TransactionType.SALARIES, getLocalDate(), shares, String.format(financeResources.getString("ContractSharePayment.text"), contract.getName()))) { - addReport(financeResources.getString("DistributedShares.text"), shares.toAmountAndSymbolString()); - - if (getCampaignOptions().isTrackTotalEarnings()) { - boolean sharesForAll = getCampaignOptions().isSharesForAll(); - - int numberOfShares = getActivePersonnel().stream() - .mapToInt(person -> person.getNumShares(this, sharesForAll)) - .sum(); - - Money singleShare = shares.dividedBy(numberOfShares); + addReport(financeResources.getString("DistributedShares.text"), shares.toAmountAndSymbolString()); - for (Person person : getActivePersonnel()) { - person.payPersonShares(this, singleShare, sharesForAll); - } + getFinances().payOutSharesToPersonnel(this, shares); } } } @@ -7821,7 +8134,7 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { getFinances().credit(TransactionType.CONTRACT_PAYMENT, getLocalDate(), remainingMoney, "Repaying payment overages for " + contract.getName()); addReport("Your account has been debited for " + remainingMoney.absolute().toAmountAndSymbolString() - + " to replay payment overages occurred during the contract " + contract.getName()); + + " to repay payment overages occurred during the contract " + contract.getName()); } // This relies on the mission being a Contract, and AtB to be on @@ -7859,7 +8172,7 @@ public int calculatePartTransitTime(PlanetarySystem system) { // calculate number of jumps by dividing by 30 int jumps = (int) Math.ceil(distance / 30.0); // you need a recharge except for the first jump - int recharges = Math.max(jumps - 1, 0); + int recharges = max(jumps - 1, 0); // if you are delivering from the same planet then no transit times int currentTransitTime = (distance > 0) ? (int) Math.ceil(getCurrentSystem().getTimeToJumpPoint(1.0)) : 0; int originTransitTime = (distance > 0) ? (int) Math.ceil(system.getTimeToJumpPoint(1.0)) : 0; @@ -8024,23 +8337,91 @@ public void setHealingTimeOptions(int newHeal, int newNaturalHeal) { if (healDiff != 0 || naturalDiff != 0) { for (Person p : getPersonnel()) { if (p.getDoctorId() != null) { - p.setDaysToWaitForHealing(Math.max( + p.setDaysToWaitForHealing(max( p.getDaysToWaitForHealing() + healDiff, 1)); } else { - p.setDaysToWaitForHealing(Math.max( + p.setDaysToWaitForHealing(max( p.getDaysToWaitForHealing() + naturalDiff, 1)); } } } } + private CampaignTransporterMap getCampaignTransporterMap(CampaignTransportType campaignTransportType) { + if (campaignTransportType.isTacticalTransport()) { + return tacticalTransporters; + } + else if (campaignTransportType.isShipTransport()) { + return shipTransporters; + } + return null; + } + + /** - * Returns our list of potential transport ships + * Returns a Map that maps Transporter types to another + * Map that maps capacity (Double) to UUID of transports + * for the specific TransportedUnitSummary type * - * @return + * @param campaignTransportType type (Enum) of TransportedUnitSummary + * @return the full map for that campaign transport type + */ + public Map>> getTransports(CampaignTransportType campaignTransportType) { + return getCampaignTransporterMap(campaignTransportType).getTransporters(); + } + + /** + * Returns list of transports that have the provided + * TransporterType and CampaignTransportType + * + * @param campaignTransportType type of campaign transport + * @param transporterType type of Transporter + * @return units that have that transport type + */ + public Set getTransportsByType(CampaignTransportType campaignTransportType, TransporterType transporterType) { + return getCampaignTransporterMap(campaignTransportType).getTransportsByType(transporterType, -1.0); //include transports with no remaining capacity + } + + + /** + * Returns list of transports for the specified + * AbstractTransportedUnitSummary class/subclass + * that has transport capacity for the + * Transporter class/subclass + * For example, getTransportsByType(SHIP_TRANSPORT, MEK_BAY, 3.0) + * would return all transports that have 3 or more Mek Bay slots + * open for the SHIP_TRANSPORT type of assignment. + * + * @param campaignTransportType type (Enum) of TransportedUnitSummary + * @param transporterType type (Enum) of Transporter + * @param unitSize capacity that the transport must be capable of + * @return units that have that transport type + */ + public Set getTransportsByType(CampaignTransportType campaignTransportType, TransporterType transporterType, double unitSize) { + return getCampaignTransporterMap(campaignTransportType).getTransportsByType(transporterType, unitSize); + } + + private boolean hasTacticalTransports() { + return tacticalTransporters.hasTransporters(); + } + + private boolean hasShipTransports() { + return shipTransporters.hasTransporters(); + } + + /** + * Do we have transports for the kind of transport? + * @param campaignTransportType class of the TransportDetail + * @return true if it has transporters, false otherwise */ - public Set getTransportShips() { - return Collections.unmodifiableSet(transportShips); + public boolean hasTransports(CampaignTransportType campaignTransportType) { + if (campaignTransportType.isTacticalTransport()) { + return hasTacticalTransports(); + } + else if (campaignTransportType.isShipTransport()) { + return hasShipTransports(); + } + return false; } public void doMaintenance(Unit u) { @@ -8137,10 +8518,10 @@ public void doMaintenance(Unit u) { qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( MekHQ.getMHQOptions().getFontColorPositiveHexColor(), "Overall quality improves from " + qualityOrig.toName(reverse) - + " to " + quality.toName(reverse)); + + " to " + quality.toName(reverse)); } else if (quality.toNumeric() < qualityOrig.toNumeric()) { qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorPositiveHexColor(), + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "Overall quality declines from " + qualityOrig.toName(reverse) + " to " + quality.toName(reverse)); } else { @@ -8325,7 +8706,8 @@ public void initTimeInRank() { LocalDate join = null; for (LogEntry e : p.getPersonnelLog()) { if (join == null) { - // If by some nightmare there is no date from the below, just use the first entry. + // If by some nightmare there is no date from the below, just use the first + // entry. join = e.getDate(); } @@ -8335,7 +8717,8 @@ public void initTimeInRank() { } } - // For that one in a billion chance the log is empty. Clone today's date and subtract a year + // For that one in a billion chance the log is empty. Clone today's date and + // subtract a year p.setLastRankChangeDate((join != null) ? join : getLocalDate().minusYears(1)); } } @@ -8410,7 +8793,7 @@ public void initAtB(boolean newCampaign) { } } - addAllStrategicFormations(this.forces); + addAllCombatTeams(this.forces); // Determine whether or not there is an active contract setHasActiveContract(); @@ -8447,7 +8830,8 @@ public boolean checkOverDueLoans() { } /** - * Checks if a turnover prompt should be displayed based on campaign options and current date. + * Checks if a turnover prompt should be displayed based on campaign options and + * current date. * * @return An integer representing the user's choice: * -1 if turnover prompt should not be displayed. @@ -8509,8 +8893,7 @@ public int checkTurnoverPrompt() { JOptionPane.INFORMATION_MESSAGE, null, options, - options[0] - ); + options[0]); } /** @@ -8646,25 +9029,156 @@ public void setAutoResolveBehaviorSettings(BehaviorSettings settings) { } /** - * Gets a string to use for addressing the commander. - * If no commander is flagged, returns a default address. + * Retrieves the address or title for the commanding officer, either in a formal or informal format. * - * @return The title of the commander, or a default string if no commander. + *

    This method checks for the presence of a flagged commander. If no commander is found, + * a general fallback address is returned based on the specified formality. If a commander is + * present, it further tailors the address based on the gender of the commander (for informal styles) + * or their rank and surname (for formal styles).

    + * + * @param isInformal A boolean flag indicating whether the address should be informal + * (true for informal, false for formal). + * @return A {@link String} representing the appropriate address for the commander, + * either formal or informal. */ - public String getCommanderAddress() { + public String getCommanderAddress(boolean isInformal) { Person commander = getFlaggedCommander(); if (commander == null) { - return resources.getString("generalFallbackAddress.text"); + if (isInformal) { + return resources.getString("generalFallbackAddressInformal.text"); + } else { + return resources.getString("generalFallbackAddress.text"); + } + } + + if (isInformal) { + Gender commanderGender = commander.getGender(); + + return switch (commanderGender) { + case MALE -> resources.getString("informalAddressMale.text"); + case FEMALE -> resources.getString("informalAddressFemale.text"); + case OTHER_MALE, OTHER_FEMALE, RANDOMIZE -> + resources.getString("generalFallbackAddressInformal.text"); + }; } String commanderRank = commander.getRankName(); - if (commanderRank.equalsIgnoreCase("None") || commanderRank.isBlank()) { - return commander.getFullName(); + if (commanderRank.equalsIgnoreCase("None") + || commanderRank.equalsIgnoreCase("-") + || commanderRank.isBlank()) { + return resources.getString("generalFallbackAddress.text"); } return commanderRank; } + public int stockUpPartsInUse(Set partsInUse) { + int bought = 0; + for(PartInUse partInUse : partsInUse) { + int toBuy = findStockUpAmount(partInUse); + if (toBuy > 0) { + IAcquisitionWork partToBuy = partInUse.getPartToBuy(); + getShoppingList().addShoppingItem(partToBuy, toBuy, this); + bought += 1; + } + } + return bought; + } + + public void stockUpPartsInUseGM(Set partsInUse) { + for(PartInUse partInUse : partsInUse) { + int toBuy = findStockUpAmount(partInUse); + while (toBuy > 0) { + IAcquisitionWork partToBuy = partInUse.getPartToBuy(); + getQuartermaster().addPart((Part) partToBuy.getNewEquipment(), 0); + -- toBuy; + } + } + } + + private int findStockUpAmount(PartInUse PartInUse) { + IAcquisitionWork partToBuy = PartInUse.getPartToBuy(); + int inventory = PartInUse.getStoreCount() + PartInUse.getTransferCount() + PartInUse.getPlannedCount(); + int needed = (int)Math.ceil(PartInUse.getRequestedStock()/100.0 * PartInUse.getUseCount()); + int toBuy = needed-inventory; + + if (PartInUse.getIsBundle() == true) { + toBuy = (int)Math.ceil((float)toBuy * PartInUse.getTonnagePerItem() / 5); + //special case for armor only, as it's bought in 5 ton blocks. Armor is the only kind of item that's assigned isBundle() + } + + return toBuy; + } + + //Simple getters and setters for our stock map + public Map getPartsInUseRequestedStockMap() { + return partsInUseRequestedStockMap; + } + + public void setPartsInUseRequestedStockMap(Map partsInUseRequestedStockMap) { + this.partsInUseRequestedStockMap = partsInUseRequestedStockMap; + } + + public boolean getIgnoreMothballed() { + return ignoreMothballed; + } + public void setIgnoreMothballed(boolean ignoreMothballed) { + this.ignoreMothballed = ignoreMothballed; + } + + public boolean getTopUpWeekly() { + return topUpWeekly; + } + public void setTopUpWeekly(boolean topUpWeekly) { + this.topUpWeekly = topUpWeekly; + } + + public PartQuality getIgnoreSparesUnderQuality() { + return ignoreSparesUnderQuality; + } + public void setIgnoreSparesUnderQuality(PartQuality ignoreSparesUnderQuality) { + this.ignoreSparesUnderQuality = ignoreSparesUnderQuality; + } + + + public void writePartInUseToXML(final PrintWriter pw, int indent) { + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ignoreMothBalled", ignoreMothballed); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "topUpWeekly", topUpWeekly); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ignoreSparesUnderQuality", + ignoreSparesUnderQuality.name()); + MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "partInUseMap"); + writePartInUseMapToXML(pw, indent); + MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "partInUseMap"); + } + + public void writePartInUseMapToXML(final PrintWriter pw, int indent) { + for(String key : partsInUseRequestedStockMap.keySet()) { + MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "partInUseMapEntry"); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partInUseMapKey", key); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partInUseMapVal", + partsInUseRequestedStockMap.get(key)); + MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "partInUseMapEntry"); + } + } + /** + * Retrieves the campaign faction icon for the specified {@link Campaign}. + * If a custom icon is defined in the campaign's unit icon configuration, that icon is used. + * Otherwise, a default faction logo is fetched based on the campaign's faction short name. + * + * @return An {@link ImageIcon} representing the faction icon for the given campaign. + */ + public ImageIcon getCampaignFactionIcon() { + ImageIcon icon; + StandardForceIcon campaignIcon = getUnitIcon(); + + if (campaignIcon.getFilename() == null) { + icon = getFactionLogo(this, getFaction().getShortName(), + true); + } else { + icon = new ImageIcon(campaignIcon.getFilename()); + } + return icon; + } } diff --git a/MekHQ/src/mekhq/campaign/CampaignFactory.java b/MekHQ/src/mekhq/campaign/CampaignFactory.java index 294024bc3a1..4e4e57b7cac 100644 --- a/MekHQ/src/mekhq/campaign/CampaignFactory.java +++ b/MekHQ/src/mekhq/campaign/CampaignFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2018-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,10 +18,14 @@ */ package mekhq.campaign; +import megamek.Version; +import megamek.common.annotations.Nullable; +import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.NullEntityException; import mekhq.campaign.io.CampaignXmlParseException; import mekhq.campaign.io.CampaignXmlParser; +import mekhq.gui.dialog.CampaignHasProblemOnLoad; import java.io.BufferedInputStream; import java.io.IOException; @@ -35,6 +39,13 @@ public class CampaignFactory { private MekHQ app; + public enum CampaignProblemType { + NONE, + CANT_LOAD_FROM_NEWER_VERSION, + CANT_LOAD_FROM_OLDER_VERSION, + ACTIVE_OR_FUTURE_CONTRACT + } + /** * Protected constructor to prevent instantiation. */ @@ -64,7 +75,7 @@ public static CampaignFactory newInstance(MekHQ app) { * the input stream. * @throws NullEntityException if the campaign contains a null entity */ - public Campaign createCampaign(InputStream is) + public @Nullable Campaign createCampaign(InputStream is) throws CampaignXmlParseException, IOException, NullEntityException { if (!is.markSupported()) { is = new BufferedInputStream(is); @@ -79,8 +90,82 @@ public Campaign createCampaign(InputStream is) // ...otherwise, assume we're an XML file. CampaignXmlParser parser = new CampaignXmlParser(is, this.app); + Campaign campaign = parser.parse(); + + if (campaign == null) { + return null; + } - return parser.parse(); + return checkForLoadProblems(campaign); + } + + /** + * Validates the campaign for loading issues and presents the user with dialogs for each problem encountered. + * + *

    This method sequentially checks for three potential problems while loading the campaign:

    + *
      + *
    • If the campaign version is newer than the application's version.
    • + *
    • If the campaign version is older than the last supported milestone version.
    • + *
    • If the campaign has active or future AtB contracts.
    • + *
    + * + *

    For each issue encountered, a dialog is displayed to the user using {@link CampaignHasProblemOnLoad}. + * The user can either cancel or proceed with loading. If the user cancels at any point, the method + * returns {@code null}. Otherwise, if no problems remain or the user chooses to proceed for all + * issues, the method returns the given {@code Campaign} object.

    + * + * @param campaign the {@link Campaign} object to validate and load + * @return the {@link Campaign} object if the user chooses to proceed with all problems or if no + * problems are detected; {@code null} if the user chooses to cancel + */ + private static Campaign checkForLoadProblems(Campaign campaign) { + final Version mhqVersion = MHQConstants.VERSION; + final Version lastMilestone = MHQConstants.LAST_MILESTONE; + final Version campaignVersion = campaign.getVersion(); + + // Check if the campaign is from a newer version + if (campaignVersion.isHigherThan(mhqVersion)) { + if (triggerProblemDialog(campaign, CampaignProblemType.CANT_LOAD_FROM_NEWER_VERSION)) { + return null; + } + } + + // Check if the campaign is from an older, unsupported version + if (campaignVersion.isLowerThan(lastMilestone)) { + if (triggerProblemDialog(campaign, CampaignProblemType.CANT_LOAD_FROM_OLDER_VERSION)) { + return null; + } + } + + // Check if the campaign has active or future AtB contracts (only if the user is changing versions) + if (!campaignVersion.equals(mhqVersion) && campaign.hasActiveAtBContract(true)) { + if (triggerProblemDialog(campaign, CampaignProblemType.ACTIVE_OR_FUTURE_CONTRACT)) { + return null; + } + } + + // All checks passed, return the campaign + return campaign; + } + + /** + * Displays the {@link CampaignHasProblemOnLoad} dialog for a given problem type and returns + * whether the user cancelled the loading process. + * + *

    The dialog informs the user about the specific problem and allows them to either + * cancel the loading process or continue despite the problem. If the user selects + * "Cancel," the method returns {@code true}. Otherwise, it returns {@code false}.

    + * + * @param campaign the {@link Campaign} object associated with the problem + * @param problemType the {@link CampaignProblemType} specifying the current issue + * @return {@code true} if the user chose to cancel loading, {@code false} otherwise + */ + private static boolean triggerProblemDialog(Campaign campaign, CampaignProblemType problemType) { + final int USER_SELECTED_CANCEL = 0; + + CampaignHasProblemOnLoad problemDialog = new CampaignHasProblemOnLoad(campaign, problemType); + + return problemDialog.getDialogChoice() == USER_SELECTED_CANCEL; } private byte[] readHeader(InputStream is) throws IOException { @@ -91,4 +176,5 @@ private byte[] readHeader(InputStream is) throws IOException { return header; } + } diff --git a/MekHQ/src/mekhq/campaign/CampaignOptions.java b/MekHQ/src/mekhq/campaign/CampaignOptions.java index a8f2769c369..987c9d329fa 100644 --- a/MekHQ/src/mekhq/campaign/CampaignOptions.java +++ b/MekHQ/src/mekhq/campaign/CampaignOptions.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2009 - Jay Lawson (jaylawson39 at yahoo.com). All Rights Reserved. - * Copyright (c) 2020-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -20,20 +20,26 @@ package mekhq.campaign; import megamek.Version; +import megamek.client.ui.swing.GUIPreferences; import megamek.codeUtilities.MathUtility; +import megamek.common.Configuration; import megamek.common.EquipmentType; import megamek.common.TechConstants; import megamek.common.enums.SkillLevel; +import megamek.common.preference.ClientPreferences; +import megamek.common.preference.PreferenceManager; +import megamek.common.util.fileUtils.MegaMekFile; import megamek.logging.MMLogger; import mekhq.MekHQ; import mekhq.Utilities; +import mekhq.campaign.autoresolve.AutoResolveMethod; import mekhq.campaign.enums.PlanetaryAcquisitionFactionLimit; import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.FinancialYearDuration; import mekhq.campaign.market.PersonnelMarket; import mekhq.campaign.market.enums.ContractMarketMethod; import mekhq.campaign.market.enums.UnitMarketMethod; -import mekhq.campaign.mission.enums.AtBLanceRole; +import mekhq.campaign.mission.enums.CombatRole; import mekhq.campaign.parts.enums.PartRepairType; import mekhq.campaign.personnel.Skills; import mekhq.campaign.personnel.enums.*; @@ -43,6 +49,7 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import java.io.File; import java.io.PrintWriter; import java.util.*; import java.util.Map.Entry; @@ -52,7 +59,7 @@ */ public class CampaignOptions { private static final MMLogger logger = MMLogger.create(CampaignOptions.class); - + private static final ClientPreferences CLIENT_PREFERENCES = PreferenceManager.getClientPreferences(); // region Magic Numbers public static final int TECH_INTRO = 0; public static final int TECH_STANDARD = 1; @@ -63,10 +70,8 @@ public class CampaignOptions { // that haven't been invented yet, or that are completely extinct public static final int TECH_UNKNOWN = 5; - public static final int TRANSIT_UNIT_DAY = 0; public static final int TRANSIT_UNIT_WEEK = 1; public static final int TRANSIT_UNIT_MONTH = 2; - public static final int TRANSIT_UNIT_NUM = 3; public static final String S_TECH = "Tech"; public static final String S_AUTO = "Automatic Success"; @@ -86,15 +91,6 @@ public static String getTechLevelName(final int techLevel) { default -> "Unknown"; }; } - - public static String getTransitUnitName(final int unit) { - return switch (unit) { - case TRANSIT_UNIT_DAY -> "Days"; - case TRANSIT_UNIT_WEEK -> "Weeks"; - case TRANSIT_UNIT_MONTH -> "Months"; - default -> "Unknown"; - }; - } // endregion Magic Numbers // region Variable Declarations @@ -148,6 +144,15 @@ public static String getTransitUnitName(final int unit) { private int isAcquisitionPenalty; private int maxAcquisitions; + // autoLogistics + private int autoLogisticsHeatSink; + private int autoLogisticsMekHead; + private int autoLogisticsMekLocation; + private int autoLogisticsNonRepairableLocation; + private int autoLogisticsArmor; + private int autoLogisticsAmmunition; + private int autoLogisticsOther; + // Delivery private int nDiceTransitTime; private int constantTransitTime; @@ -487,9 +492,9 @@ public static String getTransitUnitName(final int unit) { private int nTasksXP; private int successXP; private int mistakeXP; - private int idleXP; - private int monthsIdleXP; - private int targetIdleXP; + private int vocationalXP; + private int vocationalXPCheckFrequency; + private int vocationalXPTargetNumber; private int contractNegotiationXP; private int adminXP; private int adminXPPeriod; @@ -563,14 +568,11 @@ public static String getTransitUnitName(final int unit) { // Contract Operations private boolean mercSizeLimited; private boolean restrictPartsByMission; - private int bonusPartExchangeValue = 500000; - private int bonusPartMaxExchangeCount = 10; private boolean limitLanceWeight; private boolean limitLanceNumUnits; private boolean useStrategy; private int baseStrategyDeployment; private int additionalStrategyDeployment; - private boolean adjustPaymentForStrategy; private final int[] atbBattleChance; private boolean generateChases; @@ -600,6 +602,11 @@ public static String getTransitUnitName(final int unit) { private int scenarioModChance; private int scenarioModBV; private boolean autoConfigMunitions; + private AutoResolveMethod autoResolveMethod; + private String strategicViewMinimapTheme; + private boolean autoResolveVictoryChanceEnabled; + private int autoResolveNumberOfScenarios; + private boolean autoResolveExperimentalPacarGuiEnabled; // endregion Against the Bot Tab // endregion Variable Declarations @@ -661,6 +668,15 @@ public CampaignOptions() { isAcquisitionPenalty = 0; maxAcquisitions = 0; + // autoLogistics + autoLogisticsHeatSink = 250; + autoLogisticsMekHead = 200; + autoLogisticsMekLocation = 100; + autoLogisticsNonRepairableLocation = 0; + autoLogisticsArmor = 500; + autoLogisticsAmmunition = 500; + autoLogisticsOther = 50; + // Delivery nDiceTransitTime = 1; constantTransitTime = 0; @@ -1104,9 +1120,9 @@ public CampaignOptions() { nTasksXP = 25; successXP = 0; mistakeXP = 0; - idleXP = 0; - monthsIdleXP = 2; - targetIdleXP = 10; + vocationalXP = 1; + vocationalXPCheckFrequency = 1; + vocationalXPTargetNumber = 7; contractNegotiationXP = 0; adminXP = 0; adminXPPeriod = 1; @@ -1130,10 +1146,6 @@ public CampaignOptions() { phenotypeProbabilities[Phenotype.VEHICLE.ordinal()] = 0; phenotypeProbabilities[Phenotype.PROTOMEK.ordinal()] = 95; phenotypeProbabilities[Phenotype.NAVAL.ordinal()] = 25; - - // Remove Milestone after 0.49.19 - phenotypeProbabilities[Phenotype.MEKWARRIOR.ordinal()] = 95; - phenotypeProbabilities[Phenotype.PROTOMEK.ordinal()] = 95; // endregion Skill Randomization Tab // region Rank System Tab @@ -1190,7 +1202,11 @@ public CampaignOptions() { useAtB = false; useStratCon = false; setSkillLevel(SkillLevel.REGULAR); - + autoResolveMethod = AutoResolveMethod.PRINCESS; + autoResolveVictoryChanceEnabled = false; + autoResolveNumberOfScenarios = 100; + autoResolveExperimentalPacarGuiEnabled = false; + strategicViewMinimapTheme = "gbc green.theme"; // Unit Administration useAero = false; useVehicles = true; @@ -1199,19 +1215,16 @@ public CampaignOptions() { // Contract Operations mercSizeLimited = false; restrictPartsByMission = true; - bonusPartExchangeValue = 500000; - bonusPartMaxExchangeCount = 10; limitLanceWeight = true; limitLanceNumUnits = true; useStrategy = true; baseStrategyDeployment = 3; additionalStrategyDeployment = 1; - adjustPaymentForStrategy = false; - atbBattleChance = new int[AtBLanceRole.values().length - 1]; - atbBattleChance[AtBLanceRole.FIGHTING.ordinal()] = 40; - atbBattleChance[AtBLanceRole.DEFENCE.ordinal()] = 20; - atbBattleChance[AtBLanceRole.SCOUTING.ordinal()] = 60; - atbBattleChance[AtBLanceRole.TRAINING.ordinal()] = 10; + atbBattleChance = new int[CombatRole.values().length - 1]; + atbBattleChance[CombatRole.MANEUVER.ordinal()] = 40; + atbBattleChance[CombatRole.FRONTLINE.ordinal()] = 20; + atbBattleChance[CombatRole.PATROL.ordinal()] = 60; + atbBattleChance[CombatRole.TRAINING.ordinal()] = 10; generateChases = true; // Scenarios @@ -3888,28 +3901,28 @@ public void setAssignPortraitOnRoleChange(final boolean assignPortraitOnRoleChan this.assignPortraitOnRoleChange = assignPortraitOnRoleChange; } - public int getIdleXP() { - return idleXP; + public int getVocationalXP() { + return vocationalXP; } - public void setIdleXP(final int idleXP) { - this.idleXP = idleXP; + public void setVocationalXP(final int vocationalXP) { + this.vocationalXP = vocationalXP; } - public int getTargetIdleXP() { - return targetIdleXP; + public int getVocationalXPTargetNumber() { + return vocationalXPTargetNumber; } - public void setTargetIdleXP(final int targetIdleXP) { - this.targetIdleXP = targetIdleXP; + public void setVocationalXPTargetNumber(final int vocationalXPTargetNumber) { + this.vocationalXPTargetNumber = vocationalXPTargetNumber; } - public int getMonthsIdleXP() { - return monthsIdleXP; + public int getVocationalXPCheckFrequency() { + return vocationalXPCheckFrequency; } - public void setMonthsIdleXP(final int monthsIdleXP) { - this.monthsIdleXP = monthsIdleXP; + public void setVocationalXPCheckFrequency(final int vocationalXPCheckFrequency) { + this.vocationalXPCheckFrequency = vocationalXPCheckFrequency; } public int getContractNegotiationXP() { @@ -4258,8 +4271,64 @@ public void setMaxAcquisitions(final int maxAcquisitions) { this.maxAcquisitions = maxAcquisitions; } + public int getAutoLogisticsHeatSink() { + return autoLogisticsHeatSink; + } + + public void setAutoLogisticsHeatSink(int autoLogisticsHeatSink) { + this.autoLogisticsHeatSink = autoLogisticsHeatSink; + } + + public int getAutoLogisticsMekHead() { + return autoLogisticsMekHead; + } + + public void setAutoLogisticsMekHead(int autoLogisticsMekHead) { + this.autoLogisticsMekHead = autoLogisticsMekHead; + } + + public int getAutoLogisticsMekLocation() { + return autoLogisticsMekLocation; + } + + public void setAutoLogisticsMekLocation(int autoLogisticsMekLocation) { + this.autoLogisticsMekLocation = autoLogisticsMekLocation; + } + + public int getAutoLogisticsNonRepairableLocation() { + return autoLogisticsNonRepairableLocation; + } + + public void setAutoLogisticsNonRepairableLocation(int autoLogisticsNonRepairableLocation) { + this.autoLogisticsNonRepairableLocation = autoLogisticsNonRepairableLocation; + } + + public int getAutoLogisticsArmor() { + return autoLogisticsArmor; + } + + public void setAutoLogisticsArmor(int autoLogisticsArmor) { + this.autoLogisticsArmor = autoLogisticsArmor; + } + + public int getAutoLogisticsAmmunition() { + return autoLogisticsAmmunition; + } + + public void setAutoLogisticsAmmunition(int autoLogisticsAmmunition) { + this.autoLogisticsAmmunition = autoLogisticsAmmunition; + } + + public int getAutoLogisticsOther() { + return autoLogisticsOther; + } + + public void setAutoLogisticsOther(int autoLogisticsOther) { + this.autoLogisticsOther = autoLogisticsOther; + } + public boolean isUseAtB() { - return useAtB; + return useAtB || useStratCon; } public void setUseAtB(final boolean useAtB) { @@ -4464,19 +4533,51 @@ public void setPlayerControlsAttachedUnits(final boolean playerControlsAttachedU } /** - * @param role the {@link AtBLanceRole} to get the battle chance for - * @return the chance of having a battle for the specified role, or {@code 0} if StratCon is enabled + * Retrieves the chance of having a battle for the specified {@link CombatRole}. + *

    + * This is a convenience method that calls {@link #getAtBBattleChance(CombatRole, boolean)} + * with {@code useStratConBypass} set to {@code false}. As a result, if StratCon is enabled, + * the method will return {@code 0} regardless of other conditions. + *

    + * + * @param role the {@link CombatRole} to evaluate the battle chance for. + * @return the chance of having a battle for the specified role. + * + * @see #getAtBBattleChance(CombatRole, boolean) + */ + public int getAtBBattleChance(CombatRole role) { + return getAtBBattleChance(role, false); + } + + /** + * Retrieves the chance of having a battle for the specified {@link CombatRole}. + *

    + * This method calculates the battle chance percentage for the provided combat role based on + * its ordinal position in the {@code atbBattleChance} array. If StratCon is enabled and the + * {@code useStratConBypass} parameter is set to {@code true}, the method immediately + * returns {@code 0}. + *

    + * Combat roles marked as {@link CombatRole#RESERVE} or {@link CombatRole#AUXILIARY} are not + * eligible for battles and also return {@code 0}. + * + * @param role the {@link CombatRole} to evaluate the battle chance for. + * @param useStratConBypass a {@code boolean} indicating whether to bypass the StratCon-check logic. + * If {@code false}, this allows the method to ignore StratCon-enabled status. */ - public int getAtBBattleChance(final AtBLanceRole role) { - if (useStratCon) { + public int getAtBBattleChance(CombatRole role, boolean useStratConBypass) { + if (useStratCon && useStratConBypass) { return 0; } - return role.isUnassigned() ? 0 : atbBattleChance[role.ordinal()]; + if (role.isReserve() || role.isAuxiliary()) { + return 0; + } + + return atbBattleChance[role.ordinal()]; } /** - * @param role the {@link AtBLanceRole} ordinal value + * @param role the {@link CombatRole} ordinal value * @param frequency the frequency to set the generation to (percent chance from * 0 to 100) */ @@ -4540,14 +4641,6 @@ public void setAdditionalStrategyDeployment(final int additionalStrategyDeployme this.additionalStrategyDeployment = additionalStrategyDeployment; } - public boolean isAdjustPaymentForStrategy() { - return adjustPaymentForStrategy; - } - - public void setAdjustPaymentForStrategy(final boolean adjustPaymentForStrategy) { - this.adjustPaymentForStrategy = adjustPaymentForStrategy; - } - public boolean isRestrictPartsByMission() { return restrictPartsByMission; } @@ -4556,22 +4649,6 @@ public void setRestrictPartsByMission(final boolean restrictPartsByMission) { this.restrictPartsByMission = restrictPartsByMission; } - public int getBonusPartExchangeValue() { - return bonusPartExchangeValue; - } - - public void setBonusPartExchangeValue(final int bonusPartExchangeValue) { - this.bonusPartExchangeValue = bonusPartExchangeValue; - } - - public int getBonusPartMaxExchangeCount() { - return bonusPartMaxExchangeCount; - } - - public void setBonusPartMaxExchangeCount(final int bonusPartMaxExchangeCount) { - this.bonusPartMaxExchangeCount = bonusPartMaxExchangeCount; - } - public boolean isLimitLanceWeight() { return limitLanceWeight; } @@ -4713,9 +4790,9 @@ public void writeToXml(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "tasksXP", tasksXP); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "mistakeXP", mistakeXP); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "successXP", successXP); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "idleXP", idleXP); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "targetIdleXP", targetIdleXP); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "monthsIdleXP", monthsIdleXP); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "vocationalXP", vocationalXP); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "vocationalXPTargetNumber", vocationalXPTargetNumber); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "vocationalXPCheckFrequency", vocationalXPCheckFrequency); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "contractNegotiationXP", contractNegotiationXP); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "adminWeeklyXP", adminXP); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "adminXPPeriod", adminXPPeriod); @@ -4776,6 +4853,15 @@ public void writeToXml(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "checkMaintenance", checkMaintenance); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "maxAcquisitions", maxAcquisitions); + // autoLogistics + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoLogisticsHeatSink", autoLogisticsHeatSink); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoLogisticsMekHead", autoLogisticsMekHead); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoLogisticsMekLocation", autoLogisticsMekLocation); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoLogisticsNonRepairableLocation", autoLogisticsNonRepairableLocation); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoLogisticsArmor", autoLogisticsArmor); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoLogisticsAmmunition", autoLogisticsAmmunition); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoLogisticsOther", autoLogisticsOther); + // region Personnel Tab // region General Personnel MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useTactics", isUseTactics()); @@ -5161,6 +5247,11 @@ public void writeToXml(final PrintWriter pw, int indent) { // region AtB Tab MHQXMLUtility.writeSimpleXMLTag(pw, indent, "skillLevel", getSkillLevel().name()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveMethod", getAutoResolveMethod().name()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveVictoryChanceEnabled", isAutoResolveVictoryChanceEnabled()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveNumberOfScenarios", getAutoResolveNumberOfScenarios()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveUseExperimentalPacarGui", isAutoResolveExperimentalPacarGuiEnabled()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "strategicViewTheme", getStrategicViewTheme().getName()); // endregion AtB Tab MHQXMLUtility.writeSimpleXMLTag(pw, indent, "phenotypeProbabilities", phenotypeProbabilities); @@ -5190,10 +5281,7 @@ public void writeToXml(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useStrategy", useStrategy); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "baseStrategyDeployment", baseStrategyDeployment); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "additionalStrategyDeployment", additionalStrategyDeployment); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "adjustPaymentForStrategy", adjustPaymentForStrategy); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "restrictPartsByMission", restrictPartsByMission); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "bonusPartExchangeValue", bonusPartExchangeValue); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "bonusPartMaxExchangeCount", bonusPartMaxExchangeCount); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "limitLanceWeight", limitLanceWeight); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "limitLanceNumUnits", limitLanceNumUnits); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "assignPortraitOnRoleChange", assignPortraitOnRoleChange); @@ -5233,7 +5321,7 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve continue; } - logger.debug("%s\n\t%s", wn2.getNodeName(), wn2.getTextContent()); + logger.debug("{}\n\t{}", wn2.getNodeName(), wn2.getTextContent()); try { // region Repair and Maintenance Tab if (wn2.getNodeName().equalsIgnoreCase("checkMaintenance")) { @@ -5307,12 +5395,18 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.successXP = Integer.parseInt(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("mistakeXP")) { retVal.mistakeXP = Integer.parseInt(wn2.getTextContent().trim()); - } else if (wn2.getNodeName().equalsIgnoreCase("idleXP")) { - retVal.idleXP = Integer.parseInt(wn2.getTextContent().trim()); - } else if (wn2.getNodeName().equalsIgnoreCase("targetIdleXP")) { - retVal.targetIdleXP = Integer.parseInt(wn2.getTextContent().trim()); - } else if (wn2.getNodeName().equalsIgnoreCase("monthsIdleXP")) { - retVal.monthsIdleXP = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("vocationalXP") + // <50.03 compatibility handler + || wn2.getNodeName().equalsIgnoreCase("idleXP")) { + retVal.vocationalXP = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("vocationalXPTargetNumber") + // <50.03 compatibility handler + || wn2.getNodeName().equalsIgnoreCase("targetIdleXP")) { + retVal.vocationalXPTargetNumber = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("vocationalXPCheckFrequency") + // <50.03 compatibility handler + || wn2.getNodeName().equalsIgnoreCase("monthsIdleXP")) { + retVal.vocationalXPCheckFrequency = Integer.parseInt(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("contractNegotiationXP")) { retVal.contractNegotiationXP = Integer.parseInt(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("adminWeeklyXP")) { @@ -5440,6 +5534,22 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } else if (wn2.getNodeName().equalsIgnoreCase("maxAcquisitions")) { retVal.maxAcquisitions = Integer.parseInt(wn2.getTextContent().trim()); + // autoLogistics + } else if (wn2.getNodeName().equalsIgnoreCase("autoLogisticsHeatSink")) { + retVal.autoLogisticsHeatSink = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("autoLogisticsMekHead")) { + retVal.autoLogisticsMekHead = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("autoLogisticsMekLocation")) { + retVal.autoLogisticsMekLocation = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("autoLogisticsNonRepairableLocation")) { + retVal.autoLogisticsNonRepairableLocation = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("autoLogisticsArmor")) { + retVal.autoLogisticsArmor = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("autoLogisticsAmmunition")) { + retVal.autoLogisticsAmmunition = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("autoLogisticsOther")) { + retVal.autoLogisticsOther = Integer.parseInt(wn2.getTextContent().trim()); + // region Personnel Tab // region General Personnel } else if (wn2.getNodeName().equalsIgnoreCase("useTactics")) { @@ -6145,6 +6255,18 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve // region AtB Tab } else if (wn2.getNodeName().equalsIgnoreCase("skillLevel")) { retVal.setSkillLevel(SkillLevel.valueOf(wn2.getTextContent().trim())); + // region ACAR Tab + } else if (wn2.getNodeName().equalsIgnoreCase("autoResolveMethod")) { + retVal.setAutoResolveMethod(AutoResolveMethod.valueOf(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("autoResolveVictoryChanceEnabled")) { + retVal.setAutoResolveVictoryChanceEnabled(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("autoResolveNumberOfScenarios")) { + retVal.setAutoResolveNumberOfScenarios(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("autoResolveUseExperimentalPacarGui")) { + retVal.setAutoResolveExperimentalPacarGuiEnabled(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("strategicViewTheme")) { + retVal.setStrategicViewTheme(wn2.getTextContent().trim()); + // endregion ACAR Tab // endregion AtB Tab } else if (wn2.getNodeName().equalsIgnoreCase("phenotypeProbabilities")) { @@ -6214,14 +6336,8 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.baseStrategyDeployment = Integer.parseInt(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("additionalStrategyDeployment")) { retVal.additionalStrategyDeployment = Integer.parseInt(wn2.getTextContent().trim()); - } else if (wn2.getNodeName().equalsIgnoreCase("adjustPaymentForStrategy")) { - retVal.adjustPaymentForStrategy = Boolean.parseBoolean(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("restrictPartsByMission")) { retVal.restrictPartsByMission = Boolean.parseBoolean(wn2.getTextContent().trim()); - } else if (wn2.getNodeName().equalsIgnoreCase("bonusPartExchangeValue")) { - retVal.bonusPartExchangeValue = Integer.parseInt(wn2.getTextContent().trim()); - } else if (wn2.getNodeName().equalsIgnoreCase("bonusPartMaxExchangeCount")) { - retVal.bonusPartMaxExchangeCount = Integer.parseInt(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("limitLanceWeight")) { retVal.limitLanceWeight = Boolean.parseBoolean(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("limitLanceNumUnits")) { @@ -6334,10 +6450,10 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } 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); + retVal.atbBattleChance[CombatRole.MANEUVER.ordinal()] = (int) Math.round(((40.0 * intensity) / (40.0 * intensity + 60.0)) * 100.0 + 0.5); + retVal.atbBattleChance[CombatRole.FRONTLINE.ordinal()] = (int) Math.round(((20.0 * intensity) / (20.0 * intensity + 80.0)) * 100.0 + 0.5); + retVal.atbBattleChance[CombatRole.PATROL.ordinal()] = (int) Math.round(((60.0 * intensity) / (60.0 * intensity + 40.0)) * 100.0 + 0.5); + retVal.atbBattleChance[CombatRole.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 @@ -6419,4 +6535,46 @@ public void migrateMarriageSurnameWeights47(final String... values) { } } // endregion File IO + public AutoResolveMethod getAutoResolveMethod() { + return autoResolveMethod; + } + + public void setAutoResolveMethod(final AutoResolveMethod autoResolveMethod) { + this.autoResolveMethod = autoResolveMethod; + } + + public void setStrategicViewTheme(String minimapStyle) { + // it is persisted here to have something in the campaign options persisted that will change the GUI preference for the theme + this.strategicViewMinimapTheme = minimapStyle; + CLIENT_PREFERENCES.setStrategicViewTheme(minimapStyle); + } + + public File getStrategicViewTheme() { + CLIENT_PREFERENCES.setStrategicViewTheme(strategicViewMinimapTheme); + return CLIENT_PREFERENCES.getStrategicViewTheme(); + } + + public boolean isAutoResolveVictoryChanceEnabled() { + return autoResolveVictoryChanceEnabled; + } + + public void setAutoResolveVictoryChanceEnabled(final boolean autoResolveVictoryChanceEnabled) { + this.autoResolveVictoryChanceEnabled = autoResolveVictoryChanceEnabled; + } + + public void setAutoResolveNumberOfScenarios(int autoResolveNumberOfScenarios) { + this.autoResolveNumberOfScenarios = autoResolveNumberOfScenarios; + } + + public int getAutoResolveNumberOfScenarios() { + return autoResolveNumberOfScenarios; + } + + public boolean isAutoResolveExperimentalPacarGuiEnabled() { + return autoResolveExperimentalPacarGuiEnabled; + } + + public void setAutoResolveExperimentalPacarGuiEnabled(boolean autoResolveExperimentalPacarGuiEnabled) { + this.autoResolveExperimentalPacarGuiEnabled = autoResolveExperimentalPacarGuiEnabled; + } } diff --git a/MekHQ/src/mekhq/campaign/CampaignSummary.java b/MekHQ/src/mekhq/campaign/CampaignSummary.java index 93face382c1..92f608b1947 100644 --- a/MekHQ/src/mekhq/campaign/CampaignSummary.java +++ b/MekHQ/src/mekhq/campaign/CampaignSummary.java @@ -32,6 +32,8 @@ import mekhq.campaign.unit.Unit; import org.apache.commons.lang3.StringUtils; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -274,31 +276,40 @@ public String getMissionSuccessReport() { } /** - * A report that gives capacity and existing tonnage of all cargo + * Generates an HTML report about the current and maximum cargo capacity. + * The current cargo capacity (cargoTons) and maximum cargo capacity (cargoCapacity) are + * rounded to 1 decimal place. The comparison between the current and maximum cargo capacity + * determines the font's color in the report. + * - If the current cargo exceeds the maximum capacity, the color is set to MHQ's defined negative color. + * - If the current cargo equals the maximum capacity, the color is set to MHQ's defined warning color. + * - In other cases, the regular color is used. * - * @return a String of the report + * @return A {@link StringBuilder} object containing the HTML formatted report of cargo usage + * against capacity. */ public StringBuilder getCargoCapacityReport() { - int cargoTonsRounded = (int) Math.round(cargoTons); - int cargoCapacityRounded = (int) Math.round(cargoCapacity); + BigDecimal roundedCargo = new BigDecimal(Double.toString(cargoTons)); + roundedCargo = roundedCargo.setScale(1, RoundingMode.HALF_UP); + + BigDecimal roundedCapacity = new BigDecimal(Double.toString(cargoCapacity)); + roundedCapacity = roundedCapacity.setScale(1, RoundingMode.HALF_UP); + + int comparison = roundedCargo.compareTo(roundedCapacity); + StringBuilder report = new StringBuilder(""); - if (cargoTonsRounded > cargoCapacityRounded) { - report.append(""); - } else if (cargoTonsRounded == cargoCapacityRounded) { + if (comparison > 0) { report.append(""); } - report.append(cargoTonsRounded) + report.append(roundedCargo) .append(" tons (") - .append(cargoCapacityRounded) + .append(roundedCapacity) .append(" tons capacity)"); - if (!report.toString().equals(cargoTonsRounded + " tons (" + cargoCapacityRounded + " tons capacity)")) { + if (!report.toString().equals(roundedCargo + " tons (" + roundedCapacity + " tons capacity)")) { report.append(""); } else { report.append(""); @@ -358,18 +369,14 @@ public String getAdministrativeCapacityReport(Campaign campaign) { */ public String getFacilityReport() { CampaignOptions campaignOptions = campaign.getCampaignOptions(); - int personnelCount; + int personnelCount; if (campaignOptions.isUseFieldKitchenIgnoreNonCombatants()) { - personnelCount = (int) campaign.getActivePersonnel().stream() - .filter(person -> !(person.getPrisonerStatus().isFree() && person.getPrimaryRole().isNone())) - .filter(person -> person.getPrimaryRole().isCombat() || person.getSecondaryRole().isCombat()) - .count(); + personnelCount = campaign.getActiveCombatPersonnel().size(); } else { - personnelCount = (int) campaign.getActivePersonnel().stream() - .filter(person -> !(person.getPrisonerStatus().isFree() && person.getPrimaryRole().isNone())) - .count(); + personnelCount = campaign.getActivePersonnel().size(); } + personnelCount -= campaign.getCurrentPrisoners().size(); StringBuilder report = new StringBuilder(); diff --git a/MekHQ/src/mekhq/campaign/CampaignTransporterMap.java b/MekHQ/src/mekhq/campaign/CampaignTransporterMap.java new file mode 100644 index 00000000000..6596d61c588 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/CampaignTransporterMap.java @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2025-2025 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; + +import megamek.common.Transporter; +import megamek.logging.MMLogger; +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.unit.AbstractTransportedUnitsSummary; +import mekhq.campaign.unit.Unit; +import mekhq.campaign.unit.enums.TransporterType; + +import java.util.*; + +/** + * It is time-consuming to determine what transporter types we can load a unit into when + * in a popup menu. This class is for keeping just enough information that we + * can quickly determine which transporters can fit a unit. + * + * @see CampaignTransportType + */ +public class CampaignTransporterMap { + private static final MMLogger logger = MMLogger.create(CampaignTransporterMap.class); + + private final Campaign campaign; + private final Map>> transportersMap = new HashMap<>(); + private CampaignTransportType campaignTransportType; + + public CampaignTransporterMap(Campaign campaign, CampaignTransportType campaignTransportType){ + this.campaign = campaign; + this.campaignTransportType = campaignTransportType; + } + + /** + * Adds an entry to the list of transporters . We'll use this + * to assign units later + * + * @param transport - The unit we want to add to this Map + */ + public void addTransporter(Unit transport) { + for (TransporterType transporterType : transport.getTransportedUnitsSummary(campaignTransportType).getTransportCapabilities()) { + addTransporterToCapacityMap(transport, transporterType); + } + } + + private void addTransporterToCapacityMap(Unit transport, TransporterType transporterType) { + double capacity = transport.getTransportedUnitsSummary(campaignTransportType).getCurrentTransportCapacity(transporterType); + Map> capacityMap = transportersMap.getOrDefault(transporterType, new HashMap<>()); + Set unitIds = capacityMap.getOrDefault(capacity, new HashSet<>()); + unitIds.add(transport.getId()); + capacityMap.put(capacity, unitIds); + transportersMap.put(transporterType, capacityMap); + } + + /** + * This will update the transport in the transport capacity + * map with new capacities + * @param transport Unit to get update our stored capacity + */ + public void updateTransportInTransporterMap(Unit transport) { + // If this unit is mothballed, let's remove it from the map and return. + if (transport.isMothballed()) { + removeTransport(transport); + return; + } + AbstractTransportedUnitsSummary transportedUnitsSummary = transport.getTransportedUnitsSummary(campaignTransportType); + for (TransporterType transporterType : transportedUnitsSummary.getTransportCapabilities()) { + if (transportersMap.containsKey(transporterType)) { + Set oldCapacities = transportersMap.get(transporterType).keySet(); + Double newCapacity = transportedUnitsSummary.getCurrentTransportCapacity(transporterType); + //First, if this is a new capacity for the map, let's manually add it + if (!oldCapacities.contains(newCapacity)) { + addTransporterToCapacityMap(transport, transporterType); + } + //Then we iterate through the existing capacities in the map, and either remove or add this transport as needed + for (Double capacity : oldCapacities) { + if (transportersMap.get(transporterType).get(capacity).contains(transport.getId())) { + if (!Objects.equals(capacity, newCapacity)) { // If it's correct, we don't need to change it + transportersMap.get(transporterType).get(capacity).remove(transport.getId()); + } + } else if (Objects.equals(capacity, newCapacity)) { + addTransporterToCapacityMap(transport, transporterType); + } + } + } + else { + logger.error(String.format("Invalid transporter type %s", transporterType)); + } + } + } + + public boolean hasTransporters() { + return !transportersMap.isEmpty(); + } + + /** + * true if this transport map contains the unit, false if not + * @param unit is in this transport map as a UUID? + * @return true if the unit is, false if not + */ + public boolean hasTransport(Unit unit) { + for (TransporterType transporterType : transportersMap.keySet()){ + for (Double capacity : transportersMap.get(transporterType).keySet()) { + if (transportersMap.get(transporterType).get(capacity).contains(unit.getId())) { + return true; + } + } + } + return false; + } + + /** + * Returns a Map that maps Transporter types to another + * Map that maps capacity (Double) to UUID of transports + * + * @return units that have space for that transport type + */ + public Map>> getTransporters() { + return Collections.unmodifiableMap(transportersMap); + } + + /** + * Returns a list of transports that can transport a unit of given size. + * For example, getTransportsByType(MEK_BAY, 3.0) would return all + * transports that have 3 or more Mek Bay slots open. + * + * @param transporterType class of Transporter + * @param unitSize the size of the unit (usually 1) + * @return units that have space for that transport type + */ + public Set getTransportsByType(TransporterType transporterType, double unitSize) { + Set units = new HashSet<>(); + Map> capacityMap = getTransporters().get(transporterType); + for (Double capacity : capacityMap.keySet()) { + if (Double.compare(capacity, unitSize) >= 0) { + for (UUID uuid : capacityMap.get(capacity)) { + units.add(campaign.getUnit(uuid)); + } + } + } + return units; + } + + /** + * Deletes an entry from the list of transit-capable transport ships. This gets + * updated when + * the unit is removed from the campaign for one reason or another + * + * @param transport - The unit we want to remove from this Set + */ + public void removeTransport(Unit transport) { + Map toRemoveMap = new HashMap<>(); + for ( TransporterType transporterType : transportersMap.keySet()) { + for (Double capacity : transportersMap.get(transporterType).keySet()) { + if (transportersMap.get(transporterType).get(capacity).contains(transport.getId())) { + toRemoveMap.put(transporterType, capacity); + } + } + } + + for (TransporterType transporterTypeToRemove : toRemoveMap.keySet()) { + double capacity = toRemoveMap.get(transporterTypeToRemove); + + transportersMap.get(transporterTypeToRemove).get(capacity).remove(transport.getId()); + if (transportersMap.get(transporterTypeToRemove).get(capacity).isEmpty()) { + transportersMap.get(transporterTypeToRemove).remove(capacity); + } + if (transportersMap.get(transporterTypeToRemove).isEmpty()) { + transportersMap.remove(transporterTypeToRemove); + } + } + } +} diff --git a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java index ee8be3e740c..b6e3b4cebb1 100644 --- a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java +++ b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java @@ -21,7 +21,7 @@ */ package mekhq.campaign; -import megamek.client.Client; +import megamek.client.IClient; import megamek.common.*; import megamek.common.annotations.Nullable; import megamek.common.event.PostGameResolution; @@ -30,6 +30,7 @@ import megamek.logging.MMLogger; import mekhq.MekHQ; import mekhq.Utilities; +import megamek.common.autoresolve.acar.SimulatedClient; import mekhq.campaign.event.PersonBattleFinishedEvent; import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; @@ -47,10 +48,13 @@ import mekhq.campaign.unit.actions.AdjustLargeCraftAmmoAction; import mekhq.gui.FileDialogs; +import java.awt.Frame; import java.io.File; import java.util.*; import java.util.stream.Collectors; +import javax.swing.JFrame; + /** * This object will be the main workhorse for the scenario * resolution wizard. It will keep track of information and be @@ -89,7 +93,7 @@ public class ResolveScenarioTracker { Campaign campaign; Scenario scenario; Optional unitList = Optional.empty(); - Client client; + IClient client; Boolean control; private PostGameResolution victoryEvent; @@ -137,6 +141,10 @@ public ResolveScenarioTracker(Scenario s, Campaign c, boolean ctrl) { } } + public boolean isAutoResolve() { + return this.client instanceof SimulatedClient; + } + public void findUnitFile() { unitList = FileDialogs.openUnits(null); } @@ -145,7 +153,7 @@ public String getUnitFilePath() { return unitList.map(File::getAbsolutePath).orElse("No file selected"); } - public void setClient(Client c) { + public void setClient(IClient c) { client = c; } @@ -363,6 +371,12 @@ public void processGame() { } } } else if (wreck.getOwner().isEnemyOf(client.getLocalPlayer())) { + // MekHQ doesn't support gun emplacements, so we don't want the player salvaging them + if (wreck instanceof GunEmplacement) { + appendKillCredit(wreck); + continue; + } + if (wreck.isDropShip() && scenario.getBoardType() != Scenario.T_SPACE) { double dropShipBonusPercentage = (double) campaign.getCampaignOptions().getDropShipBonusPercentage() / 100; @@ -371,7 +385,7 @@ public void processGame() { dropShipBonus = dropShipBonus.plus( generateNewTestUnit(wreck).getSellValue().multipliedBy(dropShipBonusPercentage)); } - + appendKillCredit(wreck); continue; } @@ -436,6 +450,7 @@ private UnitStatus processAlliedUnit(Entity e) { return us; } + /** * This checks whether an entity has any blown off limbs. If the battlefield * was not controlled it marks the limb as destroyed. if the battlefield was @@ -1734,7 +1749,7 @@ && getCampaign().getCampaignOptions().isUseAtBPrisonerDefection()) { } for (Loot loot : actualLoot) { - loot.getLoot(campaign, scenario); + loot.getLoot(campaign, scenario, unitsStatus); } scenario.setStatus(resolution); @@ -2149,4 +2164,5 @@ public void setEvent(PostGameResolution gve) { public boolean playerHasBattlefieldControl() { return control; } + } diff --git a/MekHQ/src/mekhq/campaign/autoresolve/AtBSetupForces.java b/MekHQ/src/mekhq/campaign/autoresolve/AtBSetupForces.java new file mode 100644 index 00000000000..5590412d321 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/autoresolve/AtBSetupForces.java @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2024-2025 - The MegaMek Team. All Rights Reserved. + * + * This program 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 2 of the License, or (at your option) + * any later version. + * + * This program 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. + * + */ + +package mekhq.campaign.autoresolve; + +import io.sentry.Sentry; +import megamek.common.*; +import megamek.common.alphaStrike.conversion.ASConverter; +import megamek.common.autoresolve.acar.SimulationContext; +import megamek.common.autoresolve.converter.*; +import megamek.common.copy.CrewRefBreak; +import megamek.common.force.Forces; +import megamek.common.options.OptionsConstants; +import megamek.common.planetaryconditions.PlanetaryConditions; +import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.AtBDynamicScenario; +import mekhq.campaign.mission.AtBScenario; +import mekhq.campaign.mission.BotForce; +import mekhq.campaign.mission.Scenario; +import mekhq.campaign.unit.Unit; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +import static megamek.common.force.Force.NO_FORCE; + +/** + * @author Luana Coppio + */ +public class AtBSetupForces extends SetupForces { + private static final MMLogger logger = MMLogger.create(AtBSetupForces.class); + + private final Campaign campaign; + private final List units; + private final AtBScenario scenario; + private final ForceConsolidation forceConsolidationMethod; + private final Set teamIds = new HashSet<>(); + private final OrderFactory orderFactory; + + public AtBSetupForces(Campaign campaign, List units, AtBScenario scenario) { + this(campaign, units, scenario, new SingletonForces(), new OrderFactory(campaign, scenario)); + } + + public AtBSetupForces(Campaign campaign, List units, AtBScenario scenario, ForceConsolidation forceConsolidationMethod) { + this(campaign, units, scenario, forceConsolidationMethod, new OrderFactory(campaign, scenario)); + } + + public AtBSetupForces(Campaign campaign, List units, AtBScenario scenario, ForceConsolidation forceConsolidationMethod, OrderFactory orderFactory) { + this.campaign = campaign; + this.units = units; + this.scenario = scenario; + this.forceConsolidationMethod = forceConsolidationMethod; + this.orderFactory = orderFactory; + setupTeamIds(); + } + + private void setupTeamIds() { + if (!units.isEmpty()) { + teamIds.add(1); + } + for (int i = 0; i < scenario.getNumBots(); i++) { + BotForce bf = scenario.getBotForce(i); + teamIds.add(bf.getTeam()); + } + } + + /** + * Create the forces for the game object, using the campaign, units and scenario + * @param game The game object to setup the forces in + */ + public void createForcesOnSimulation(SimulationContext game) { + setupPlayer(game); + setupBots(game); + forceConsolidationMethod.consolidateForces(game); + convertForcesIntoFormations(game); + } + + @Override + public void addOrdersToForces(SimulationContext context) { + var orders = orderFactory.getOrders(); + context.getOrders().clear(); + context.getOrders().addAll(orders); + context.getOrders().resetOrders(); + } + + @Override + public boolean isTeamPresent(int teamId) { + return teamIds.contains(teamId); + } + + private static class FailedToConvertForceToFormationException extends RuntimeException { + public FailedToConvertForceToFormationException(Throwable cause) { + super(cause); + } + } + + /** + * Convert the forces in the game to formations, this is the most important step in the setup of the game, + * it converts every top level force into a single formation, and those formations are then added to the game + * and used in the auto resolve in place of the original entities + * @param game The game object to convert the forces in + */ + private void convertForcesIntoFormations(SimulationContext game) { + for(var force : game.getForces().getTopLevelForces()) { + try { + var formation = new EntityAsUnit(force, game).convert(); + formation.setTargetFormationId(Entity.NONE); + formation.setOwnerId(force.getOwnerId()); + game.addUnit(formation); + game.getForces().addEntity(formation, force.getId()); + } catch (Exception e) { + Sentry.captureException(e); + var entities = game.getForces().getFullEntities(force).stream().filter(Entity.class::isInstance) + .map(Entity.class::cast).toList(); + logger.error("Error converting force to formation {} - {}", force, entities, e); + throw new FailedToConvertForceToFormationException(e); + } + } + } + + /** + * Setup the player, its forces and entities in the game, it also sets the player skill level. + * @param game The game object to setup the player in + */ + private void setupPlayer(SimulationContext game) { + var player = getCleanPlayer(); + game.addPlayer(player.getId(), player); + var entities = setupPlayerForces(player); + var playerSkill = campaign.getReputation().getAverageSkillLevel(); + game.setPlayerSkillLevel(player.getId(), playerSkill); + sendEntities(entities, game); + } + + /** + * Setup the bots, their forces and entities in the game, it also sets the player skill level. + * @param game The game object to setup the bots in + */ + private void setupBots(SimulationContext game) { + var enemySkill = (scenario.getContract(campaign)).getEnemySkill(); + var allySkill = (scenario.getContract(campaign)).getAllySkill(); + var localBots = new HashMap(); + for (int i = 0; i < scenario.getNumBots(); i++) { + BotForce bf = scenario.getBotForce(i); + String name = bf.getName(); + if (localBots.containsKey(name)) { + int append = 2; + while (localBots.containsKey(name + append)) { + append++; + } + name += append; + } + var highestPlayerId = game.getPlayersList().stream().mapToInt(Player::getId).max().orElse(0); + Player bot = new Player(highestPlayerId + 1, name); + bot.setTeam(bf.getTeam()); + localBots.put(name, bot); + configureBot(bot, bf); + game.addPlayer(bot.getId(), bot); + if (bot.isEnemyOf(campaign.getPlayer())) { + game.setPlayerSkillLevel(bot.getId(), enemySkill); + } else { + game.setPlayerSkillLevel(bot.getId(), allySkill); + } + bf.generateRandomForces(units, campaign); + var entities = bf.getFullEntityList(campaign); + var botEntities = setupBotEntities(bot, entities, bf.getDeployRound()); + sendEntities(botEntities, game); + } + } + + /** + * Create a player object from the campaign and scenario wichi doesnt have a reference to the original player + * @return The clean player object + */ + private Player getCleanPlayer() { + var campaignPlayer = campaign.getPlayer(); + var player = new Player(campaignPlayer.getId(), campaign.getName()); + player.setCamouflage(campaign.getCamouflage().clone()); + player.setColour(campaign.getColour()); + player.setStartingPos(scenario.getStartingPos()); + player.setStartOffset(scenario.getStartOffset()); + player.setStartWidth(scenario.getStartWidth()); + player.setStartingAnyNWx(scenario.getStartingAnyNWx()); + player.setStartingAnyNWy(scenario.getStartingAnyNWy()); + player.setStartingAnySEx(scenario.getStartingAnySEx()); + player.setStartingAnySEy(scenario.getStartingAnySEy()); + player.setTeam(1); + player.setNbrMFActive(scenario.getNumPlayerMinefields(Minefield.TYPE_ACTIVE)); + player.setNbrMFConventional(scenario.getNumPlayerMinefields(Minefield.TYPE_CONVENTIONAL)); + player.setNbrMFInferno(scenario.getNumPlayerMinefields(Minefield.TYPE_INFERNO)); + player.setNbrMFVibra(scenario.getNumPlayerMinefields(Minefield.TYPE_VIBRABOMB)); + player.getTurnInitBonus(); + return player; + } + + /** + * Setup the player forces and entities for the game + * @param player The player object to setup the forces for + * @return A list of entities for the player + */ + private List setupPlayerForces(Player player) { + boolean useDropship = false; + if (scenario.getCombatRole().isPatrol()) { + for (Entity en : scenario.getAlliesPlayer()) { + if (en.getUnitType() == UnitType.DROPSHIP) { + useDropship = true; + break; + } + } + if (!useDropship) { + for (Unit unit : units) { + if (unit.getEntity().getUnitType() == UnitType.DROPSHIP) { + useDropship = true; + break; + } + } + } + } + var entities = new ArrayList(); + + for (Unit unit : units) { + // Get the Entity + var entity = ASConverter.getUndamagedEntity(unit.getEntity()); + // Set the TempID for auto reporting + if (Objects.isNull(entity)) { + continue; + } + + entity.setExternalIdAsString(unit.getId().toString()); + // Set the owner + entity.setOwner(player); + + // If this unit is a spacecraft, set the crew size and marine size values + if (entity.isLargeCraft() || (entity.getUnitType() == UnitType.SMALL_CRAFT)) { + entity.setNCrew(unit.getActiveCrew().size()); + entity.setNMarines(unit.getMarineCount()); + } + // Calculate deployment round + int deploymentRound = entity.getDeployRound(); + if (!(scenario instanceof AtBDynamicScenario)) { + int speed = entity.getWalkMP(); + if (entity.getJumpMP() > 0) { + if (entity instanceof Infantry) { + speed = entity.getJumpMP(); + } else { + speed++; + } + } + // Set scenario type-specific delay + deploymentRound = Math.max(entity.getDeployRound(), scenario.getDeploymentDelay() - speed); + // Lances deployed in scout roles always deploy units in 6-walking speed turns + if (scenario.getCombatRole().isPatrol() + && (scenario.getCombatTeamById(campaign) != null) + && (scenario.getCombatTeamById(campaign).getForceId() == scenario.getCombatTeamId()) + && !useDropship) { + deploymentRound = Math.max(deploymentRound, 6 - speed); + } + } + entity.setDeployRound(deploymentRound); + var force = campaign.getForceFor(unit); + if (force != null) { + entity.setForceString(force.getFullMMName()); + } else if (!unit.getEntity().getForceString().isBlank()) { + // this was added mostly to make it easier to run tests + entity.setForceString(unit.getEntity().getForceString()); + } + var newCrewRef = new CrewRefBreak(unit.getEntity().getCrew()).copy(); + entity.setCrew(newCrewRef); + entities.add(entity); + } + + for (Entity entity : scenario.getAlliesPlayer()) { + if (null == entity) { + continue; + } + entity.setOwner(player); + + int deploymentRound = entity.getDeployRound(); + if (!(scenario instanceof AtBDynamicScenario)) { + int speed = entity.getWalkMP(); + if (entity.getJumpMP() > 0) { + if (entity instanceof Infantry) { + speed = entity.getJumpMP(); + } else { + speed++; + } + } + deploymentRound = Math.max(entity.getDeployRound(), scenario.getDeploymentDelay() - speed); + if (!useDropship + && scenario.getCombatRole().isPatrol() + && (scenario.getCombatTeamById(campaign) != null) + && (scenario.getCombatTeamById(campaign).getForceId() == scenario.getCombatTeamId())) { + deploymentRound = Math.max(deploymentRound, 6 - speed); + } + } + + entity.setDeployRound(deploymentRound); + entities.add(entity); + } + + return entities; + } + + /** + * Setup the map settings for the game, not relevant at the moment, as the map settings are not used in the autoresolve currently + * @return The map settings object + */ + private MapSettings setupMapSettings() { + MapSettings mapSettings = MapSettings.getInstance(); + mapSettings.setBoardSize(scenario.getMapX(), scenario.getMapY()); + mapSettings.setMapSize(1, 1); + mapSettings.getBoardsSelectedVector().clear(); + + // if the scenario is taking place in space, do space settings instead + if (scenario.getBoardType() == Scenario.T_SPACE + || scenario.getTerrainType().equals("Space")) { + mapSettings.setMedium(MapSettings.MEDIUM_SPACE); + mapSettings.getBoardsSelectedVector().add(MapSettings.BOARD_GENERATED); + } else if (scenario.isUsingFixedMap()) { + String board = scenario.getMap().replace(".board", ""); + board = board.replace("\\", "/"); + mapSettings.getBoardsSelectedVector().add(board); + + if (scenario.getBoardType() == Scenario.T_ATMOSPHERE) { + mapSettings.setMedium(MapSettings.MEDIUM_ATMOSPHERE); + } + } else { + File mapgenFile = new File("data/mapgen/" + scenario.getMap() + ".xml"); + try (InputStream is = new FileInputStream(mapgenFile)) { + mapSettings = MapSettings.getInstance(is); + } catch (IOException ex) { + Sentry.captureException(ex); + logger.error( + String.format("Could not load map file data/mapgen/%s.xml", scenario.getMap()), + ex); + } + + if (scenario.getBoardType() == Scenario.T_ATMOSPHERE) { + mapSettings.setMedium(MapSettings.MEDIUM_ATMOSPHERE); + } + + // duplicate code, but getting a new instance of map settings resets the size + // parameters + mapSettings.setBoardSize(scenario.getMapX(), scenario.getMapY()); + mapSettings.setMapSize(1, 1); + mapSettings.getBoardsSelectedVector().add(MapSettings.BOARD_GENERATED); + } + return mapSettings; + } + + /** + * Configure the bot player object with the bot force data + * @param bot The bot player object + * @param botForce The bot force data + */ + private void configureBot(Player bot, BotForce botForce) { + bot.setTeam(botForce.getTeam()); + // set deployment + bot.setStartingPos(botForce.getStartingPos()); + bot.setStartOffset(botForce.getStartOffset()); + bot.setStartWidth(botForce.getStartWidth()); + bot.setStartingAnyNWx(botForce.getStartingAnyNWx()); + bot.setStartingAnyNWy(botForce.getStartingAnyNWy()); + bot.setStartingAnySEx(botForce.getStartingAnySEx()); + bot.setStartingAnySEy(botForce.getStartingAnySEy()); + + // set camo + bot.setCamouflage(botForce.getCamouflage().clone()); + bot.setColour(botForce.getColour()); + } + + /** + * Setup the bot entities for the game + * @param bot The bot player object + * @param originalEntities The original entities for the bot + * @param deployRound The deployment round for the bot + * @return A list of entities for the bot + */ + private List setupBotEntities(Player bot, List originalEntities, int deployRound) { + String forceName = bot.getName() + "|1"; + var entities = new ArrayList(); + + for (Entity originalBotEntity : originalEntities) { + var entity = ASConverter.getUndamagedEntity(originalBotEntity); + if (entity == null) { + logger.warn("Could not convert entity for bot {} - {}", bot.getName(), originalBotEntity); + continue; + } + + entity.setOwner(bot); + entity.setForceString(forceName); + entity.setCrew(new CrewRefBreak(entity.getCrew()).copy()); + entity.setId(originalBotEntity.getId()); + entity.setExternalIdAsString(originalBotEntity.getExternalIdAsString()); + entity.setCommander(originalBotEntity.isCommander()); + + if (entity.getDeployRound() == 0) { + entity.setDeployRound(deployRound); + } + entities.add(entity); + } + return entities; + } + + /** + * Get the planetary conditions for the game, not used at the moment in the auto resolve, but planed for the future + * @return The planetary conditions object + */ + private PlanetaryConditions getPlanetaryConditions() { + PlanetaryConditions planetaryConditions = new PlanetaryConditions(); + if (campaign.getCampaignOptions().isUseLightConditions()) { + planetaryConditions.setLight(scenario.getLight()); + } + if (campaign.getCampaignOptions().isUseWeatherConditions()) { + planetaryConditions.setWeather(scenario.getWeather()); + planetaryConditions.setWind(scenario.getWind()); + planetaryConditions.setFog(scenario.getFog()); + planetaryConditions.setEMI(scenario.getEMI()); + planetaryConditions.setBlowingSand(scenario.getBlowingSand()); + planetaryConditions.setTemperature(scenario.getModifiedTemperature()); + } + if (campaign.getCampaignOptions().isUsePlanetaryConditions()) { + planetaryConditions.setAtmosphere(scenario.getAtmosphere()); + planetaryConditions.setGravity(scenario.getGravity()); + } + return planetaryConditions; + } + + /** + * Send the entities to the game object + * @param entities The entities to send + * @param game the game object to send the entities to + */ + private void sendEntities(List entities, SimulationContext game) { + Map forceMapping = new HashMap<>(); + for (final Entity entity : new ArrayList<>(entities)) { + lastTouchesBeforeSendingEntity(game, entity); + game.getPlayer(entity.getOwnerId()).changeInitialEntityCount(1); + + // Restore forces from MULs or other external sources from the forceString, if + // any + if (!entity.getForceString().isBlank()) { + List forceList = Forces.parseForceString(entity); + int realId = NO_FORCE; + boolean topLevel = true; + + for (megamek.common.force.Force force : forceList) { + if (!forceMapping.containsKey(force.getId())) { + if (topLevel) { + realId = game.getForces().addTopLevelForce(force, entity.getOwner()); + } else { + megamek.common.force.Force parent = game.getForces().getForce(realId); + realId = game.getForces().addSubForce(force, parent); + } + forceMapping.put(force.getId(), realId); + } else { + realId = forceMapping.get(force.getId()); + } + topLevel = false; + } + entity.setForceString(""); + entity.setIGame(game); + game.addEntity(entity); + game.getForces().addEntity(entity, realId); + } + } + } + + private static void lastTouchesBeforeSendingEntity(SimulationContext game, Entity entity) { + if (entity instanceof ProtoMek) { + int numPlayerProtos = game.getSelectedEntityCount(new EntitySelector() { + private final int ownerId = entity.getOwnerId(); + @Override + public boolean accept(Entity entity) { + return (entity instanceof ProtoMek) && (ownerId == entity.getOwnerId()); + } + }); + + entity.setUnitNumber((short) (numPlayerProtos / 5)); + } + + if (Entity.NONE == entity.getId()) { + entity.setId(game.getNextEntityId()); + } + + // Give the unit a spotlight, if it has the spotlight quirk + entity.setExternalSearchlight(entity.hasExternalSearchlight() + || entity.hasQuirk(OptionsConstants.QUIRK_POS_SEARCHLIGHT)); + } +} diff --git a/MekHQ/src/mekhq/campaign/autoresolve/AutoResolveMethod.java b/MekHQ/src/mekhq/campaign/autoresolve/AutoResolveMethod.java new file mode 100644 index 00000000000..a269e21ca1b --- /dev/null +++ b/MekHQ/src/mekhq/campaign/autoresolve/AutoResolveMethod.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 - The MegaMek Team. All Rights Reserved. + * + * This program 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 2 of the License, or (at your option) + * any later version. + * + * This program 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. + * + */ +package mekhq.campaign.autoresolve; + + +import megamek.common.internationalization.Internationalization; +import mekhq.utilities.MHQInternationalization; + +import java.util.Optional; + +/** + * @author Luana Coppio + */ +public enum AutoResolveMethod { + PRINCESS("AutoResolveMethod.PRINCESS.text", "AutoResolveMethod.PRINCESS.toolTipText"), + ABSTRACT_COMBAT("AutoResolveMethod.ABSTRACT_COMBAT.text", "AutoResolveMethod.ABSTRACT_COMBAT.toolTipText"); + + private final String name; + private final String toolTipText; + + AutoResolveMethod(final String name, final String toolTipText) { + this.name = MHQInternationalization.getText(name); + this.toolTipText = Internationalization.getText(toolTipText); + } + + public String getToolTipText() { + return toolTipText; + } + + public String getName() { + return name; + } + + public static Optional fromIntSafe(int index) { + if (index < 0 || index >= values().length) { + return Optional.empty(); + } + return Optional.of(values()[index]); + } + + public static Optional fromStringSafe(String method) { + return switch (method.toUpperCase()) { + case "PRINCESS" -> Optional.of(PRINCESS); + case "ABSTRACT_COMBAT" -> Optional.of(ABSTRACT_COMBAT); + default -> Optional.empty(); + }; + } + + @Override + public String toString() { + return name; + } +} diff --git a/MekHQ/src/mekhq/campaign/autoresolve/OrderFactory.java b/MekHQ/src/mekhq/campaign/autoresolve/OrderFactory.java new file mode 100644 index 00000000000..b1b3e005d83 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/autoresolve/OrderFactory.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2024-2025 - The MegaMek Team. All Rights Reserved. + * + * This program 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 2 of the License, or (at your option) + * any later version. + * + * This program 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. + * + */ + +package mekhq.campaign.autoresolve; + +import megamek.common.Entity; +import megamek.common.OffBoardDirection; +import megamek.common.autoresolve.acar.order.Condition; +import megamek.common.autoresolve.acar.order.Order; +import megamek.common.autoresolve.acar.order.OrderType; +import megamek.common.autoresolve.acar.order.Orders; +import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.Scenario; +import mekhq.campaign.mission.ScenarioObjective; + +import java.util.Set; + +/** + * @author Luana Coppio + */ +public class OrderFactory { + private static final MMLogger logger = MMLogger.create(OrderFactory.class); + + private final Campaign campaign; + private final Scenario scenario; + private Orders orders; + + public OrderFactory(Campaign campaign, Scenario scenario) { + this.campaign = campaign; + this.scenario = scenario; + } + + public Orders getOrders() { + this.orders = new Orders(); + var scenarioObjectives = scenario.getScenarioObjectives(); + for (var objective : scenarioObjectives) { + createOrderFromObjective(campaign.getPlayer().getId(), objective); + } + return orders; + } + + private void addOrder(Order order) { + logger.info("Adding order {}", order); + this.orders.add(order); + } + + private void createOrderFromObjective(int ownerId, ScenarioObjective objective) { + + switch (objective.getObjectiveCriterion()) { + case Capture: // no capture, only destroy! + case Destroy: + createDestroyOrder(ownerId, objective); + break; + case Preserve: + createPreserveOrder(ownerId, objective); + break; + case ReachMapEdge: + createReachMapEdgeOrder(ownerId, objective); + break; + case ForceWithdraw: + createForceWithdrawOrder(ownerId, objective); + break; + case PreventReachMapEdge: + createPreventReachMapEdgeOrder(ownerId, objective); + break; + case Custom: + default: + // do nothing for custom bc what can be done? + break; + }; + } + // Make the enemy withdraw + private void createForceWithdrawOrder(int ownerId, ScenarioObjective objective) { + var orderBuilder = Order.OrderBuilder.anOrder(ownerId, OrderType.ATTACK_TARGET_NOT_WITHDRAWING) + .withCondition(Condition.alwaysTrue()); + if (objective.getTimeLimitType() != ScenarioObjective.TimeLimitType.None) { + orderBuilder.withCondition(context -> context.getCurrentRound() <= objective.getTimeLimit()); + } + addOrder(orderBuilder.build()); + } + + private void createReachMapEdgeOrder(int ownerId, ScenarioObjective objective) { + if (objective.getDestinationEdge() != null && objective.getDestinationEdge() != OffBoardDirection.NONE) { + var northSide = Set.of(OffBoardDirection.NORTH, OffBoardDirection.EAST); + var orderType = northSide.contains(objective.getDestinationEdge()) ? OrderType.FLEE_NORTH : OrderType.FLEE_SOUTH; + var orderBuilder = Order.OrderBuilder.anOrder(ownerId, orderType) + .withCondition(Condition.alwaysTrue()); + orders.add(orderBuilder.build()); + } else { + var orderBuilder = Order.OrderBuilder.anOrder(ownerId, scenario.getStartingPos() > 4 ? OrderType.FLEE_NORTH : OrderType.FLEE_SOUTH) + .withCondition(Condition.alwaysTrue()); + addOrder(orderBuilder.build()); + } + } + + private void createPreventReachMapEdgeOrder(int ownerId, ScenarioObjective objective) { + var orderBuilder = Order.OrderBuilder.anOrder(ownerId, OrderType.ATTACK_TARGET_WITHDRAWING) + .withCondition(Condition.alwaysTrue()); + if (objective.getTimeLimitType() != ScenarioObjective.TimeLimitType.None) { + orderBuilder.withCondition(context -> context.getCurrentRound() <= objective.getTimeLimit()); + } + addOrder(orderBuilder.build()); + } + + private void createPreserveOrder(int ownerId, ScenarioObjective objective) { + if (!objective.getAssociatedForceNames().isEmpty()) { + var order = createOrderForPreserveAssociatedForces(ownerId, objective); + addOrder(order); + } + if (!objective.getAssociatedUnitIDs().isEmpty()) { + var order = createOrderForPreserveAssociatedUnits(ownerId, objective); + addOrder(order); + } + } + + private Order createOrderForPreserveAssociatedForces(int ownerId, ScenarioObjective objective) { + var orderBuilder = Order.OrderBuilder.anOrder(ownerId, OrderType.WITHDRAW_IF_CONDITION_IS_MET) + .withCondition(context -> { + var attackTypeOrders = Set.of(OrderType.ATTACK_TARGET, OrderType.ATTACK_TARGET_NOT_WITHDRAWING, OrderType.ATTACK_TARGET_WITHDRAWING); + var hasOutstandingAttackOrders = context.getOrders().getOrders(ownerId).stream() + .filter(o -> attackTypeOrders.contains(o.getOrderType())).anyMatch(order -> order.isEligible(context)); + int totalUnits = 0; + var currentUnits = 0; + for (var forceName : objective.getAssociatedForceNames()) { + for (var force : context.getForces().getTopLevelForces()) { + if (force.getName().equals(forceName)) { + totalUnits += force.getEntities().size(); + for (var entityId : force.getEntities()) { + currentUnits += context.getSelectedEntityCount(entity -> entity.getId() == entityId); + } + } + } + } + + if (totalUnits == 0) { + return true; + } else { + + var timeLimit = false; + if (objective.getTimeLimitType() != ScenarioObjective.TimeLimitType.None && objective.isTimeLimitAtMost()) { + timeLimit = context.getCurrentRound() >= objective.getTimeLimit(); + } + if (!hasOutstandingAttackOrders) { + if (objective.getAmountType().equals(ScenarioObjective.ObjectiveAmountType.Fixed)) { + return currentUnits < objective.getFixedAmount() || timeLimit; + } else { + int percent = (int) ((totalUnits / (double) currentUnits) * 100); + return percent < objective.getPercentage() || timeLimit; + } + } else { + if (timeLimit) { + return context.getCurrentRound() >= objective.getTimeLimit(); + } else { + return true; + } + } + } + }); + + return orderBuilder.build(); + } + + private Order createOrderForPreserveAssociatedUnits(int ownerId, ScenarioObjective objective) { + var orderBuilder = Order.OrderBuilder.anOrder(ownerId, OrderType.WITHDRAW_IF_CONDITION_IS_MET) + .withCondition(context -> { + var inGameObjects = context.getInGameObjects(); + var unitIds = objective.getAssociatedUnitIDs(); + var entitiesOfInterest = inGameObjects.stream().filter(Entity.class::isInstance).map(Entity.class::cast) + .filter(e -> e.getOwnerId() == ownerId).toList(); + var currentUnits = 0; + var totalUnits = unitIds.size(); + if (totalUnits == 0) { + return true; + } + for (var unit : entitiesOfInterest) { + if (unitIds.contains(unit.getExternalIdAsString())) { + currentUnits++; + } + } + var timeLimit = false; + if (objective.getTimeLimitType() != ScenarioObjective.TimeLimitType.None && objective.isTimeLimitAtMost()) { + timeLimit = context.getCurrentRound() >= objective.getTimeLimit(); + } + if (objective.getAmountType().equals(ScenarioObjective.ObjectiveAmountType.Fixed)) { + return currentUnits < objective.getFixedAmount() || timeLimit; + } else { + int percent = (int) ((totalUnits / (double) currentUnits) * 100); + return percent < objective.getPercentage() || timeLimit; + } + }); + return orderBuilder.build(); + } + + private void createDestroyOrder(int ownerId, ScenarioObjective objective) { + var orderBuilder = Order.OrderBuilder.anOrder(ownerId, OrderType.ATTACK_TARGET); + if (objective.getPercentage() == 100) { + orderBuilder.withCondition(Condition.alwaysTrue()); + } else { + orderBuilder.withCondition(context -> { + var enemyPlayers = context.getPlayersList().stream().filter(p -> p.isEnemyOf(campaign.getPlayer())).toList(); + var totalUnits = 0; + var currentUnits = 0; + for (var player: enemyPlayers) { + totalUnits += context.getStartingNumberOfUnits(player.getId()); + currentUnits += context.getActiveFormations(player).size(); + } + + if (totalUnits == 0) { + return true; + } + if (objective.getAmountType().equals(ScenarioObjective.ObjectiveAmountType.Fixed)) { + return (totalUnits - currentUnits) < objective.getFixedAmount(); + } else { + var currentPercent = 100 - (int) (currentUnits / (double) totalUnits); + return (objective.getPercentage() - currentPercent) > 0; + } + }); + } + addOrder(orderBuilder.build()); + } +} diff --git a/MekHQ/src/mekhq/campaign/enums/CampaignTransportType.java b/MekHQ/src/mekhq/campaign/enums/CampaignTransportType.java new file mode 100644 index 00000000000..1756fbc5aeb --- /dev/null +++ b/MekHQ/src/mekhq/campaign/enums/CampaignTransportType.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2025-2025 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.enums; + +import megamek.common.*; +import mekhq.campaign.unit.*; +import mekhq.campaign.unit.enums.TransporterType; + +/** + * Enum for the different transport types used in MekHQ. + * Campaign Transports allow a unit to be + * assigned a transport (another unit). + * The different transport types primarily differ + * in the Transporters they allow. + * @see Unit + * @see TransporterType + */ +public enum CampaignTransportType +{ + //region Enum declarations + /** + * Units assigned a ship transport, if both units are in the battle + * the unit will attempt to load onto the transport when deployed into battle. + * Ship transports are intended to be used for long-term travel or space combat + * and only allow units to be transported in long-term Transporters like Bays or + * Docking Collars. + * @see Bay + * @see DockingCollar + */ + SHIP_TRANSPORT(TransportShipAssignment.class, ShipTransportedUnitsSummary.class), + /** + * Units assigned a tactical transport, if both units are in the battle + * the unit will attempt to load onto the transport when deployed into battle. + * Tactical Transporters are meant to represent short-term transport - Infantry in + * an Infantry compartment, or Battle Armor on Battle Armor Handles. It still allows + * units to be loaded into bays though for tactical Dropship assaults. + * @see InfantryCompartment + * @see BattleArmorHandles + */ + TACTICAL_TRANSPORT(TransportAssignment.class, TacticalTransportedUnitsSummary.class); + // endregion Enum declarations + + + // region Variable declarations + private final Class transportAssignmentType; + private final Class transportedUnitsSummaryType; + // endregion Variable declarations + + // region Constructors + CampaignTransportType(Class transportAssignmentType, Class transportedUnitsSummaryType) { + this.transportAssignmentType = transportAssignmentType; + this.transportedUnitsSummaryType = transportedUnitsSummaryType; + } + // endregion Constructors + + + // region Boolean Comparison Methods + + /** + * Is this a Ship Transport? + * @return true if this is a SHIP_TRANSPORT + */ + public boolean isShipTransport() { return this == SHIP_TRANSPORT; } + + /** + * Is this a Tactical Transport? + * @return true if this is a TACTICAL_TRANSPORT + */ + public boolean isTacticalTransport() { return this == TACTICAL_TRANSPORT; } + // endregion Boolean Comparison Methods + + // region Getters + + /** + * Different Transport Types use different transport assignments. + * @return Transport Assignment class used by this transport type + */ + public Class getTransportAssignmentType() { return transportAssignmentType; } + + /** + * Different Transport Types use different transported units summaries. + * @return Transported Unit Summary used by this transport type + */ + public Class getTransportedUnitsSummaryType() { return transportedUnitsSummaryType; } + // endregion Getters +} diff --git a/MekHQ/src/mekhq/campaign/event/PersonCrewAssignmentEvent.java b/MekHQ/src/mekhq/campaign/event/PersonCrewAssignmentEvent.java index 9be245c3918..5a708203bb1 100644 --- a/MekHQ/src/mekhq/campaign/event/PersonCrewAssignmentEvent.java +++ b/MekHQ/src/mekhq/campaign/event/PersonCrewAssignmentEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 The MegaMek Team. All rights reserved. + * Copyright (c) 2017-2025 The MegaMek Team. All rights reserved. * * This file is part of MekHQ. * @@ -18,22 +18,57 @@ */ package mekhq.campaign.event; +import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; import mekhq.campaign.personnel.Person; import mekhq.campaign.unit.Unit; +import static mekhq.campaign.force.Force.FORCE_NONE; + /** - * Triggered when a person is assigned to or removed from a unit as pilot or crew. + * Represents an event triggered when a {@link Person} is assigned to or removed from a {@link Unit}, + * either as a pilot or part of the crew. + * + *

    This event is a subclass of {@link PersonChangedEvent} and adds the context of the + * {@link Unit} involved in the assignment or removal.

    * + *

    If the {@link Unit} is associated with a force, the force's commander information will + * be updated accordingly through the {@link Force#updateCommander(Campaign)} method.

    */ public class PersonCrewAssignmentEvent extends PersonChangedEvent { private final Unit unit; - public PersonCrewAssignmentEvent(Person crew, Unit unit) { + /** + * Creates a new {@code PersonCrewAssignmentEvent}. + * + * @param campaign The {@link Campaign} to which this event belongs. + * @param crew The {@link Person} assigned or removed from the {@link Unit}. + * @param unit The {@link Unit} involved in the assignment or removal. + * + *

    If the {@code unit} is associated with a force, the force's commander information is updated + * during the construction of this event by calling {@link Force#updateCommander(Campaign)}.

    + */ + public PersonCrewAssignmentEvent(Campaign campaign, Person crew, Unit unit) { super(crew); this.unit = unit; + + int forceId = unit.getForceId(); + + if (forceId != FORCE_NONE) { + Force force = campaign.getForce(forceId); + + if (force != null) { + force.updateCommander(campaign); + } + } } + /** + * Gets the {@link Unit} associated with this event. + * + * @return The {@link Unit} involved in the assignment or removal. + */ public Unit getUnit() { return unit; } diff --git a/MekHQ/src/mekhq/campaign/finances/Accountant.java b/MekHQ/src/mekhq/campaign/finances/Accountant.java index f436fe7d509..4a2d8ac582b 100644 --- a/MekHQ/src/mekhq/campaign/finances/Accountant.java +++ b/MekHQ/src/mekhq/campaign/finances/Accountant.java @@ -20,12 +20,16 @@ */ package mekhq.campaign.finances; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import megamek.common.Entity; import mekhq.campaign.Campaign; import mekhq.campaign.CampaignOptions; import mekhq.campaign.Hangar; +import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.parts.Part; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.enums.PersonnelRole; @@ -228,4 +232,27 @@ public Money getContractBase() { return getTheoreticalPayroll(getCampaignOptions().isInfantryDontCount()); } } + + /** + * Returns a map of every Person and their salary. + * + * @see Finances#debit(TransactionType, LocalDate, Money, String, Map, boolean) + * @return map of personnel to their pay, including pool as a null key + */ + public Map getPayRollSummary() { + Map payRollSummary = new HashMap<>(); + for (Person p : getCampaign().getActivePersonnel()) { + payRollSummary.put(p, p.getSalary(getCampaign())); + } + // And pay our pool + payRollSummary.put(null, Money.of( + (getCampaign().getCampaignOptions().getRoleBaseSalaries() + [PersonnelRole.ASTECH.ordinal()].getAmount().doubleValue() + * getCampaign().getAstechPool()) + + (getCampaign().getCampaignOptions().getRoleBaseSalaries() + [PersonnelRole.MEDIC.ordinal()].getAmount().doubleValue() + * getCampaign().getMedicPool()))); + + return payRollSummary; + } } diff --git a/MekHQ/src/mekhq/campaign/finances/Finances.java b/MekHQ/src/mekhq/campaign/finances/Finances.java index 715aca7f18a..cc28f4f5980 100644 --- a/MekHQ/src/mekhq/campaign/finances/Finances.java +++ b/MekHQ/src/mekhq/campaign/finances/Finances.java @@ -19,25 +19,6 @@ */ package mekhq.campaign.finances; -import java.io.BufferedWriter; -import java.io.File; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.time.LocalDate; -import java.time.Period; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; -import java.util.ResourceBundle; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVPrinter; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.common.annotations.Nullable; import megamek.logging.MMLogger; import mekhq.MekHQ; @@ -52,6 +33,25 @@ import mekhq.io.FileType; import mekhq.utilities.MHQXMLUtility; import mekhq.utilities.ReportingUtilities; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.Period; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * @author Jay Lawson (jaylawson39 at yahoo.com) @@ -159,6 +159,17 @@ public int getPartialYearsInDebt(LocalDate date) { return 0; } + /** + * Debits (removes) money from the campaign's balance. + * Consider the debit method that takes a Map of Person to Money + * if this debit is for paying your crew. + * @param type TransactionType being debited + * @param date when the transaction occurred + * @param amount Money to remove from the campaign's balanace + * @param reason String displayed in the ledger + * @return true if the transaction succeeds, false if it doesn't, + * such as from insufficient balance + */ public boolean debit(final TransactionType type, final LocalDate date, final Money amount, final String reason) { if (getBalance().isLessThan(amount)) { @@ -173,6 +184,41 @@ public boolean debit(final TransactionType type, final LocalDate date, final Mon return true; } + + /** + * Debits (removes) money from the campaign's balance. + * When debiting money to people in the Campaign, + * if TrackTotalEarnings is true we'll want to pay each + * Person what they're owed. Use this method to debit (remove) + * money from your Campaign's balance while paying it to + * the provided people (Person) in the individualPayoutsMap. + * @param type TransactionType being debited + * @param date when the transaction occurred + * @param amount total money - it's usually displayed outside of this method + * @param reason String displayed in the ledger + * @param individualPayouts Map of Person to the Money they're owed + * @param isTrackTotalEarnings true if we want to apply earnings to individual people (Person) + * @return true if the transaction succeeds, false if it doesn't, + * such as from insufficient balance + */ + public boolean debit(final TransactionType type, final LocalDate date, Money amount, String reason, Map individualPayouts, boolean isTrackTotalEarnings) { + if (debit(type, date, amount, reason)) { + if (isTrackTotalEarnings && !individualPayouts.isEmpty()) { + for (Person person : individualPayouts.keySet()) { + Money payout = individualPayouts.get(person); + if (person != null) { // Null person will be used for temp personnel + person.payPerson(payout); + } + } + } else { + logger.error(String.format("Individual Payouts is Empty! Transaction Type: %s Date: %s Reason: %s",type, date, reason)); + } + return true; + } + + return false; + } + public void credit(final TransactionType type, final LocalDate date, final Money amount, final String reason) { Transaction t = new Transaction(type, date, amount, reason); @@ -312,19 +358,17 @@ public void newDay(final Campaign campaign, final LocalDate yesterday, final Loc } if (campaign.getCampaignOptions().isPayForSalaries()) { + Money payRollCost = campaign.getAccountant().getPayRoll(); if (debit(TransactionType.SALARIES, today, payRollCost, - resourceMap.getString("Salaries.title"))) { + resourceMap.getString("Salaries.title"), + campaign.getAccountant().getPayRollSummary(), + campaign.getCampaignOptions().isTrackTotalEarnings())) { campaign.addReport( - String.format(resourceMap.getString("Salaries.text"), - payRollCost.toAmountAndSymbolString())); + String.format(resourceMap.getString("Salaries.text"), + payRollCost.toAmountAndSymbolString())); - if (campaign.getCampaignOptions().isTrackTotalEarnings()) { - for (Person person : campaign.getActivePersonnel()) { - person.payPersonSalary(campaign); - } - } } else { campaign.addReport( String.format("" @@ -445,37 +489,51 @@ private void payoutShares(Campaign campaign, Contract contract, LocalDate date) if (campaign.getCampaignOptions().isUseAtB() && campaign.getCampaignOptions().isUseShareSystem() && (contract instanceof AtBContract)) { Money shares = contract.getMonthlyPayOut().multipliedBy(contract.getSharesPercent()) - .dividedBy(100); - if (debit(TransactionType.SALARIES, date, shares, + .dividedBy(100); + if (shares.isGreaterThan(Money.zero())) { + if (debit(TransactionType.SALARIES, date, shares, String.format(resourceMap.getString("ContractSharePayment.text"), contract.getName()))) { - campaign.addReport(resourceMap.getString("DistributedShares.text"), shares.toAmountAndSymbolString()); + campaign.addReport(resourceMap.getString("DistributedShares.text"), shares.toAmountAndSymbolString()); - if (campaign.getCampaignOptions().isTrackTotalEarnings()) { - int numberOfShares = 0; - boolean sharesForAll = campaign.getCampaignOptions().isSharesForAll(); - for (Person person : campaign.getActivePersonnel()) { - numberOfShares += person.getNumShares(campaign, sharesForAll); - } - - Money singleShare = shares.dividedBy(numberOfShares); - for (Person person : campaign.getActivePersonnel()) { - person.payPersonShares(campaign, singleShare, sharesForAll); - } - } - } else { - /* - * This should not happen, as the shares payment should be less than the - * contract - * payment that has just been made. - */ - campaign.addReport("" - + resourceMap.getString("InsufficientFunds.text"), resourceMap.getString("Shares.text"), + payOutSharesToPersonnel(campaign, shares); + } else { + /* + * This should not happen, as the shares payment should be less than the + * contract payment that has just been made. + */ + campaign.addReport("" + + resourceMap.getString("InsufficientFunds.text"), resourceMap.getString("Shares.text"), ""); - logger.error("Attempted to payout share amount larger than the payment of the contract"); + logger.error("Attempted to payout share amount larger than the payment of the contract"); + } } } } +/** + * Shares calculate the amount debited without iterating + * through all the personnel, so it's not more efficient + * to provide that information to debit. Pay out shares + * manually for now. + * @param campaign where to pull personnel from + * @param shares total value of the shares to pay out + */ +public void payOutSharesToPersonnel(Campaign campaign, Money shares) { + if (campaign.getCampaignOptions().isTrackTotalEarnings()) { + boolean sharesForAll = campaign.getCampaignOptions().isSharesForAll(); + + int numberOfShares = campaign.getActivePersonnel().stream() + .mapToInt(person -> person.getNumShares(campaign, sharesForAll)) + .sum(); + + Money singleShare = shares.dividedBy(numberOfShares); + + for (Person person : campaign.getActivePersonnel()) { + person.payPersonShares(campaign, singleShare, sharesForAll); + } + } +} + public Money checkOverdueLoanPayments(Campaign campaign) { List newLoans = new ArrayList<>(); Money overdueAmount = Money.zero(); diff --git a/MekHQ/src/mekhq/campaign/finances/enums/FinancialTerm.java b/MekHQ/src/mekhq/campaign/finances/enums/FinancialTerm.java index 8c209084f6f..342272d55d1 100644 --- a/MekHQ/src/mekhq/campaign/finances/enums/FinancialTerm.java +++ b/MekHQ/src/mekhq/campaign/finances/enums/FinancialTerm.java @@ -133,7 +133,7 @@ public LocalDate nextValidDate(final LocalDate yesterday, final LocalDate today) // twelve months if today is in an even quarter or nine months otherwise return endsToday(yesterday, today) ? today.plusMonths(6) : today.with(IsoFields.DAY_OF_QUARTER, 1) - .plusMonths(((today.get(IsoFields.QUARTER_OF_YEAR) % 2) == 1) ? 12 : 9); + .plusMonths((today.get(IsoFields.QUARTER_OF_YEAR) % 2 != 0) ? 12 : 9); case ANNUALLY: default: // First, use today if the term would end today or otherwise adjust to the first @@ -163,7 +163,7 @@ public boolean endsToday(final LocalDate yesterday, final LocalDate today) { return today.get(IsoFields.QUARTER_OF_YEAR) != yesterday.get(IsoFields.QUARTER_OF_YEAR); case SEMIANNUALLY: return (today.get(IsoFields.QUARTER_OF_YEAR) != yesterday.get(IsoFields.QUARTER_OF_YEAR)) - && ((today.get(IsoFields.QUARTER_OF_YEAR) % 2) == 1); + && (today.get(IsoFields.QUARTER_OF_YEAR) % 2 != 0); case ANNUALLY: default: return today.getYear() != yesterday.getYear(); diff --git a/MekHQ/src/mekhq/campaign/force/StrategicFormation.java b/MekHQ/src/mekhq/campaign/force/CombatTeam.java similarity index 82% rename from MekHQ/src/mekhq/campaign/force/StrategicFormation.java rename to MekHQ/src/mekhq/campaign/force/CombatTeam.java index b0212b0e0a0..acb6d6cc073 100644 --- a/MekHQ/src/mekhq/campaign/force/StrategicFormation.java +++ b/MekHQ/src/mekhq/campaign/force/CombatTeam.java @@ -2,7 +2,7 @@ * Lance.java * * Copyright (c) 2011 - Carl Spain. All rights reserved. - * Copyright (c) 2020-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,7 +21,11 @@ */ package mekhq.campaign.force; -import megamek.common.*; +import megamek.common.Compute; +import megamek.common.Entity; +import megamek.common.EntityWeightClass; +import megamek.common.Infantry; +import megamek.common.annotations.Nullable; import megamek.logging.MMLogger; import mekhq.MekHQ; import mekhq.campaign.Campaign; @@ -29,8 +33,8 @@ import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.AtBScenario; import mekhq.campaign.mission.atb.AtBScenarioFactory; -import mekhq.campaign.mission.enums.AtBLanceRole; import mekhq.campaign.mission.enums.AtBMoraleLevel; +import mekhq.campaign.mission.enums.CombatRole; import mekhq.campaign.personnel.Person; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Faction; @@ -50,20 +54,20 @@ import static megamek.common.Entity.ETYPE_PROTOMEK; import static megamek.common.Entity.ETYPE_TANK; import static megamek.common.EntityWeightClass.WEIGHT_ULTRA_LIGHT; -import static mekhq.campaign.force.Force.STRATEGIC_FORMATION_OVERRIDE_NONE; -import static mekhq.campaign.force.Force.STRATEGIC_FORMATION_OVERRIDE_TRUE; +import static mekhq.campaign.force.Force.COMBAT_TEAM_OVERRIDE_NONE; +import static mekhq.campaign.force.Force.COMBAT_TEAM_OVERRIDE_TRUE; import static mekhq.campaign.force.FormationLevel.LANCE; /** * Used by Against the Bot & StratCon to track additional information about each force * on the TO&E that has at least one unit assigned. Extra info includes whether - * the force counts as a Strategic Formation eligible for assignment to a scenario role + * the force counts as a Combat Team eligible for assignment to a scenario role * and what the assignment is on which contract. * * @author Neoancient */ -public class StrategicFormation { - private static final MMLogger logger = MMLogger.create(StrategicFormation.class); +public class CombatTeam { + private static final MMLogger logger = MMLogger.create(CombatTeam.class); public static final int LANCE_SIZE = 4; public static final int STAR_SIZE = 5; @@ -77,7 +81,7 @@ public class StrategicFormation { private int forceId; private int missionId; - private AtBLanceRole role; + private CombatRole role; private UUID commanderId; /** @@ -132,11 +136,11 @@ public static int getStandardForceSize(Faction faction, int formationLevelDepth) /** * Default constructor */ - public StrategicFormation() {} + public CombatTeam() {} - public StrategicFormation(int forceId, Campaign campaign) { + public CombatTeam(int forceId, Campaign campaign) { this.forceId = forceId; - role = AtBLanceRole.UNASSIGNED; + role = CombatRole.RESERVE; missionId = -1; for (AtBContract contract : campaign.getActiveAtBContracts()) { missionId = ((contract.getParentContract() == null) @@ -166,11 +170,11 @@ public void setContract(AtBContract atBContract) { } } - public AtBLanceRole getRole() { + public CombatRole getRole() { return role; } - public void setRole(AtBLanceRole role) { + public void setRole(CombatRole role) { this.role = role; } @@ -308,7 +312,7 @@ public boolean isEligible(Campaign campaign) { } if (!force.isCombatForce()) { - force.setStrategicFormation(false); + force.setCombatTeamStatus(false); return false; } @@ -319,73 +323,68 @@ public boolean isEligible(Campaign campaign) { int size = getSize(campaign); if (size < getStandardForceSize(campaign.getFaction()) - 1 || size > getStandardForceSize(campaign.getFaction()) + 2) { - force.setStrategicFormation(false); + force.setCombatTeamStatus(false); return false; } } if (campaign.getCampaignOptions().isLimitLanceWeight() && getWeightClass(campaign) > EntityWeightClass.WEIGHT_ASSAULT) { - force.setStrategicFormation(false); + force.setCombatTeamStatus(false); return false; } - boolean hasGround = false; - for (UUID id : force.getUnits()) { - Unit unit = campaign.getUnit(id); - if (unit != null) { - Entity entity = unit.getEntity(); - - if (entity != null) { - if (entity.getUnitType() >= UnitType.JUMPSHIP) { - force.setStrategicFormation(false); - return false; - } - if ((entity.getEntityType() & ETYPE_GROUND) != 0) { - hasGround = true; - } - } - } - } - - int isOverridden = force.getOverrideStrategicFormation(); - if (isOverridden != STRATEGIC_FORMATION_OVERRIDE_NONE) { - boolean overrideState = isOverridden == STRATEGIC_FORMATION_OVERRIDE_TRUE; - force.setStrategicFormation(overrideState); + int isOverridden = force.getOverrideCombatTeam(); + if (isOverridden != COMBAT_TEAM_OVERRIDE_NONE) { + boolean overrideState = isOverridden == COMBAT_TEAM_OVERRIDE_TRUE; + force.setCombatTeamStatus(overrideState); List associatedForces = force.getAllParents(); associatedForces.addAll(force.getAllSubForces()); for (Force associatedForce : associatedForces) { - associatedForce.setStrategicFormation(false); + associatedForce.setCombatTeamStatus(false); } return overrideState; } - if (hasGround) { - List childForces = force.getAllSubForces(); + // This should never be getAllUnits() as otherwise parent nodes will be assessed as being + // automatically eligible to be Combat Teams preventing child nodes from being Combat Teams + if (force.getUnits().isEmpty()) { + force.setCombatTeamStatus(false); + return false; + } - for (Force childForce : childForces) { - if (childForce.isStrategicFormation()) { - force.setStrategicFormation(false); - return false; - } + List childForces = force.getAllSubForces(); + + for (Force childForce : childForces) { + if (childForce.isCombatTeam()) { + force.setCombatTeamStatus(false); + return false; + } + } + + List parentForces = force.getAllParents(); + + for (Force parentForce : parentForces) { + if (parentForce.isCombatTeam()) { + force.setCombatTeamStatus(false); + return false; } - List parentForces = force.getAllParents(); + if (parentForce.isConvoyForce()) { + force.setCombatTeamStatus(false); + return false; + } - for (Force parentForce : parentForces) { - if (parentForce.isStrategicFormation()) { - force.setStrategicFormation(false); - return false; - } + if (!parentForce.isCombatForce()) { + return false; } } - force.setStrategicFormation(hasGround); - - return hasGround; + force.setCombatTeamStatus(true); + return true; } /* Code to find unit commander from ForceViewPanel */ @@ -399,8 +398,8 @@ public static LocalDate getBattleDate(LocalDate today) { public AtBScenario checkForBattle(Campaign campaign) { // Make sure there is a battle first - if ((campaign.getCampaignOptions().getAtBBattleChance(role) == 0) - || (Compute.randomInt(100) > campaign.getCampaignOptions().getAtBBattleChance(role))) { + if ((campaign.getCampaignOptions().getAtBBattleChance(role, true) == 0) + || (Compute.randomInt(100) > campaign.getCampaignOptions().getAtBBattleChance(role, true))) { // No battle return null; } @@ -433,7 +432,7 @@ public AtBScenario checkForBattle(Campaign campaign) { */ switch (role) { - case FIGHTING: { + case MANEUVER: { roll = Compute.randomInt(40) + battleTypeMod; if (roll < 1) { return AtBScenarioFactory.createScenario(campaign, this, @@ -471,7 +470,7 @@ public AtBScenario checkForBattle(Campaign campaign) { getBattleDate(campaign.getLocalDate())); } } - case SCOUTING: { + case PATROL: { roll = Compute.randomInt(60) + battleTypeMod; if (roll < 1) { return AtBScenarioFactory.createScenario(campaign, this, @@ -509,7 +508,7 @@ public AtBScenario checkForBattle(Campaign campaign) { getBattleDate(campaign.getLocalDate())); } } - case DEFENCE: { + case FRONTLINE: { roll = Compute.randomInt(20) + battleTypeMod; if (roll < 1) { return AtBScenarioFactory.createScenario(campaign, this, @@ -592,13 +591,13 @@ public void writeToXML(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "lance"); } - public static StrategicFormation generateInstanceFromXML(Node wn) { - StrategicFormation retVal = null; + public static CombatTeam generateInstanceFromXML(Node wn) { + CombatTeam retVal = null; NamedNodeMap attrs = wn.getAttributes(); Node classNameNode = attrs.getNamedItem("type"); String className = classNameNode.getTextContent(); try { - retVal = (StrategicFormation) Class.forName(className).newInstance(); + retVal = (CombatTeam) Class.forName(className).newInstance(); NodeList nl = wn.getChildNodes(); for (int x = 0; x < nl.getLength(); x++) { @@ -609,7 +608,7 @@ public static StrategicFormation generateInstanceFromXML(Node wn) { } else if (wn2.getNodeName().equalsIgnoreCase("missionId")) { retVal.missionId = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("role")) { - retVal.setRole(AtBLanceRole.parseFromString(wn2.getTextContent().trim())); + retVal.setRole(CombatRole.parseFromString(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("commanderId")) { retVal.commanderId = UUID.fromString(wn2.getTextContent()); } @@ -662,49 +661,49 @@ public static double calculateTotalWeight(Campaign campaign, int forceId) { } /** - * This static method updates the strategic formations across the campaign. - * It starts at the top level force, and calculates the strategic formations for each sub-force. - * It keeps only the eligible strategic formations and imports them into the campaign. + * This static method updates the combat teams across the campaign. + * It starts at the top level force, and calculates the combat teams for each sub-force. + * It keeps only the eligible combat teams and imports them into the campaign. * After every formation is processed, an 'OrganizationChangedEvent' is triggered by that force. * * @param campaign the current campaign. */ - public static void recalculateStrategicFormations(Campaign campaign) { - Hashtable strategicFormations = campaign.getStrategicFormationsTable(); - StrategicFormation strategicFormation = strategicFormations.get(0); // This is the origin node + public static void recalculateCombatTeams(Campaign campaign) { + Hashtable combatTeamsTable = campaign.getCombatTeamsTable(); + CombatTeam combatTeam = combatTeamsTable.get(0); // This is the origin node Force force = campaign.getForce(0); // Does the force already exist in our hashtable? If so, update it accordingly - if (strategicFormation != null) { - boolean isEligible = strategicFormation.isEligible(campaign); + if (combatTeam != null) { + boolean isEligible = combatTeam.isEligible(campaign); if (!isEligible) { - campaign.removeStrategicFormation(0); + campaign.removeCombatTeam(0); } - force.setStrategicFormation(isEligible); + force.setCombatTeamStatus(isEligible); // Otherwise, create a new formation and then add it to the table, if appropriate } else { - strategicFormation = new StrategicFormation(0, campaign); - boolean isEligible = strategicFormation.isEligible(campaign); + combatTeam = new CombatTeam(0, campaign); + boolean isEligible = combatTeam.isEligible(campaign); if (isEligible) { - campaign.addStrategicFormation(strategicFormation); + campaign.addCombatTeam(combatTeam); } - force.setStrategicFormation(isEligible); + force.setCombatTeamStatus(isEligible); } // Update the TO&E and then begin recursively walking it MekHQ.triggerEvent(new OrganizationChangedEvent(force)); - recalculateSubForceStrategicStatus(campaign, campaign.getStrategicFormationsTable(), force); + recalculateSubForceStrategicStatus(campaign, campaign.getCombatTeamsTable(), force); } /** - * This method is used to update the strategic formations for the campaign working downwards + * This method is used to update the combat teams for the campaign working downwards * from a specified node, through all of its sub-forces. - * It creates a new {@link StrategicFormation} for each sub-force and checks its eligibility. - * Eligible formations are imported into the campaign, and the strategic formation status of + * It creates a new {@link CombatTeam} for each sub-force and checks its eligibility. + * Eligible formations are imported into the campaign, and the combat team status of * the respective force is set to {@code true}. * After every force is processed, an 'OrganizationChangedEvent' is triggered. * This function runs recursively on each sub-force, effectively traversing the complete TO&E. @@ -713,37 +712,54 @@ public static void recalculateStrategicFormations(Campaign campaign) { * @param workingNode the {@link Force} node from which the method starts working down through * all its sub-forces. */ - private static void recalculateSubForceStrategicStatus(Campaign campaign, Hashtable strategicFormations, Force workingNode) { + private static void recalculateSubForceStrategicStatus(Campaign campaign, + Hashtable combatTeamsTable, + Force workingNode) { for (Force force : workingNode.getSubForces()) { int forceId = force.getId(); - StrategicFormation strategicFormation = strategicFormations.get(forceId); + CombatTeam combatTeam = combatTeamsTable.get(forceId); // Does the force already exist in our hashtable? If so, update it accordingly - if (strategicFormation != null) { - boolean isEligible = strategicFormation.isEligible(campaign); + if (combatTeam != null) { + boolean isEligible = combatTeam.isEligible(campaign); if (!isEligible) { - campaign.removeStrategicFormation(forceId); + campaign.removeCombatTeam(forceId); } - force.setStrategicFormation(isEligible); + force.setCombatTeamStatus(isEligible); // Otherwise, create a new formation and then add it to the table, if appropriate } else { - strategicFormation = new StrategicFormation(forceId, campaign); - boolean isEligible = strategicFormation.isEligible(campaign); + combatTeam = new CombatTeam(forceId, campaign); + boolean isEligible = combatTeam.isEligible(campaign); if (isEligible) { - campaign.addStrategicFormation(strategicFormation); + campaign.addCombatTeam(combatTeam); } - force.setStrategicFormation(isEligible); + force.setCombatTeamStatus(isEligible); } // Update the TO&E and then continue recursively walking it MekHQ.triggerEvent(new OrganizationChangedEvent(force)); - recalculateSubForceStrategicStatus(campaign, campaign.getStrategicFormationsTable(), force); + recalculateSubForceStrategicStatus(campaign, campaign.getCombatTeamsTable(), force); } } + + /** + * Retrieves the force associated with the given campaign using the stored force ID. + * + *

    + * This method returns a {@link Force} object corresponding to the stored {@code forceId}, + * if it exists within the specified campaign. If no matching force is found, {@code null} + * is returned. + *

    + * + * @param campaign the campaign containing the forces to search for the specified {@code forceId} + * @return the {@link Force} object associated with the {@code forceId}, or {@code null} if not found + */ + public @Nullable Force getForce(Campaign campaign) { + return campaign.getForce(forceId); + } } diff --git a/MekHQ/src/mekhq/campaign/force/Force.java b/MekHQ/src/mekhq/campaign/force/Force.java index 85ea4d16560..955d8402dc7 100644 --- a/MekHQ/src/mekhq/campaign/force/Force.java +++ b/MekHQ/src/mekhq/campaign/force/Force.java @@ -33,7 +33,7 @@ import mekhq.campaign.icons.LayeredForceIcon; import mekhq.campaign.icons.StandardForceIcon; import mekhq.campaign.icons.enums.LayeredForceIconLayer; -import mekhq.campaign.icons.enums.LayeredForceIconOperationalStatus; +import mekhq.campaign.icons.enums.OperationalStatus; import mekhq.campaign.log.ServiceLogger; import mekhq.campaign.mission.Scenario; import mekhq.campaign.personnel.Person; @@ -68,17 +68,18 @@ public class Force { // pathway to force icon public static final int FORCE_NONE = -1; - public static final int STRATEGIC_FORMATION_OVERRIDE_NONE = -1; - public static final int STRATEGIC_FORMATION_OVERRIDE_FALSE = 0; - public static final int STRATEGIC_FORMATION_OVERRIDE_TRUE = 1; + public static final int COMBAT_TEAM_OVERRIDE_NONE = -1; + public static final int COMBAT_TEAM_OVERRIDE_FALSE = 0; + public static final int COMBAT_TEAM_OVERRIDE_TRUE = 1; private String name; private StandardForceIcon forceIcon; private Camouflage camouflage; private String desc; private boolean combatForce; - private boolean isStrategicFormation; - private int overrideStrategicFormation; + private boolean convoyForce; + private boolean isCombatTeam; + private int overrideCombatTeam; private FormationLevel formationLevel; private FormationLevel overrideFormationLevel; private Force parentForce; @@ -100,8 +101,9 @@ public Force(String name) { setCamouflage(new Camouflage()); setDescription(""); this.combatForce = true; - this.isStrategicFormation = false; - this.overrideStrategicFormation = STRATEGIC_FORMATION_OVERRIDE_NONE; + this.convoyForce = false; + this.isCombatTeam = false; + this.overrideCombatTeam = COMBAT_TEAM_OVERRIDE_NONE; this.formationLevel = FormationLevel.NONE; this.overrideFormationLevel = FormationLevel.NONE; this.parentForce = null; @@ -159,7 +161,7 @@ public void setDescription(String d) { } public boolean isCombatForce() { - return combatForce; + return combatForce && !convoyForce; } public void setCombatForce(boolean combatForce, boolean setForSubForces) { @@ -171,20 +173,38 @@ public void setCombatForce(boolean combatForce, boolean setForSubForces) { } } - public boolean isStrategicFormation() { - return isStrategicFormation; + public boolean isCombatTeam() { + return isCombatTeam; } - public void setStrategicFormation(final boolean isStrategicFormation) { - this.isStrategicFormation = isStrategicFormation; + /** + * @return {@code true} if this is a convoy force, {@code false} otherwise. + */ + public boolean isConvoyForce() { + return convoyForce; + } + + /** + * Sets the status of the force as a convoy force. If requested, propagate this status to all + * sub-forces recursively. + * + * @param convoyForce {@code true} to mark force as a convoy force, {@code false} to mark force + * as non-convoy. + */ + public void setConvoyForce(boolean convoyForce) { + this.convoyForce = convoyForce; + } + + public void setCombatTeamStatus(final boolean isCombatTeam) { + this.isCombatTeam = isCombatTeam; } - public int getOverrideStrategicFormation() { - return overrideStrategicFormation; + public int getOverrideCombatTeam() { + return overrideCombatTeam; } - public void setOverrideStrategicFormation(final int overrideStrategicFormation) { - this.overrideStrategicFormation = overrideStrategicFormation; + public void setOverrideCombatTeam(final int overrideCombatTeam) { + this.overrideCombatTeam = overrideCombatTeam; } public FormationLevel getFormationLevel() { @@ -384,13 +404,13 @@ public Vector getUnits() { /** * @param combatForcesOnly to only include combat forces or to also include - * non-combat forces + * support forces * @return all the unit ids in this force and all of its subforces */ public Vector getAllUnits(boolean combatForcesOnly) { Vector allUnits; - if (combatForcesOnly && !isCombatForce()) { + if (combatForcesOnly && !isCombatForce() && !isConvoyForce()) { allUnits = new Vector<>(); } else { allUnits = new Vector<>(units); @@ -513,7 +533,16 @@ public UUID getForceCommanderID() { return forceCommanderID; } - public void setForceCommanderID(UUID commanderID) { + /** + * Sets the force commander ID to the provided UUID. + * You probably want to use + * setOverrideForceCommanderID(UUID) followed by + * updateCommander(campaign). + * @see #setOverrideForceCommanderID(UUID) + * @see #updateCommander(Campaign) + * @param commanderID UUID of the commander + */ + private void setForceCommanderID(UUID commanderID) { forceCommanderID = commanderID; } @@ -569,18 +598,20 @@ public void updateCommander(Campaign campaign) { List eligibleCommanders = getEligibleCommanders(campaign); if (eligibleCommanders.isEmpty()) { + forceCommanderID = null; overrideForceCommanderID = null; + updateCombatTeamCommanderIfCombatTeam(campaign); return; } if (overrideForceCommanderID != null) { if (eligibleCommanders.contains(overrideForceCommanderID)) { forceCommanderID = overrideForceCommanderID; + updateCombatTeamCommanderIfCombatTeam(campaign); if (getParentForce() != null) { getParentForce().updateCommander(campaign); } - return; } else { overrideForceCommanderID = null; @@ -602,12 +633,22 @@ public void updateCommander(Campaign campaign) { } forceCommanderID = highestRankedPerson.getId(); + updateCombatTeamCommanderIfCombatTeam(campaign); if (getParentForce() != null) { getParentForce().updateCommander(campaign); } } + private void updateCombatTeamCommanderIfCombatTeam(Campaign campaign) { + if (isCombatTeam()) { + CombatTeam combatTeam = campaign.getCombatTeamsTable().getOrDefault(getId(), null); + if (combatTeam != null) { + combatTeam.setCommander(getForceCommanderID()); + } + } + } + public void removeSubForce(int id) { int idx = 0; boolean found = false; @@ -632,10 +673,10 @@ public void removeSubForce(int id) { * @return a list of the operational statuses for units in this force and in all * of its subForces. */ - public List updateForceIconOperationalStatus(final Campaign campaign) { + public List updateForceIconOperationalStatus(final Campaign campaign) { // First, update all subForces, collecting their unit statuses into a single // list - final List statuses = getSubForces().stream() + final List statuses = getSubForces().stream() .flatMap(subForce -> subForce.updateForceIconOperationalStatus(campaign).stream()) .collect(Collectors.toList()); @@ -643,7 +684,7 @@ public List updateForceIconOperationalStatus( statuses.addAll(getUnits().stream() .map(campaign::getUnit) .filter(Objects::nonNull) - .map(LayeredForceIconOperationalStatus::determineLayeredForceIconOperationalStatus) + .map(OperationalStatus::determineLayeredForceIconOperationalStatus) .toList()); // Can only update the icon for LayeredForceIcons, but still need to return the @@ -662,7 +703,7 @@ public List updateForceIconOperationalStatus( // the ordinal of the force's status. Then assign the operational status to // this. final int index = (int) round(statuses.stream().mapToInt(Enum::ordinal).sum() / (statuses.size() * 1.0)); - final LayeredForceIconOperationalStatus status = LayeredForceIconOperationalStatus.values()[index]; + final OperationalStatus status = OperationalStatus.values()[index]; ((LayeredForceIcon) getForceIcon()).getPieces().put(LayeredForceIconLayer.SPECIAL_MODIFIER, new ArrayList<>()); ((LayeredForceIcon) getForceIcon()).getPieces().get(LayeredForceIconLayer.SPECIAL_MODIFIER) @@ -685,11 +726,13 @@ public void writeToXML(PrintWriter pw1, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "desc", desc); } MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "combatForce", combatForce); - MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "overrideStrategicFormation", overrideStrategicFormation); + MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "convoyForce", convoyForce); + MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "overrideCombatTeam", overrideCombatTeam); MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "formationLevel", formationLevel.toString()); MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "populateOriginNode", overrideFormationLevel.toString()); MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "scenarioId", scenarioId); MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "techId", techId); + MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "overrideForceCommanderID", overrideForceCommanderID); MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "forceCommanderID", forceCommanderID); if (!units.isEmpty()) { MHQXMLUtility.writeSimpleXMLOpenTag(pw1, indent++, "units"); @@ -709,44 +752,48 @@ public void writeToXML(PrintWriter pw1, int indent) { MHQXMLUtility.writeSimpleXMLCloseTag(pw1, --indent, "force"); } - public static @Nullable Force generateInstanceFromXML(Node wn, Campaign c, Version version) { - Force retVal = new Force(""); - NamedNodeMap attrs = wn.getAttributes(); - Node idNameNode = attrs.getNamedItem("id"); + public static @Nullable Force generateInstanceFromXML(Node workingNode, Campaign campaign, Version version) { + Force force = new Force(""); + NamedNodeMap attributes = workingNode.getAttributes(); + Node idNameNode = attributes.getNamedItem("id"); String idString = idNameNode.getTextContent(); try { - NodeList nl = wn.getChildNodes(); - retVal.id = Integer.parseInt(idString); + NodeList childNodes = workingNode.getChildNodes(); + force.id = Integer.parseInt(idString); - for (int x = 0; x < nl.getLength(); x++) { - Node wn2 = nl.item(x); + for (int x = 0; x < childNodes.getLength(); x++) { + Node wn2 = childNodes.item(x); if (wn2.getNodeName().equalsIgnoreCase("name")) { - retVal.setName(wn2.getTextContent().trim()); + force.setName(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase(StandardForceIcon.XML_TAG)) { - retVal.setForceIcon(StandardForceIcon.parseFromXML(wn2)); + force.setForceIcon(StandardForceIcon.parseFromXML(wn2)); } else if (wn2.getNodeName().equalsIgnoreCase(LayeredForceIcon.XML_TAG)) { - retVal.setForceIcon(LayeredForceIcon.parseFromXML(wn2)); + force.setForceIcon(LayeredForceIcon.parseFromXML(wn2)); } else if (wn2.getNodeName().equalsIgnoreCase(Camouflage.XML_TAG)) { - retVal.setCamouflage(Camouflage.parseFromXML(wn2)); + force.setCamouflage(Camouflage.parseFromXML(wn2)); } else if (wn2.getNodeName().equalsIgnoreCase("desc")) { - retVal.setDescription(wn2.getTextContent().trim()); + force.setDescription(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("combatForce")) { - retVal.setCombatForce(Boolean.parseBoolean(wn2.getTextContent().trim()), false); - } else if (wn2.getNodeName().equalsIgnoreCase("overrideStrategicFormation")) { - retVal.setOverrideStrategicFormation(Integer.parseInt(wn2.getTextContent().trim())); + force.setCombatForce(Boolean.parseBoolean(wn2.getTextContent().trim()), false); + } else if (wn2.getNodeName().equalsIgnoreCase("convoyForce")) { + force.setConvoyForce(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("overrideCombatTeam")) { + force.setOverrideCombatTeam(Integer.parseInt(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("formationLevel")) { - retVal.setFormationLevel(FormationLevel.parseFromString(wn2.getTextContent().trim())); + force.setFormationLevel(FormationLevel.parseFromString(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("populateOriginNode")) { - retVal.setOverrideFormationLevel(FormationLevel.parseFromString(wn2.getTextContent().trim())); + force.setOverrideFormationLevel(FormationLevel.parseFromString(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("scenarioId")) { - retVal.scenarioId = Integer.parseInt(wn2.getTextContent()); + force.scenarioId = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("techId")) { - retVal.techId = UUID.fromString(wn2.getTextContent()); + force.techId = UUID.fromString(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("overrideForceCommanderID")) { + force.overrideForceCommanderID = UUID.fromString(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("forceCommanderID")) { - retVal.forceCommanderID = UUID.fromString(wn2.getTextContent()); + force.forceCommanderID = UUID.fromString(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("units")) { - processUnitNodes(retVal, wn2, version); + processUnitNodes(force, wn2, version); } else if (wn2.getNodeName().equalsIgnoreCase("subforces")) { NodeList nl2 = wn2.getChildNodes(); for (int y = 0; y < nl2.getLength(); y++) { @@ -763,19 +810,17 @@ public void writeToXML(PrintWriter pw1, int indent) { continue; } - retVal.addSubForce(generateInstanceFromXML(wn3, c, version), true); + force.addSubForce(generateInstanceFromXML(wn3, campaign, version), true); } } } - c.importForce(retVal); + campaign.importForce(force); } catch (Exception ex) { logger.error("", ex); return null; } - retVal.updateCommander(c); - - return retVal; + return force; } private static void processUnitNodes(Force retVal, Node wn, Version version) { diff --git a/MekHQ/src/mekhq/campaign/handler/PostScenarioDialogHandler.java b/MekHQ/src/mekhq/campaign/handler/PostScenarioDialogHandler.java new file mode 100644 index 00000000000..ed77362e4c2 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/handler/PostScenarioDialogHandler.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 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.handler; + +import megamek.client.generator.RandomNameGenerator; +import megamek.client.generator.RandomUnitGenerator; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.Kill; +import mekhq.campaign.ResolveScenarioTracker; +import mekhq.campaign.event.ScenarioResolvedEvent; +import mekhq.campaign.mission.AtBScenario; +import mekhq.campaign.mission.Scenario; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.autoAwards.AutoAwardsController; +import mekhq.campaign.personnel.enums.PersonnelStatus; +import mekhq.gui.CampaignGUI; +import mekhq.gui.dialog.RetirementDefectionDialog; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; + +/** + * @author Luana Coppio + */ +public class PostScenarioDialogHandler { + + /** + * Post game resolution checks, dialogs and actions. + * @param tracker The tracker that contains all the information about the scenario resolution. + * @param control Whether the player controlled the battlefield at the end of the scenario. + */ + public static void handle(CampaignGUI campaignGUI, Campaign campaign, Scenario currentScenario, + ResolveScenarioTracker tracker, boolean control) { + postCombatRetirementCheck(campaignGUI, campaign, currentScenario); + postCombatAutoApplyAward(campaign, tracker); + postCombatMissingInActionToPrisonerOfWarStatus(campaign, tracker, control); + restartRats(campaign); + cleanupTempImageFiles(); + // we need to trigger ScenarioResolvedEvent before stopping the thread or + // currentScenario may become null + MekHQ.triggerEvent(new ScenarioResolvedEvent(currentScenario)); + } + + private static void restartRats(Campaign campaign) { + if (campaign.getCampaignOptions().isUseAtB()) { + RandomUnitGenerator.getInstance(); + RandomNameGenerator.getInstance(); + } + } + + private static void cleanupTempImageFiles() { + final File tempImageDirectory = new File("data/images/temp"); + if (tempImageDirectory.isDirectory()) { + var listFiles = tempImageDirectory.listFiles(); + if (listFiles == null) { + // This may happen if the directory is not accessible or if someone creates a file which the name collides + // with the folder + return; + } + // This can't be null because of the above + Stream.of(listFiles).filter(file -> file.getName().endsWith(".png")) + .forEach(File::delete); + } + } + + private static void postCombatRetirementCheck(CampaignGUI campaignGUI, Campaign campaign, Scenario currentScenario) { + if (!campaign.getRetirementDefectionTracker().getRetirees().isEmpty()) { + RetirementDefectionDialog rdd = new RetirementDefectionDialog(campaignGUI, + campaign.getMission(currentScenario.getMissionId()), false); + + if (!rdd.wasAborted()) { + campaign.applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments()); + } + } + } + + private static void postCombatAutoApplyAward(Campaign campaign, ResolveScenarioTracker tracker) { + if (campaign.getCampaignOptions().isEnableAutoAwards()) { + HashMap personnel = new HashMap<>(); + HashMap> scenarioKills = new HashMap<>(); + + for (UUID personId : tracker.getPeopleStatus().keySet()) { + Person person = campaign.getPerson(personId); + ResolveScenarioTracker.PersonStatus status = tracker.getPeopleStatus().get(personId); + int injuryCount = 0; + + if (!person.getStatus().isDead() || campaign.getCampaignOptions().isIssuePosthumousAwards()) { + if (status.getHits() > person.getHitsPrior()) { + injuryCount = status.getHits() - person.getHitsPrior(); + } + } + + personnel.put(personId, injuryCount); + scenarioKills.put(personId, tracker.getPeopleStatus().get(personId).getKills()); + } + + boolean isCivilianHelp = false; + + if (tracker.getScenario() instanceof AtBScenario atbScenario) { + isCivilianHelp = atbScenario.getScenarioType() == AtBScenario.CIVILIANHELP; + } + + AutoAwardsController autoAwardsController = new AutoAwardsController(); + autoAwardsController.PostScenarioController(campaign, personnel, scenarioKills, isCivilianHelp); + } + } + + private static void postCombatMissingInActionToPrisonerOfWarStatus(Campaign campaign, ResolveScenarioTracker tracker, boolean control) { + for (UUID personId : tracker.getPeopleStatus().keySet()) { + Person person = campaign.getPerson(personId); + + if (person.getStatus() == PersonnelStatus.MIA && !control) { + person.changeStatus(campaign, campaign.getLocalDate(), PersonnelStatus.POW); + } + } + } +} diff --git a/MekHQ/src/mekhq/campaign/icons/enums/LayeredForceIconOperationalStatus.java b/MekHQ/src/mekhq/campaign/icons/enums/OperationalStatus.java similarity index 76% rename from MekHQ/src/mekhq/campaign/icons/enums/LayeredForceIconOperationalStatus.java rename to MekHQ/src/mekhq/campaign/icons/enums/OperationalStatus.java index 23a955b4f1b..cb167f28863 100644 --- a/MekHQ/src/mekhq/campaign/icons/enums/LayeredForceIconOperationalStatus.java +++ b/MekHQ/src/mekhq/campaign/icons/enums/OperationalStatus.java @@ -22,6 +22,8 @@ import mekhq.MHQConstants; import mekhq.campaign.unit.Unit; +import static megamek.codeUtilities.MathUtility.clamp; + /** * This is the Operational Status of a force or unit, as part of automatically assigning and * updating the force's LayeredForceIcon on a new day. It is also used to determine the Operation @@ -29,7 +31,7 @@ * * @author Justin "Windchild" Bowen */ -public enum LayeredForceIconOperationalStatus { +public enum OperationalStatus { //region Enum Declarations FULLY_OPERATIONAL(MHQConstants.LAYERED_FORCE_ICON_OPERATIONAL_STATUS_FULLY_OPERATIONAL_FILENAME), SUBSTANTIALLY_OPERATIONAL(MHQConstants.LAYERED_FORCE_ICON_OPERATIONAL_STATUS_SUBSTANTIALLY_OPERATIONAL_FILENAME), @@ -43,7 +45,7 @@ public enum LayeredForceIconOperationalStatus { //endregion Variable Declarations //region Constructors - LayeredForceIconOperationalStatus(final String filename) { + OperationalStatus(final String filename) { this.filename = filename; } //endregion Constructors @@ -82,7 +84,7 @@ public boolean isFactoryFresh() { * @param unit the specified unit * @return the determined operational status */ - public static LayeredForceIconOperationalStatus determineLayeredForceIconOperationalStatus(final Unit unit) { + public static OperationalStatus determineLayeredForceIconOperationalStatus(final Unit unit) { if (unit.isMothballing() || unit.isMothballed() || !unit.isPresent() || unit.isRefitting() || !unit.isRepairable() || !unit.isFunctional()) { return NOT_OPERATIONAL; @@ -95,4 +97,21 @@ public static LayeredForceIconOperationalStatus determineLayeredForceIconOperati default -> NOT_OPERATIONAL; }; } + + /** + * Retrieves the {@code LayeredForceIconOperationalStatus} corresponding to the given ordinal value. + *

    + * If the specified ordinal is out of range, it will be clamped to ensure it lies within the valid range + * of the available enumeration values. + * + * @param ordinal the ordinal value to map to a {@code LayeredForceIconOperationalStatus}. + * If the value is less than 0, it will be clamped to 0. If it exceeds the + * maximum ordinal value, it will be clamped to the last index. + * @return the corresponding {@code LayeredForceIconOperationalStatus} enum value for the adjusted ordinal. + */ + public static OperationalStatus fromInt(int ordinal) { + ordinal = clamp(ordinal, 0, values().length); + + return values()[ordinal]; + } } diff --git a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java index fb573fd1cc8..f50b2db86c1 100644 --- a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java +++ b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2018-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -34,9 +34,10 @@ import mekhq.Utilities; import mekhq.campaign.*; import mekhq.campaign.againstTheBot.AtBConfiguration; +import mekhq.campaign.enums.CampaignTransportType; import mekhq.campaign.finances.Finances; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.force.Force; -import mekhq.campaign.force.StrategicFormation; import mekhq.campaign.icons.UnitIcon; import mekhq.campaign.market.PersonnelMarket; import mekhq.campaign.market.ShoppingList; @@ -47,6 +48,7 @@ import mekhq.campaign.mission.Scenario; import mekhq.campaign.mod.am.InjuryTypes; import mekhq.campaign.parts.*; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.parts.equipment.*; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.PersonnelOptions; @@ -78,7 +80,7 @@ import java.util.*; import java.util.Map.Entry; -import static mekhq.campaign.force.StrategicFormation.recalculateStrategicFormations; +import static mekhq.campaign.force.CombatTeam.recalculateCombatTeams; import static org.apache.commons.lang3.ObjectUtils.firstNonNull; @@ -138,6 +140,7 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException { throw new CampaignXmlParseException(String.format("Illegal version of %s failed to parse", campaignEle.getAttribute("version"))); } + retVal.setVersion(version); // Indicates whether or not new units were written to disk while // loading the Campaign file. If so, we need to kick back off loading @@ -236,6 +239,8 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException { // Okay, so what element is it? String xn = wn.getNodeName(); + + if (xn.equalsIgnoreCase("campaignOptions")) { retVal.setCampaignOptions(CampaignOptions.generateCampaignOptionsFromXml(wn, version)); } else if (xn.equalsIgnoreCase("randomSkillPreferences")) { @@ -284,11 +289,13 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException { retVal.setUnitMarket(retVal.getCampaignOptions().getUnitMarketMethod().getUnitMarket()); retVal.getUnitMarket().fillFromXML(wn, retVal, version); foundUnitMarket = true; - } else if (xn.equalsIgnoreCase("lances") || xn.equalsIgnoreCase("strategicFormations")) { - processStrategicFormationNodes(retVal, wn); + } else if (xn.equalsIgnoreCase("lances") || xn.equalsIgnoreCase("combatTeams")) { + processCombatTeamNodes(retVal, wn); } else if (xn.equalsIgnoreCase("retirementDefectionTracker")) { retVal.setRetirementDefectionTracker( RetirementDefectionTracker.generateInstanceFromXML(wn, retVal)); + } else if (xn.equalsIgnoreCase("personnelWhoAdvancedInXP")) { + retVal.setPersonnelWhoAdvancedInXP(processPersonnelWhoAdvancedInXP(wn, retVal)); } else if (xn.equalsIgnoreCase("automatedMothballUnits")) { retVal.setAutomatedMothballUnits(processAutomatedMothballNodes(wn, retVal)); } else if (xn.equalsIgnoreCase("shipSearchStart")) { @@ -306,6 +313,8 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException { ); } else if (xn.equalsIgnoreCase("customPlanetaryEvents")) { updatePlanetaryEventsFromXML(wn); + } else if (xn.equalsIgnoreCase("partsInUse")) { + processPartsInUse(retVal, wn); } } else { // If it's a text node or attribute or whatever at this level, @@ -355,10 +364,10 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException { // determine if we've missed any lances and add those back into the campaign if (options.isUseAtB()) { - Hashtable lances = retVal.getStrategicFormationsTable(); + Hashtable lances = retVal.getCombatTeamsTable(); for (Force f : retVal.getAllForces()) { if (!f.getUnits().isEmpty() && (null == lances.get(f.getId()))) { - lances.put(f.getId(), new StrategicFormation(f.getId(), retVal)); + lances.put(f.getId(), new CombatTeam(f.getId(), retVal)); logger.warn(String.format("Added missing Lance %s to AtB list", f.getName())); } } @@ -429,6 +438,14 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException { s.addUnit(unit.getId()); } } + + //Update the campaign transport availability if this is a transport. + //If it's empty we should be able to just ignore it + for (CampaignTransportType campaignTransportType : CampaignTransportType.values()) { + if (unit.hasTransportedUnits(campaignTransportType)) { + retVal.updateTransportInTransports(campaignTransportType, unit); + } + } }); logger.info(String.format("[Campaign Load] Pilot references fixed in %dms", @@ -773,7 +790,7 @@ private static void processInfoNode(Campaign retVal, Node wni, Version version) retVal.setNewReports(newReports); } - private static void processStrategicFormationNodes(Campaign campaign, Node workingNode) { + private static void processCombatTeamNodes(Campaign campaign, Node workingNode) { NodeList workingNodes = workingNode.getChildNodes(); // Okay, let's iterate through the children, eh? @@ -786,17 +803,17 @@ private static void processStrategicFormationNodes(Campaign campaign, Node worki } if (!wn2.getNodeName().equalsIgnoreCase("lance") - && !wn2.getNodeName().equalsIgnoreCase("strategicFormations")) { + && !wn2.getNodeName().equalsIgnoreCase("combatTeam")) { // Error condition of sorts! // Errr, what should we do here? - logger.error("Unknown node type not loaded in strategicFormations nodes: " + wn2.getNodeName()); + logger.error("Unknown node type not loaded in combatTeam nodes: " + wn2.getNodeName()); continue; } - StrategicFormation strategicFormation = StrategicFormation.generateInstanceFromXML(wn2); + CombatTeam combatTeam = CombatTeam.generateInstanceFromXML(wn2); - if (strategicFormation != null) { - campaign.addStrategicFormation(strategicFormation); + if (combatTeam != null) { + campaign.addCombatTeam(combatTeam); } } } @@ -857,7 +874,12 @@ private static void processForces(Campaign retVal, Node wn, Version version) { } } - recalculateStrategicFormations(retVal); + // This removes the risk of having forces with invalid leadership getting locked in + for (Force force : retVal.getAllForces()) { + force.updateCommander(retVal); + } + + recalculateCombatTeams(retVal); logger.info("Load of Force Organization complete!"); } @@ -955,6 +977,53 @@ private static void processFameAndInfamyNodes(Campaign relativeValue, Node worki FameAndInfamyController.parseFromXML(workingNode.getChildNodes(), relativeValue); } + /** + * Processes a list of personnel who advanced in experience points (XP) from a given XML node. + *

    + * This method reads the child nodes of the provided XML {@code workingNode} and extracts the personnel + * listed under the "personWhoAdvancedInXP" nodes. It retrieves the corresponding {@link Person} objects + * from the provided {@link Campaign} using their unique UUIDs. If a person cannot be found, an error + * is logged. The method returns a list of processed {@link Person} objects. + *

    + * + * @param workingNode The XML node containing the "personWhoAdvancedInXP" elements to be processed. + * @param campaign The {@link Campaign} instance used to fetch the {@link Person} objects based on UUIDs. + * @return A {@link List} of {@link Person} objects representing the personnel who advanced in XP. + * If no valid personnel are found, an empty list is returned. + */ + private static List processPersonnelWhoAdvancedInXP(Node workingNode, Campaign campaign) { + logger.info("Loading personnelWhoAdvancedInXP Nodes from XML..."); + + List personWhoAdvancedInXP = new ArrayList<>(); + + NodeList workingList = workingNode.getChildNodes(); + for (int x = 0; x < workingList.getLength(); x++) { + Node childNode = workingList.item(x); + + // If it's not an element node, we ignore it. + if (childNode.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + if (!childNode.getNodeName().equalsIgnoreCase("personWhoAdvancedInXP")) { + logger.error("Unknown node type not loaded in personnelWhoAdvancedInXP nodes: " + + childNode.getNodeName()); + continue; + } + + Person person = campaign.getPerson(UUID.fromString(childNode.getTextContent())); + + if (person == null) { + logger.error("Unknown UUID: " + childNode.getTextContent()); + } + + personWhoAdvancedInXP.add(person); + } + + logger.info("Load personWhoAdvancedInXP Nodes Complete!"); + return personWhoAdvancedInXP; + } + private static List processAutomatedMothballNodes(Node workingNode, Campaign campaign) { logger.info("Loading Automated Mothball Nodes from XML..."); @@ -1609,6 +1678,79 @@ private static void updatePlanetaryEventsFromXML(Node wn) { } } + private static void processPartsInUse(Campaign retVal, Node wn) { + NodeList wList = wn.getChildNodes(); + + for (int i = 0; i < wList.getLength(); i++) { + Node wn2 = wList.item(i); + + if (wn2.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + if (wn2.getNodeName().equalsIgnoreCase("ignoreMothBalled")) { + retVal.setIgnoreMothballed(Boolean.parseBoolean(wn2.getTextContent())); + } else if (wn2.getNodeName().equalsIgnoreCase("topUpWeekly")) { + retVal.setTopUpWeekly(Boolean.parseBoolean(wn2.getTextContent())); + } else if (wn2.getNodeName().equalsIgnoreCase("ignoreSparesUnderQuality")) { + PartQuality ignoreQuality = PartQuality.valueOf(wn2.getTextContent()); + retVal.setIgnoreSparesUnderQuality(ignoreQuality); + } else if (wn2.getNodeName().equalsIgnoreCase("partInUseMap")) { + processPartsInUseRequestedStockMap(retVal, wn2); + } else { + logger.error("Unkown node type not loaded in PartInUse nodes: " + wn2.getNodeName()); + continue; + } + } + } + + private static void processPartsInUseRequestedStockMap(Campaign retVal, Node wn) { + NodeList wList = wn.getChildNodes(); + + Map partInUseStockMap = new LinkedHashMap<>(); + + for (int i = 0; i < wList.getLength(); i++) { + Node wn2 = wList.item(i); + + if (wn2.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + if (!wn2.getNodeName().equalsIgnoreCase("partInUseMapEntry")) { + logger.error("Unkown node tpye not loaded in PartInUseStockMap nodes: " + wn2.getNodeName()); + } + + processPartsInUseRequestedStockMapVal(retVal, wn2, partInUseStockMap); + + } + + retVal.setPartsInUseRequestedStockMap(partInUseStockMap); + } + + private static void processPartsInUseRequestedStockMapVal(Campaign retVal, Node wn, Map partsInUseRequestedStockMap) { + NodeList wList = wn.getChildNodes(); + + String key = null; + double val = 0; + + for(int i = 0; i < wList.getLength(); i++) { + Node wn2 = wList.item(i); + + if (wn2.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + if (wn2.getNodeName().equalsIgnoreCase("partInUseMapKey")) { + key = wn2.getTextContent(); + } else if (wn2.getNodeName().equalsIgnoreCase("partInUseMapVal")) { + val = Double.parseDouble(wn2.getTextContent()); + } + } + if (key != null) { + partsInUseRequestedStockMap.put(key, val); + } + } + //region Migration Methods //region Ancestry Migration private static final Map> ancestryMigrationMap = new HashMap<>(); diff --git a/MekHQ/src/mekhq/campaign/market/PartsStore.java b/MekHQ/src/mekhq/campaign/market/PartsStore.java index 38555d1cf7e..9031fc114d8 100644 --- a/MekHQ/src/mekhq/campaign/market/PartsStore.java +++ b/MekHQ/src/mekhq/campaign/market/PartsStore.java @@ -172,12 +172,14 @@ private void stockWeaponsAmmoAndEquipment(Campaign c) { } else if (et instanceof MiscType && ((MiscType) et).hasFlag(MiscType.F_MASC)) { if (et.hasSubType(MiscType.S_SUPERCHARGER)) { for (int rating = 10; rating <= 400; rating += 5) { - for (double eton = 0.5; eton <= 10.5; eton += 0.5) { + // eton 0.5 to 10.5 inclusive + for (int i = 1; i <= 21; i++) { + double eton = i * 0.5; double weight = Engine.ENGINE_RATINGS[(int) Math.ceil(rating / 5.0)]; - double minweight = weight * 0.5f; + double minweight = weight * 0.5; minweight = Math.ceil( (TestEntity.ceilMaxHalf(minweight, TestEntity.Ceil.HALFTON) / 10.0) * 2.0) / 2.0; - double maxweight = weight * 2.0f; + double maxweight = weight * 2.0; maxweight = Math.ceil( (TestEntity.ceilMaxHalf(maxweight, TestEntity.Ceil.HALFTON) / 10.0) * 2.0) / 2.0; if (eton < minweight || eton > maxweight) { @@ -358,7 +360,9 @@ private void stockEngines(Campaign c) { } private void stockGyros(Campaign c) { - for (double i = 0.5; i <= 8.0; i += 0.5) { + // values of 0.5 to 8.0 inclusive + for (int r = 1; r <= 16; r++) { + double i = r * 0.5; // standard at intervals of 1.0, up to 4 if (i % 1.0 == 0 && i <= 4.0) { parts.add(new MekGyro(0, Mek.GYRO_STANDARD, i, false, c)); @@ -369,7 +373,7 @@ private void stockGyros(Campaign c) { parts.add(new MekGyro(0, Mek.GYRO_COMPACT, i, false, c)); } // XL at 0.5 intervals up to 2 - if (i % 0.5 == 0 && i <= 2.0) { + if (i <= 2.0) { parts.add(new MekGyro(0, Mek.GYRO_XL, i, false, c)); } // Heavy duty at 2.0 intervals diff --git a/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java b/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java index c328a0c2abb..9d344558a94 100644 --- a/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java +++ b/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java @@ -18,25 +18,8 @@ */ package mekhq.campaign.market; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.stream.Collectors; - -import mekhq.campaign.universe.PlanetarySystem; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.Version; -import megamek.common.Entity; -import megamek.common.MekFileParser; -import megamek.common.MekSummary; -import megamek.common.MekSummaryCache; -import megamek.common.TargetRoll; +import megamek.common.*; import megamek.common.event.Subscribe; import megamek.common.loaders.EntityLoadingException; import megamek.logging.MMLogger; @@ -49,10 +32,16 @@ import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.unit.HangarStatistics; -import mekhq.campaign.universe.Factions; +import mekhq.campaign.universe.PlanetarySystem; import mekhq.module.PersonnelMarketServiceManager; import mekhq.module.api.PersonnelMarketMethod; import mekhq.utilities.MHQXMLUtility; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.PrintWriter; +import java.time.LocalDate; +import java.util.*; public class PersonnelMarket { private static final MMLogger logger = MMLogger.create(PersonnelMarket.class); @@ -105,68 +94,110 @@ public void handleCampaignOptionsEvent(OptionsChangedEvent ev) { setType(ev.getOptions().getPersonnelMarketName()); } - /* - * Generate new personnel to be added to the - * market availability pool + /** + * Generates new personnel for the current day, adding them to the campaign's personnel market if applicable. + * The method handles removing outdated personnel, checking hiring hall and capital conditions, + * and updating the personnel pool. + *

    + * The process includes: + *

      + *
    • Removing personnel already listed for the day.
    • + *
    • Clearing the personnel list entirely if it does not meet hiring hall or capital + * requirements (based on campaign settings).
    • + *
    • Generating new personnel through the associated generation method, if applicable.
    • + *
    • Triggering updates in the personnel market with an event.
    • + *
    • Optionally generating a personnel market report, if this is enabled in campaign options.
    • + *
    + * + * @param campaign The {@link Campaign} related to the current gameplay context. Used to + * determine the campaign's current planetary system, date, settings, factions, and more. */ public void generatePersonnelForDay(Campaign campaign) { - List capitals = Factions.getInstance().getFactions().stream() - .filter(faction -> faction.validIn(campaign.getLocalDate())) - .map(faction -> faction.getStartingPlanet(campaign.getLocalDate())) - .collect(Collectors.toList()); + PlanetarySystem location = campaign.getLocation().getCurrentSystem(); + LocalDate today = campaign.getLocalDate(); - PlanetarySystem currentSystem = campaign.getCurrentSystem(); - - boolean updated = false; + // Determine conditions + boolean isOnPlanet = campaign.getLocation().isOnPlanet(); + boolean useCapitalsHiringHallsOnly = campaign.getCampaignOptions().isUsePersonnelHireHiringHallOnly(); + boolean isHiringHall = location.isHiringHall(today); + boolean isCapital = location.getFactionSet(today).stream() + .anyMatch(faction -> location.equals(faction.getStartingPlanet(campaign, today))); + // Remove existing personnel for the day if (!personnel.isEmpty()) { removePersonnelForDay(campaign); - if (campaign.getCampaignOptions().isUsePersonnelHireHiringHallOnly() - && !currentSystem.isHiringHall(campaign.getLocalDate()) - && !capitals.contains(currentSystem.getId())) { + + // If only capitals/hiring halls are allowed and the location fails both conditions, clear personnel + if (!isOnPlanet || (useCapitalsHiringHallsOnly && !isHiringHall && !isCapital)) { removeAll(); + return; } } - if (null != method) { - List newPersonnel = new ArrayList<>(); - - if (!campaign.getCampaignOptions().isUsePersonnelHireHiringHallOnly() - || currentSystem.isHiringHall(campaign.getLocalDate()) - || capitals.contains(currentSystem.getId())) { - newPersonnel = method.generatePersonnelForDay(campaign); - } - - if ((null != newPersonnel) && !newPersonnel.isEmpty()) { + // Generate new personnel if `method` is defined and conditions allow + boolean updated = false; + if (method != null && (!useCapitalsHiringHallsOnly || isHiringHall || isCapital)) { + List newPersonnel = method.generatePersonnelForDay(campaign); + if (newPersonnel != null && !newPersonnel.isEmpty()) { personnel.addAll(newPersonnel); updated = true; + + // Notify about new personnel in the market MekHQ.triggerEvent(new MarketNewPersonnelEvent(newPersonnel)); } } + // Skip further processing if no personnel are present + if (personnel.isEmpty()) { + return; + } + + // Generate campaign reports if the personnel market was updated if (updated && campaign.getCampaignOptions().isPersonnelMarketReportRefresh()) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("Personnel market updated"); - if (campaign.getCampaignOptions().getPersonnelMarketName().equals("Campaign Ops") && !personnel.isEmpty()) { - stringBuilder.append(':'); - Person person = personnel.get(0); - String expLevel = SkillType.getExperienceLevelName(person.getExperienceLevel(campaign, false)); - if (expLevel.equals("Elite") || expLevel.equals("Ultra-Green")) { - stringBuilder.append("
    An "); - } else { - stringBuilder.append("
    A "); - } - stringBuilder.append(" ") - .append(SkillType.getColoredExperienceLevelName(person.getExperienceLevel(campaign, false))) - .append(' ') - .append(person.getPrimaryRole().toString()) - .append("") - .append(" named ") - .append(person.getFullName()) - .append(" is available."); + generatePersonnelReport(campaign); + } + } + + /** + * Generates and adds a report to the campaign about new personnel added to the personnel market. + *

    + * If the corresponding option is enabled in the campaign settings, this function produces a detailed, + * user-facing report about the most notable individual in the new personnel pool. The report includes + * their experience level, primary role, and name. + *

    + * The generated report is in HTML format and provides easy access to the personnel market interface. + * + * @param campaign The {@link Campaign} to which the report will be added. + */ + private void generatePersonnelReport(Campaign campaign) { + StringBuilder report = new StringBuilder(); + report.append("Personnel market updated"); + + if (campaign.getCampaignOptions().getPersonnelMarketName().equals("Campaign Ops")) { + report.append(':'); + + // Add details about the first personnel's experience, primary role, and name + Person person = personnel.get(0); + int experienceLevel = person.getExperienceLevel(campaign, false); + String expLevel = SkillType.getExperienceLevelName(experienceLevel); + + if (expLevel.equals("Elite") || expLevel.equals("Ultra-Green")) { + report.append("
    An "); + } else { + report.append("
    A "); } - campaign.addReport(stringBuilder.toString()); + + report.append("") + .append(SkillType.getColoredExperienceLevelName(experienceLevel)) + .append(' ') + .append(person.getPrimaryRole()) + .append("") + .append(" named ") + .append(person.getFullName()) + .append(" is available."); } + + campaign.addReport(report.toString()); } /* diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java index b106407860a..5247afa3a7b 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java @@ -5,6 +5,7 @@ import megamek.common.enums.SkillLevel; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; import mekhq.campaign.market.enums.ContractMarketMethod; import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.Contract; @@ -12,6 +13,7 @@ import mekhq.campaign.mission.enums.AtBContractType; import mekhq.campaign.mission.enums.ContractCommandRights; import mekhq.campaign.rating.IUnitRating; +import mekhq.campaign.universe.Faction; import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.RandomFactionGenerator; import mekhq.utilities.MHQXMLUtility; @@ -21,11 +23,11 @@ import java.io.PrintWriter; import java.util.*; -import static java.lang.Math.floor; -import static java.lang.Math.max; -import static java.lang.Math.round; +import static java.lang.Math.min; import static megamek.common.Compute.d6; -import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; +import static megamek.common.enums.SkillLevel.REGULAR; +import static megamek.common.enums.SkillLevel.VETERAN; +import static mekhq.campaign.force.CombatTeam.getStandardForceSize; import static mekhq.campaign.mission.AtBContract.getEffectiveNumUnits; /** @@ -39,6 +41,12 @@ public abstract class AbstractContractMarket { public static final int CLAUSE_TRANSPORT = 3; public static final int CLAUSE_NUM = 4; + /** + * The portion of combat teams we expect to be performing combat actions. + * This is one in 'x' where 'x' is the value set here. + */ + static final double COMBAT_FORCE_DIVIDER = 2; + protected List contracts = new ArrayList<>(); protected int lastId = 0; @@ -179,57 +187,130 @@ protected void updateReport(Campaign campaign) { } /** - * Determines the number of required lances to be deployed for a contract. For Mercenary subcontracts - * this defaults to 1; otherwise, the number is based on the number of combat units in the - * campaign. Modified by a 2d6 roll if {@code bypassVariance} is {@code false}. - * @param campaign the current campaign - * @param contract the relevant contract - * @param bypassVariance if {@code true} requirements will not be semi-randomized. - * @return The number of lances required to be deployed. + * Calculates the required number of combat teams for a contract based on campaign options, + * contract details, and variance factors. + * + *

    + * This method determines the number of combat teams needed to deploy, taking into account factors such as: + *

      + *
    • Whether the contract is a subcontract (returns 1 as a base case).
    • + *
    • The base formation sizes (lance-level and company-level) and the effective unit forces.
    • + *
    • The maximum deployable combat teams, adjusted based on campaign strategy options.
    • + *
    • Whether variance bypass is enabled, applying a flat reduction to available forces.
    • + *
    • Variance adjustments applied through a dice roll, affecting the availability of forces.
    • + *
    + * The method ensures values are clamped to maintain a minimum deployment of at least 1 combat + * team while not exceeding the maximum deployable combat teams. + * + * @param campaign the campaign containing relevant options and faction information + * @param contract the contract that specifies details such as subcontract status + * @param bypassVariance a flag indicating whether variance adjustments should be bypassed + * @return the calculated number of required combat teams, ensuring it meets game rules and constraints */ - public int calculateRequiredLances(Campaign campaign, AtBContract contract, boolean bypassVariance) { - int maxDeployedLances = max(calculateMaxDeployedLances(campaign), 1); + public int calculateRequiredCombatTeams(Campaign campaign, AtBContract contract, boolean bypassVariance) { + // Return 1 combat team if the contract is a subcontract if (contract.isSubcontract()) { return 1; - } else { - int formationSize = getStandardForceSize(campaign.getFaction()); - int availableForces = max(getEffectiveNumUnits(campaign) / formationSize, 1); - - // We allow for one reserve force per 3 depth 0 forces (lances, etc) - availableForces -= max((int) floor((double) availableForces / 3), 1); - - if (!bypassVariance) { - int roll = d6(2); - - if (roll == 2) { - availableForces = (int) round((double) availableForces * 0.25); - } else if (roll == 3) { - availableForces = (int) round((double) availableForces * 0.5); - } else if (roll < 5) { - availableForces = (int) round((double) availableForces * 0.75); - } else if (roll == 12) { - availableForces = (int) round((double) availableForces * 1.75); - } else if (roll == 11) { - availableForces = (int) round((double) availableForces * 1.5); - } else if (roll > 9) { - availableForces = (int) round((double) availableForces * 1.25); - } - } + } + + // Calculate base formation size and effective unit force + Faction faction = campaign.getFaction(); + int lanceLevelFormationSize = getStandardForceSize(faction); - return MathUtility.clamp(availableForces, 1, maxDeployedLances); + int effectiveForces = Math.max(getEffectiveNumUnits(campaign) / lanceLevelFormationSize, 1); + + // Calculate maximum deployed forces based on strategy options + int maxDeployableCombatTeams = effectiveForces; + if (campaign.getCampaignOptions().isUseStrategy()) { + maxDeployableCombatTeams = Math.max(calculateMaxDeployableCombatTeams(campaign), 1); } + + // Clamp available forces to the max deployable limit + int availableForces = Math.min(effectiveForces, maxDeployableCombatTeams); + + // If bypassing variance, apply flat reduction (reduce force by 1/3) + if (bypassVariance) { + return Math.max(availableForces - calculateBypassVarianceReduction(availableForces), 1); + } + + // Apply variance based on a die roll + int varianceRoll = d6(2); + double varianceFactor = calculateVarianceFactor(varianceRoll); + + // Adjust available forces based on variance, ensuring minimum clamping + int adjustedForces = availableForces - (int) Math.floor((double) availableForces / varianceFactor); + + if (adjustedForces < 1) { + adjustedForces = 1; + } + + // Return the clamped value, ensuring it does not exceed max-deployable forces + return Math.min(adjustedForces, maxDeployableCombatTeams); } /** - * Determine the maximum number of lances the force can deploy. The result is based on the - * commander's Strategy skill and various campaign options. - * @param campaign - * @return the maximum number of lances that can be deployed on the contract. + * Calculates the variance factor based on the given roll value and a fixed formation size divisor. + * + *

    + * The variance factor is determined by applying a multiplier to the fixed formation size divisor. + * The multiplier varies based on the roll value: + *

      + *
    • Roll 2: Multiplier is 0.75.
    • + *
    • Roll 3: Multiplier is 0.5.
    • + *
    • Roll 4: Multiplier is 0.25.
    • + *
    • Rolls 10, 11, 12: Multipliers are 1.25, 1.5, and 1.75 respectively.
    • + *
    • Rolls 5-9: Default multiplier is 1.0 (no change).
    • + *
    + * + * @param roll the roll value used to determine the multiplier + * @return the calculated variance factor as a double + */ + private double calculateVarianceFactor(int roll) { + return switch (roll) { + case 2 -> COMBAT_FORCE_DIVIDER * 0.25; + case 3 -> COMBAT_FORCE_DIVIDER * 0.5; + case 4 -> COMBAT_FORCE_DIVIDER * 0.75; + case 10 -> COMBAT_FORCE_DIVIDER * 1.25; + case 11 -> COMBAT_FORCE_DIVIDER * 1.5; + case 12 -> COMBAT_FORCE_DIVIDER * 1.75; + default -> COMBAT_FORCE_DIVIDER; // 5-9 + }; + } + + /** + * Calculates the bypass variance reduction based on the available forces. + * + *

    + * The reduction is calculated by dividing the available forces by a fixed factor of 3 + * and rounding down to the nearest whole number. This value is used in scenarios where + * variance adjustments are bypassed. + *

    + * + * @param availableForces the total number of forces available + * @return the bypass variance reduction as an integer */ - public int calculateMaxDeployedLances(Campaign campaign) { - return campaign.getCampaignOptions().getBaseStrategyDeployment() + - campaign.getCampaignOptions().getAdditionalStrategyDeployment() * - campaign.getCommanderStrategy(); + private int calculateBypassVarianceReduction(int availableForces) { + return (int) Math.floor((double) availableForces / 3); + } + + /** + * Calculates the maximum number of deployable combat teams based on the given campaign's options. + * + *

    + * This method retrieves campaign options and calculates the total deployable combat teams using + * the base strategy deployment, additional strategy deployment, and the campaign's commander strategy. + *

    + * + * @param campaign the campaign object containing the necessary data to perform the calculation + * @return the total number of deployable combat teams + */ + public int calculateMaxDeployableCombatTeams(Campaign campaign) { + CampaignOptions options = campaign.getCampaignOptions(); + int baseStrategyDeployment = options.getBaseStrategyDeployment(); + int additionalStrategyDeployment = options.getAdditionalStrategyDeployment(); + int commanderStrategy = campaign.getCommanderStrategy(); + + return baseStrategyDeployment + additionalStrategyDeployment * commanderStrategy; } protected SkillLevel getSkillRating(int roll) { @@ -238,7 +319,7 @@ protected SkillLevel getSkillRating(int roll) { } else if (roll <= 9) { return SkillLevel.REGULAR; } else if (roll <= 11) { - return SkillLevel.VETERAN; + return VETERAN; } else { return SkillLevel.ELITE; } @@ -273,7 +354,7 @@ protected void rollCommandClause(final Contract contract, final int modifier) { protected void rollSalvageClause(AtBContract contract, int mod, int contractMaxSalvagePercentage) { contract.setSalvageExchange(false); - int roll = Math.min(d6(2) + mod, 13); + int roll = min(d6(2) + mod, 13); if (roll < 2) { contract.setSalvagePct(0); } else if (roll < 4) { @@ -282,9 +363,9 @@ protected void rollSalvageClause(AtBContract contract, int mod, int contractMaxS do { r = d6(2); } while (r < 4); - contract.setSalvagePct(Math.min((r - 3) * 10, contractMaxSalvagePercentage)); + contract.setSalvagePct(min((r - 3) * 10, contractMaxSalvagePercentage)); } else { - contract.setSalvagePct(Math.min((roll - 3) * 10, contractMaxSalvagePercentage)); + contract.setSalvagePct(min((roll - 3) * 10, contractMaxSalvagePercentage)); } } @@ -299,7 +380,7 @@ protected void rollSupportClause(AtBContract contract, int mod) { } else if (roll == 8) { contract.setBattleLossComp(10); } else { - contract.setBattleLossComp(Math.min((roll - 8) * 20, 100)); + contract.setBattleLossComp(min((roll - 8) * 20, 100)); } } @@ -375,65 +456,187 @@ protected void setIsRiotDuty(AtBContract contract) { } } + /** + * Sets the ally rating (skill and quality) for the contract. + * The ally rating is determined by modifiers influenced by the employer faction, + * contract type, historical context, and a random roll. + * + *

    The calculated ally skill and quality ratings are assigned to the contract.

    + * + * @param contract the contract for which the ally rating is being set. + * @param year the year of the contract, used for calculating historical modifiers. + */ protected void setAllyRating(AtBContract contract, int year) { - int mod = 0; - if (contract.getEnemy().isRebelOrPirate()) { - mod -= 1; - } + int mod = calculateFactionModifiers(contract.getEmployerFaction()); + mod += calculateContractTypeModifiers(contract.getContractType(), contract.isAttacker()); - if (contract.getContractType().isGuerrillaWarfare() || contract.getContractType().isCadreDuty()) { - mod -= 3; - } else if (contract.getContractType().isGarrisonDuty() || contract.getContractType().isSecurityDuty()) { - mod -= 2; + // Assign ally skill rating + contract.setAllySkill(getSkillRating(d6(2) + mod)); + + // Apply historical modifiers + if (!contract.getEmployerFaction().isClan()) { + mod += calculateHistoricalModifiers(year); + } else { + // Apply Clan clamping + if (contract.isAttacker()) { + if (contract.getAllySkill().ordinal() < VETERAN.ordinal()) { + contract.setAllySkill(VETERAN); + } + } else { + if (contract.getAllySkill().ordinal() < REGULAR.ordinal()) { + contract.setAllySkill(SkillLevel.REGULAR); + } + } } - if (AtBContract.isMinorPower(contract.getEmployerCode())) { - mod -= 1; + // Assign ally quality rating + contract.setAllyQuality(getQualityRating(d6(2) + mod)); + } + + /** + * Sets the enemy rating (skill and quality) for the contract. + * The enemy rating is determined by modifiers based on the enemy faction, + * whether the faction is attacking or defending, historical context, and a random roll. + * + *

    The calculated enemy skill and quality ratings are assigned to the contract.

    + * + * @param contract the contract for which the enemy rating is being set. + * @param year the year of the contract, used for calculating historical modifiers. + */ + protected void setEnemyRating(AtBContract contract, int year) { + Faction enemyFaction = Factions.getInstance().getFaction(contract.getEnemyCode()); + int mod = calculateFactionModifiers(enemyFaction); + + // Adjust modifiers based on attack/defense roles + if (!contract.isAttacker()) { + mod += 1; } - if (contract.getEnemy().isIndependent()) { - mod -= 2; + // Assign enemy skill rating + contract.setEnemySkill(getSkillRating(d6(2) + mod)); + + // Apply historical modifiers + if (!enemyFaction.isClan()) { + mod += calculateHistoricalModifiers(year); + } else { + // Apply Clan clamping + if (!contract.isAttacker()) { + if (contract.getAllySkill().ordinal() < VETERAN.ordinal()) { + contract.setAllySkill(VETERAN); + } + } else { + if (contract.getAllySkill().ordinal() < REGULAR.ordinal()) { + contract.setAllySkill(SkillLevel.REGULAR); + } + } } - if (contract.getContractType().isPlanetaryAssault()) { - mod += 1; + // Assign enemy quality rating + contract.setEnemyQuality(getQualityRating(d6(2) + mod)); + } + + /** + * Calculates the modifiers for a faction based on its attributes, such as whether it is: + * a rebel, pirate, independent, a minor power, or a Clan faction. + * + *

    Faction modifiers are determined as follows:

    + *
      + *
    • Rebel or Pirate factions receive a penalty of -3.
    • + *
    • Independent factions receive a penalty of -2.
    • + *
    • Minor powers receive a penalty of -1.
    • + *
    • Clan factions receive a bonus of +4.
    • + *
    + * + * @param faction the faction for which the modifiers are being calculated. + * @return the calculated modifier for the faction. + */ + private int calculateFactionModifiers(Faction faction) { + int mod = 0; + + if (faction.isRebelOrPirate()) { + mod -= 3; } - if (Factions.getInstance().getFaction(contract.getEmployerCode()).isClan() && !contract.isAttacker()) { - // facing front-line units - mod += 1; + if (faction.isIndependent()) { + mod -= 2; } - contract.setAllySkill(getSkillRating(d6(2) + mod)); - if (year > 2950 && year < 3039 && - !Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) { + + if (faction.isMinorPower()) { mod -= 1; } - contract.setAllyQuality(getQualityRating(d6(2) + mod)); + + if (faction.isClan()) { + mod += 4; + } + + return mod; } - protected void setEnemyRating(AtBContract contract, int year) { + /** + * Calculates the modifiers for a contract based on its type and whether the faction + * is in an attacker role or defender role. + * + *

    Contract type modifiers are determined as follows:

    + *
      + *
    • Guerrilla Warfare or Cadre Duty incurs a penalty of -3.
    • + *
    • Garrison Duty or Security Duty incurs a penalty of -2.
    • + *
    • An attacking faction receives a bonus of +1.
    • + *
    + * + * @param contractType the type of the contract (e.g., Guerrilla Warfare, Cadre Duty, etc.). + * @param isAttacker a boolean indicating whether the faction is in an attacker role. + * @return the calculated modifier for the contract type. + */ + private int calculateContractTypeModifiers(AtBContractType contractType, boolean isAttacker) { int mod = 0; - if (contract.getEnemy().isRebelOrPirate()) { + + if (contractType.isGuerrillaWarfare() || contractType.isCadreDuty()) { + mod -= 3; + } else if (contractType.isGarrisonDuty() || contractType.isSecurityDuty()) { mod -= 2; } - if (contract.getContractType().isGuerrillaWarfare()) { - mod += 2; - } - if (contract.getContractType().isPlanetaryAssault()) { + + if (isAttacker) { mod += 1; } - if (AtBContract.isMinorPower(contract.getEmployerCode())) { - mod -= 1; - } - if (Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) { - mod += contract.isAttacker() ? 2 : 4; - } - contract.setEnemySkill(getSkillRating(d6(2) + mod)); - if (year > 2950 && year < 3039 && - !Factions.getInstance().getFaction(contract.getEnemyCode()).isClan()) { + + return mod; + } + + /** + * Calculates modifiers based on the historical period in which the given year falls. + * Modifiers are applied to non-Clan factions based on the progressive degradation or + * recovery of combat capabilities during the Succession Wars and Renaissance periods. + * + *

    The modifiers are determined as follows:

    + *
      + *
    • The Second Succession War (2830-2865): a penalty of -1.
    • + *
    • The Third Succession War (2866-3038): a penalty of -2.
    • + *
    • The Renaissance start period (3039-3049): a penalty of -1.
    • + *
    + * + * @param year the year of the contract, which determines the historical period. + * @return the calculated historical modifier to be applied. + */ + private int calculateHistoricalModifiers(int year) { + final int SECOND_SUCCESSION_WAR_START = 2830; + final int THIRD_SUCCESSION_WAR_START = 2866; + final int RENAISSANCE_START = 3039; + final int RENAISSANCE_END = 3049; + + int mod = 0; + + if ((year >= SECOND_SUCCESSION_WAR_START) && (year < THIRD_SUCCESSION_WAR_START)) { mod -= 1; + } else if ((year >= THIRD_SUCCESSION_WAR_START) && (year < RENAISSANCE_START)) { + mod -= 2; + } else if (year >= RENAISSANCE_START) { + if (year < RENAISSANCE_END) { + mod -= 1; + } } - contract.setEnemyQuality(getQualityRating(d6(2) + mod)); + + return mod; } public void writeToXML(final PrintWriter pw, int indent) { diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java index 9dcac0d8bf6..e90911131fd 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java @@ -41,6 +41,10 @@ import java.util.ArrayList; import java.util.Set; +import static java.lang.Math.floor; +import static megamek.codeUtilities.MathUtility.clamp; +import static mekhq.campaign.mission.AtBContract.getEffectiveNumUnits; + /** * Contract offers that are generated monthly under AtB rules. * @@ -298,7 +302,7 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u contract.calculateLength(campaign.getCampaignOptions().isVariableContractLength()); setContractClauses(contract, unitRatingMod, campaign); - contract.setRequiredLances(calculateRequiredLances(campaign, contract, false)); + contract.setRequiredCombatTeams(calculateRequiredCombatTeams(campaign, contract, false)); contract.setMultiplier(calculatePaymentMultiplier(campaign, contract)); contract.setPartsAvailabilityLevel(contract.getContractType().calculatePartsAvailabilityLevel()); @@ -392,7 +396,7 @@ protected AtBContract generateAtBSubcontract(Campaign campaign, } contract.setTransportComp(100); - contract.setRequiredLances(calculateRequiredLances(campaign, contract, false)); + contract.setRequiredCombatTeams(calculateRequiredCombatTeams(campaign, contract, false)); contract.setMultiplier(calculatePaymentMultiplier(campaign, contract)); contract.setPartsAvailabilityLevel(contract.getContractType().calculatePartsAvailabilityLevel()); contract.calculateContract(campaign); @@ -405,15 +409,40 @@ protected AtBContract generateAtBSubcontract(Campaign campaign, return contract; } + /** + * Creates and adds a follow-up contract based on the just concluded contract. + *

    + * This method generates a new contract (`AtBContract`) as a follow-up to the provided `contract`. + * Certain properties of the original contract, such as employer, enemy, skill, system location, + * and other details, are carried over or modified as necessary based on the contract type. + * The method ensures that the follow-up contract contains all necessary details and is + * correctly initialized. + *

    + * + *

    + * Order Dependency: The operations in this method must match the order specified in + * `generateAtBContract` to maintain compatibility and consistency. + *

    + * + * @param campaign the {@link Campaign} to which the follow-up contract belongs. + * This is used for retrieving campaign-wide settings and applying modifiers. + * @param contract the {@link AtBContract} that serves as the base for generating the follow-up contract. + * Key details from this contract are reused or adapted for the follow-up. + */ private void addFollowup(Campaign campaign, AtBContract contract) { if (followupContracts.containsValue(contract.getId())) { return; } + + // The order in this method needs to match generateAtBContract + AtBContract followup = new AtBContract("Followup Contract"); + lastId++; + followup.setId(lastId); + contractIds.put(lastId, followup); + followup.setEmployerCode(contract.getEmployerCode(), campaign.getGameYear()); - followup.setEnemyCode(contract.getEnemyCode()); - followup.setSystemId(contract.getSystemId()); switch (contract.getContractType()) { case DIVERSIONARY_RAID: followup.setContractType(AtBContractType.OBJECTIVE_RAID); @@ -427,23 +456,21 @@ private void addFollowup(Campaign campaign, default: break; } - followup.setAllySkill(contract.getAllySkill()); - followup.setAllyQuality(contract.getAllyQuality()); + + followup.setEnemyCode(contract.getEnemyCode()); followup.setEnemySkill(contract.getEnemySkill()); followup.setEnemyQuality(contract.getEnemyQuality()); + setAttacker(followup); + followup.setSystemId(contract.getSystemId()); + followup.setAllySkill(contract.getAllySkill()); + followup.setAllyQuality(contract.getAllyQuality()); followup.calculateLength(campaign.getCampaignOptions().isVariableContractLength()); setContractClauses(followup, campaign.getAtBUnitRatingMod(), campaign); - - contract.setRequiredLances(calculateRequiredLances(campaign, contract, false)); - contract.setMultiplier(calculatePaymentMultiplier(campaign, contract)); - + followup.setRequiredCombatTeams(calculateRequiredCombatTeams(campaign, followup, false)); + followup.setMultiplier(calculatePaymentMultiplier(campaign, followup)); followup.setPartsAvailabilityLevel(followup.getContractType().calculatePartsAvailabilityLevel()); - followup.initContractDetails(campaign); followup.calculateContract(campaign); - lastId++; - followup.setId(lastId); - contractIds.put(lastId, followup); contracts.add(followup); followupContracts.put(followup.getId(), contract.getId()); @@ -451,12 +478,16 @@ private void addFollowup(Campaign campaign, @Override public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract) { - int unitRatingMod = campaign.getAtBUnitRatingMod(); double multiplier = 1.0; - // IntOps reputation factor then Dragoons rating + + // Operations tempo + multiplier *= contract.getContractType().getOperationsTempoMultiplier(); + + // Reputation multiplier if (campaign.getCampaignOptions().getUnitRatingMethod().isCampaignOperations()) { - multiplier *= (unitRatingMod * 0.2) + 0.5; + multiplier *= (campaign.getReputation().getReputationRating() * 0.2) + 0.5; } else { + int unitRatingMod = campaign.getAtBUnitRatingMod(); if (unitRatingMod >= IUnitRating.DRAGOON_A) { multiplier *= 2.0; } else if (unitRatingMod == IUnitRating.DRAGOON_B) { @@ -468,8 +499,7 @@ public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract } } - multiplier *= contract.getContractType().getOperationsTempoMultiplier(); - + // Employer multiplier final Faction employer = Factions.getInstance().getFaction(contract.getEmployerCode()); final Faction enemy = contract.getEnemy(); if (employer.isISMajorOrSuperPower() || employer.isClan()) { @@ -484,19 +514,45 @@ public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract multiplier *= 1.1; } - int baseRequiredLances = calculateRequiredLances(campaign, contract, true); - int requiredLances = contract.getRequiredLances(); + // Unofficial modifiers + double unofficialMultiplier = getUnofficialMultiplier(campaign, contract); - multiplier *= (double) requiredLances / baseRequiredLances; - - int maxDeployedLances = calculateMaxDeployedLances(campaign); - if (requiredLances > maxDeployedLances && campaign.getCampaignOptions().isAdjustPaymentForStrategy()) { - multiplier *= (double) maxDeployedLances / (double) requiredLances; + if (unofficialMultiplier > 0) { + multiplier *= (1.0 + unofficialMultiplier); + } else if (unofficialMultiplier < 0) { + multiplier *= (1.0 - unofficialMultiplier); } return multiplier; } + private static double getUnofficialMultiplier(Campaign campaign, AtBContract contract) { + double modifier = 0; // we apply these modifiers all together to avoid spiking the final pay + + // Adjust pay based on the percentage of the players' forces required by the contract + int maximumLanceCount = campaign.getAllCombatTeams().size(); + int reserveLanceCount = (int) floor(maximumLanceCount / COMBAT_FORCE_DIVIDER); + int requiredCombatTeams = contract.getRequiredCombatTeams(); + + if (reserveLanceCount > 0) { // Ensure we don't divide by zero + double reservesDifference = ((requiredCombatTeams - reserveLanceCount) / (double) reserveLanceCount); + + modifier += reservesDifference; + } + + // Adjust pay based on difficulty if FG3 is enabled + if (campaign.getCampaignOptions().isUseGenericBattleValue()) { + int difficulty = clamp(contract.calculateContractDifficulty(campaign), 0, 10); + int baseDifficulty = 5; // 2.5 skulls + + double difficultyDifference = ((difficulty - baseDifficulty) / (double) baseDifficulty); + + modifier += difficultyDifference; + } + + return modifier; + } + @Override public void checkForFollowup(Campaign campaign, AtBContract contract) { AtBContractType type = contract.getContractType(); @@ -541,7 +597,7 @@ private void setContractClauses(AtBContract contract, int unitRatingMod, Campaig if (campaign.getCampaignOptions().isMercSizeLimited() && campaign.getFaction().isMercenary()) { int max = (unitRatingMod + 1) * 12; - int numMods = (AtBContract.getEffectiveNumUnits(campaign) - max) / 2; + int numMods = (getEffectiveNumUnits(campaign) - max) / 2; while (numMods > 0) { mods.mods[Compute.randomInt(4)]--; numMods--; diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java index 062369afa85..fbdd73ae22c 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java @@ -204,7 +204,7 @@ private Optional generateContract(Campaign campaign, ReputationCont // Step 6: Determine the initial contract clauses setContractClauses(contract, contractTerms); // Step 7: Determine the number of required lances (Not CamOps RAW) - contract.setRequiredLances(calculateRequiredLances(campaign, contract, false)); + contract.setRequiredCombatTeams(calculateRequiredCombatTeams(campaign, contract, false)); // Step 8: Calculate the payment contract.setMultiplier(calculatePaymentMultiplier(campaign, contract)); // Step 9: Determine parts availability diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/ContractAutomation.java b/MekHQ/src/mekhq/campaign/market/contractMarket/ContractAutomation.java index f7ba93dc3a0..351eab7e8a3 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/ContractAutomation.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/ContractAutomation.java @@ -20,9 +20,7 @@ */ package mekhq.campaign.market.contractMarket; -import megamek.client.ui.swing.util.UIUtil; import megamek.common.Entity; -import megamek.common.annotations.Nullable; import megamek.logging.MMLogger; import mekhq.MekHQ; import mekhq.campaign.Campaign; @@ -31,19 +29,13 @@ import mekhq.campaign.finances.Money; import mekhq.campaign.force.Force; import mekhq.campaign.mission.Contract; -import mekhq.campaign.personnel.Person; import mekhq.campaign.unit.Unit; import mekhq.campaign.unit.actions.ActivateUnitAction; import mekhq.campaign.unit.actions.MothballUnitAction; -import mekhq.campaign.universe.Factions; +import mekhq.gui.dialog.ContractAutomationDialog; -import javax.swing.*; -import java.awt.*; -import java.util.List; import java.util.*; -import static megamek.common.icons.AbstractIcon.DEFAULT_ICON_FILENAME; - /** * The ContractAutomation class provides a suite of methods * used in automating actions when a contract starts. @@ -69,16 +61,13 @@ public static void contractStartPrompt(Campaign campaign, Contract contract) { } // Initial setup - final Person speaker = getSpeaker(campaign); - final String speakerName = getSpeakerName(campaign, speaker); - final ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); - - final String commanderAddress = campaign.getCommanderAddress(); + final String commanderAddress = campaign.getCommanderAddress(false); // Mothballing String message = String.format(resources.getString("mothballDescription.text"), commanderAddress); - if (createDialog(speakerName, speakerIcon, message)) { + ContractAutomationDialog mothballDialog = new ContractAutomationDialog(campaign, message, true); + if (mothballDialog.isDialogConfirmed()) { campaign.setAutomatedMothballUnits(performAutomatedMothballing(campaign)); } @@ -99,161 +88,17 @@ public static void contractStartPrompt(Campaign campaign, Contract contract) { message = String.format(resources.getString("transitDescription.text"), targetSystem, employerName, travelDays, totalCost); - if (createDialog(speakerName, speakerIcon, message)) { + + ContractAutomationDialog transitDialog = new ContractAutomationDialog(campaign, message, false); + if (transitDialog.isDialogConfirmed()) { campaign.getLocation().setJumpPath(jumpPath); campaign.getUnits().forEach(unit -> unit.setSite(Unit.SITE_FACILITY_BASIC)); campaign.getApp().getCampaigngui().refreshAllTabs(); campaign.getApp().getCampaigngui().refreshLocation(); - } - } - - /** - * @param campaign The current campaign - * @return The highest ranking Admin/Transport character. If none are found, returns {@code null}. - */ - private static @Nullable Person getSpeaker(Campaign campaign) { - List admins = campaign.getAdmins(); - - if (admins.isEmpty()) { - return null; - } - - List transportAdmins = new ArrayList<>(); - - for (Person admin : admins) { - if (admin.getPrimaryRole().isAdministratorTransport() - || admin.getSecondaryRole().isAdministratorTransport()) { - transportAdmins.add(admin); - } - } - if (transportAdmins.isEmpty()) { - return null; + campaign.addReport(String.format(resources.getString("transitDescription.supplemental"), + targetSystem, travelDays)); } - - Person speaker = transportAdmins.get(0); - - for (Person admin : transportAdmins) { - if (admin.outRanksUsingSkillTiebreaker(campaign, speaker)) { - speaker = admin; - } - } - - return speaker; - } - - /** - * Gets the name of the individual to be displayed in the dialog. - * If the person is {@code null}, it uses the campaign's name. - * - * @param campaign The current campaign - * @param speaker The person who will be speaking, or {@code null}. - * @return The name to be displayed. - */ - private static String getSpeakerName(Campaign campaign, @Nullable Person speaker) { - if (speaker == null) { - return campaign.getName(); - } else { - return speaker.getFullTitle(); - } - } - - /** - * Gets the icon representing the speaker. - * If the speaker is {@code null}, it defaults to displaying the campaign's icon, or the - * campaign's faction icon. - * - * @param campaign The current campaign - * @param speaker The person who is speaking, or {@code null}. - * @return The icon of the speaker, campaign, or faction. - */ - private static ImageIcon getSpeakerIcon(Campaign campaign, @Nullable Person speaker) { - ImageIcon icon; - - if (speaker == null) { - String fallbackIconFilename = campaign.getUnitIcon().getFilename(); - - if (fallbackIconFilename == null || fallbackIconFilename.equals(DEFAULT_ICON_FILENAME)) { - icon = Factions.getFactionLogo(campaign, campaign.getFaction().getShortName(), true); - } else { - icon = campaign.getUnitIcon().getImageIcon(); - } - } else { - icon = speaker.getPortrait().getImageIcon(); - } - - Image originalImage = icon.getImage(); - Image scaledImage = originalImage.getScaledInstance(100, -1, Image.SCALE_SMOOTH); - return new ImageIcon(scaledImage); - } - - /** - * Displays a dialog for user interaction. - * The dialog uses a custom formatted message and includes options for user to confirm or decline. - * - * @param speakerName The title of the speaker to be displayed. - * @param speakerIcon The {@link ImageIcon} of the person speaking. - * @param message The message to be displayed in the dialog. - * @return {@code true} if the user confirms, {@code false} otherwise. - */ - private static boolean createDialog(String speakerName, ImageIcon speakerIcon, String message) { - final int WIDTH = UIUtil.scaleForGUI(400); - - // Custom button text - JButton confirmButton = new JButton(resources.getString("generalConfirm.text")); - JButton declineButton = new JButton(resources.getString("generalDecline.text")); - - // Create a custom message with a border - String descriptionTitle = String.format("%s", speakerName); - - // Create ImageIcon JLabel - JLabel iconLabel = new JLabel(speakerIcon); - iconLabel.setHorizontalAlignment(JLabel.CENTER); - - // Create description JPanel - JPanel descriptionPanel = new JPanel(); - descriptionPanel.setLayout(new BoxLayout(descriptionPanel, BoxLayout.PAGE_AXIS)); - JLabel description = new JLabel( - String.format("
    %s
    ", - WIDTH, message)); - description.setBorder(BorderFactory.createTitledBorder(descriptionTitle)); - descriptionPanel.add(description); - - // Create Buttons Panel - JPanel buttonsPanel = new JPanel(); - buttonsPanel.add(confirmButton); - buttonsPanel.add(declineButton); - - // Create main JPanel and add icon and description - JPanel mainPanel = new JPanel(new BorderLayout()); - mainPanel.add(iconLabel, BorderLayout.NORTH); - mainPanel.add(descriptionPanel, BorderLayout.CENTER); - mainPanel.add(buttonsPanel, BorderLayout.SOUTH); - - // Create JDialog - final JDialog dialog = new JDialog(); - dialog.setTitle(resources.getString("generalTitle.text")); - dialog.setModal(true); - dialog.setContentPane(mainPanel); - dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - dialog.pack(); - dialog.setLocationRelativeTo(null); - - boolean[] result = new boolean[1]; - - // Add functionality to buttons - confirmButton.addActionListener(e -> { - result[0] = true; - dialog.dispose(); - }); - declineButton.addActionListener(e -> { - result[0] = false; - dialog.dispose(); - }); - - dialog.setVisible(true); - - return result[0]; } /** @@ -320,6 +165,11 @@ public static void performAutomatedActivation(Campaign campaign) { ActivateUnitAction activateUnitAction = new ActivateUnitAction(null, true); for (Unit unit : units) { + if (unit == null) { + // <50.03 compatibility handler + continue; + } + if (unit.isMothballed()) { activateUnitAction.execute(campaign, unit); MekHQ.triggerEvent(new UnitChangedEvent(unit)); diff --git a/MekHQ/src/mekhq/campaign/market/procurement/Procurement.java b/MekHQ/src/mekhq/campaign/market/procurement/Procurement.java new file mode 100644 index 00000000000..de97c58c16c --- /dev/null +++ b/MekHQ/src/mekhq/campaign/market/procurement/Procurement.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 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.market.procurement; + +import megamek.common.Compute; +import megamek.common.ITechnology; +import megamek.common.SimpleTechLevel; +import megamek.common.enums.SkillLevel; +import megamek.logging.MMLogger; +import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.equipment.AmmoBin; +import mekhq.campaign.universe.Faction; + +import java.util.ArrayList; +import java.util.List; + +import static megamek.common.ITechnology.*; +import static megamek.common.SimpleTechLevel.INTRO; +import static megamek.common.SimpleTechLevel.STANDARD; + +/** + * The Procurement class encapsulates the logic for deciding the availability + * of parts based on factors such as era, technologies, and treaties between + * various factions. It is capable of making procurement checks to simulate + * rarity and scarcity of parts in a given time frame. + */ +public class Procurement { + private final int negotiatorSkillRating; + private final int gameYear; + private final int techEra; + private final Faction originFaction; + private final int factionTechCode; + private static final MMLogger logger = MMLogger.create(Procurement.class); + + /** + * Procurement constructor. + * Initializes class instance with negotiator skill rating, game year, and originating faction. + * + * @param negotiatorSkillRating the skill rating of the negotiator. + * @param gameYear the current year of the game. + * @param originFaction the faction from where procurement is initiated. + */ + public Procurement(int negotiatorSkillRating, int gameYear, Faction originFaction) { + this.negotiatorSkillRating = negotiatorSkillRating; + this.gameYear = gameYear; + this.originFaction = originFaction; + + factionTechCode = getFactionTechCode(originFaction); + techEra = getTechEra(gameYear); + } + + /** + * Given a faction, returns the corresponding faction code. + * + * @param faction Faction instance + * @return returns corresponding faction code. + */ + public static int getFactionTechCode(Faction faction) { + int allCodesCount = MM_FACTION_CODES.length; + for (int i = 0; i < allCodesCount; i++) { + if (MM_FACTION_CODES[i].equals(faction.getShortName())) { + return i; + } + } + + logger.info("Unable to retrieve Tech Faction. Using fallback."); + + if (faction.isClan()) { + logger.info("Returning: Clan"); + return F_CLAN; + } else if (faction.isPeriphery()) { + logger.info("Returning: Periphery"); + return F_PER; + } else { + logger.info("Returning: Inner Sphere"); + return F_IS; + } + } + + /** + * Makes procurement checks for a list of parts and returns successful parts. + * + * @param parts List of parts that require procurement checks + * @param useHardExtinction a boolean flag that indicates whether to enforce hard extinctions. + * @param isResupply Flag indicating if procurement is for resupplying parts + * @return List of parts that were successful in the procurement checks + */ + public List makeProcurementChecks(List parts, boolean useHardExtinction, boolean isResupply) { + List successfulParts = new ArrayList<>(); + + for (Part part : parts) { + int targetNumber = getProcurementTargetNumber(part, useHardExtinction, isResupply); + int roll = Compute.d6(2); + if (roll >= targetNumber) { + successfulParts.add(part); + } + } + + return successfulParts; + } + + /** + * Given a part, this method calculates the procurement target number. + * + * @param part The part for which the procurement target number is to be calculated. + * @param useHardExtinction a boolean flag that indicates whether to enforce hard extinctions. + * @param isResupply Flag indicating if procurement is for resupplying parts + * @return The calculated procurement target number + */ + private int getProcurementTargetNumber(Part part, boolean useHardExtinction, boolean isResupply) { + // Get the base target number + int targetNumber; + if (part instanceof AmmoBin) { + targetNumber = getConsumableBaseTargetNumber(part, useHardExtinction); + } else { + targetNumber = getBaseTargetNumber(part, useHardExtinction); + } + + if (targetNumber > 12) { + return targetNumber; + } + + // Get the modifiers + if (part.isClan() && !originFaction.isClan()) { + if (gameYear >= 3050 && gameYear <= 3070) { + targetNumber += 3; + } + } + + targetNumber += getNegotiatorModifier(); + + if (isResupply) { + targetNumber -= 2; + } + + return Math.max(2, targetNumber); + } + + /** + * Returns negotiator modifier based on the negotiator's skill rating. + * + * @return Modifier for the procurement process based on negotiator's skill level + */ + private int getNegotiatorModifier() { + if (negotiatorSkillRating == SkillLevel.NONE.ordinal()) { + return 4; + } + + if (negotiatorSkillRating == SkillLevel.ULTRA_GREEN.ordinal()) { + return 3; + } + + if (negotiatorSkillRating == SkillLevel.VETERAN.ordinal()) { + return -2; + } + + if (negotiatorSkillRating >= SkillLevel.ELITE.ordinal()) { + return -3; + } + + return 0; + } + + /** + * Returns the base target number for the given consumable part. + * + * @param part The part for which the target number should be calculated. + * @param useHardExtinction a boolean flag that indicates whether to enforce hard extinctions. + * @return The calculated base target number. + */ + private int getConsumableBaseTargetNumber(Part part, boolean useHardExtinction) { + int availability = getAvailability(part, useHardExtinction); + + int targetNumber = switch (availability) { + case RATING_A -> 2; + case RATING_B -> 3; + case RATING_C -> 4; + case RATING_D -> 6; + case RATING_E -> 8; + case RATING_F -> 10; + // This value is deliberately impossible on 2d6 + default -> 13; // X or F* + }; + + SimpleTechLevel techLevel = part.getStaticTechLevel(); + + if (techLevel == INTRO || techLevel == STANDARD) { + targetNumber -= 2; + } + + if (techLevel == SimpleTechLevel.ADVANCED) { + targetNumber--; + } + + return Math.max(2, targetNumber); + } + + /** + * Returns the base target number for the given part. + * + * @param part The part for which the target number should be calculated. + * @param useHardExtinction a boolean flag that indicates whether to enforce hard extinctions. + * @return The calculated base target number. + */ + private int getBaseTargetNumber(Part part, boolean useHardExtinction) { + int availability = getAvailability(part, useHardExtinction); + + return switch (availability) { + case RATING_A -> 3; + case RATING_B -> 4; + case RATING_C -> 6; + case RATING_D -> 8; + case RATING_E -> 10; + case RATING_F -> 11; + // This value is deliberately impossible on 2d6 + default -> 13; // X or F* + }; + } + + /** + * Returns the availability rate for the given part. + * + * @param part The part for which the availability is to be calculated. + * @param useHardExtinction a boolean flag that indicates whether to enforce hard extinctions + * @return The calculated availability rate. + */ + private int getAvailability(Part part, boolean useHardExtinction) { + int availability = part.calcYearAvailability(gameYear, originFaction.isClan(), factionTechCode); + + if (part.getTechBase() == TECH_BASE_IS) { + availability = getInnerSphereTechBaseRating(part, availability); + return performTrulyExtinctCheck(part, availability, useHardExtinction); + } + + if (part.getTechBase() == TECH_BASE_CLAN) { + availability = getClanTechBaseRating(part, availability); + return performTrulyExtinctCheck(part, availability, useHardExtinction); + } + + // Tech Base: All + availability = getCommonTechBaseRating(part, availability); + return performTrulyExtinctCheck(part, availability, useHardExtinction); + } + + /** + * Assess the extinction state of a specific part and adjusts its availability + * rating based on whether the part is truly extinct or not. + * + * @param part The part for which to assess the extinction state. + * @param availability The calculated availability rate for the part. + * @param useHardExtinction a boolean flag that indicates whether to enforce hard extinctions. + * @return The revised availability rate based on the performed extinction check. + */ + private int performTrulyExtinctCheck(Part part, int availability, boolean useHardExtinction) { + if (part.isExtinct(gameYear, originFaction.isClan(), factionTechCode)) { + int extinctionYear = part.getExtinctionDate(originFaction.isClan(), factionTechCode); + + if ((gameYear > extinctionYear) && useHardExtinction) { + return RATING_X; + } + + if (gameYear < (extinctionYear + 10)) { + return Compute.d6() > 3 ? availability : RATING_X; + } + } + + return availability; + } + + /** + * Procedure for calculating the final part availability for Clan tech base. + * + * @param part The part for which the availability is to be calculated. + * @param availability The calculated availability rate for the part. + * @return The revised availability rate based on Clan Tech Base rules. + */ + private int getClanTechBaseRating(Part part, int availability) { + if (originFaction.isClan()) { + if (gameYear >= part.getCommonDate()) { + return Math.max(RATING_A, availability - 1); + } + } + + if (techEra < ERA_CLAN) { + return RATING_X; + } + + availability++; + + if (availability > RATING_F) { + return Compute.d6() > 3 ? RATING_F : RATING_X; + } + + return availability; + } + + /** + * Procedure for calculating the final part availability for Inner Sphere tech base. + * + * @param part The part for which the availability is to be calculated. + * @param availability The calculated availability rate for the part. + * @return The revised availability rate based on Inner Sphere Tech Base rules. + */ + private int getInnerSphereTechBaseRating(Part part, int availability) { + if (originFaction.isClan()) { + if (techEra < ERA_CLAN && part.getPrototypeDate(false) >= 2780) { + return ITechnology.RATING_X; + } else { + int extinctionYear = part.getExtinctionDate(true); + + if ((techEra == ERA_SW) + && (gameYear >= extinctionYear)) { + return Compute.d6() > 3 ? RATING_F : RATING_X; + } else { + return availability; + } + } + } + + return getCommonTechBaseRating(part, availability); + } + + /** + * Procedure for calculating the final part availability for common tech base. + * + * @param part The part for which the availability is to be calculated. + * @param availability The calculated availability rate for the part. + * @return The revised availability rate based on Common Tech Base rules. + */ + private int getCommonTechBaseRating(Part part, int availability) { + if (!originFaction.isClan()) { + if (part.getBaseAvailability(techEra) >= RATING_E) { + if (availability == RATING_FSTAR || availability == RATING_X) { + int extinctionYear = part.getExtinctionDate(); + + if ((techEra == ERA_SW) + && (gameYear >= extinctionYear)) { + return Compute.d6() > 3 ? RATING_F : RATING_X; + } else { + return availability; + } + } + } + } + + return availability; + } +} diff --git a/MekHQ/src/mekhq/campaign/market/unitMarket/AbstractUnitMarket.java b/MekHQ/src/mekhq/campaign/market/unitMarket/AbstractUnitMarket.java index 6d4fe6b1ca9..3dfcf193332 100644 --- a/MekHQ/src/mekhq/campaign/market/unitMarket/AbstractUnitMarket.java +++ b/MekHQ/src/mekhq/campaign/market/unitMarket/AbstractUnitMarket.java @@ -81,7 +81,7 @@ public void setOffers(final List offers) { * * @param campaign the campaign to process the Unit Market new day using */ - public abstract void processNewDay(final Campaign campaign); + public abstract void processNewDay(Campaign campaign); // region Generate Offers /** @@ -91,7 +91,7 @@ public void setOffers(final List offers) { * * @param campaign the campaign to generate the unit offers for */ - public abstract void generateUnitOffers(final Campaign campaign); + public abstract void generateUnitOffers(Campaign campaign); /** * This adds a number of unit offers @@ -107,8 +107,8 @@ public void setOffers(final List offers) { * @param quality the quality of the unit to generate * @param priceTarget the target number used to determine the percent */ - public abstract void addOffers(final Campaign campaign, final int number, UnitMarketType market, final int unitType, - @Nullable Faction faction, final int quality, final int priceTarget); + public abstract void addOffers(Campaign campaign, int number, UnitMarketType market, int unitType, + @Nullable Faction faction, int quality, int priceTarget); /** * @param campaign the campaign to use to generate the unit @@ -214,8 +214,8 @@ public String addSingleUnit(final Campaign campaign, final UnitMarketType market * @param faction the faction to generate the weight for * @return the generated weight */ - protected abstract int generateWeight(final Campaign campaign, final int unitType, - final Faction faction); + protected abstract int generateWeight(Campaign campaign, int unitType, + Faction faction); /** * @param campaign the campaign to use to generate the transit duration @@ -244,7 +244,7 @@ protected void writeRefreshReport(final Campaign campaign) { * * @param campaign the campaign to use in determining the offers to remove */ - protected abstract void removeUnitOffers(final Campaign campaign); + protected abstract void removeUnitOffers(Campaign campaign); // endregion Offer Removal // endregion Process New Day diff --git a/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java b/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java index 2821feaafc5..327314c9e41 100644 --- a/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java +++ b/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java @@ -243,21 +243,20 @@ public void addOffers(final Campaign campaign, final int num, UnitMarketType mar if ((specialUnitChance == 1) || (Compute.randomInt(specialUnitChance) == 0)) { // this will need to be incremented by 1, // whenever we add additional unit types to this special handler - int roll = Compute.randomInt(2); + int roll = Compute.randomInt(6); // while this gives even chances for each role, // it gives greater control to the user // to define how often they want to see special units. - // really, this is just a band-aid fix, and ideally we wouldn't be filtering out these units in the first place + // really, this is just a Band-Aid fix, and ideally we wouldn't be filtering out these units in the first place switch (roll) { - case 0: - missionRoles.add(MissionRole.SUPPORT); - break; - case 1: - missionRoles.add(MissionRole.ARTILLERY); - break; - default: - throw new IllegalStateException("Unexpected value in mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java/addOffers: " + roll); + case 0 -> missionRoles.add(MissionRole.CIVILIAN); + case 1 -> missionRoles.add(MissionRole.SUPPORT); + case 2 -> missionRoles.add(MissionRole.CARGO); + case 3, 4, 5 -> missionRoles.add(MissionRole.ARTILLERY); + default -> throw new IllegalStateException( + "Unexpected value in mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java/addOffers: " + + roll); } } } diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 6bf29b5b2bb..b987425ee56 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-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -27,23 +27,26 @@ import megamek.client.ratgenerator.RATGenerator; import megamek.client.ratgenerator.UnitTable; import megamek.client.ui.swing.util.PlayerColour; -import megamek.common.Compute; import megamek.common.Entity; import megamek.common.TargetRoll; 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.logging.MMLogger; import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; import mekhq.campaign.event.MissionChangedEvent; import mekhq.campaign.finances.Money; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.force.Force; import mekhq.campaign.market.enums.UnitMarketType; import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.enums.AtBContractType; import mekhq.campaign.mission.enums.AtBMoraleLevel; +import mekhq.campaign.mission.enums.ScenarioStatus; import mekhq.campaign.personnel.Bloodname; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.backgrounds.BackgroundsController; @@ -52,6 +55,7 @@ import mekhq.campaign.stratcon.StratconCampaignState; import mekhq.campaign.stratcon.StratconContractDefinition; import mekhq.campaign.stratcon.StratconContractInitializer; +import mekhq.campaign.stratcon.StratconTrackState; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Faction; import mekhq.campaign.universe.Factions; @@ -74,28 +78,35 @@ import java.util.List; import java.util.*; -import static java.lang.Math.ceil; -import static java.lang.Math.max; -import static java.lang.Math.round; +import static java.lang.Math.*; import static megamek.client.ratgenerator.ModelRecord.NETWORK_NONE; import static megamek.client.ratgenerator.UnitTable.findTable; +import static megamek.codeUtilities.ObjectUtility.getRandomItem; import static megamek.common.Compute.d6; +import static megamek.common.Compute.randomInt; import static megamek.common.UnitType.MEK; import static megamek.common.enums.SkillLevel.ELITE; import static megamek.common.enums.SkillLevel.REGULAR; import static megamek.common.enums.SkillLevel.parseFromInteger; import static megamek.common.enums.SkillLevel.parseFromString; +import static mekhq.campaign.force.CombatTeam.getStandardForceSize; import static mekhq.campaign.force.FormationLevel.BATTALION; import static mekhq.campaign.force.FormationLevel.COMPANY; -import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; import static mekhq.campaign.mission.AtBDynamicScenarioFactory.getEntity; import static mekhq.campaign.mission.BotForceRandomizer.UNIT_WEIGHT_UNSPECIFIED; +import static mekhq.campaign.mission.enums.AtBMoraleLevel.ADVANCING; +import static mekhq.campaign.mission.enums.AtBMoraleLevel.DOMINATING; +import static mekhq.campaign.mission.enums.AtBMoraleLevel.OVERWHELMING; +import static mekhq.campaign.mission.enums.AtBMoraleLevel.STALEMATE; import static mekhq.campaign.rating.IUnitRating.*; +import static mekhq.campaign.stratcon.StratconContractDefinition.getContractDefinition; import static mekhq.campaign.universe.Factions.getFactionLogo; import static mekhq.campaign.universe.fameAndInfamy.BatchallFactions.BATCHALL_FACTIONS; import static mekhq.gui.dialog.HireBulkPersonnelDialog.overrideSkills; import static mekhq.gui.dialog.HireBulkPersonnelDialog.reRollAdvantages; import static mekhq.gui.dialog.HireBulkPersonnelDialog.reRollLoyalty; +import static mekhq.utilities.EntityUtilities.getEntityFromUnitId; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; /** * Contract class for use with Against the Bot rules @@ -144,7 +155,7 @@ public class AtBContract extends Contract { protected int extensionLength; - protected int requiredLances; + protected int requiredCombatTeams; protected AtBMoraleLevel moraleLevel; protected LocalDate routEnd; protected int partsAvailabilityLevel; @@ -156,7 +167,7 @@ public class AtBContract extends Contract { protected int contractScoreArbitraryModifier; protected int moraleMod = 0; - protected int numBonusParts; + private Money routedPayout = null; /* lasts for a month, then removed at next events roll */ protected boolean priorLogisticsFailure; @@ -224,9 +235,8 @@ public AtBContract(String name) { sharesPct = 0; batchallAccepted = true; - setMoraleLevel(AtBMoraleLevel.STALEMATE); + setMoraleLevel(STALEMATE); routEnd = null; - numBonusParts = 0; priorLogisticsFailure = false; specialEventScenarioDate = null; battleTypeMod = 0; @@ -408,37 +418,57 @@ public static int calculateRequiredLances(Campaign campaign) { return max(getEffectiveNumUnits(campaign) / formationSize, 1); } + /** + * Calculates the effective number of units available in the given campaign based on unit types and roles. + * + *

    + * This method iterates through all combat teams in the specified campaign, ignoring combat teams with + * the auxiliary role. For each valid combat team, it retrieves the associated force and evaluates + * all units within that force. The unit contribution to the total is determined based on its type: + *

      + *
    • TANK, VTOL, NAVAL, CONV_FIGHTER, AEROSPACEFIGHTER: Adds 1 for non-clan factions, + * and 0.5 for clan factions.
    • + *
    • PROTOMEK: Adds 0.2 to the total.
    • + *
    • BATTLE_ARMOR, INFANTRY: Adds 0 (excluded from the total).
    • + *
    • Other types: Adds 1 to the total.
    • + *
    + * + *

    + * Units that aren’t associated with a valid combat team or can’t be fetched due to missing + * data are ignored. The final result is returned as an integer by flooring the calculated total. + *

    + * + * @param campaign the campaign containing the combat teams and units to evaluate + * @return the effective number of units as an integer + */ public static int getEffectiveNumUnits(Campaign campaign) { double numUnits = 0; - for (UUID uuid : campaign.getForces().getAllUnits(true)) { - if (null == campaign.getUnit(uuid)) { + for (CombatTeam combatTeam : campaign.getAllCombatTeams()) { + Force force = combatTeam.getForce(campaign); + + if (force == null) { continue; } - switch (campaign.getUnit(uuid).getEntity().getUnitType()) { - case MEK: - numUnits += 1; - break; - case UnitType.TANK: - case UnitType.VTOL: - case UnitType.NAVAL: - numUnits += campaign.getFaction().isClan() ? 0.5 : 1; - break; - case UnitType.CONV_FIGHTER: - case UnitType.AEROSPACEFIGHTER: - if (campaign.getCampaignOptions().isUseAero()) { - numUnits += campaign.getFaction().isClan() ? 0.5 : 1; - } - break; - case UnitType.PROTOMEK: - numUnits += 0.2; - break; - case UnitType.BATTLE_ARMOR: - case UnitType.INFANTRY: - default: - /* don't count */ + + for (UUID unitId : force.getAllUnits(true)) { + Entity entity = getEntityFromUnitId(campaign, unitId); + + if (entity == null) { + continue; + } + + numUnits += switch (entity.getUnitType()) { + case UnitType.TANK, UnitType.VTOL, UnitType.NAVAL, UnitType.CONV_FIGHTER, + UnitType.AEROSPACEFIGHTER + -> campaign.getFaction().isClan() ? 0.5 : 1; + case UnitType.PROTOMEK -> 0.2; + case UnitType.BATTLE_ARMOR, UnitType.INFANTRY -> 0; + default -> 1; // All other unit types + }; } } - return (int) numUnits; + + return (int) floor(numUnits); } public static boolean isMinorPower(final String factionCode) { @@ -457,18 +487,52 @@ public static boolean isMinorPower(final String factionCode) { public void checkMorale(Campaign campaign, LocalDate today) { // Check whether enemy forces have been reinforced, and whether any current rout continues // beyond its expected date - boolean routContinue = Compute.randomInt(4) == 0; + boolean routContinue = randomInt(4) == 0; // If there is a rout end date, and it's past today, update morale and enemy state accordingly - if (routEnd != null && !routContinue) { + if (routEnd != null) { + if (routContinue) { + return; + } + if (today.isAfter(routEnd)) { - setMoraleLevel(AtBMoraleLevel.STALEMATE); + int roll = randomInt(8); + + // We use variable morale levels to spike morale up to a value above Stalemate. + // This works with the regenerated Scenario Odds to crease very high intensity + // spikes in otherwise low-key Garrison-type contracts. + AtBMoraleLevel newMoraleLevel = switch (roll) { + case 2,3,4,5 -> ADVANCING; + case 6,7 -> DOMINATING; + case 8 -> OVERWHELMING; + default -> STALEMATE; // 0-1 + }; + + // If we have a StratCon enabled contract, regenerate Scenario Odds + if (stratconCampaignState != null) { + StratconContractDefinition contractDefinition = getContractDefinition(getContractType()); + + if (contractDefinition != null) { + List definedScenarioOdds = contractDefinition.getScenarioOdds(); + + int scenarioOddsMultiplier = randomInt(20) == 0 ? 2 : 1; + + for (StratconTrackState trackState : stratconCampaignState.getTracks()) { + int baseScenarioOdds = getRandomItem(definedScenarioOdds); + + trackState.setScenarioOdds(baseScenarioOdds * scenarioOddsMultiplier); + } + } + } + + moraleLevel = newMoraleLevel; routEnd = null; - updateEnemy(campaign, today); // mix it up a little - } else { - setMoraleLevel(AtBMoraleLevel.ROUTED); + if (contractType.isGarrisonDuty()) { + updateEnemy(campaign, today); // mix it up a little + } } + return; } @@ -477,30 +541,6 @@ public void checkMorale(Campaign campaign, LocalDate today) { logger.info(String.format("Current Morale: %s (%s)", getMoraleLevel().toString(), getMoraleLevel().ordinal())); - // Confidence: - int enemySkillRating = getEnemySkill().getAdjustedValue() - 2; - int allySkillRating = getAllySkill().getAdjustedValue() - 2; - - if (getCommandRights().isIndependent()) { - allySkillRating = (campaign.getCampaignOptions().getUnitRatingMethod().isFMMR() ? getAllySkill() - : campaign.getReputation().getAverageSkillLevel()).getAdjustedValue(); - allySkillRating -= 2; - } - - final LocalDate THE_GREAT_REFUSAL = LocalDate.of(3060, 4, 12); - - if (campaign.getLocalDate().isBefore(THE_GREAT_REFUSAL)) { - if (getEnemy().isClan() && !getEmployerFaction().isClan()) { - enemySkillRating++; - } else if (!getEnemy().isClan() && getEmployerFaction().isClan()) { - allySkillRating++; - } - } - - int confidence = enemySkillRating - allySkillRating; - targetNumber.addModifier(confidence, "confidence"); - logger.info(String.format("Confidence: %s", confidence >= 0 ? "+" + confidence : confidence)); - // Reliability: int reliability = getEnemyQuality(); @@ -511,29 +551,8 @@ public void checkMorale(Campaign campaign, LocalDate today) { reliability = switch (reliability) { case DRAGOON_F -> -1; - case DRAGOON_D -> { - if (Compute.randomInt(1) == 0) { - yield -1; - } else { - yield 0; - } - } - case DRAGOON_C -> 0; - case DRAGOON_B -> { - if (Compute.randomInt(1) == 0) { - yield 0; - } else { - yield +1; - } - } - case DRAGOON_A -> +1; - default -> { // DRAGOON_ASTAR - if (Compute.randomInt(1) == 0) { - yield +1; - } else { - yield +2; - } - } + case DRAGOON_A, DRAGOON_ASTAR -> +1; + default -> 0; // DRAGOON_D, DRAGOON_C, DRAGOON_B }; if (enemy.isRebel() @@ -565,17 +584,19 @@ public void checkMorale(Campaign campaign, LocalDate today) { continue; } - if (scenario.getStatus().isOverallVictory()) { + ScenarioStatus scenarioStatus = scenario.getStatus(); + + if (scenarioStatus.isOverallVictory()) { victories++; - } else if (scenario.getStatus().isOverallDefeat()) { + } else if (scenarioStatus.isOverallDefeat()) { defeats++; } - if (scenario.getStatus().isDecisiveVictory()) { + if (scenarioStatus.isDecisiveVictory()) { victories++; - } else if (scenario.getStatus().isDecisiveDefeat()) { + } else if (scenarioStatus.isDecisiveDefeat() || scenarioStatus.isRefusedEngagement()) { defeats++; - } else if (scenario.getStatus().isPyrrhicVictory()) { + } else if (scenarioStatus.isPyrrhicVictory()) { victories--; } } @@ -584,15 +605,15 @@ public void checkMorale(Campaign campaign, LocalDate today) { if (victories > defeats) { if (victories >= (defeats * 2)) { - performanceModifier -= 4; - } else { performanceModifier -= 2; + } else { + performanceModifier -= 1; } } else if (defeats > victories) { if (defeats >= (victories * 2)) { - performanceModifier += 4; - } else { performanceModifier += 2; + } else { + performanceModifier += 1; } } @@ -608,29 +629,25 @@ public void checkMorale(Campaign campaign, LocalDate today) { // Morale level determination based on roll value final AtBMoraleLevel[] moraleLevels = AtBMoraleLevel.values(); - if (roll < 2) { - setMoraleLevel(moraleLevels[max(getMoraleLevel().ordinal() - 2, 0)]); - logger.info("Result: Morale Level -2"); - } else if (roll < 5) { - setMoraleLevel(moraleLevels[max(getMoraleLevel().ordinal() - 1, 0)]); + if (roll < 5) { + moraleLevel = moraleLevels[max(getMoraleLevel().ordinal() - 1, 0)]; logger.info("Result: Morale Level -1"); - } else if ((roll > 12)) { - setMoraleLevel(moraleLevels[Math.min(getMoraleLevel().ordinal() + 2, moraleLevels.length - 1)]); - logger.info("Result: Morale Level +1"); } else if ((roll > 9)) { - setMoraleLevel(moraleLevels[Math.min(getMoraleLevel().ordinal() + 1, moraleLevels.length - 1)]); - logger.info("Result: Morale Level +2"); + moraleLevel = moraleLevels[min(getMoraleLevel().ordinal() + 1, moraleLevels.length - 1)]; + logger.info("Result: Morale Level +1"); } else { logger.info("Result: Morale Unchanged"); } // Additional morale updates if morale level is set to 'Routed' and contract type is a garrison type - if (getMoraleLevel().isRouted()) { - if (getContractType().isGarrisonType()) { + if (moraleLevel.isRouted()) { + if (contractType.isGarrisonType() || contractType.isReliefDuty()) { routEnd = today.plusMonths(max(1, d6() - 3)).minusDays(1); } else { - campaign.addReport("With the enemy routed, any remaining objectives have been successfully completed." + - " The contract will conclude tomorrow."); + campaign.addReport("With the enemy routed, any remaining objectives have been" + + " successfully completed. The contract will conclude tomorrow."); + int remainingMonths = getMonthsLeft(campaign.getLocalDate().plusDays(1)); + routedPayout = getMonthlyPayOut().multipliedBy(remainingMonths); setEndDate(today.plusDays(1)); } } @@ -696,7 +713,7 @@ public int getRepairLocation(final int unitRating) { repairLocation++; } - return Math.min(repairLocation, Unit.SITE_FACTORY_CONDITIONS); + return min(repairLocation, Unit.SITE_FACTORY_CONDITIONS); } public void addMoraleMod(int mod) { @@ -707,14 +724,14 @@ public int getScore() { int score = employerMinorBreaches - playerMinorBreaches; int battles = 0; boolean earlySuccess = false; - for (Scenario s : getCompletedScenarios()) { + for (Scenario scenario : getCompletedScenarios()) { // Special Scenarios get no points for victory and only -1 for defeat. - if ((s instanceof AtBScenario) && ((AtBScenario) s).isSpecialScenario()) { - if (s.getStatus().isOverallDefeat()) { + if ((scenario instanceof AtBScenario) && ((AtBScenario) scenario).isSpecialScenario()) { + if (scenario.getStatus().isOverallDefeat() || scenario.getStatus().isRefusedEngagement()) { score--; } } else { - switch (s.getStatus()) { + switch (scenario.getStatus()) { case DECISIVE_VICTORY: case VICTORY: case MARGINAL_VICTORY: @@ -736,9 +753,9 @@ public int getScore() { } } - if ((s instanceof AtBScenario) - && (((AtBScenario) s).getScenarioType() == AtBScenario.BASEATTACK) - && ((AtBScenario) s).isAttacker() && s.getStatus().isOverallVictory()) { + if ((scenario instanceof AtBScenario) + && (((AtBScenario) scenario).getScenarioType() == AtBScenario.BASEATTACK) + && ((AtBScenario) scenario).isAttacker() && scenario.getStatus().isOverallVictory()) { earlySuccess = true; } else if (getMoraleLevel().isRouted() && !getContractType().isGarrisonType()) { earlySuccess = true; @@ -760,46 +777,82 @@ public int getContractScoreArbitraryModifier() { return contractScoreArbitraryModifier; } - public void doBonusRoll(Campaign campaign) { + /** + * Performs a bonus roll to determine and execute a random campaign bonus. + * The roll is simulated using 1d6, and the outcome triggers different bonus + * effects based on the roll value. The effects may involve recruiting + * dependents, adding new units, or other benefits as determined by the + * campaign options and roll outcome. + * + * @param campaign the current {@link Campaign} instance. + * @param isPostScenario a {@code boolean} indicating if this roll occurs post-scenario + * (used to determine specific behaviors for roll = 3). + * + * @return {@code true} if specific post-scenario behavior is triggered (roll = 3), + * otherwise {@code false}. + * + * @throws IllegalStateException if an unexpected roll value is encountered. + */ + public boolean doBonusRoll(Campaign campaign, boolean isPostScenario) { + final CampaignOptions campaignOptions = campaign.getCampaignOptions(); + int number; int roll = d6(); - switch (roll) { - case 1: /* 1d6 dependents */ - if (campaign.getCampaignOptions().isUseRandomDependentAddition()) { + return switch (roll) { + case 1 -> { /* 1d6 dependents */ + if (campaignOptions.isUseRandomDependentAddition()) { number = d6(); campaign.addReport("Bonus: " + number + " dependent" + ((number > 1) ? "s" : "")); for (int i = 0; i < number; i++) { - Person p = campaign.newDependent(Gender.RANDOMIZE); - campaign.recruitPerson(p); + Person person = campaign.newDependent(Gender.RANDOMIZE); + campaign.recruitPerson(person); } + } else { + campaign.addReport("Bonus: Ronin"); + recruitRonin(campaign); } - break; - case 2: + yield false; + } + case 2 -> { campaign.addReport("Bonus: Ronin"); recruitRonin(campaign); - break; - case 3: - numBonusParts++; - campaign.addReport("Bonus: Part"); - break; - case 4: + yield false; + } + case 3 -> { // Resupply + if (campaignOptions.isUseAtB() && !campaignOptions.isUseStratCon()) { + campaign.addReport("Bonus: Ronin"); + recruitRonin(campaign); + yield false; + } else { + if (isPostScenario) { + yield true; + } else { + campaign.addReport("Bonus: Support Point"); + stratconCampaignState.setSupportPoints(1); + yield false; + } + } + } + case 4 -> { campaign.addReport("Bonus: Unit"); addBonusUnit(campaign, UnitType.TANK); - break; - case 5: + yield false; + } + case 5 -> { campaign.addReport("Bonus: Unit"); addBonusUnit(campaign, UnitType.AEROSPACEFIGHTER); - break; - case 6: + yield false; + } + case 6 -> { campaign.addReport("Bonus: Unit"); addBonusUnit(campaign, MEK); - break; - default: - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/mission/AtBContract.java/doBonusRoll: " + roll); - } + yield false; + } + default -> throw new IllegalStateException( + "Unexpected value in mekhq/campaign/mission/AtBContract.java/doBonusRoll: " + roll); + }; } /** @@ -827,17 +880,25 @@ private static void recruitRonin(Campaign campaign) { * @param unitType the type of unit for the bonus */ private void addBonusUnit(Campaign campaign, int unitType) { - String faction = employerCode; - int quality = allyQuality; - - if (Compute.randomInt(2) > 0) { - faction = enemyCode; - quality = enemyQuality; - } + // Determine faction and quality + String faction = (randomInt(2) > 0) ? enemyCode : employerCode; + int quality = (faction.equals(enemyCode)) ? enemyQuality : allyQuality; + // Try to generate the new unit Entity newUnit = getEntity(faction, REGULAR, quality, unitType, UNIT_WEIGHT_UNSPECIFIED, null, campaign); - campaign.addNewUnit(newUnit, false, 0); + + // If failed, try again with the campaign's faction + if (newUnit == null) { + String defaultFaction = campaign.getFaction().getShortName(); + newUnit = getEntity(defaultFaction, REGULAR, quality, unitType, + UNIT_WEIGHT_UNSPECIFIED, null, campaign); + } + + // Add the unit to the campaign if successfully generated + if (newUnit != null) { + campaign.addNewUnit(newUnit, false, 0); + } } public boolean isSubcontract() { @@ -868,38 +929,38 @@ public void setAttacker(boolean isAttacker) { this.isAttacker = isAttacker; } - public void checkEvents(Campaign c) { - if (c.getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { + public void checkEvents(Campaign campaign) { + if (campaign.getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { nextWeekBattleTypeMod = 0; } - if (c.getLocalDate().getDayOfMonth() == 1) { + if (campaign.getLocalDate().getDayOfMonth() == 1) { if (priorLogisticsFailure) { partsAvailabilityLevel++; priorLogisticsFailure = false; } - switch (getContractType().generateEventType()) { + switch (getContractType().generateEventType(campaign)) { case EVT_BONUSROLL: - c.addReport("Special Event: "); - doBonusRoll(c); + campaign.addReport("Special Event: "); + doBonusRoll(campaign, false); break; case EVT_SPECIAL_SCENARIO: - c.addReport("Special Event: Special scenario this month"); - specialEventScenarioDate = getRandomDayOfMonth(c.getLocalDate()); - specialEventScenarioType = getContractType().generateSpecialScenarioType(c); + campaign.addReport("Special Event: Special scenario this month"); + specialEventScenarioDate = getRandomDayOfMonth(campaign.getLocalDate()); + specialEventScenarioType = getContractType().generateSpecialScenarioType(campaign); break; case EVT_CIVILDISTURBANCE: - c.addReport("Special Event: Civil disturbance
    Next enemy morale roll gets +1 modifier"); + campaign.addReport("Special Event: Civil disturbance
    Next enemy morale roll gets +1 modifier"); moraleMod++; break; case EVT_SPORADICUPRISINGS: - c.addReport("Special Event: Sporadic uprisings
    +2 to next enemy morale roll"); + campaign.addReport("Special Event: Sporadic uprisings
    +2 to next enemy morale roll"); moraleMod += 2; break; case EVT_REBELLION: - c.addReport("Special Event: Rebellion
    +2 to next enemy morale roll"); - specialEventScenarioDate = getRandomDayOfMonth(c.getLocalDate()); + campaign.addReport("Special Event: Rebellion
    +2 to next enemy morale roll"); + specialEventScenarioDate = getRandomDayOfMonth(campaign.getLocalDate()); specialEventScenarioType = AtBScenario.CIVILIANRIOT; break; case EVT_BETRAYAL: @@ -928,22 +989,22 @@ public void checkEvents(Campaign c) { break; } employerMinorBreaches++; - c.addReport(text); + campaign.addReport(text); break; case EVT_TREACHERY: - c.addReport( + campaign.addReport( "Special Event: Treachery
    Bad information from employer. Next Enemy Morale roll gets +1. Employer minor breach."); moraleMod++; employerMinorBreaches++; break; case EVT_LOGISTICSFAILURE: - c.addReport( + campaign.addReport( "Special Event: Logistics Failure
    Parts availability for the next month are one level lower."); partsAvailabilityLevel--; priorLogisticsFailure = true; break; case EVT_REINFORCEMENTS: - c.addReport("Special Event: Reinforcements
    The next Enemy Morale roll gets a -1."); + campaign.addReport("Special Event: Reinforcements
    The next Enemy Morale roll gets a -1."); moraleMod--; break; case EVT_SPECIALEVENTS: @@ -955,8 +1016,17 @@ public void checkEvents(Campaign c) { break; case 2: text += "Internal Dissension"; - specialEventScenarioDate = getRandomDayOfMonth(c.getLocalDate()); - specialEventScenarioType = AtBScenario.AMBUSH; + if (!campaign.getCampaignOptions().isUseStratCon()) { + specialEventScenarioDate = getRandomDayOfMonth(campaign.getLocalDate()); + specialEventScenarioType = AtBScenario.AMBUSH; + } else { + StratconCampaignState campaignState = getStratconCampaignState(); + + if (campaignState != null) { + text += ": -1 Support Point"; + campaignState.addSupportPoints(-1); + } + } break; case 3: text += "ComStar Interdict: Base availability level decreases one level for the rest of the contract."; @@ -971,7 +1041,7 @@ public void checkEvents(Campaign c) { partsAvailabilityLevel++; break; case 6: - final String unitName = c.getUnitMarket().addSingleUnit(c, + final String unitName = campaign.getUnitMarket().addSingleUnit(campaign, UnitMarketType.EMPLOYER, MEK, getEmployerFaction(), DRAGOON_F, 50); if (unitName != null) { @@ -981,11 +1051,11 @@ UnitMarketType.EMPLOYER, MEK, getEmployerFaction(), } break; } - c.addReport(text); + campaign.addReport(text); break; case EVT_BIGBATTLE: - c.addReport("Special Event: Big battle this month"); - specialEventScenarioDate = getRandomDayOfMonth(c.getLocalDate()); + campaign.addReport("Special Event: Big battle this month"); + specialEventScenarioDate = getRandomDayOfMonth(campaign.getLocalDate()); specialEventScenarioType = getContractType().generateBigBattleType(); break; } @@ -999,19 +1069,19 @@ UnitMarketType.EMPLOYER, MEK, getEmployerFaction(), * or big battle event is rolled. */ if ((specialEventScenarioDate != null) - && !specialEventScenarioDate.isBefore(c.getLocalDate())) { - LocalDate nextMonday = c.getLocalDate().plusDays(8 - c.getLocalDate().getDayOfWeek().getValue()); + && !specialEventScenarioDate.isBefore(campaign.getLocalDate())) { + LocalDate nextMonday = campaign.getLocalDate().plusDays(8 - campaign.getLocalDate().getDayOfWeek().getValue()); if (specialEventScenarioDate.isBefore(nextMonday)) { - AtBScenario s = AtBScenarioFactory.createScenario(c, null, + AtBScenario s = AtBScenarioFactory.createScenario(campaign, null, specialEventScenarioType, false, specialEventScenarioDate); - c.addScenario(s, this); - if (c.getCampaignOptions().isUsePlanetaryConditions()) { - s.setPlanetaryConditions(this, c); + campaign.addScenario(s, this); + if (campaign.getCampaignOptions().isUsePlanetaryConditions()) { + s.setPlanetaryConditions(this, campaign); } - s.setForces(c); + s.setForces(campaign); specialEventScenarioDate = null; } } @@ -1019,7 +1089,7 @@ UnitMarketType.EMPLOYER, MEK, getEmployerFaction(), public LocalDate getRandomDayOfMonth(LocalDate today) { return LocalDate.of(today.getYear(), today.getMonth(), - Compute.randomInt(today.getMonth().length(today.isLeapYear())) + 1); + randomInt(today.getMonth().length(today.isLeapYear())) + 1); } public boolean contractExtended(final Campaign campaign) { @@ -1034,7 +1104,7 @@ public boolean contractExtended(final Campaign campaign) { } final int extension; - final int roll = d6(); + int roll = d6(); if (roll == 1) { extension = max(1, getLength() / 2); } else if (roll == 2) { @@ -1048,6 +1118,22 @@ public boolean contractExtended(final Campaign campaign) { warName, extension, ((extension == 1) ? " month" : " months"))); setEndDate(getEndingDate().plusMonths(extension)); extensionLength += extension; + + // We spike morale to create a jump in contract difficulty + // - essentially the reason why the employer is using the emergency clause. + int moraleOrdinal = moraleLevel.ordinal(); + roll = d6(2) / 2; + + // we need to reset routEnd to null otherwise we'll attempt to rally + if (routEnd != null) { + routEnd = null; + } + + moraleOrdinal = min(moraleOrdinal + roll, OVERWHELMING.ordinal()); + moraleLevel = AtBMoraleLevel.values()[moraleOrdinal]; + + campaign.addReport(moraleLevel.getToolTipText()); + MekHQ.triggerEvent(new MissionChangedEvent(this)); return true; } @@ -1103,12 +1189,14 @@ protected int writeToXMLBegin(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "enemyCamoFileName", getEnemyCamouflage().getFilename()); } MHQXMLUtility.writeSimpleXMLTag(pw, indent, "enemyColour", getEnemyColour().name()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "requiredLances", getRequiredLances()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "requiredCombatTeams", getRequiredCombatTeams()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "moraleLevel", getMoraleLevel().name()); if (routEnd != null) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "routEnd", routEnd); } - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "numBonusParts", getNumBonusParts()); + if (routedPayout != null) { + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "routedPayout", routedPayout); + } MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partsAvailabilityLevel", getPartsAvailabilityLevel()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "extensionLength", extensionLength); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "sharesPct", sharesPct); @@ -1179,12 +1267,18 @@ public void loadFieldsFromXmlNode(Node wn) throws ParseException { getEnemyCamouflage().setFilename(wn2.getTextContent().trim()); } else if (wn2.getTextContent().equalsIgnoreCase("enemyColour")) { setEnemyColour(PlayerColour.parseFromString(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("requiredLances")) { - requiredLances = Integer.parseInt(wn2.getTextContent()); + // <50.03 compatibility handler + } else if (wn2.getNodeName().equalsIgnoreCase("requiredLances") + || wn2.getNodeName().equalsIgnoreCase("requiredCombatTeams")) { + requiredCombatTeams = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("moraleLevel")) { setMoraleLevel(AtBMoraleLevel.parseFromString(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("routEnd")) { routEnd = MHQXMLUtility.parseDate(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("routedPayout")) { + String cleanValue = wn2.getTextContent().trim().replaceAll("[^0-9.]", ""); + double value = Double.parseDouble(cleanValue); + routedPayout = Money.of(value); } else if (wn2.getNodeName().equalsIgnoreCase("partsAvailabilityLevel")) { partsAvailabilityLevel = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("extensionLength")) { @@ -1193,8 +1287,6 @@ public void loadFieldsFromXmlNode(Node wn) throws ParseException { sharesPct = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("batchallAccepted")) { batchallAccepted = Boolean.parseBoolean(wn2.getTextContent()); - } else if (wn2.getNodeName().equalsIgnoreCase("numBonusParts")) { - numBonusParts = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("playerMinorBreaches")) { playerMinorBreaches = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("employerMinorBreaches")) { @@ -1402,12 +1494,12 @@ public void setEnemyColour(PlayerColour enemyColour) { this.enemyColour = Objects.requireNonNull(enemyColour); } - public int getRequiredLances() { - return requiredLances; + public int getRequiredCombatTeams() { + return requiredCombatTeams; } - public void setRequiredLances(int required) { - requiredLances = required; + public void setRequiredCombatTeams(int required) { + requiredCombatTeams = required; } public int getPartsAvailabilityLevel() { @@ -1480,18 +1572,6 @@ public void setContractScoreArbitraryModifier(int newModifier) { contractScoreArbitraryModifier = newModifier; } - public int getNumBonusParts() { - return numBonusParts; - } - - public void addBonusParts(int num) { - numBonusParts += num; - } - - public void useBonusPart() { - numBonusParts--; - } - public int getBattleTypeMod() { return battleTypeMod + nextWeekBattleTypeMod; } @@ -1508,7 +1588,7 @@ public void setStratconCampaignState(StratconCampaignState state) { public void acceptContract(Campaign campaign) { if (campaign.getCampaignOptions().isUseStratCon()) { StratconContractInitializer.initializeCampaignState(this, campaign, - StratconContractDefinition.getContractDefinition(getContractType())); + getContractDefinition(getContractType())); } } @@ -1584,7 +1664,7 @@ public AtBContract(Contract c, Campaign campaign) { enemyCode = "REB"; } - requiredLances = calculateRequiredLances(campaign); + requiredCombatTeams = calculateRequiredLances(campaign); setPartsAvailabilityLevel(getContractType().calculatePartsAvailabilityLevel()); @@ -1840,8 +1920,10 @@ public JPanel getContractDifficultySkulls(Campaign campaign) { JPanel panel = new JPanel(new FlowLayout()); // Load and scale the images - ImageIcon skullFull = new ImageIcon("data/images/misc/challenge_estimate_full.png"); - ImageIcon skullHalf = new ImageIcon("data/images/misc/challenge_estimate_half.png"); + ImageIcon skullFull = scaleImageIconToWidth( + new ImageIcon("data/images/misc/challenge_estimate_full.png"), 50); + ImageIcon skullHalf = scaleImageIconToWidth( + new ImageIcon("data/images/misc/challenge_estimate_half.png"), 50); int iterations = difficulty; @@ -1849,7 +1931,7 @@ public JPanel getContractDifficultySkulls(Campaign campaign) { iterations = 5; } - if (iterations % 2 == 1) { + if (iterations % 2 != 0) { iterations--; iterations /= 2; @@ -1870,10 +1952,36 @@ public JPanel getContractDifficultySkulls(Campaign campaign) { } /** - * Calculates the contract difficulty based on the given campaign and parameters. + * Calculates the difficulty of a contract based on the relative power of enemy forces, + * player forces, and any allied forces involved in the campaign. + * + *

    The method evaluates the enemy's estimated power against the player's strengths + * and considers allied contributions depending on the assigned command rights. + * The result is a difficulty level mapped between 1 and 10, where higher values + * represent more challenging contracts.

    + * + * @param campaign The {@link Campaign} object representing the current game state. + * Used to extract information about the player's forces, enemy forces, + * and allied forces. + * + * @return An integer representing the difficulty of the contract: + *
      + *
    • 1 = very easy
    • + *
    • 10 = extremely difficult
    • + *
    + *

    + * WARNING: Returns `-99` (defined as `ERROR`) if the enemy's power cannot be calculated. + *

    + *

    Mapped Result Explanation:

    + * The method divides the absolute percentage difference between enemy and player forces by 20 + * (rounding up), then adjusts the difficulty accordingly: + *
      + *
    • If the player's forces are stronger, the difficulty is adjusted downward from a baseline of 5.
    • + *
    • If the enemy's forces are stronger, the difficulty is adjusted upward from a baseline of 5.
    • + *
    • If an error is encountered, the difficulty is returned as -99
    • + *
    + * The result is clamped to fit between the valid range of 1 and 10. Or -99 if an error is encounterd. * - * @param campaign The campaign object containing the necessary data. - * @return The contract difficulty as an integer value. */ public int calculateContractDifficulty(Campaign campaign) { final int ERROR = -99; @@ -1928,7 +2036,7 @@ public int calculateContractDifficulty(Campaign campaign) { mappedValue = 5 + mappedValue; } - return Math.min(max(mappedValue, 1), 10); + return min(max(mappedValue, 1), 10); } /** @@ -2145,4 +2253,8 @@ public int getTransportRoll() { public void setTransportRoll(int roll) { transportRoll = roll; } + + public @Nullable Money getRoutedPayout() { + return routedPayout; + } } diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenario.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenario.java index 32be95056e3..19f71e8557a 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenario.java @@ -24,8 +24,8 @@ import megamek.common.enums.SkillLevel; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.force.Force; -import mekhq.campaign.force.StrategicFormation; import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; import mekhq.campaign.mission.ScenarioForceTemplate.ForceGenerationMethod; import mekhq.campaign.mission.atb.AtBScenarioModifier; @@ -393,11 +393,11 @@ public Person getLanceCommander(Campaign campaign) { return null; // if we don't have forces, just a bunch of units, then get the highest-ranked? } - StrategicFormation strategicFormation = campaign.getStrategicFormationsTable().get(getForceIDs().get(0)); + CombatTeam combatTeam = campaign.getCombatTeamsTable().get(getForceIDs().get(0)); - if (strategicFormation != null) { - strategicFormation.refreshCommander(campaign); - return strategicFormation.getCommander(campaign); + if (combatTeam != null) { + combatTeam.refreshCommander(campaign); + return combatTeam.getCommander(campaign); } else { return null; } diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java index 20f56264b95..1765804806b 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2019-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -52,8 +52,8 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.enums.Phenotype; import mekhq.campaign.rating.IUnitRating; -import mekhq.campaign.stratcon.StratconBiomeManifest; -import mekhq.campaign.stratcon.StratconContractInitializer; +import mekhq.campaign.stratcon.*; +import mekhq.campaign.stratcon.StratconFacility.FacilityType; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.*; import mekhq.campaign.universe.Faction.Tag; @@ -66,16 +66,18 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import static java.lang.Math.max; import static java.lang.Math.round; -import static megamek.client.ratgenerator.MissionRole.CIVILIAN; +import static megamek.client.ratgenerator.MissionRole.*; import static megamek.common.Compute.randomInt; import static megamek.common.UnitType.*; import static megamek.common.planetaryconditions.Wind.TORNADO_F4; -import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; +import static mekhq.campaign.force.CombatTeam.getStandardForceSize; import static mekhq.campaign.mission.Scenario.T_GROUND; import static mekhq.campaign.mission.ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_AERO_MIX; import static mekhq.campaign.mission.ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_CIVILIANS; import static mekhq.campaign.mission.ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX; +import static mekhq.campaign.universe.IUnitGenerator.unitTypeSupportsWeightClass; /** * This class handles the creation and substantive manipulation of @@ -119,6 +121,7 @@ public static AtBDynamicScenario initializeScenarioFromTemplate(ScenarioTemplate AtBDynamicScenario scenario = new AtBDynamicScenario(); scenario.setName(template.name); + scenario.setStratConScenarioType(template.getStratConScenarioType()); scenario.setDesc(template.detailedBriefing); scenario.setTemplate(template); scenario.setEffectiveOpforSkill(contract.getEnemySkill()); @@ -163,7 +166,7 @@ public static AtBDynamicScenario initializeScenarioFromTemplate(ScenarioTemplate // reinforcements to aerospace battles // space battles are even more restrictive if (template.mapParameters.getMapLocation() == MapLocation.LowAtmosphere) { - defaultReinforcements.setAllowedUnitType(ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_AERO_MIX); + defaultReinforcements.setAllowedUnitType(SPECIAL_UNIT_TYPE_ATB_AERO_MIX); } else if (template.mapParameters.getMapLocation() == MapLocation.Space) { defaultReinforcements.setAllowedUnitType(AEROSPACEFIGHTER); } @@ -209,7 +212,7 @@ public static void finalizeScenario(AtBDynamicScenario scenario, AtBContract con int generatedLanceCount = generateForces(scenario, contract, campaign); // approximate estimate, anyway. - scenario.setLanceCount(generatedLanceCount + (playerForceUnitCount / 4)); + scenario.setForceCount(generatedLanceCount + (playerForceUnitCount / 4)); setScenarioMapSize(scenario); scenario.setScenarioMap(campaign.getCampaignOptions().getFixedMapChance()); setDeploymentZones(scenario); @@ -338,23 +341,43 @@ public static int generateFixedForce(AtBDynamicScenario scenario, AtBContract co } /** - * "Meaty" function that generates a set of forces for the given scenario from - * the given force template, - * subject to several other restrictions + * Generates a set of forces for a given scenario based on the provided force template and other + * parameters. + *

    + * This method creates `lances` or other units, tailored to the context of the scenario, + * campaign, and contract. These forces are generated according to numerous configurable + * criteria, including: + *

      + *
    • The scenario's alignment (Allied, Opposing, Third Party, etc.).
    • + *
    • The force template’s settings (e.g., Battle Value (BV) scaling, unit count scaling).
    • + *
    • Planetary modifiers (e.g., atmosphere, toxic conditions, gravity, etc.).
    • + *
    • The campaign’s rules on faction relations and planetary ownership.
    • + *
    + *

    + * Forces are configured for their roles, available equipment, and alignment parameters, + * adapting to special cases such as unidentified third-party factions or forces involving + * planetary owners. The method integrates numerous campaign-related modifiers and ensures + * compliance with configured budgets (e.g., BV, unit count). + *

    + *

    + * Generated forces are stored in the scenario's bot forces, where they can be used for + * gameplay or additional adjustments as required. + *

    * - * @param scenario Scenario for which we're generating forces - * @param contract The contract on which we're currently working. Used - * for skill/quality/planetary info parameters - * @param campaign The current campaign - * @param effectiveBV The effective battle value, up to this point, of - * player and allied units - * @param effectiveUnitCount The effective unit count, up to this point, of - * player and allied units - * @param weightClass The average weight class to generate this force at - * @param forceTemplate The force template to use to generate the force - * @param isScenarioModifier true if the source of generateForce() was a - * scenario modifier - * @return How many "lances" or other individual units were generated. + * @param scenario The {@link AtBDynamicScenario} for which the forces are being generated. + * @param contract The {@link AtBContract} providing context about the campaign and planetary parameters + * for force generation, including faction and alignment. + * @param campaign The active {@link Campaign} to which the forces will be added. + * @param effectiveBV The effective Battle Value (BV) of allied and player units prior to force generation. + * @param effectiveUnitCount The effective count of allied and player units prior to force generation. + * @param weightClass The weight class (light, medium, heavy, assault) to focus on when selecting units for + * force generation. + * @param forceTemplate The {@link ScenarioForceTemplate} used to guide force generation, defining parameters + * like generation method, unit types, and alignment. + * @param isScenarioModifier A boolean indicating whether the force generation is the result of a scenario modifier. + * If true, scenario-specific effects on force generation are applied. + * + * @return The number of "lances" or other unit groups successfully generated. */ public static int generateForce(AtBDynamicScenario scenario, AtBContract contract, Campaign campaign, int effectiveBV, int effectiveUnitCount, int weightClass, @@ -539,7 +562,7 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac if (forceTemplate.getAllowedUnitType() == SPECIAL_UNIT_TYPE_ATB_MIX) { requiredRoles.put(MEK, new ArrayList<>(baseRoles)); requiredRoles.put(TANK, new ArrayList<>(baseRoles)); - } else if (forceTemplate.getAllowedUnitType() == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_AERO_MIX) { + } else if (forceTemplate.getAllowedUnitType() == SPECIAL_UNIT_TYPE_ATB_AERO_MIX) { requiredRoles.put(CONV_FIGHTER, new ArrayList<>(baseRoles)); requiredRoles.put(AEROSPACEFIGHTER, new ArrayList<>(baseRoles)); } else if (forceTemplate.getAllowedUnitType() == SPECIAL_UNIT_TYPE_ATB_CIVILIANS) { @@ -558,9 +581,9 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac if (allowsConvInfantry && (isTainted || isLowPressure || isLowGravity)) { Collection infantryRoles = new HashSet<>(); if (isLowGravity) { - infantryRoles.add(MissionRole.MARINE); + infantryRoles.add(MARINE); } else { - infantryRoles.add(MissionRole.XCT); + infantryRoles.add(XCT); } if (requiredRoles.containsKey(INFANTRY)) { requiredRoles.get(INFANTRY).addAll(infantryRoles); @@ -578,23 +601,23 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac if (!requiredRoles.containsKey(MEK)) { requiredRoles.put(MEK, new HashSet<>()); } - requiredRoles.get(MEK).add((MissionRole.ARTILLERY)); + requiredRoles.get(MEK).add((ARTILLERY)); } if (artilleryCarriers == SPECIAL_UNIT_TYPE_ATB_MIX || artilleryCarriers == TANK) { if (!requiredRoles.containsKey(TANK)) { requiredRoles.put(TANK, new HashSet<>()); } - requiredRoles.get(TANK).add((MissionRole.ARTILLERY)); + requiredRoles.get(TANK).add((ARTILLERY)); } if (artilleryCarriers == INFANTRY) { if (!requiredRoles.containsKey(INFANTRY)) { requiredRoles.put(INFANTRY, new HashSet<>()); } - requiredRoles.get(INFANTRY).add((MissionRole.ARTILLERY)); + requiredRoles.get(INFANTRY).add((ARTILLERY)); } } - ArrayList generatedEntities = new ArrayList<>(); + List generatedEntities = new ArrayList<>(); boolean stopGenerating = false; String currentLanceWeightString = ""; @@ -635,12 +658,7 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac // All other unit types use the force generator system to randomly select units } else { - boolean allowConventionalAircraft = isPlanetOwner - && actualUnitType == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_AERO_MIX - && scenario.getTemplate().mapParameters.getMapLocation() != MapLocation.Space - && scenario.getAtmosphere().isDenserThan(Atmosphere.THIN); - - if (actualUnitType == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_AERO_MIX) { + if (actualUnitType == SPECIAL_UNIT_TYPE_ATB_AERO_MIX) { lanceSize = getAeroLanceSize(faction); } @@ -649,15 +667,15 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac // a Mek/vehicle mixed formation. Similarly, SPECIAL_UNIT_TYPE_ATB_AERO_MIX may // generate all Aerospace Fighters, Conventional Fighters, or a mixed formation. List unitTypes = generateUnitTypes(actualUnitType, lanceSize, quality, - factionCode, allowsTanks, allowConventionalAircraft, campaign); + factionCode, allowsTanks, campaign); // Formations composed entirely of Meks, aerospace fighters (but not conventional), // and ground vehicles use weight categories as do SPECIAL_UNIT_TYPE_ATB_MIX. // Formations of other types, plus artillery formations do not use weight classes. + boolean supportsWeightClass = unitTypeSupportsWeightClass(actualUnitType); if ((actualUnitType == SPECIAL_UNIT_TYPE_ATB_MIX || actualUnitType == SPECIAL_UNIT_TYPE_ATB_CIVILIANS - || IUnitGenerator.unitTypeSupportsWeightClass(actualUnitType)) - && !forceTemplate.getUseArtillery()) { + || supportsWeightClass)) { // Generate a specific weight class for each unit based on the formation weight // class and lower/upper bounds @@ -726,10 +744,10 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac break; } + Game cGame = campaign.getGame(); + TeamLoadOutGenerator tlg = new TeamLoadOutGenerator(cGame); if (campaign.getCampaignOptions().isAutoConfigMunitions()) { // Configure *all* generated units with appropriate munitions (for BV calcs) - Game cGame = campaign.getGame(); - TeamLoadOutGenerator tlg = new TeamLoadOutGenerator(cGame); ArrayList arrayGeneratedLance = new ArrayList<>(generatedLance); // bin fill ratio will be adjusted by the load out generator based on piracy and // quality @@ -743,7 +761,14 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac tlg.reconfigureEntities(arrayGeneratedLance, factionCode, mt, rp); } else { // Load the fighters with bombs - TeamLoadOutGenerator.populateAeroBombs(generatedLance, campaign.getGameYear(), onGround, ownerBaseQuality, isPirate); + tlg.populateAeroBombs( + generatedLance, + campaign.getGameYear(), + onGround, + ownerBaseQuality, + isPirate, + faction.getShortName() + ); } } @@ -782,8 +807,6 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac // For BV-scaled forces, check whether to stop generating after each formation is // generated. if (forceTemplate.getGenerationMethod() == ForceGenerationMethod.BVScaled.ordinal()) { - // Check random number vs. percentage of the BV budget already generated, with - // the percentage chosen based on unit rating double currentPercentage = ((double) forceBV / forceBVBudget) * 100; stopGenerating = currentPercentage > 100; @@ -817,7 +840,21 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac forceBV = 0; + boolean isClan = faction.isClan(); + + if (isClan) { + logger.info("Faction is Clan, skipping culling"); + } + for (Entity entity : generatedEntities) { + if (isClan) { + forceComposition.add(entity); + int battleValue = getBattleValue(campaign, entity); + forceBV += battleValue; + + continue; + } + // We count transported units and their transporters as one unit when building a force. // This prevents issues where we cull an APC, leaving infantry stranded. if (entity.getTransportId() != Entity.NONE) { @@ -854,6 +891,47 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac generatedEntities.clear(); generatedEntities.addAll(forceComposition); + + // If we're generating an aircraft force for the planetary owner, + // there is a chance they may reinforce with additional Conventional Fighters. + if (campaign.getCampaignOptions().isUseStratCon() + && forceTemplate.getAllowedUnitType() == SPECIAL_UNIT_TYPE_ATB_AERO_MIX) { + if (isPlanetOwner && !faction.isClan()) { + int baseFighterCount = getAeroLanceSize(faction); + int fighterMultiplier = 0; + + try { + StratconTrackState scenarioHomeTrack = getStratconTrackState(scenario, contract); + + if (scenarioHomeTrack != null) { + for (StratconFacility facility : scenarioHomeTrack.getFacilities().values()) { + if (facility.getFacilityType().equals(FacilityType.AirBase)) { + fighterMultiplier++; + } + } + } + } catch (Exception ignored) {} + + boolean allowConventionalAircraft = + scenario.getTemplate().mapParameters.getMapLocation() != MapLocation.Space + && scenario.getAtmosphere().isDenserThan(Atmosphere.THIN); + + if (fighterMultiplier > 0 && allowConventionalAircraft) { + baseFighterCount *= fighterMultiplier; + + // Create a list with `baseFighterCount` entries, all set to CONV_FIGHTER + List conventionalAircraft = new ArrayList<>(); + for (int i = 0; i < baseFighterCount; i++) { + conventionalAircraft.add(CONV_FIGHTER); + } + + List generatedLance = generateLance(factionCode, skill, quality, + conventionalAircraft, new HashMap<>(), campaign); + + generatedEntities.addAll(generatedLance); + } + } + } } logger.info(String.format("Final force %s / %s %s BV", @@ -881,6 +959,11 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac } scenario.addBotForce(generatedForce, forceTemplate, campaign); + if (!contract.isBatchallAccepted()) { + logger.info("Player refused the contract's Batchall and is now being punished for their" + + " overconfidence. No bidding takes place."); + } + if (generatedForce.getTeam() != 1 && campaign.getCampaignOptions().isUseGenericBattleValue() && BatchallFactions.usesBatchalls(factionCode) @@ -897,11 +980,27 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac int playerBattleValue = calculateEffectiveBV(scenario, campaign, true); int playerUnitValue = calculateEffectiveUnitCount(scenario, campaign, true); + forceBVBudget = (int) (playerBattleValue * forceMultiplier); + + logger.info(String.format("Base bidding budget is %s BV2. This is seed force" + + " multiplied by scenario force multiplier", forceBVBudget)); + + forceBVBudget = (int) round(forceBVBudget * getHonorRating(campaign, factionCode)); + + logger.info(String.format("Honor Rating changed it to %s BV2", forceBVBudget)); + + if (isScenarioModifier) { + forceBVBudget = (int) round(forceBVBudget * ((double) campaign.getCampaignOptions().getScenarioModBV() / 100)); + + logger.info(String.format("As this force came from a Scenario Modifier it's" + + " budget has been modified based on campaign settings and is now: %s BV2", + forceBVBudget)); + } + // First bid away units that exceed the player's estimated Battle Value - forceBVBudget = (int) round(playerBattleValue * getHonorRating(campaign, factionCode)); forceBV = 0; - List forceComposition = new ArrayList<>(); + ArrayList forceComposition = new ArrayList<>(); Collections.shuffle(generatedEntities); for (Entity entity : generatedEntities) { @@ -916,6 +1015,8 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac if (forceBV > forceBVBudget) { bidAwayForces.add(entity); + logger.info(String.format("Bidding away %s (%s)", entity.getDisplayName(), + entity.getCrew().getName())); continue; } @@ -930,15 +1031,17 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac forceBV += battleValue; } else { bidAwayForces.add(entity); + logger.info(String.format("Bidding away %s (%s)", entity.getDisplayName(), + entity.getCrew().getName())); } } if (forceComposition.isEmpty()) { + logger.info("We ended up with an empty force, grabbing a unit at random."); implementForceCompositionFallback(generatedEntities, forceComposition); } - generatedEntities.clear(); - generatedEntities.addAll(forceComposition); + generatedForce.setFixedEntityList(forceComposition); // We don't want to sub in Battle Armor for forces that are meant to only have a // certain number of units. @@ -1010,6 +1113,36 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac return generatedLanceCount; } + /** + * Retrieves the {@link StratconTrackState} associated with the given {@link AtBDynamicScenario}. + *

    + * This method iterates over all {@link StratconTrackState} instances in the provided {@link AtBContract}'s + * {@link StratconCampaignState} to find the track that contains a {@link StratconScenario} corresponding to + * the specified {@link AtBDynamicScenario}. If a match is found, the track is returned; + * otherwise, {@code null} is returned. + *

    + * + * @param scenario the {@link AtBDynamicScenario} whose associated track is to be identified. + * @param contract the {@link AtBContract} containing the {@link StratconCampaignState} with all available tracks. + * @return the {@link StratconTrackState} that contains the {@link StratconScenario} backed by the specified + * {@link AtBDynamicScenario}, or {@code null} if no matching track is found. + */ + private static @Nullable StratconTrackState getStratconTrackState(AtBDynamicScenario scenario, + AtBContract contract) { + List tracks = contract.getStratconCampaignState().getTracks(); + StratconTrackState scenarioHomeTrack = null; + + for (StratconTrackState track : tracks) { + for (StratconScenario stratconScenario : track.getScenarios().values()) { + if (stratconScenario.getBackingScenarioID() == scenario.getId()) { + scenarioHomeTrack = track; + break; + } + } + } + return scenarioHomeTrack; + } + /** * This method creates a fallback force consisting of a single unit and any units occupying one * of its transporters. @@ -1020,7 +1153,7 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac * @param generatedEntities An ArrayList of Entities that have been generated. * @param forceComposition A List of Entities representing a force composition to be updated. */ - private static void implementForceCompositionFallback(ArrayList generatedEntities, + private static void implementForceCompositionFallback(List generatedEntities, List forceComposition) { for (Entity entity : generatedEntities) { if (entity.getTransportId() != Entity.NONE) { @@ -1367,10 +1500,10 @@ public static ScenarioObjective translateTemplateObjective(AtBDynamicScenario sc /** * Scale the scenario's objective time limits, if called for, by the number of - * units - * that have associated force templates that "contribute to the unit count". + * units that have associated force templates that "contribute to the unit count". */ - private static void scaleObjectiveTimeLimits(AtBDynamicScenario scenario, Campaign campaign) { + public static void scaleObjectiveTimeLimits(AtBDynamicScenario scenario, Campaign campaign) { + final int DEFAULT_TIME_LIMIT = 6; int primaryUnitCount = 0; for (int forceID : scenario.getPlayerForceTemplates().keySet()) { @@ -1387,9 +1520,27 @@ private static void scaleObjectiveTimeLimits(AtBDynamicScenario scenario, Campai } } + // We want a minimum value here, to avoid situations where we spawn scenarios with only a + // single turn requirement. + // This effectively treats any player-aligned forces less than 6 as 6. + primaryUnitCount = max(DEFAULT_TIME_LIMIT, 6); + for (ScenarioObjective objective : scenario.getScenarioObjectives()) { + // We set a default time to protect against instances where setting time limit fails + // for some reason. + objective.setTimeLimit(DEFAULT_TIME_LIMIT); + if (objective.getTimeLimitType() == TimeLimitType.ScaledToPrimaryUnitCount) { - objective.setTimeLimit(primaryUnitCount * objective.getTimeLimitScaleFactor()); + Integer scaleFactor = objective.getTimeLimitScaleFactor(); + + // If we fail to fetch scaleFactor, log it and use a placeholder value of 1 + if (scaleFactor == null) { + logger.error(String.format("Failed to fetch scaleFactor for scenario template %s. Using fallback value of 1", + scenario.getTemplate().name)); + scaleFactor = 1; + } + + objective.setTimeLimit(primaryUnitCount * scaleFactor); } } } @@ -1535,8 +1686,8 @@ public static void setScenarioMapSize(AtBDynamicScenario scenario) { } // increment map size by template-specified increments - mapSizeX += template.mapParameters.getWidthScalingIncrement() * scenario.getLanceCount(); - mapSizeY += template.mapParameters.getHeightScalingIncrement() * scenario.getLanceCount(); + mapSizeX += template.mapParameters.getWidthScalingIncrement() * scenario.getForceCount(); + mapSizeY += template.mapParameters.getHeightScalingIncrement() * scenario.getForceCount(); // 50/50 odds to rotate the map 90 degrees if specified. if (template.mapParameters.isAllowRotation()) { @@ -1654,13 +1805,9 @@ public static Entity getEntity(String faction, * @return A randomly selected Entity from the parameters specified, with crew. * May return null. */ - public static @Nullable Entity getEntity(String faction, - SkillLevel skill, - int quality, - int unitType, - int weightClass, - @Nullable Collection rolesByType, - Campaign campaign) { + public static @Nullable Entity getEntity(String faction, SkillLevel skill, int quality, int unitType, + int weightClass, @Nullable Collection rolesByType, + Campaign campaign) { MekSummary unitData; // Set up random unit generation parameters @@ -1668,11 +1815,18 @@ public static Entity getEntity(String faction, params.setFaction(faction); params.setQuality(quality); params.setUnitType(unitType); - params.setWeightClass(weightClass); params.setYear(campaign.getGameYear()); + params.setMissionRoles(rolesByType); + + // This filter is to ensure we don't generate trailers or other units that cannot move + if (unitType != GUN_EMPLACEMENT) { + params.setFilter(mekSummary -> mekSummary.getWalkMp() >= 1); + } if (rolesByType != null && !rolesByType.isEmpty()) { - params.setMissionRoles(rolesByType); + params.setWeightClass(shouldBypassWeightClass(rolesByType) + ? UNIT_WEIGHT_UNSPECIFIED + : weightClass); } // Vehicles and infantry require some additional processing @@ -1684,27 +1838,40 @@ public static Entity getEntity(String faction, unitData = campaign.getUnitGenerator().generate(params); } - if (unitData == null) { - if (!params.getMissionRoles().isEmpty()) { - Entity secondChanceEntity = getEntity(faction, skill, quality, unitType, weightClass, campaign); - - if (secondChanceEntity == null) { - logger.warn(String.format("Unable to randomly generate %s %s with roles: %s." + - " Second chance generation also failed.", - EntityWeightClass.getClassName(params.getWeightClass()), - getTypeName(unitType), - params.getMissionRoles().stream().map(Enum::name).collect(Collectors.joining(",")))); - } else { - return secondChanceEntity; - } - } return null; } return createEntityWithCrew(faction, skill, campaign, unitData); } + /** + * Determines whether the weight class constraints should be bypassed based on the given mission roles. + *

    + * This method evaluates if the provided {@code rolesByType} contain any of the predefined mission roles + * that should bypass the weight class restrictions. The bypassed roles are selected because they use + * relatively small or exclusive unit pools, which improves the likelihood of successfully finding + * an appropriate unit. + *

    + * + * @param rolesByType a collection of mission roles to evaluate. + * @return {@code true} if any role in {@code rolesByType} matches one of the predefined bypassed roles. + */ + private static boolean shouldBypassWeightClass(Collection rolesByType) { + // These roles were picked as their pool is relatively small, or they are exclusive in nature. + // This ensures we have a greater chance of successfully pulling an appropriate unit. + List bypassedRoles = List.of(CIVILIAN, SUPPORT, ARTILLERY, MISSILE_ARTILLERY, + MIXED_ARTILLERY, APC, SPECOPS, ENGINEER, MINESWEEPER, MINELAYER); + + for (MissionRole role : rolesByType) { + if (bypassedRoles.contains(role)) { + return true; + } + } + + return false; + } + /** * Randomly creates a ground vehicle, or VTOL if campaign options allows, with a * randomly @@ -1722,8 +1889,6 @@ public static Entity getEntity(String faction, public static Entity getTankEntity(UnitGeneratorParameters params, SkillLevel skill, Campaign campaign) { - MekSummary ms; - // useful debugging statement that forces generation of specific units rather // than random ones // return getEntityByName("Heavy Tracked APC", params.getFaction(), skill, @@ -1739,11 +1904,15 @@ public static Entity getTankEntity(UnitGeneratorParameters params, MekSummary unitData = campaign.getUnitGenerator().generate(params); if (unitData == null) { - if (!params.getMissionRoles().isEmpty()) { - logger.warn(String.format("Unable to randomly generate %s %s with roles: %s", - EntityWeightClass.getClassName(params.getWeightClass()), + if (params.getMissionRoles() != null && !params.getMissionRoles().isEmpty()) { + logger.info(String.format("Unable to randomly generate %s %s with roles: %s", + params.getWeightClass(), getTypeName(TANK), params.getMissionRoles().stream().map(Enum::name).collect(Collectors.joining(",")))); + } else { + logger.info(String.format("Unable to randomly generate %s %s with no roles.", + params.getWeightClass(), + getTypeName(TANK))); } return null; } @@ -1784,9 +1953,9 @@ public static Entity getInfantryEntity(UnitGeneratorParameters params, if (unitData == null) { // If XCT troops were requested but none were found, generate without the role - if (useTempXCT && params.getMissionRoles().contains(MissionRole.XCT)) { + if (useTempXCT && params.getMissionRoles().contains(XCT)) { noXCTParams = params.clone(); - noXCTParams.getMissionRoles().remove(MissionRole.XCT); + noXCTParams.getMissionRoles().remove(XCT); unitData = campaign.getUnitGenerator().generate(noXCTParams); temporaryXCT = true; } @@ -1896,13 +2065,13 @@ public static List fillTransports(AtBScenario scenario, transportedRoles.put(INFANTRY, requiredRoles.containsKey(INFANTRY) ? new ArrayList<>(requiredRoles.get(INFANTRY)) : new ArrayList<>()); - transportedRoles.get(INFANTRY).remove((MissionRole.ARTILLERY)); + transportedRoles.get(INFANTRY).remove((ARTILLERY)); transportedRoles.put(BATTLE_ARMOR, requiredRoles.containsKey(BATTLE_ARMOR) ? new ArrayList<>(requiredRoles.get(BATTLE_ARMOR)) : new ArrayList<>()); - transportedRoles.get(BATTLE_ARMOR).remove((MissionRole.ARTILLERY)); + transportedRoles.get(BATTLE_ARMOR).remove((ARTILLERY)); } List transportedUnits = new ArrayList<>(); @@ -1969,7 +2138,7 @@ private static List fillTransport(AtBScenario scenario, if (bay instanceof InfantryCompartment) { double bayCapacity = bay.getUnused(); - int remainingCount = (int) Math.max(1, Math.floor(bayCapacity / IUnitGenerator.FOOT_PLATOON_INFANTRY_WEIGHT)); + int remainingCount = (int) max(1, Math.floor(bayCapacity / IUnitGenerator.FOOT_PLATOON_INFANTRY_WEIGHT)); while (remainingCount > 0) { // Set base random generation parameters @@ -1999,9 +2168,9 @@ private static List fillTransport(AtBScenario scenario, if (transportedUnit == null && allowInfantry) { newParams.setMissionRoles(requiredRoles.getOrDefault(INFANTRY, new HashSet<>())); if (transport.getUnitType() == VTOL - && !newParams.getMissionRoles().contains(MissionRole.XCT)) { + && !newParams.getMissionRoles().contains(XCT)) { UnitGeneratorParameters paratrooperParams = newParams.clone(); - paratrooperParams.addMissionRole(MissionRole.PARATROOPER); + paratrooperParams.addMissionRole(PARATROOPER); transportedUnit = generateTransportedInfantryUnit(paratrooperParams, bayCapacity, skill, true, campaign); } else { @@ -2079,7 +2248,7 @@ private static Entity generateTransportedInfantryUnit(UnitGeneratorParameters pa // include other types if (bayCapacity <= IUnitGenerator.FOOT_PLATOON_INFANTRY_WEIGHT) { - if (newParams.getMissionRoles().contains(MissionRole.PARATROOPER)) { + if (newParams.getMissionRoles().contains(PARATROOPER)) { newParams.setMovementModes(IUnitGenerator.ALL_INFANTRY_MODES); } else { newParams.getMovementModes().add(EntityMovementMode.INF_LEG); @@ -2090,9 +2259,9 @@ private static Entity generateTransportedInfantryUnit(UnitGeneratorParameters pa if (unitData == null) { // If XCT troops were requested but none were found, generate without the role - if (useTempXCT && newParams.getMissionRoles().contains(MissionRole.XCT)) { + if (useTempXCT && newParams.getMissionRoles().contains(XCT)) { noXCTParams = newParams.clone(); - noXCTParams.getMissionRoles().remove(MissionRole.XCT); + noXCTParams.getMissionRoles().remove(XCT); unitData = campaign.getUnitGenerator().generate(noXCTParams); temporaryXCT = true; } @@ -2121,9 +2290,9 @@ private static Entity generateTransportedInfantryUnit(UnitGeneratorParameters pa if (unitData == null) { // If XCT troops were requested but none were found, generate without the role - if (useTempXCT && newParams.getMissionRoles().contains(MissionRole.XCT)) { + if (useTempXCT && newParams.getMissionRoles().contains(XCT)) { noXCTParams = newParams.clone(); - noXCTParams.getMissionRoles().remove(MissionRole.XCT); + noXCTParams.getMissionRoles().remove(XCT); unitData = campaign.getUnitGenerator().generate(noXCTParams); temporaryXCT = true; } @@ -2183,7 +2352,7 @@ private static Entity generateTransportedBAUnit(UnitGeneratorParameters params, if (bayCapacity != IUnitGenerator.NO_WEIGHT_LIMIT) { newParams.setFilter(inf -> inf.getTons() <= bayCapacity); } else { - newParams.addMissionRole(MissionRole.MECHANIZED_BA); + newParams.addMissionRole(MECHANIZED_BA); } MekSummary unitData = campaign.getUnitGenerator().generate(newParams); @@ -2192,7 +2361,7 @@ private static Entity generateTransportedBAUnit(UnitGeneratorParameters params, if (unitData == null) { if (bayCapacity != IUnitGenerator.NO_WEIGHT_LIMIT && retryAsMechanized) { newParams.setFilter(null); - newParams.addMissionRole((MissionRole.MECHANIZED_BA)); + newParams.addMissionRole((MECHANIZED_BA)); unitData = campaign.getUnitGenerator().generate(newParams); } if (unitData == null) { @@ -2271,7 +2440,7 @@ public static List generateBAForNova(AtBScenario scenario, List params.setFaction(factionCode); params.setQuality(quality); params.setYear(campaign.getGameYear()); - params.addMissionRole(MissionRole.MECHANIZED_BA); + params.addMissionRole(MECHANIZED_BA); params.setWeightClass(UNIT_WEIGHT_UNSPECIFIED); Entity transportedUnit = generateTransportedBAUnit(params, IUnitGenerator.NO_WEIGHT_LIMIT, skill, false, @@ -2378,7 +2547,7 @@ private static Entity getEntityByName(String name, String factionCode, SkillLeve int skillRoll = Compute.d6(1); if (skillRoll == 1) { - skillValue = Math.max(1, skillValue - 1); + skillValue = max(1, skillValue - 1); } else if (skillRoll == 6) { skillValue = Math.min(7, skillValue + 1); } @@ -2525,10 +2694,7 @@ private static String adjustWeightsForFaction(String weights, String faction) { /** * Generates a selection of unit types, typically composing a lance, star, Level - * II, or similar - * tactical formation. - * TODO: generate ProtoMek points when Clan mixed stars are called for - * TODO: generate Clan mixed nova stars e.g. two points of Meks, two of vehicles, one ProtoMek point + * II, or similar tactical formation. * * @param unitTypeCode The type of units to generate, also accepts * SPECIAL_UNIT_TYPE_ATB_MIX for @@ -2542,13 +2708,8 @@ private static String adjustWeightsForFaction(String weights, String faction) { * May * contain duplicates. */ - private static List generateUnitTypes(int unitTypeCode, - int unitCount, - int forceQuality, - String factionCode, - boolean allowTanks, - boolean allowConventionalAircraft, - Campaign campaign) { + private static List generateUnitTypes(int unitTypeCode, int unitCount, int forceQuality, + String factionCode, boolean allowTanks, Campaign campaign) { List unitTypes = new ArrayList<>(unitCount); int actualUnitType = unitTypeCode; @@ -2587,11 +2748,11 @@ private static List generateUnitTypes(int unitTypeCode, vehicleLanceWeight++; } else { mekLanceWeight++; - mixedLanceWeight = Math.max(0, mixedLanceWeight - 1); - vehicleLanceWeight = Math.max(0, vehicleLanceWeight - 1); + mixedLanceWeight = max(0, mixedLanceWeight - 1); + vehicleLanceWeight = max(0, vehicleLanceWeight - 1); } } else if (faction.isMinorPower() || faction.isPirate()) { - mekLanceWeight = Math.max(0, mekLanceWeight - 1); + mekLanceWeight = max(0, mekLanceWeight - 1); mixedLanceWeight++; vehicleLanceWeight++; } @@ -2664,56 +2825,7 @@ private static List generateUnitTypes(int unitTypeCode, } } } else if (unitTypeCode == SPECIAL_UNIT_TYPE_ATB_AERO_MIX) { - Faction faction = Factions.getInstance().getFaction(factionCode); - - if (campaign.getCampaignOptions().isUseVehicles() && allowConventionalAircraft - && (!faction.isClan() || (faction.isClan() && campaign.getCampaignOptions().isClanVehicles()))) { - - // Use the Mek/Vehicle/Mixed ratios from campaign options as weighted values for - // random unit types. - // Then modify based on faction. - int aeroFlightWeight = campaign.getCampaignOptions().getOpForLanceTypeVehicles(); - int mixedFlightWeight = campaign.getCampaignOptions().getOpForLanceTypeMixed(); - int conventionalLanceWeight = campaign.getCampaignOptions().getOpForLanceTypeMeks(); - - if (faction.isClan()) { - aeroFlightWeight += 2; - mixedFlightWeight = 0; - conventionalLanceWeight = Math.max(0, conventionalLanceWeight - 2); - } else if (faction.isMinorPower() || faction.isPirate()) { - aeroFlightWeight = Math.max(0, aeroFlightWeight - 1); - mixedFlightWeight++; - conventionalLanceWeight++; - } - - int totalWeight = aeroFlightWeight + mixedFlightWeight + conventionalLanceWeight; - - // Roll for unit types - if (totalWeight <= 0) { - actualUnitType = AEROSPACEFIGHTER; - } else { - int roll = randomInt(totalWeight); - - if (roll < conventionalLanceWeight) { - actualUnitType = CONV_FIGHTER; - // Mixed units randomly select between Aerospace or Conventional Fighter - } else if (roll < conventionalLanceWeight + mixedFlightWeight) { - for (int x = 0; x < unitCount; x++) { - boolean addConventional = randomInt(2) == 0; - if (addConventional) { - unitTypes.add(CONV_FIGHTER); - } else { - unitTypes.add(AEROSPACEFIGHTER); - } - } - return unitTypes; - } else { - actualUnitType = AEROSPACEFIGHTER; - } - } - } else { - actualUnitType = AEROSPACEFIGHTER; - } + actualUnitType = AEROSPACEFIGHTER; } // Add unit types to the list of actual unity types @@ -2871,19 +2983,19 @@ private static List generateClanUnitTypes(int unitCount, if (requiredRoles != null && !requiredRoles.isEmpty()) { for (int curType : requiredRoles.keySet()) { - if (requiredRoles.get(curType).contains(MissionRole.RECON)) { + if (requiredRoles.get(curType).contains(RECON)) { if (curType == MEK || curType == PROTOMEK) { weights = adjustForMaxWeight(weights, EntityWeightClass.WEIGHT_MEDIUM); } } - if (requiredRoles.get(curType).contains(MissionRole.APC)) { + if (requiredRoles.get(curType).contains(APC)) { if (curType == TANK || curType == VTOL) { weights = adjustForMaxWeight(weights, EntityWeightClass.WEIGHT_MEDIUM); } } - if (requiredRoles.get(curType).contains(MissionRole.CAVALRY)) { + if (requiredRoles.get(curType).contains(CAVALRY)) { if (curType == MEK) { weights = adjustForMaxWeight(weights, EntityWeightClass.WEIGHT_HEAVY); } else if (curType == TANK || curType == PROTOMEK) { @@ -2891,7 +3003,7 @@ private static List generateClanUnitTypes(int unitCount, } } - if (requiredRoles.get(curType).contains(MissionRole.RAIDER)) { + if (requiredRoles.get(curType).contains(RAIDER)) { if (curType == MEK || curType == PROTOMEK) { weights = adjustForMaxWeight(weights, EntityWeightClass.WEIGHT_HEAVY); } @@ -3108,95 +3220,196 @@ private static List generateLance(String faction, SkillLevel skill, int * tactical group. */ private static List generateLance(String faction, SkillLevel skill, int quality, - List unitTypes, String weights, Map> rolesByType, - Campaign campaign, AtBScenario scenario, boolean allowsTanks) { + List unitTypes, String weights, Map> rolesByType, + Campaign campaign, AtBScenario scenario, boolean allowsTanks) { + List generatedEntities = new ArrayList<>(); // If the number of unit types and number of weight classes don't match, - // generate the lower - // of the two counts + // generate the lower of the two counts int unitTypeSize = unitTypes.size(); if (unitTypeSize > weights.length()) { logger.error( - String.format("More unit types (%d) provided than weights (%d). Truncating generated lance.", - unitTypes.size(), - weights.length())); + String.format("More unit types (%d) provided than weights (%d). Truncating generated lance.", + unitTypes.size(), + weights.length())); unitTypeSize = weights.length(); } - for (int i = 0; i < unitTypeSize; i++) { - Entity newEntity = getNewEntity(faction, skill, quality, unitTypes, weights, rolesByType, - campaign, i); + for (int unitIndex = 0; unitIndex < unitTypeSize; unitIndex++) { + Entity entity = getNewEntity(faction, skill, quality, unitTypes, weights, rolesByType, + campaign, unitIndex); - if (newEntity == null) { - logger.info(String.format("Failed to generate unit of type %s, weight %s. Beginning substitution.", - getTypeName(unitTypes.get(i)), - EntityWeightClass.getClassName(AtBConfiguration.decodeWeightStr(weights, i)))); + if (entity != null) { + generatedEntities.add(entity); + continue; + } - // If we've failed to get an entity, we start adjusting weight categories to see - // if we hit something valid. - // We start at lighter weights as, generally, they're less impactful than heavier units. - List individualType = List.of(unitTypes.get(i)); + String role = null; + Integer type = unitTypes.get(unitIndex); + if (type != null) { + Collection roleByType = rolesByType.get(type); + if (roleByType != null) { + role = roleByType.toString(); + } + } - Map> individualRole = new HashMap<>(); - individualRole.put(0, rolesByType.getOrDefault(unitTypes.get(i), List.of())); + logger.info(String.format("Failed to generate unit of type %s, weight %s, role %s." + + " Beginning substitution.", + getTypeName(unitTypes.get(unitIndex)), + EntityWeightClass.getClassName(AtBConfiguration.decodeWeightStr(weights, unitIndex)), + role != null ? "roles (" + role + ')' : "")); - List weightClasses = List.of("UL", "L", "M", "H", "A"); + entity = substituteEntity(faction, skill, quality, weights, rolesByType, + campaign, unitTypes, unitIndex, unitTypes); - for (String weight : weightClasses) { - newEntity = getNewEntity(faction, skill, quality, individualType, weight, individualRole, campaign, 0); + if (entity != null) { + generatedEntities.add(entity); + continue; + } - if (newEntity != null) { - logger.info(String.format("Substitution successful (%s)", - EntityWeightClass.getClassName(AtBConfiguration.decodeWeightStr(weights, i)))); - break; - } + // fallback unitType list container + List fallbackUnitType = unitTypes; + + // starting of error scenarios + if (unitTypes.get(unitIndex) == DROPSHIP) { + fallbackUnitType = List.of(DROPSHIP); + entity = substituteEntity(faction, skill, quality, weights, rolesByType, + campaign, unitTypes, unitIndex, fallbackUnitType); + } else if (scenario.getBoardType() == T_GROUND) { + if (allowsTanks && unitTypes.get(unitIndex) != TANK) { + logger.info("Switching unit type to Tank"); + fallbackUnitType = List.of(TANK); + + entity = substituteEntity(faction, skill, quality, weights, rolesByType, + campaign, unitTypes, unitIndex, fallbackUnitType); } - // If we still haven't got a valid entity, use hardcoded fallbacks. - if (newEntity == null) { - logger.info("Substitution unsuccessful. Using hardcoded fallbacks"); - - if (unitTypes.get(0) == DROPSHIP) { - newEntity = getNewEntity(faction, skill, quality, List.of(DROPSHIP), - weights, Map.of(DROPSHIP, List.of(CIVILIAN)), - campaign, 0); + if (unitTypes.get(unitIndex) != MEK) { + logger.info("Switching unit type to Mek"); + fallbackUnitType = List.of(MEK); - if (newEntity != null) { - logger.info("Substitution successful. Substituted with Civilian DropShip."); - } - } else { - if (scenario.getBoardType() == T_GROUND && allowsTanks) { - newEntity = getNewEntity(faction, skill, quality, List.of(TANK), - weights, null, campaign, 0); + entity = substituteEntity(faction, skill, quality, weights, rolesByType, + campaign, unitTypes, unitIndex, fallbackUnitType); + } - if (newEntity != null) { - logger.info("Substitution successful. Substituted with Tank."); - } - } else { - newEntity = getNewEntity(faction, skill, quality, List.of(AEROSPACEFIGHTER), - weights, null, campaign, 0); + // Abandon attempts to generate by role + if (entity == null) { + logger.info("Removing role requirements."); + entity = substituteEntity(faction, skill, quality, weights, null, + campaign, unitTypes, unitIndex, unitTypes); + } + } else { + if (unitTypes.get(unitIndex) != AEROSPACEFIGHTER) { + logger.info("Switching unit type to Aerospace Fighter"); + fallbackUnitType = List.of(AEROSPACEFIGHTER); - if (newEntity != null) { - logger.info("Substitution successful. Substituted with Aerospace Fighter."); - } - } - } + entity = substituteEntity(faction, skill, quality, weights, rolesByType, + campaign, unitTypes, unitIndex, fallbackUnitType); + } - if (newEntity == null) { - logger.info("Substitution unsuccessful. Abandoning attempts to generate unit."); - } + // Abandon attempts to generate by role + if (entity == null) { + logger.info("Removing role requirements."); + entity = substituteEntity(faction, skill, quality, weights, null, + campaign, unitTypes, unitIndex, fallbackUnitType); } } - if (newEntity != null) { - generatedEntities.add(newEntity); + if (entity != null) { + generatedEntities.add(entity); + } else { + logger.info("Substitution unsuccessful. Abandoning attempts to generate unit."); } } return generatedEntities; } + /** + * Attempts to substitute an entity with a fallback unit type in a series of steps. + * When the initial entity generation fails, this method makes various changes to the unit type + * and weight and tries again until it either successfully generates a new entity + * or exhausts all alternatives. + * + * @param faction The faction for the new Entity being generated + * @param skill The skill level for the new Entity being generated + * @param quality The quality for the new Entity being generated + * @param weights The weights for the unit types being considered + * @param rolesByType The roles available for each unit type + * @param campaign The campaign to which this Entity will be added + * @param unitTypes The unit types available for substitution + * @param unitIndex The index of the unit being replaced in the unitTypes list + * @param fallbackUnitType The fallback unit type to be used if normal generation steps fail + * @return The new generated Entity or null if substitution unsuccessful + */ + private static @Nullable Entity substituteEntity(String faction, SkillLevel skill, int quality, + String weights, Map> rolesByType, + Campaign campaign, List unitTypes, int unitIndex, + List fallbackUnitType) { + logger.info("Attempting to generate again"); + + Entity entity = getNewEntity(faction, skill, quality, fallbackUnitType, weights, rolesByType, + campaign, unitIndex); + + if (entity != null) { + logger.info("Substitution successful."); + return entity; + } + + logger.info("That didn't help, cycling weights."); + entity = attemptSubstitutionViaWeight(faction, skill, quality, weights, + rolesByType, campaign, unitTypes, unitIndex); + + if (entity != null) { + logger.info("Substitution successful."); + return entity; + } else { + logger.info("Unable to substitute entity."); + return null; + } + } + + /** + * Attempts to generate an Entity by substituting weight classes in a specific order. + * Starting from the lightest (`UL`) to the heaviest (`A`), each weight class is attempted + * until a valid Entity can be generated, or all weight classes have been tried. If a valid + * Entity is generated, that Entity is returned; otherwise, the method returns null. + * + * @param faction The faction for the new Entity being generated. + * @param skill The SkillLevel for the new Entity being generated. + * @param quality The quality for the new Entity being generated. + * @param weights A String representing the weights for the unit types being considered. + * @param rolesByType The roles available for each unit type. This may be null. + * @param campaign The campaign to which this Entity will be added. + * @param individualType The unit types available for substitution. + * @param unitIndex The index of the unit type being replaced in the unitTypes list. + * @return The new Entity generated or null if substitution is unsuccessful. + */ + private static @Nullable Entity attemptSubstitutionViaWeight(String faction, SkillLevel skill, + int quality, String weights, + @Nullable Map> rolesByType, + Campaign campaign, + List individualType, + int unitIndex) { + List weightClasses = List.of("UL", "L", "M", "H", "A"); + + Entity entity; + + for (String weight : weightClasses) { + entity = getNewEntity(faction, skill, quality, individualType, weight, + rolesByType, campaign, unitIndex); + + if (entity != null) { + logger.info(String.format("Substitution successful (%s)", + EntityWeightClass.getClassName(AtBConfiguration.decodeWeightStr(weights, unitIndex)))); + return entity; + } + } + + return null; + } + /** * Retrieve a new instance of Entity with the given parameters. * @@ -3207,23 +3420,41 @@ private static List generateLance(String faction, SkillLevel skill, int * @param weights the unit weights string * @param rolesByType the mapping of unit types to mission roles * @param campaign the campaign associated with the entity - * @param i the index of the unit type in the unitTypes list + * @param unitIndex the index of the unit type in the unitTypes list * * @return a new instance of Entity with the specified parameters */ private static Entity getNewEntity(String faction, SkillLevel skill, int quality, List unitTypes, String weights, @Nullable Map> rolesByType, - Campaign campaign, int i) { + Campaign campaign, int unitIndex) { Collection roles; if (rolesByType != null) { - roles = rolesByType.getOrDefault(unitTypes.get(i), new ArrayList<>()); + if (unitTypes.size() == 1) { + roles = rolesByType.getOrDefault(unitTypes.get(0), new ArrayList<>()); + } else { + roles = rolesByType.getOrDefault(unitTypes.get(unitIndex), new ArrayList<>()); + } } else { roles = null; } - return getEntity(faction, skill, quality, unitTypes.get(i), - AtBConfiguration.decodeWeightStr(weights, i), roles, campaign); + + int weight; + if (weights.length() == 1 || (weights.charAt(0) == 'U' && weights.length() == 2)) { + weight = AtBConfiguration.decodeWeightStr(weights, 0); + } else { + weight = AtBConfiguration.decodeWeightStr(weights, unitIndex); + } + + int unitType; + if (unitTypes.size() == 1) { + unitType = unitTypes.get(0); + } else { + unitType = unitTypes.get(unitIndex); + } + + return getEntity(faction, skill, quality, unitType, weight, roles, campaign); } /** @@ -3480,17 +3711,34 @@ public static void setDeploymentTurns(BotForce botForce, ScenarioForceTemplate f * @param campaign The campaign in which the scenario is occurring. */ public static void setPlayerDeploymentTurns(AtBDynamicScenario scenario, Campaign campaign) { - // make note of battle commander strategy + ArrayList primaryForceIDs = new ArrayList<>(); + + if (campaign.getCampaignOptions().isUseStratCon()) { + AtBContract contract = scenario.getContract(campaign); + StratconCampaignState campaignState = contract.getStratconCampaignState(); + + for (StratconTrackState track : campaignState.getTracks()) { + StratconScenario stratconScenario = track.getBackingScenariosMap().get(scenario.getId()); + if (stratconScenario != null) { + primaryForceIDs = stratconScenario.getPrimaryForceIDs(); + } + } + + if (primaryForceIDs.isEmpty()) { + logger.warn(String.format("Unable to find primary force for scenario %s (%d)", scenario.getName(), scenario.getId())); + } + } + + // Make note of battle commander strategy int strategy = scenario.getLanceCommanderSkill(SkillType.S_STRATEGY, campaign); - // for player forces where there's an associated force template, we can set the - // deployment turn explicitly - // or use a stagger algorithm. - // for player forces where there's not an associated force template, we - // calculate the deployment turn - // as if they were reinforcements + // For player forces where there's an associated force template, we can set the + // deployment turn explicitly or use a stagger algorithm. + // For player forces where there's not an associated force template, we calculate the + // deployment turn as if they were reinforcements for (int forceID : scenario.getForceIDs()) { ScenarioForceTemplate forceTemplate = scenario.getPlayerForceTemplates().get(forceID); + List forceEntities = new ArrayList<>(); Force playerForce = campaign.getForce(forceID); @@ -3502,14 +3750,45 @@ public static void setPlayerDeploymentTurns(AtBDynamicScenario scenario, Campaig } // now, attempt to set deployment turns - // if the force has a template, then use the appropriate algorithm - // otherwise, treat it as reinforcements + // if the force has a template, then use the appropriate algorithm otherwise, treat it + // as reinforcements if (forceTemplate != null) { int deployRound = forceTemplate.getArrivalTurn(); + // Override to ensure we're treating primary forces correctly. + // This is to resolve a very annoying bug with StratCon where force templates are + // not stored when saving, so cannot be fetched later. At the time of writing, + // we've not been able to track down the origin of that bug. Though, from my own + // searching, it looks like the issue might be with the Scenario class and fixing + // it there would likely break things for non-StratCon users. -- Illiani + Collection templates = scenario.getPlayerForceTemplates().values(); + for (ScenarioForceTemplate template : templates) { + if (template == null) { + // I don't know why 'templates' sometimes contains 'null' templates, but it + // does and this stops them from gumming up the works. + continue; + } + + if (Objects.equals(template.getForceName(), ScenarioForceTemplate.PRIMARY_FORCE_TEMPLATE_ID)) { + if (primaryForceIDs.contains(forceID)) { + deployRound = template.getArrivalTurn(); + } else { + deployRound = ScenarioForceTemplate.ARRIVAL_TURN_AS_REINFORCEMENTS; + } + break; + } + } + + // After we're overwritten the template, as necessary, continue if (deployRound == ScenarioForceTemplate.ARRIVAL_TURN_STAGGERED_BY_LANCE) { + logger.info(String.format("We're using staggered deployment turn calculation for %s", + playerForce.getName())); + setDeploymentTurnsStaggeredByLance(forceEntities); } else if (deployRound == ScenarioForceTemplate.ARRIVAL_TURN_AS_REINFORCEMENTS) { + logger.info(String.format("We're using reinforcement deployment turn calculation for %s", + playerForce.getName())); + setDeploymentTurnsForReinforcements(forceEntities, strategy + scenario.getFriendlyReinforcementDelayReduction()); // Here we selectively overwrite the earlier entries @@ -3534,11 +3813,16 @@ public static void setPlayerDeploymentTurns(AtBDynamicScenario scenario, Campaig } } } else { + logger.info(String.format("We're using normal deployment turn calculation for %s", + playerForce.getName())); + for (Entity entity : forceEntities) { entity.setDeployRound(deployRound); } } } else { + logger.info(String.format("We're using a fallback deployment turn calculation for %s", + playerForce.getName())); setDeploymentTurnsForReinforcements(forceEntities, strategy); } } @@ -3645,7 +3929,7 @@ private static void setDeploymentTurnsStaggered(List entityList, int tur // since we're iterating through the same unchanged collection, we can use // implicit indexing. - entity.setDeployRound(Math.max(0, maxWalkMP - entityWalkMPs.get(x) - actualTurnModifier)); + entity.setDeployRound(max(0, maxWalkMP - entityWalkMPs.get(x) - actualTurnModifier)); } } @@ -3671,13 +3955,15 @@ public static void setDeploymentTurnsForReinforcements(List entityList, */ public static void setDeploymentTurnsForReinforcements(List entityList, int turnModifier, boolean isDelayed) { - int minimumSpeed = 999; int arrivalScale = REINFORCEMENT_ARRIVAL_SCALE; // First, we organize the reinforcements into pools. // This ensures each Force's reinforcements are handled separately. Map delayByForce = new HashMap<>(); + int actualArrivalTurn = 0; + int delayedArrivalScale = REINFORCEMENT_ARRIVAL_SCALE * (randomInt(2) + 2); + // first, we figure out the slowest "atb speed" of this group. for (Entity entity : entityList) { if (isDelayed) { @@ -3686,7 +3972,6 @@ public static void setDeploymentTurnsForReinforcements(List entityList, if (delayByForce.containsKey(forceId)) { arrivalScale = delayByForce.get(forceId); } else { - int delayedArrivalScale = REINFORCEMENT_ARRIVAL_SCALE * (randomInt(2) + 2); delayByForce.put(forceId, delayedArrivalScale); arrivalScale = delayedArrivalScale; } @@ -3697,13 +3982,7 @@ public static void setDeploymentTurnsForReinforcements(List entityList, continue; } - int speed = calculateAtBSpeed(entity); - - // don't reduce minimum speed to 0, since dividing by zero further down is - // problematic - if ((speed < minimumSpeed) && (speed > 0)) { - minimumSpeed = speed; - } + int speed = max(1, calculateAtBSpeed(entity)); // the actual arrival turn will be the scale divided by the slowest speed. // so, a group of Atlases (3/5) should arrive at turn 7 (20 / 3) @@ -3711,8 +3990,14 @@ public static void setDeploymentTurnsForReinforcements(List entityList, // a group of Ostscouts (8/12/8) should arrive on turn 2 (20 / 9, rounded down) // we then subtract the passed-in turn modifier, which is usually the // commander's strategy skill level. - int actualArrivalTurn = Math.max(0, (arrivalScale / minimumSpeed) - turnModifier); + int rollingArrivalTurn = max(0, (arrivalScale / speed) - turnModifier); + + if (rollingArrivalTurn > actualArrivalTurn) { + actualArrivalTurn = rollingArrivalTurn; + } + } + for (Entity entity : entityList) { entity.setDeployRound(actualArrivalTurn); } } diff --git a/MekHQ/src/mekhq/campaign/mission/AtBScenario.java b/MekHQ/src/mekhq/campaign/mission/AtBScenario.java index 25d00707714..6a2cabc31d0 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBScenario.java @@ -1,7 +1,7 @@ /* * AtBScenario.java * - * Copyright (C) 2014-2021 - The MegaMek Team. All Rights Reserved. + * Copyright (C) 2014-2024 - The MegaMek Team. All Rights Reserved. * Copyright (c) 2014 Carl Spain. All rights reserved. * * This file is part of MekHQ. @@ -40,16 +40,19 @@ import mekhq.campaign.Campaign; import mekhq.campaign.againstTheBot.AtBConfiguration; import mekhq.campaign.againstTheBot.AtBStaticWeightGenerator; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.force.Force; -import mekhq.campaign.force.StrategicFormation; import mekhq.campaign.mission.ObjectiveEffect.ObjectiveEffectType; import mekhq.campaign.mission.ScenarioObjective.ObjectiveCriterion; import mekhq.campaign.mission.atb.IAtBScenario; -import mekhq.campaign.mission.enums.AtBLanceRole; +import mekhq.campaign.mission.enums.CombatRole; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.stratcon.StratconBiomeManifest; import mekhq.campaign.stratcon.StratconBiomeManifest.MapTypeList; +import mekhq.campaign.stratcon.StratconCampaignState; +import mekhq.campaign.stratcon.StratconScenario; +import mekhq.campaign.stratcon.StratconTrackState; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.*; import mekhq.utilities.MHQXMLUtility; @@ -140,12 +143,12 @@ public abstract class AtBScenario extends Scenario implements IAtBScenario { EntityWeightClass.WEIGHT_HEAVY }; - public static final int NO_LANCE = -1; + public static final int NO_COMBAT_TEAM = -1; private boolean attacker; - private int strategicFormationId; // -1 if scenario is not generated for a specific lance (special scenario, big + private int combatTeamId; // -1 if scenario is not generated for a specific lance (special scenario, big // battle) - private AtBLanceRole lanceRole; /* + private CombatRole combatRole; /* * set when scenario is created in case it is changed for the next week before * the scenario is resolved; * specifically affects scenarios generated for scout lances, in which the @@ -154,7 +157,7 @@ public abstract class AtBScenario extends Scenario implements IAtBScenario { */ private int deploymentDelay; - private int lanceCount; + private int forceCount; private int rerollsRemaining; private int enemyHome; @@ -207,8 +210,8 @@ public abstract class AtBScenario extends Scenario implements IAtBScenario { public AtBScenario() { super(); - strategicFormationId = -1; - lanceRole = AtBLanceRole.UNASSIGNED; + combatTeamId = NO_COMBAT_TEAM; + combatRole = CombatRole.RESERVE; alliesPlayer = new ArrayList<>(); alliesPlayerStub = new ArrayList<>(); attachedUnitIds = new ArrayList<>(); @@ -218,13 +221,13 @@ public AtBScenario() { numPlayerMinefields = new HashMap<>(); deploymentDelay = 0; - lanceCount = 0; + forceCount = 0; rerollsRemaining = 0; TCO = TerrainConditionsOddsManifest.getInstance(); SB = StratconBiomeManifest.getInstance(); } - public void initialize(Campaign c, StrategicFormation lance, boolean attacker, LocalDate date) { + public void initialize(Campaign campaign, CombatTeam combatTeam, boolean attacker, LocalDate date) { setAttacker(attacker); alliesPlayer = new ArrayList<>(); @@ -235,16 +238,16 @@ public void initialize(Campaign c, StrategicFormation lance, boolean attacker, L survivalBonus = new ArrayList<>(); entityIds = new HashMap<>(); - if (null == lance) { - strategicFormationId = -1; - lanceRole = AtBLanceRole.UNASSIGNED; + if (null == combatTeam) { + combatTeamId = NO_COMBAT_TEAM; + combatRole = CombatRole.RESERVE; } else { - this.strategicFormationId = lance.getForceId(); - lanceRole = lance.getRole(); - setMissionId(lance.getMissionId()); + this.combatTeamId = combatTeam.getForceId(); + combatRole = combatTeam.getRole(); + setMissionId(combatTeam.getMissionId()); - for (UUID id : c.getForce(lance.getForceId()).getAllUnits(true)) { - entityIds.put(id, c.getUnit(id).getEntity()); + for (UUID id : campaign.getForce(combatTeam.getForceId()).getAllUnits(true)) { + entityIds.put(id, campaign.getUnit(id).getEntity()); } } @@ -256,7 +259,7 @@ public void initialize(Campaign c, StrategicFormation lance, boolean attacker, L gravity = (float) 1.0; deploymentDelay = 0; setDate(date); - lanceCount = 0; + forceCount = 0; rerollsRemaining = 0; if (isStandardScenario()) { @@ -265,7 +268,7 @@ public void initialize(Campaign c, StrategicFormation lance, boolean attacker, L setName(getScenarioTypeDescription()); } - initBattle(c); + initBattle(campaign); } public String getDesc() { @@ -321,15 +324,15 @@ private void initBattle(Campaign campaign) { setMapSize(); setMapFile(); if (isStandardScenario()) { - lanceCount = 1; + forceCount = 1; } else if (isBigBattle()) { - lanceCount = 2; + forceCount = 2; } - if (null != getStrategicFormation(campaign)) { - getStrategicFormation(campaign).refreshCommander(campaign); - if (null != getStrategicFormation(campaign).getCommander(campaign).getSkill(SkillType.S_TACTICS)) { - rerollsRemaining = getStrategicFormation(campaign).getCommander(campaign).getSkill(SkillType.S_TACTICS).getLevel(); + if (null != getCombatTeamById(campaign)) { + getCombatTeamById(campaign).refreshCommander(campaign); + if (null != getCombatTeamById(campaign).getCommander(campaign).getSkill(SkillType.S_TACTICS)) { + rerollsRemaining = getCombatTeamById(campaign).getCommander(campaign).getSkill(SkillType.S_TACTICS).getLevel(); } } } @@ -438,23 +441,23 @@ public void setMapSize() { } public int getMapX() { - int base = getMapSizeX() + 5 * lanceCount; + int base = getMapSizeX() + 5 * forceCount; return Math.max(base, 20); } public int getMapY() { - int base = getMapSizeY() + 5 * lanceCount; + int base = getMapSizeY() + 5 * forceCount; return Math.max(base, 20); } public int getBaseMapX() { - return (5 * getLanceCount()) + getMapSizeX(); + return (5 * getForceCount()) + getMapSizeX(); } public int getBaseMapY() { - return (5 * getLanceCount()) + getMapSizeY(); + return (5 * getForceCount()) + getMapSizeY(); } public void setMapFile(String terrainType) { @@ -702,7 +705,7 @@ public void setForces(Campaign campaign) { private void setStandardScenarioForces(Campaign campaign) { /* Find the number of attached units required by the command rights clause */ int attachedUnitWeight = EntityWeightClass.WEIGHT_MEDIUM; - if (lanceRole.isScouting() || lanceRole.isTraining()) { + if (combatRole.isPatrol() || combatRole.isTraining()) { attachedUnitWeight = EntityWeightClass.WEIGHT_LIGHT; } int numAttachedPlayer = 0; @@ -832,7 +835,7 @@ private void setStandardScenarioForces(Campaign campaign) { getContract(campaign).getEnemy()), EntityWeightClass.WEIGHT_ASSAULT, campaign); } - } else if (getLanceRole().isScouting()) { + } else if (getCombatRole().isPatrol()) { /* * Set allied forces to deploy in (6 - speed) turns just as player's units, * but only if not deploying by DropShip. @@ -888,7 +891,11 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(getAllyBotForce(getContract(campaign), getStartingPos(), playerHome, allyEntities), campaign); } - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), campaign); + CombatTeam combatTeam = getCombatTeamById(campaign); + + if (combatTeam != null) { + addEnemyForce(enemyEntities, combatTeam.getWeightClass(campaign), campaign); + } addBotForce(getEnemyBotForce(getContract(campaign), enemyHome, enemyHome, enemyEntities), campaign); } @@ -994,7 +1001,7 @@ private void addEnemyLance(List list, int weight, int maxWeight, Campaig addLance(list, getContract(campaign).getEnemyCode(), getContract(campaign).getEnemySkill(), getContract(campaign).getEnemyQuality(), weight, maxWeight, campaign); - lanceCount++; + forceCount++; } /** @@ -1453,12 +1460,15 @@ protected void addAeroReinforcements(Campaign campaign) { // TODO: replace with TeamLoadoutGenerator call once 0.50.1 errata go in boolean isAeroMap = getBoardType() == T_SPACE || getBoardType() == T_ATMOSPHERE; - - TeamLoadOutGenerator.populateAeroBombs(aircraft, - campaign.getGameYear(), - !isAeroMap, - contract.getEnemyQuality(), - contract.getEnemy().isPirate()); + TeamLoadOutGenerator tlg = new TeamLoadOutGenerator(campaign.getGame()); + tlg.populateAeroBombs( + aircraft, + campaign.getGameYear(), + !isAeroMap, + contract.getEnemyQuality(), + contract.getEnemy().isPirate(), + contract.getEnemy().getShortName() + ); BotForce bf = getEnemyBotForce(getContract(campaign), enemyHome, enemyHome, aircraft); bf.setName(bf.getName() + " (Air Support)"); @@ -1640,10 +1650,10 @@ protected void setObjectives(Campaign c, AtBContract contract) { @Override protected void writeToXMLEnd(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "attacker", isAttacker()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lanceForceId", strategicFormationId); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lanceRole", lanceRole.name()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lanceForceId", combatTeamId); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "combatRole", combatRole.name()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "deploymentDelay", deploymentDelay); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lanceCount", lanceCount); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "forceCount", forceCount); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "rerollsRemaining", rerollsRemaining); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "modifiedTemperature", modifiedTemperature); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "terrainType", terrainType); @@ -1734,15 +1744,17 @@ protected void loadFieldsFromXmlNode(final Node wn, final Version version, final if (wn2.getNodeName().equalsIgnoreCase("attacker")) { setAttacker(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("lanceForceId")) { - strategicFormationId = Integer.parseInt(wn2.getTextContent()); - } else if (wn2.getNodeName().equalsIgnoreCase("lanceRole")) { - lanceRole = AtBLanceRole.parseFromString(wn2.getTextContent().trim()); + combatTeamId = Integer.parseInt(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("combatRole")) { + combatRole = CombatRole.parseFromString(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("deploymentDelay")) { deploymentDelay = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("usingFixedMap")) { setUsingFixedMap(Boolean.parseBoolean(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("lanceCount")) { - lanceCount = Integer.parseInt(wn2.getTextContent()); + // <50.02 compatibility handler + } else if (wn2.getNodeName().equalsIgnoreCase("lanceCount") + || wn2.getNodeName().equalsIgnoreCase("forceCount")) { + forceCount = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("rerollsRemaining")) { rerollsRemaining = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("modifiedTemperature")) { @@ -1962,20 +1974,24 @@ public List filterUntransportedUnits(List entities) { return retVal; } - public int getStrategicFormationId() { - return strategicFormationId; + public int getCombatTeamId() { + return combatTeamId; } - public AtBLanceRole getLanceRole() { - return lanceRole; + public CombatRole getCombatRole() { + return combatRole; } - public StrategicFormation getStrategicFormation(Campaign campaign) { - return campaign.getStrategicFormationsTable().get(strategicFormationId); + public @Nullable CombatTeam getCombatTeamById(Campaign campaign) { + if (combatTeamId == NO_COMBAT_TEAM) { + return null; + } + + return campaign.getCombatTeamsTable().get(combatTeamId); } - public void setLance(StrategicFormation strategicFormation) { - strategicFormationId = strategicFormation.getForceId(); + public void setCombatTeam(CombatTeam combatTeam) { + combatTeamId = combatTeam.getForceId(); } /** @@ -2020,12 +2036,12 @@ public void setDeploymentDelay(int delay) { this.deploymentDelay = delay; } - public int getLanceCount() { - return lanceCount; + public int getForceCount() { + return forceCount; } - public void setLanceCount(int lanceCount) { - this.lanceCount = lanceCount; + public void setForceCount(int forceCount) { + this.forceCount = forceCount; } public int getRerollsRemaining() { @@ -2103,4 +2119,51 @@ public String getDeploymentInstructions() { public boolean canStartScenario(Campaign c) { return c.getLocalDate().equals(getDate()) && super.canStartScenario(c); } + + /** + * Retrieves the {@link StratconScenario} associated with the current mission ID. + * + *

    The method first retrieves the {@link AtBContract} from the given campaign. + * If the contract, its {@link StratconCampaignState}, or any required track data + * is unavailable, the method returns {@code null}. It iterates through all + * {@link StratconTrackState} objects in the campaign state and their associated scenarios. + * If a {@link StratconScenario} contains a non-null {@link AtBDynamicScenario} whose + * mission ID matches the current mission ID, it is returned. + * + * @param campaign the {@link Campaign} instance being queried for the scenario + * @return the matching {@link StratconScenario} if found, or {@code null} if no match + * is found or any required data is missing + * @throws NullPointerException if {@code campaign} is {@code null} + */ + public @Nullable StratconScenario getStratconScenario(Campaign campaign) { + // Get contract + AtBContract contract = getContract(campaign); + if (contract == null) { + return null; + } + + // Fetch campaign state + StratconCampaignState campaignState = contract.getStratconCampaignState(); + if (campaignState == null) { + return null; + } + + // Find associated StratCon Scenario, if any + for (StratconTrackState track : campaignState.getTracks()) { + Collection trackScenarios = track.getScenarios().values(); + + for (StratconScenario scenario : trackScenarios) { + AtBDynamicScenario backingScenario = scenario.getBackingScenario(); + if (backingScenario == null) { + continue; + } + + if (backingScenario.getMissionId() == getMissionId()) { + return scenario; + } + } + } + + return null; + } } diff --git a/MekHQ/src/mekhq/campaign/mission/CommonObjectiveFactory.java b/MekHQ/src/mekhq/campaign/mission/CommonObjectiveFactory.java index dea23e10264..41ff74033e6 100644 --- a/MekHQ/src/mekhq/campaign/mission/CommonObjectiveFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/CommonObjectiveFactory.java @@ -23,6 +23,7 @@ import megamek.common.OffBoardDirection; import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; import mekhq.campaign.mission.ObjectiveEffect.EffectScalingType; import mekhq.campaign.mission.ObjectiveEffect.ObjectiveEffectType; import mekhq.campaign.mission.ScenarioObjective.ObjectiveCriterion; @@ -31,7 +32,7 @@ import java.util.ResourceBundle; import java.util.UUID; -import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; +import static mekhq.campaign.force.CombatTeam.getStandardForceSize; /** * This class contains code for the creation of some common objectives for an AtB scenario @@ -230,8 +231,12 @@ private static void addAssignedPlayerUnitsToObjective(AtBScenario scenario, Camp // some scenarios have a lance assigned // some scenarios have individual units assigned - if (scenario.getStrategicFormationId() != AtBScenario.NO_LANCE) { - objective.addForce(campaign.getForce(scenario.getStrategicFormationId()).getName()); + if (scenario.getCombatTeamId() != AtBScenario.NO_COMBAT_TEAM) { + Force force = campaign.getForce(scenario.getCombatTeamId()); + + if (force != null) { + objective.addForce(campaign.getForce(scenario.getCombatTeamId()).getName()); + } } else { int unitCount = 0; diff --git a/MekHQ/src/mekhq/campaign/mission/Contract.java b/MekHQ/src/mekhq/campaign/mission/Contract.java index 4718d3a3279..7af8dbdb769 100644 --- a/MekHQ/src/mekhq/campaign/mission/Contract.java +++ b/MekHQ/src/mekhq/campaign/mission/Contract.java @@ -135,6 +135,11 @@ public void setEmployer(String s) { this.employer = s; } + /** + * Returns the contract length in months. + * + * @return the number and corresponding length of the contact in months as an integer + */ public int getLength() { return nMonths; } @@ -183,6 +188,10 @@ public int getTransportComp() { return transportComp; } + public String getTransportCompString() { + return String.valueOf(transportComp) + "%"; + } + public void setTransportComp(int s) { transportComp = s; } @@ -191,6 +200,10 @@ public int getStraightSupport() { return straightSupport; } + public String getStraightSupportString() { + return String.valueOf(straightSupport) + "%"; + } + public void setStraightSupport(int s) { straightSupport = Math.max(0, Math.min(100, s)); } @@ -215,6 +228,10 @@ public int getBattleLossComp() { return battleLossComp; } + public String getBattleLossCompString() { + return String.valueOf(battleLossComp) + "%"; + } + public void setBattleLossComp(int s) { battleLossComp = Math.max(0, Math.min(100, s)); } @@ -223,6 +240,10 @@ public int getSalvagePct() { return salvagePct; } + public String getSalvagePctString() { + return String.valueOf(salvagePct) + "%"; + } + public void setSalvagePct(int s) { salvagePct = s; } diff --git a/MekHQ/src/mekhq/campaign/mission/Loot.java b/MekHQ/src/mekhq/campaign/mission/Loot.java index eb35a5f8d09..29c11d85eb3 100644 --- a/MekHQ/src/mekhq/campaign/mission/Loot.java +++ b/MekHQ/src/mekhq/campaign/mission/Loot.java @@ -20,13 +20,6 @@ */ package mekhq.campaign.mission; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashMap; - -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.Version; import megamek.common.Entity; import megamek.common.MekFileParser; @@ -34,14 +27,30 @@ import megamek.common.MekSummaryCache; import megamek.common.loaders.EntityLoadingException; import megamek.logging.MMLogger; +import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.campaign.ResolveScenarioTracker.UnitStatus; import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; +import mekhq.campaign.mission.enums.ScenarioType; +import mekhq.campaign.parts.Armor; import mekhq.campaign.parts.Part; import mekhq.campaign.parts.enums.PartQuality; +import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.unit.Unit; import mekhq.utilities.MHQXMLUtility; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.PrintWriter; +import java.util.*; + +import static mekhq.campaign.mission.resupplyAndCaches.GenerateResupplyContents.RESUPPLY_MINIMUM_PART_WEIGHT; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.RESUPPLY_AMMO_TONNAGE; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.RESUPPLY_ARMOR_TONNAGE; +import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; /** * @author Jay Lawson (jaylawson39 at yahoo.com) @@ -113,76 +122,157 @@ public void clearParts() { } public String getShortDescription() { - String desc = getName() + " - "; + StringBuilder description = new StringBuilder(""); + + description.append("").append(getName()).append(""); if (!cash.isZero()) { - desc += cash.toAmountAndSymbolString(); + description.append("
    - ").append(cash.toAmountAndSymbolString()); } - if (!units.isEmpty()) { - String s = units.size() + " unit"; - if (units.size() > 1) { - s += "s"; - } - - if (!cash.isZero()) { - s = ", " + s; - } - desc += s; + for (Entity entity : units) { + description.append("
    - ").append(entity.getDisplayName()); } - if (!parts.isEmpty()) { - String s = parts.size() + " part"; - if (parts.size() > 1) { - s += "s"; - } - - if (!cash.isZero() || !units.isEmpty()) { - s = ", " + s; + for (Part part : parts) { + description.append("
    - ").append(part.getName()); + if (part.isClan()) { + description.append(" (Clan)"); } - desc += s; + description.append(" [").append(part.getQualityName()).append(']'); } - return desc; + description.append(""); + + return description.toString(); } /** - * Looting method that adds loot to the campaign, including cash, parts, and - * units. + * Handles the looting process after a scenario is completed by adding loot to the campaign. + * The loot can include cash rewards, salvageable parts, and units, along with handling + * special scenarios like resupply interception. + * + *

    This method evaluates the loot based on the scenario type and unit statuses, + * ensuring that captured, lost, or excess loot is appropriately managed.

    * - * @param campaign the campaign to add the loot to - * @param scenario the scenario during which the loot was acquired + * @param campaign the campaign to which the looted resources (e.g., cash, parts, units) are added + * @param scenario the specific scenario during which the loot was acquired + * @param unitsStatuses a mapping of unit IDs to their respective status after the scenario + * (e.g., whether units are lost, captured, or operational) */ - public void getLoot(Campaign campaign, Scenario scenario) { + public void getLoot(Campaign campaign, Scenario scenario, + Hashtable unitsStatuses) { + final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Loot", + MekHQ.getMHQOptions().getLocale()); + + boolean isResupply = scenario.getStratConScenarioType() == ScenarioType.SPECIAL_RESUPPLY; + double cargo = 0; + + // If we're looting as the result of a StratCon Emergency Convoy Defence scenario, + // we need to determine how much of the convoy survived + if (isResupply) { + List allUnitIds = new ArrayList<>( + scenario.getForces(campaign).getAllUnits(false)); + + for (UUID unitId : allUnitIds) { + Unit unit = campaign.getUnit(unitId); + + if (unit != null) { + if (unitsStatuses.containsKey(unitId)) { + if (unitsStatuses.get(unitId).isTotalLoss()) { + logger.debug("Unit {} is total loss", unit.getName()); + continue; + } + + if (unitsStatuses.get(unitId).isLikelyCaptured()) { + logger.debug("Unit {} is likely captured", unit.getName()); + continue; + } + } + + cargo += unit.getCargoCapacity(); + } + } + logger.debug("cargo capacity: {}", cargo); + } + if (cash.isPositive()) { logger.debug("Looting cash: {}", cash); campaign.getFinances().credit(TransactionType.MISCELLANEOUS, campaign.getLocalDate(), cash, "Reward for " + getName() + " during " + scenario.getName()); + + campaign.addReport(String.format(resources.getString("looted.cash"), + cash.toAmountAndSymbolString(), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); } - for (Part p : parts) { - logger.debug("Looting part: {}", p.getName()); + List abandonedParts = new ArrayList<>(); + List lootedParts = new ArrayList<>(); + Collections.shuffle(parts); + + logger.info("Looting parts: {}", parts.toString()); + + for (Part part : parts) { + double partWeight = part.getTonnage(); + partWeight = partWeight == 0 ? RESUPPLY_MINIMUM_PART_WEIGHT : partWeight; + + if (part instanceof AmmoBin) { + partWeight = RESUPPLY_AMMO_TONNAGE; + } else if (part instanceof Armor) { + partWeight = RESUPPLY_ARMOR_TONNAGE; + } + + if (isResupply) { + if (cargo - partWeight < 0) { + abandonedParts.add("
    - " + part.getName() + " (" + partWeight + " tons)"); + continue; + } else { + cargo -= partWeight; + } + } + + logger.debug("Looting part: {}", part.getName()); - campaign.getQuartermaster().addPart(p, 0); + lootedParts.add("
    - " + part.getName() + " (" + partWeight + " tons)"); + campaign.getQuartermaster().addPart(part, 0); logger.debug("Looting parts complete"); } + if (!lootedParts.isEmpty()) { + String lootedPartsReport = lootedParts.toString() + .replace("[", "") + .replace("]", ""); + campaign.addReport(String.format(resources.getString("looted.successful.parts"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG, lootedPartsReport)); + } + + if (!abandonedParts.isEmpty()) { + String abandonedPartsReport = abandonedParts.toString() + .replace("[", "") + .replace("]", ""); + campaign.addReport(String.format(resources.getString("looted.failed.parts"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG, abandonedPartsReport)); + } + // This only needs to be done once, so we do it outside the 'loot units' loop // for efficiency HashMap qualityAndModifier = getQualityAndModifier( campaign.getMission(scenario.getMissionId())); - for (Entity e : units) { - logger.debug("Looting unit: {}", e.getDisplayName()); + for (Entity entity : units) { + logger.debug("Looting unit: {}", entity.getDisplayName()); if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { qualityAndModifier.put("quality", Unit.getRandomUnitQuality(qualityAndModifier.get("modifier")).toNumeric()); } - campaign.addNewUnit(e, false, 0, + campaign.addNewUnit(entity, false, 0, PartQuality.fromNumeric(qualityAndModifier.get("quality"))); logger.debug("Looting units complete"); @@ -243,12 +333,24 @@ public void writeToXML(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "loot"); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "name", name); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "cash", getCash()); - for (Entity e : units) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "entityName", e.getShortNameRaw()); + for (Entity entity : units) { + // This null protection was implemented in 50.03 to guard against a bug in the + // depreciated Legacy AtB Digital GM. + if (entity == null) { + continue; + } + + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "entityName", entity.getShortNameRaw()); } - for (Part p : parts) { - p.writeToXML(pw, indent); + for (Part part : parts) { + // This null protection was implemented in 50.03 to guard against a bug in the + // depreciated Legacy AtB Digital GM. + if (part == null) { + continue; + } + + part.writeToXML(pw, indent); } MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "loot"); } diff --git a/MekHQ/src/mekhq/campaign/mission/ObjectiveEffect.java b/MekHQ/src/mekhq/campaign/mission/ObjectiveEffect.java index 035436a6160..8fcae1a4271 100644 --- a/MekHQ/src/mekhq/campaign/mission/ObjectiveEffect.java +++ b/MekHQ/src/mekhq/campaign/mission/ObjectiveEffect.java @@ -90,17 +90,17 @@ public enum ObjectiveEffectType { /* * In StratCon, relevant if scenario is about a facility, said facility remains in play. */ - FacilityRemains("Facility Remains Intact", false), + FacilityRemains("Facility remains with current controller", false), /* * In StratCon, relevant if scenario is about a facility, said facility is removed from play. */ - FacilityRemoved("Facility Destroyed", false), + FacilityRemoved("Facility destroyed and removed from area of operations", false), /* * In StratCon, relevant if scenario is about a facility, said facility changes ownership. */ - FacilityCaptured("Facility Captured", false); + FacilityCaptured("Facility captured and changes controller", false); private final String descriptiveText; private boolean magnitudeIsRelevant; diff --git a/MekHQ/src/mekhq/campaign/mission/Scenario.java b/MekHQ/src/mekhq/campaign/mission/Scenario.java index 4bf26f8e86e..ef1e4f1f977 100644 --- a/MekHQ/src/mekhq/campaign/mission/Scenario.java +++ b/MekHQ/src/mekhq/campaign/mission/Scenario.java @@ -3,6 +3,7 @@ * * Copyright (C) 2011-2016 - The MegaMek Team. All Rights Reserved. * Copyright (c) 2011 Jay Lawson (jaylawson39 at yahoo.com). All rights reserved. + * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,34 +22,13 @@ */ package mekhq.campaign.mission; -import java.io.PrintWriter; -import java.text.ParseException; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.Vector; - -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.Version; import megamek.client.ui.swing.lobby.LobbyUtility; import megamek.common.Board; import megamek.common.Entity; import megamek.common.MapSettings; import megamek.common.annotations.Nullable; -import megamek.common.planetaryconditions.Atmosphere; -import megamek.common.planetaryconditions.BlowingSand; -import megamek.common.planetaryconditions.EMI; -import megamek.common.planetaryconditions.Fog; -import megamek.common.planetaryconditions.Light; -import megamek.common.planetaryconditions.PlanetaryConditions; -import megamek.common.planetaryconditions.Weather; -import megamek.common.planetaryconditions.Wind; +import megamek.common.planetaryconditions.*; import megamek.logging.MMLogger; import mekhq.MekHQ; import mekhq.campaign.Campaign; @@ -58,8 +38,17 @@ import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.atb.IAtBScenario; import mekhq.campaign.mission.enums.ScenarioStatus; +import mekhq.campaign.mission.enums.ScenarioType; import mekhq.campaign.unit.Unit; import mekhq.utilities.MHQXMLUtility; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.PrintWriter; +import java.text.ParseException; +import java.time.LocalDate; +import java.util.*; /** * @author Jay Lawson (jaylawson39 at yahoo.com) @@ -78,6 +67,7 @@ public class Scenario implements IPlayerSettings { private int boardType = T_GROUND; private String name; + private ScenarioType stratConScenarioType; private String desc; private String report; private ScenarioStatus status; @@ -101,6 +91,9 @@ public class Scenario implements IPlayerSettings { protected List botForces; protected List botForcesStubs; + // linked Interception Scenario + private int LinkedScenarioID; + // stores external id of bot forces private Map externalIDLookup; @@ -145,8 +138,9 @@ public Scenario() { this(null); } - public Scenario(String n) { - this.name = n; + public Scenario(String name) { + this.name = name; + stratConScenarioType = ScenarioType.NONE; desc = ""; report = ""; setStatus(ScenarioStatus.CURRENT); @@ -159,6 +153,7 @@ public Scenario(String n) { botForces = new ArrayList<>(); botForcesStubs = new ArrayList<>(); externalIDLookup = new HashMap<>(); + LinkedScenarioID = 0; light = Light.DAY; weather = Weather.CLEAR; @@ -193,6 +188,14 @@ public void setName(String n) { this.name = n; } + public ScenarioType getStratConScenarioType() { + return stratConScenarioType; + } + + public void setStratConScenarioType(ScenarioType type) { + this.stratConScenarioType = type; + } + public String getDescription() { return desc; } @@ -225,6 +228,14 @@ public void setDate(final @Nullable LocalDate date) { this.date = date; } + public int getLinkedScenario() { + return LinkedScenarioID; + } + + public void setLinkedScenarioID(int ScenarioID) { + LinkedScenarioID = ScenarioID; + } + public boolean hasObjectives() { return (scenarioObjectives != null) && !scenarioObjectives.isEmpty(); } @@ -248,6 +259,7 @@ public void setCloaked(boolean cloaked) { this.cloaked = cloaked; } + @Override public int getStartingPos() { return startingPos; } @@ -472,7 +484,7 @@ public void setMinWindStrength(Wind strength) { /** * Create a PlanetaryConditions object from variables - * + * * @return PlanetaryConditions object */ public PlanetaryConditions createPlanetaryConditions() { @@ -498,7 +510,7 @@ public PlanetaryConditions createPlanetaryConditions() { * Read the values from a PlanetaryConditions object into the Scenario variables * for planetary conditions. * This is necessary because MekHQ has XML and MegaMek doesn't. - * + * * @param planetaryConditions A PlanetaryConditions object */ public void readPlanetaryConditions(PlanetaryConditions planetaryConditions) { @@ -531,7 +543,7 @@ public Map> getPlayerTransportLinkages() { /** * Adds a transport-cargo pair to the internal transport relationship store. - * + * * @param transportId the UUID of the transport object * @param cargoId the UUID of the cargo being transported */ @@ -701,7 +713,7 @@ public List getBotForcesStubs() { * Get a List of all traitor Units in this scenario. This function just combines * the results from * BotForce#getTraitorUnits across all BotForces. - * + * * @param c - A Campaign pointer * @return a List of traitor Units */ @@ -718,7 +730,7 @@ public List getTraitorUnits(Campaign c) { * external id values. This should * also be usable against entities that are ejected pilots from the original * traitor entity. - * + * * @param en a MegaMek Entity * @param c a Campaign pointer * @return a boolean indicating whether this entity is a traitor in this @@ -736,16 +748,14 @@ public boolean isTraitor(Entity en, Campaign c) { } // also make sure that the crew's external id does not match a traitor in // case of ejected pilots - if ((null != en.getCrew()) && !"-1".equals(en.getCrew().getExternalIdAsString()) && - isTraitor(UUID.fromString(en.getCrew().getExternalIdAsString()))) { - return true; - } - return false; + return (null != en.getCrew()) + && !"-1".equals(en.getCrew().getExternalIdAsString()) + && isTraitor(UUID.fromString(en.getCrew().getExternalIdAsString())); } /** * Given a Person's id, is that person a traitor in this Scenario - * + * * @param personId - a UUID giving a person's id in the campaign * @return a boolean indicating if this person is a traitor in the Scenario */ @@ -772,7 +782,7 @@ public void setExternalIDLookup(HashMap externalIDLookup) { * the unit type will be checked to make sure it is valid. The function also * checks to see if the unit is * a traitor unit which will disallow deployment. - * + * * @param unit - The Unit to be deployed * @param campaign - a pointer to the Campaign * @return true if the unit is eligible, otherwise false @@ -809,10 +819,7 @@ public boolean canDeployUnits(Vector units, Campaign campaign) { } } if (null != deploymentLimit) { - if ((deploymentLimit.getCurrentQuantity(this, campaign) + additionalQuantity) > deploymentLimit - .getQuantityCap(campaign)) { - return false; - } + return (deploymentLimit.getCurrentQuantity(this, campaign) + additionalQuantity) <= deploymentLimit.getQuantityCap(campaign); } return true; } @@ -839,10 +846,7 @@ public boolean canDeployForces(Vector forces, Campaign c) { } } if (null != deploymentLimit) { - if ((deploymentLimit.getCurrentQuantity(this, c) + additionalQuantity) > deploymentLimit - .getQuantityCap(c)) { - return false; - } + return (deploymentLimit.getCurrentQuantity(this, c) + additionalQuantity) <= deploymentLimit.getQuantityCap(c); } return true; } @@ -866,6 +870,7 @@ public boolean includesRequiredUnits(Campaign c) { } public boolean canStartScenario(Campaign c) { + if (!getStatus().isCurrent()) { return false; } @@ -875,10 +880,7 @@ public boolean canStartScenario(Campaign c) { if (!includesRequiredPersonnel(c)) { return false; } - if (!includesRequiredUnits(c)) { - return false; - } - return true; + return includesRequiredUnits(c); } public void writeToXML(final PrintWriter pw, int indent) { @@ -889,6 +891,7 @@ public void writeToXML(final PrintWriter pw, int indent) { protected int writeToXMLBegin(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "scenario", "id", id, "type", getClass()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "name", getName()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "stratConScenarioType", stratConScenarioType.name()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "desc", desc); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "report", report); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "startingPos", startingPos); @@ -900,6 +903,7 @@ protected int writeToXMLBegin(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "startingAnySEy", startingAnySEy); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "status", getStatus().name()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "id", id); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "LinkedScenarioID", LinkedScenarioID); if (null != stub) { stub.writeToXML(pw, indent); } else { @@ -1023,6 +1027,8 @@ public static Scenario generateInstanceFromXML(Node wn, Campaign c, Version vers if (wn2.getNodeName().equalsIgnoreCase("name")) { retVal.setName(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("stratConScenarioType")) { + retVal.setStratConScenarioType(ScenarioType.parseFromString(wn2.getTextContent())); } else if (wn2.getNodeName().equalsIgnoreCase("status")) { retVal.setStatus(ScenarioStatus.parseFromString(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("id")) { @@ -1033,6 +1039,8 @@ public static Scenario generateInstanceFromXML(Node wn, Campaign c, Version vers retVal.setReport(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("forceStub")) { retVal.stub = ForceStub.generateInstanceFromXML(wn2, version); + } else if (wn2.getNodeName().equalsIgnoreCase("LinkedScenarioID")) { + retVal.LinkedScenarioID = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("date")) { retVal.date = MHQXMLUtility.parseDate(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("cloaked")) { diff --git a/MekHQ/src/mekhq/campaign/mission/ScenarioObjectiveProcessor.java b/MekHQ/src/mekhq/campaign/mission/ScenarioObjectiveProcessor.java index 78d975624bc..cfad77a3050 100644 --- a/MekHQ/src/mekhq/campaign/mission/ScenarioObjectiveProcessor.java +++ b/MekHQ/src/mekhq/campaign/mission/ScenarioObjectiveProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2019-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,15 +21,21 @@ import megamek.common.Entity; import megamek.common.OffBoardDirection; import mekhq.MHQConstants; +import mekhq.campaign.Campaign; import mekhq.campaign.ResolveScenarioTracker; import mekhq.campaign.force.Force; import mekhq.campaign.mission.ObjectiveEffect.EffectScalingType; import mekhq.campaign.mission.ObjectiveEffect.ObjectiveEffectType; import mekhq.campaign.mission.enums.ScenarioStatus; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; +import mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType; import mekhq.campaign.stratcon.StratconRulesManager; +import org.apache.logging.log4j.LogManager; import java.util.*; +import static mekhq.campaign.mission.resupplyAndCaches.PerformResupply.performResupply; + /** * Handles processing for objectives for a scenario that has them * @author NickAragua @@ -201,22 +207,15 @@ public void updateObjectiveEntityState(Entity entity, boolean forceEntityEscape, private boolean entityMeetsObjective(Entity entity, ScenarioObjective objective, Set objectiveUnitIDs, boolean opponentHasBattlefieldControl) { if (objectiveUnitIDs.contains(entity.getExternalIdAsString())) { - switch (objective.getObjectiveCriterion()) { - case Destroy: - return entityIsDestroyed(entity, opponentHasBattlefieldControl); - case ForceWithdraw: - return entityIsForcedWithdrawal(entity); - case Capture: - return entityIsCaptured(entity, !opponentHasBattlefieldControl); - case PreventReachMapEdge: - return !entityHasReachedDestinationEdge(entity, objective); - case Preserve: - return !entityIsDestroyed(entity, opponentHasBattlefieldControl); - case ReachMapEdge: - return entityHasReachedDestinationEdge(entity, objective); - default: - return false; - } + return switch (objective.getObjectiveCriterion()) { + case Destroy -> entityIsDestroyed(entity, opponentHasBattlefieldControl); + case ForceWithdraw -> entityIsForcedWithdrawal(entity); + case Capture -> entityIsCaptured(entity, !opponentHasBattlefieldControl); + case PreventReachMapEdge -> !entityHasReachedDestinationEdge(entity, objective); + case Preserve -> !entityIsDestroyed(entity, opponentHasBattlefieldControl); + case ReachMapEdge -> entityHasReachedDestinationEdge(entity, objective); + default -> false; + }; } return false; @@ -313,7 +312,7 @@ public ScenarioStatus determineScenarioStatus(Scenario scenario, * @param tracker The tracker from which to draw unit data * @param dryRun Whether we're actually applying the objectives or just generating a report. */ - public String processObjective(ScenarioObjective objective, int qualifyingUnitCount, Boolean completionOverride, + public String processObjective(Campaign campaign, ScenarioObjective objective, int qualifyingUnitCount, Boolean completionOverride, ResolveScenarioTracker tracker, boolean dryRun) { // if we've overridden the objective completion flag, great, otherwise, calculate it here boolean objectiveMet = completionOverride == null ? objectiveMet(objective, qualifyingUnitCount) : completionOverride; @@ -331,7 +330,7 @@ public String processObjective(ScenarioObjective objective, int qualifyingUnitCo } for (ObjectiveEffect effect : objectiveEffects) { - sb.append(processObjectiveEffect(effect, + sb.append(processObjectiveEffect(campaign, effect, effect.effectScaling == EffectScalingType.Inverted ? numUnitsFailedObjective : qualifyingUnitCount, tracker, dryRun)); sb.append("\n\t"); @@ -341,12 +340,32 @@ public String processObjective(ScenarioObjective objective, int qualifyingUnitCo } /** - * Processes an individual objective effect. - * @param effect - * @param scaleFactor If it's scaled, how much to scale it by - * @param tracker + * Processes the specified objective effect during a scenario and applies the corresponding + * changes to the campaign or contract. This method determines the type of effect and executes + * its logic accordingly, supporting both dry-run and active execution modes. + * + *

    Effect types supported include (but are not limited to): + *

      + *
    • Scenario victory or defeat point adjustments.
    • + *
    • Updates to contract scores, morale, or support points.
    • + *
    • Triggering contract victory or defeat conditions.
    • + *
    • Handling AtB-specific bonuses, such as bonus resupply points or resource drops.
    • + *
    • Updating the status of strategic facilities (e.g., capturing/destroying facilities).
    • + *
    + * + *

    When run in dry-run mode, the method simply returns a description of the effect without + * applying any changes to the campaign or contract. In active mode, the effect is applied to + * the appropriate {@link Campaign}, {@link AtBContract}, or scenario entities.

    + * + * @param campaign the {@link Campaign} instance being updated. + * @param effect the {@link ObjectiveEffect} defining the effect type and its further details. + * @param scaleFactor the factor by which the objective effect is scaled (used for value calculations). + * @param tracker the {@link ResolveScenarioTracker} providing context about the scenario, mission, and contract. + * @param dryRun a {@code boolean} flag indicating if the method should process in dry-run mode + * ({@code true}) or actively apply the effect ({@code false}). + * @return a {@link String} describing the effect, used during dry-run mode; otherwise an empty string is returned. */ - private String processObjectiveEffect(ObjectiveEffect effect, int scaleFactor, + private String processObjectiveEffect(Campaign campaign, ObjectiveEffect effect, int scaleFactor, ResolveScenarioTracker tracker, boolean dryRun) { switch (effect.effectType) { case ScenarioVictory: @@ -361,9 +380,7 @@ private String processObjectiveEffect(ObjectiveEffect effect, int scaleFactor, break; case ContractScoreUpdate: // if atb contract, update contract score by how many units met criterion * scaling - if (tracker.getMission() instanceof AtBContract) { - AtBContract contract = (AtBContract) tracker.getMission(); - + if (tracker.getMission() instanceof AtBContract contract) { int effectMultiplier = effect.effectScaling == EffectScalingType.Fixed ? 1 : scaleFactor; int scoreEffect = effect.howMuch * effectMultiplier; @@ -375,9 +392,7 @@ private String processObjectiveEffect(ObjectiveEffect effect, int scaleFactor, } break; case SupportPointUpdate: - if (tracker.getMission() instanceof AtBContract) { - AtBContract contract = (AtBContract) tracker.getMission(); - + if (tracker.getMission() instanceof AtBContract contract) { if (contract.getStratconCampaignState() != null) { int effectMultiplier = effect.effectScaling == EffectScalingType.Fixed ? 1 : scaleFactor; int numSupportPoints = effect.howMuch * effectMultiplier; @@ -410,16 +425,23 @@ private String processObjectiveEffect(ObjectiveEffect effect, int scaleFactor, case BVBudgetUpdate: break; case AtBBonus: - if (tracker.getMission() instanceof AtBContract) { - AtBContract contract = (AtBContract) tracker.getMission(); - + if (tracker.getMission() instanceof AtBContract contract) { int effectMultiplier = effect.effectScaling == EffectScalingType.Fixed ? 1 : scaleFactor; int numBonuses = effect.howMuch * effectMultiplier; if (dryRun) { return String.format("%d AtB bonus rolls", numBonuses); } else { + int dropSize = 0; for (int x = 0; x < numBonuses; x++) { - contract.doBonusRoll(tracker.getCampaign()); + dropSize += contract.doBonusRoll(tracker.getCampaign(), true) + ? 1 : 0; + } + + if (dropSize > 0) { + LogManager.getLogger().info("ScenarioObjectiveProcessor.java"); + campaign.addReport("Bonus: Captured Supplies"); + Resupply resupply = new Resupply(campaign, contract, ResupplyType.RESUPPLY_LOOT); + performResupply(resupply, contract, dropSize); } } } diff --git a/MekHQ/src/mekhq/campaign/mission/ScenarioTemplate.java b/MekHQ/src/mekhq/campaign/mission/ScenarioTemplate.java index b48c2fdf67d..8c08de33c5a 100644 --- a/MekHQ/src/mekhq/campaign/mission/ScenarioTemplate.java +++ b/MekHQ/src/mekhq/campaign/mission/ScenarioTemplate.java @@ -19,20 +19,6 @@ package mekhq.campaign.mission; -import java.io.File; -import java.io.FileInputStream; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import javax.xml.namespace.QName; -import javax.xml.transform.Source; - -import org.w3c.dom.Node; - import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBElement; import jakarta.xml.bind.Marshaller; @@ -40,11 +26,28 @@ import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlElementWrapper; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.adapters.XmlAdapter; +import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import megamek.logging.MMLogger; import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; import mekhq.campaign.mission.ScenarioForceTemplate.ForceGenerationMethod; import mekhq.campaign.mission.ScenarioMapParameters.MapLocation; +import mekhq.campaign.mission.enums.ScenarioType; import mekhq.utilities.MHQXMLUtility; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.w3c.dom.Node; + +import javax.xml.namespace.QName; +import javax.xml.transform.Source; +import java.io.File; +import java.io.FileInputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * This is the root data structure for organizing information related to a @@ -58,8 +61,12 @@ public class ScenarioTemplate implements Cloneable { public static final String ROOT_XML_ELEMENT_NAME = "ScenarioTemplate"; public static final String PRIMARY_PLAYER_FORCE_ID = "Player"; + private static final Logger log = LogManager.getLogger(ScenarioTemplate.class); public String name; + @XmlElement(name = "stratConScenarioType") + @XmlJavaTypeAdapter(value = ScenarioTypeAdapter.class) + private ScenarioType stratConScenarioType = ScenarioType.NONE; public String shortBriefing; public String detailedBriefing; @@ -77,27 +84,41 @@ public class ScenarioTemplate implements Cloneable { @Override public ScenarioTemplate clone() { - ScenarioTemplate st = new ScenarioTemplate(); - st.name = this.name; - st.shortBriefing = this.shortBriefing; - st.detailedBriefing = this.detailedBriefing; - st.isHostileFacility = this.isHostileFacility; - st.isAlliedFacility = this.isAlliedFacility; + ScenarioTemplate template = new ScenarioTemplate(); + template.name = this.name; + template.stratConScenarioType = this.stratConScenarioType; + template.shortBriefing = this.shortBriefing; + template.detailedBriefing = this.detailedBriefing; + template.isHostileFacility = this.isHostileFacility; + template.isAlliedFacility = this.isAlliedFacility; for (ScenarioForceTemplate sft : scenarioForces.values()) { - st.scenarioForces.put(sft.getForceName(), sft.clone()); + template.scenarioForces.put(sft.getForceName(), sft.clone()); } for (String mod : scenarioModifiers) { - st.scenarioModifiers.add(mod); + template.scenarioModifiers.add(mod); } for (ScenarioObjective obj : scenarioObjectives) { - st.scenarioObjectives.add(new ScenarioObjective(obj)); + template.scenarioObjectives.add(new ScenarioObjective(obj)); } - st.mapParameters = (ScenarioMapParameters) mapParameters.clone(); + template.mapParameters = mapParameters.clone(); + + return template; + } - return st; + public ScenarioType getStratConScenarioType() { + return (this.stratConScenarioType != null) ? this.stratConScenarioType : ScenarioType.NONE; + } + + public void setStratConScenarioType(String scenarioType) { + try { + this.stratConScenarioType = ScenarioType.valueOf(scenarioType.trim().toUpperCase()); + } catch (IllegalArgumentException e) { + logger.error("Invalid ScenarioType: " + scenarioType, e); + this.stratConScenarioType = ScenarioType.NONE; + } } /** @@ -288,8 +309,8 @@ public static ScenarioTemplate Deserialize(Node xmlNode) { try { JAXBContext context = JAXBContext.newInstance(ScenarioTemplate.class); - Unmarshaller um = context.createUnmarshaller(); - JAXBElement templateElement = um.unmarshal(xmlNode, ScenarioTemplate.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + JAXBElement templateElement = unmarshaller.unmarshal(xmlNode, ScenarioTemplate.class); resultingTemplate = templateElement.getValue(); } catch (Exception e) { logger.error("Error Deserializing Scenario Template", e); @@ -297,4 +318,22 @@ public static ScenarioTemplate Deserialize(Node xmlNode) { return resultingTemplate; } + + public static class ScenarioTypeAdapter extends XmlAdapter { + @Override + public ScenarioType unmarshal(String value) { + try { + return ScenarioType.valueOf(value.trim().toUpperCase()); + } catch (IllegalArgumentException iae) { + MMLogger.create(ScenarioTypeAdapter.class).error("Error Invalid ScenarioType in XML: " + value); + return ScenarioType.NONE; // Default for invalid values + } + } + + @Override + public String marshal(ScenarioType scenarioType) { + // Converts Enum back to String for XML + return String.valueOf(scenarioType); + } + } } diff --git a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java index 4650763d7e8..0d0636c7377 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java @@ -21,7 +21,7 @@ import megamek.codeUtilities.ObjectUtility; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; -import mekhq.campaign.force.StrategicFormation; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.AtBScenario; import mekhq.campaign.mission.atb.scenario.*; @@ -29,6 +29,8 @@ import java.time.LocalDate; import java.util.*; +import static mekhq.campaign.mission.AtBScenario.NO_COMBAT_TEAM; + public class AtBScenarioFactory { private static final MMLogger logger = MMLogger.create(AtBScenarioFactory.class); @@ -67,7 +69,7 @@ public static List> getScenarios(int type) { return scenarioMap.get(type); } - public static AtBScenario createScenario(Campaign c, StrategicFormation lance, int type, boolean attacker, LocalDate date) { + public static AtBScenario createScenario(Campaign c, CombatTeam lance, int type, boolean attacker, LocalDate date) { List> classList = getScenarios(type); Class selectedClass; @@ -126,9 +128,9 @@ public static void createScenariosForNewWeek(Campaign campaign) { } // If we have an active contract, then we can progress with generation - Hashtable strategicFormations = campaign.getStrategicFormationsTable(); + Hashtable combatTeamsTable = campaign.getCombatTeamsTable(); - List sList; + List scenarios; List assignedLances = new ArrayList<>(); List dontGenerateForces; boolean hasBaseAttack; @@ -137,7 +139,7 @@ public static void createScenariosForNewWeek(Campaign campaign) { // We only need to process active AtB contracts that haven't hit their end date for (final AtBContract contract : campaign.getActiveAtBContracts()) { // region Value Initialization - sList = new ArrayList<>(); + scenarios = new ArrayList<>(); dontGenerateForces = new ArrayList<>(); hasBaseAttack = false; hasBaseAttackAttacker = false; @@ -151,13 +153,15 @@ public static void createScenariosForNewWeek(Campaign campaign) { // the generation rules are followed for all active scenarios not just new // scenarios for (final AtBScenario scenario : contract.getCurrentAtBScenarios()) { - // Add any currently assigned strategicFormations to the assignedLances - assignedLances.add(scenario.getStrategicFormationId()); + // Add any currently assigned combatTeamsTable to the assignedLances + if (scenario.getCombatTeamId() != NO_COMBAT_TEAM) { + assignedLances.add(scenario.getCombatTeamId()); + } // Remove any active scenarios from the contract, and add them to the current // scenarios list instead contract.getScenarios().remove(scenario); - sList.add(scenario); + scenarios.add(scenario); dontGenerateForces.add(scenario.getId()); // If we have a current base attack (attacker) scenario, no other scenarios @@ -174,14 +178,14 @@ public static void createScenariosForNewWeek(Campaign campaign) { // endregion Current Scenarios // region Generate Scenarios - // Generate scenarios for strategicFormations based on their current situation + // Generate scenarios for combatTeamsTable based on their current situation if (!hasBaseAttackAttacker) { - for (StrategicFormation strategicFormation : strategicFormations.values()) { - // Don't generate scenarios for any strategicFormations already assigned, those assigned to a - // different contract, those not assigned to a contract, or for illegible strategicFormations - if (assignedLances.contains(strategicFormation.getForceId()) || (strategicFormation.getContract(campaign) == null) - || !strategicFormation.isEligible(campaign) || (strategicFormation.getMissionId() != contract.getId()) - || !strategicFormation.getContract(campaign).isActiveOn(campaign.getLocalDate(), true)) { + for (CombatTeam combatTeam : combatTeamsTable.values()) { + // Don't generate scenarios for any combatTeamsTable already assigned, those assigned to a + // different contract, those not assigned to a contract, or for illegible combatTeamsTable + if (assignedLances.contains(combatTeam.getForceId()) || (combatTeam.getContract(campaign) == null) + || !combatTeam.isEligible(campaign) || (combatTeam.getMissionId() != contract.getId()) + || !combatTeam.getContract(campaign).isActiveOn(campaign.getLocalDate(), true)) { continue; } @@ -191,13 +195,13 @@ public static void createScenariosForNewWeek(Campaign campaign) { continue; } - // Attempt to generate a scenario for the strategicFormation - AtBScenario scenario = strategicFormation.checkForBattle(campaign); + // Attempt to generate a scenario for the combatTeam + AtBScenario scenario = combatTeam.checkForBattle(campaign); // If one is generated, then add it to the scenario list if (scenario != null) { - sList.add(scenario); - assignedLances.add(strategicFormation.getForceId()); + scenarios.add(scenario); + assignedLances.add(combatTeam.getForceId()); // We care if the scenario is a Base Attack, as one must be generated if the // current contract's morale is Unbreakable @@ -226,64 +230,66 @@ public static void createScenariosForNewWeek(Campaign campaign) { * first to those assigned to the same contract, * then to those assigned to defense roles */ - List lList = new ArrayList<>(); - for (StrategicFormation strategicFormation : strategicFormations.values()) { - if ((strategicFormation.getMissionId() == contract.getId()) - && strategicFormation.getRole().isDefence() && strategicFormation.isEligible(campaign)) { - lList.add(strategicFormation); + List lList = new ArrayList<>(); + for (CombatTeam combatTeam : combatTeamsTable.values()) { + if ((combatTeam.getMissionId() == contract.getId()) + && combatTeam.getRole().isFrontline() && combatTeam.isEligible(campaign)) { + lList.add(combatTeam); } } if (lList.isEmpty()) { - for (StrategicFormation strategicFormation : strategicFormations.values()) { - if ((strategicFormation.getMissionId() == contract.getId()) && strategicFormation.isEligible(campaign)) { - lList.add(strategicFormation); + for (CombatTeam combatTeam : combatTeamsTable.values()) { + if ((combatTeam.getMissionId() == contract.getId()) && combatTeam.isEligible(campaign)) { + lList.add(combatTeam); } } } if (lList.isEmpty()) { - for (StrategicFormation strategicFormation : strategicFormations.values()) { - if (strategicFormation.isEligible(campaign)) { - lList.add(strategicFormation); + for (CombatTeam combatTeam : combatTeamsTable.values()) { + if (combatTeam.isEligible(campaign)) { + lList.add(combatTeam); } } } if (!lList.isEmpty()) { - StrategicFormation strategicFormation = ObjectUtility.getRandomItem(lList); - AtBScenario atbScenario = AtBScenarioFactory.createScenario(campaign, strategicFormation, - AtBScenario.BASEATTACK, false, StrategicFormation.getBattleDate(campaign.getLocalDate())); + CombatTeam combatTeam = ObjectUtility.getRandomItem(lList); + AtBScenario atbScenario = AtBScenarioFactory.createScenario(campaign, combatTeam, + AtBScenario.BASEATTACK, false, CombatTeam.getBattleDate(campaign.getLocalDate())); if (atbScenario != null) { - if ((strategicFormation.getMissionId() == atbScenario.getMissionId()) - || (strategicFormation.getMissionId() == StrategicFormation.NO_MISSION)) { - for (int i = 0; i < sList.size(); i++) { - if (sList.get(i).getStrategicFormationId() == strategicFormation.getForceId()) { + if ((combatTeam.getMissionId() == atbScenario.getMissionId()) + || (combatTeam.getMissionId() == CombatTeam.NO_MISSION)) { + for (int i = 0; i < scenarios.size(); i++) { + if ((scenarios.get(i).getCombatTeamId() != NO_COMBAT_TEAM) + && (scenarios.get(i).getCombatTeamId() == combatTeam.getForceId())) { if (dontGenerateForces.contains(atbScenario.getId())) { dontGenerateForces.remove(atbScenario.getId()); } - sList.set(i, atbScenario); + scenarios.set(i, atbScenario); break; } } } else { - // edge case: strategicFormation assigned to another mission gets assigned the scenario, + // edge case: combatTeam assigned to another mission gets assigned the scenario, // we need to remove any scenario they are assigned to already - campaign.getMission(strategicFormation.getMissionId()).getScenarios() + campaign.getMission(combatTeam.getMissionId()).getScenarios() .removeIf(scenario -> (scenario instanceof AtBScenario) - && (((AtBScenario) scenario).getStrategicFormationId() == strategicFormation.getForceId())); + && (((AtBScenario) scenario).getCombatTeamId() != NO_COMBAT_TEAM) + && (((AtBScenario) scenario).getCombatTeamId() == combatTeam.getForceId())); } - if (!sList.contains(atbScenario)) { - sList.add(atbScenario); + if (!scenarios.contains(atbScenario)) { + scenarios.add(atbScenario); } - if (!assignedLances.contains(strategicFormation.getForceId())) { - assignedLances.add(strategicFormation.getForceId()); + if (!assignedLances.contains(combatTeam.getForceId())) { + assignedLances.add(combatTeam.getForceId()); } } else { logger.error("Unable to generate Base Attack scenario."); } } else { - logger.warn("No strategicFormations assigned to mission " + contract.getName() + logger.warn("No combatTeamsTable assigned to mission " + contract.getName() + ". Can't generate an Unbreakable Morale base defense mission for this force."); } } @@ -295,7 +301,7 @@ public static void createScenariosForNewWeek(Campaign campaign) { // until it happens. // Therefore, all other currently generated scenarios need to be cleared if (hasBaseAttackAttacker) { - sList.removeIf(atbScenario -> !(atbScenario.isAttacker() + scenarios.removeIf(atbScenario -> !(atbScenario.isAttacker() && (atbScenario.getScenarioType() == AtBScenario.BASEATTACK))); } // endregion Base Attack (Attacker) Generated @@ -304,8 +310,8 @@ public static void createScenariosForNewWeek(Campaign campaign) { // Finally, sort the scenarios by date and add to the campaign, and generate // forces // for the scenario if required - sList.sort((s1, s2) -> ObjectUtility.compareNullable(s1.getDate(), s2.getDate(), LocalDate::compareTo)); - for (AtBScenario atbScenario : sList) { + scenarios.sort((s1, s2) -> ObjectUtility.compareNullable(s1.getDate(), s2.getDate(), LocalDate::compareTo)); + for (AtBScenario atbScenario : scenarios) { campaign.addScenario(atbScenario, contract); if (!dontGenerateForces.contains(atbScenario.getId())) { atbScenario.setForces(campaign); diff --git a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifier.java b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifier.java index fb75a507eea..87f3f6f4079 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifier.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifier.java @@ -18,16 +18,6 @@ */ package mekhq.campaign.mission.atb; -import java.io.File; -import java.io.FileInputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.namespace.QName; -import javax.xml.transform.Source; - import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBElement; import jakarta.xml.bind.Marshaller; @@ -47,6 +37,15 @@ import mekhq.campaign.mission.ScenarioObjective; import mekhq.utilities.MHQXMLUtility; +import javax.xml.namespace.QName; +import javax.xml.transform.Source; +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Data structure representing a scenario modifier for dynamic AtB scenarios * @@ -367,7 +366,7 @@ public void processModifier(AtBDynamicScenario scenario, Campaign campaign, Even if (eventTiming == getEventTiming()) { if ((getAdditionalBriefingText() != null) && !getAdditionalBriefingText().isBlank()) { AtBScenarioModifierApplicator.appendScenarioBriefingText(scenario, - String.format("%s: %s", getModifierName(), getAdditionalBriefingText())); + getAdditionalBriefingText()); } if (getForceDefinition() != null) { diff --git a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java index 9d4c5feabe6..b4764936c28 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java @@ -28,8 +28,11 @@ import megamek.logging.MMLogger; import mekhq.campaign.Campaign; import mekhq.campaign.force.Force; -import mekhq.campaign.mission.*; +import mekhq.campaign.mission.AtBDynamicScenario; +import mekhq.campaign.mission.BotForce; +import mekhq.campaign.mission.ScenarioForceTemplate; import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; +import mekhq.campaign.mission.ScenarioObjective; import mekhq.campaign.mission.atb.AtBScenarioModifier.EventTiming; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.Skills; @@ -40,9 +43,8 @@ import java.util.UUID; -import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; -import static mekhq.campaign.mission.AtBDynamicScenarioFactory.generateForce; -import static mekhq.campaign.mission.AtBDynamicScenarioFactory.randomForceWeight; +import static mekhq.campaign.force.CombatTeam.getStandardForceSize; +import static mekhq.campaign.mission.AtBDynamicScenarioFactory.*; /** * Class that handles the application of scenario modifier actions to @@ -74,9 +76,9 @@ public static void addForce(Campaign campaign, AtBDynamicScenario scenario, Scen */ private static void postAddForce(Campaign campaign, AtBDynamicScenario scenario, ScenarioForceTemplate templateToApply) { - int effectiveBV = AtBDynamicScenarioFactory.calculateEffectiveBV(scenario, campaign, false); - int effectiveUnitCount = AtBDynamicScenarioFactory.calculateEffectiveUnitCount(scenario, campaign, false); - int deploymentZone = AtBDynamicScenarioFactory.calculateDeploymentZone(templateToApply, scenario, + int effectiveBV = calculateEffectiveBV(scenario, campaign, false); + int effectiveUnitCount = calculateEffectiveUnitCount(scenario, campaign, false); + int deploymentZone = calculateDeploymentZone(templateToApply, scenario, templateToApply.getForceName()); int weightClass = randomForceWeight(); @@ -90,12 +92,13 @@ private static void postAddForce(Campaign campaign, AtBDynamicScenario scenario, // the most recently added bot force is the one we just generated BotForce generatedBotForce = scenario.getBotForce(scenario.getNumBots() - 1); generatedBotForce.setStartingPos(deploymentZone); - AtBDynamicScenarioFactory.setDeploymentTurns(generatedBotForce, templateToApply, scenario, campaign); - AtBDynamicScenarioFactory.setDestinationZone(generatedBotForce, templateToApply); + setDeploymentTurns(generatedBotForce, templateToApply, scenario, campaign); + setDestinationZone(generatedBotForce, templateToApply); // at this point, we have to re-translate the scenario objectives // since we're adding a force that could potentially go into any of them - AtBDynamicScenarioFactory.translateTemplateObjectives(scenario, campaign); + translateTemplateObjectives(scenario, campaign); + scaleObjectiveTimeLimits(scenario, campaign); } /** @@ -380,7 +383,7 @@ public static void applyObjective(AtBDynamicScenario scenario, Campaign campaign // if we're doing it after, we have to translate it individually if (timing == EventTiming.PostForceGeneration) { - ScenarioObjective actualObjective = AtBDynamicScenarioFactory.translateTemplateObjective(scenario, + ScenarioObjective actualObjective = translateTemplateObjective(scenario, campaign, objective); scenario.getScenarioObjectives().add(actualObjective); } diff --git a/MekHQ/src/mekhq/campaign/mission/atb/scenario/BaseAttackBuiltInScenario.java b/MekHQ/src/mekhq/campaign/mission/atb/scenario/BaseAttackBuiltInScenario.java index 62286ce7956..5891af7a97a 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/scenario/BaseAttackBuiltInScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/scenario/BaseAttackBuiltInScenario.java @@ -24,10 +24,11 @@ import megamek.common.Entity; import megamek.common.EntityWeightClass; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.*; import mekhq.campaign.mission.ObjectiveEffect.ObjectiveEffectType; import mekhq.campaign.mission.atb.AtBScenarioEnabled; -import mekhq.campaign.mission.enums.AtBLanceRole; +import mekhq.campaign.mission.enums.CombatRole; import java.util.ArrayList; @@ -98,7 +99,13 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti * Ally deploys 2 lances of a lighter weight class than the player, * minimum light */ - int allyForceWeight = Math.max(getStrategicFormation(campaign).getWeightClass(campaign) - 1, EntityWeightClass.WEIGHT_LIGHT); + CombatTeam combatTeam = getCombatTeamById(campaign); + int allyForceWeight; + if (combatTeam != null) { + allyForceWeight = Math.max(getCombatTeamById(campaign).getWeightClass(campaign) - 1, EntityWeightClass.WEIGHT_LIGHT); + } else { + allyForceWeight = EntityWeightClass.WEIGHT_LIGHT; + } addLance(allyEntities, getContract(campaign).getEmployerCode(), getContract(campaign).getAllySkill(), getContract(campaign).getAllyQuality(), allyForceWeight, campaign); addLance(allyEntities, getContract(campaign).getEmployerCode(), getContract(campaign).getAllySkill(), @@ -138,14 +145,16 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(new BotForce(BASE_TURRET_FORCE_ID, isAttacker() ? 2 : 1, defenderStart, defenderHome, turretForce), campaign); /* Roll 2x on bot lances roll */ - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), campaign); + int weightClass = combatTeam != null ? combatTeam.getWeightClass(campaign) : EntityWeightClass.WEIGHT_LIGHT; + + addEnemyForce(enemyEntities, weightClass, campaign); addBotForce(getEnemyBotForce(getContract(campaign), enemyStart, getEnemyHome(), enemyEntities), campaign); // the "second" enemy force will either flee in the same direction as // the first enemy force in case of the player being the attacker // or where it came from in case of player being defender ArrayList secondBotEntities = new ArrayList<>(); - addEnemyForce(secondBotEntities, getStrategicFormation(campaign).getWeightClass(campaign), campaign); + addEnemyForce(secondBotEntities, weightClass, campaign); BotForce secondBotForce = getEnemyBotForce(getContract(campaign), isAttacker() ? enemyStart : secondAttackerForceStart, isAttacker() ? getEnemyHome() : secondAttackerForceStart, secondBotEntities); @@ -179,8 +188,8 @@ public void setObjectives(Campaign campaign, AtBContract contract) { // while completing this scenario on others just puts the morale to // Rout for a while ObjectiveEffect victoryEffect = new ObjectiveEffect(); - final AtBLanceRole requiredLanceRole = contract.getContractType().getRequiredLanceRole(); - if (requiredLanceRole.isFighting() || requiredLanceRole.isScouting()) { + final CombatRole requiredLanceRole = contract.getContractType().getRequiredCombatRole(); + if (requiredLanceRole.isManeuver() || requiredLanceRole.isPatrol()) { victoryEffect.effectType = ObjectiveEffectType.ContractVictory; destroyHostiles.addDetail(getResourceBundle().getString("battleDetails.baseAttack.attacker.details.winnerFightScout")); } else { diff --git a/MekHQ/src/mekhq/campaign/mission/atb/scenario/BreakthroughBuiltInScenario.java b/MekHQ/src/mekhq/campaign/mission/atb/scenario/BreakthroughBuiltInScenario.java index 38ef0705fb3..83bed19b38c 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/scenario/BreakthroughBuiltInScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/scenario/BreakthroughBuiltInScenario.java @@ -19,12 +19,10 @@ package mekhq.campaign.mission.atb.scenario; import megamek.client.bot.princess.BehaviorSettingsFactory; -import megamek.common.Board; -import megamek.common.Compute; -import megamek.common.Entity; -import megamek.common.OffBoardDirection; +import megamek.common.*; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.*; import mekhq.campaign.mission.atb.AtBScenarioEnabled; @@ -52,7 +50,7 @@ public String getResourceKey() { @Override public int getMapX() { // make it a little wider for bigger scenarios - return 18 + this.getLanceCount(); + return 18 + this.getForceCount(); } @Override @@ -92,7 +90,9 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(allyEntitiesForce, campaign); } - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), campaign); + CombatTeam combatTeam = getCombatTeamById(campaign); + int weightClass = combatTeam != null ? combatTeam.getWeightClass(campaign) : EntityWeightClass.WEIGHT_LIGHT; + addEnemyForce(enemyEntities, weightClass, campaign); BotForce botForce = getEnemyBotForce(getContract(campaign), enemyStart, getEnemyHome(), enemyEntities); try { diff --git a/MekHQ/src/mekhq/campaign/mission/atb/scenario/ChaseBuiltInScenario.java b/MekHQ/src/mekhq/campaign/mission/atb/scenario/ChaseBuiltInScenario.java index a52b4931734..bc9dfbc14f2 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/scenario/ChaseBuiltInScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/scenario/ChaseBuiltInScenario.java @@ -23,6 +23,7 @@ import megamek.common.*; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.*; import mekhq.campaign.mission.atb.AtBScenarioEnabled; @@ -49,7 +50,7 @@ public String getResourceKey() { @Override public int getMapX() { - return 18 + getLanceCount(); + return 18 + getForceCount(); } @Override @@ -80,9 +81,11 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(allyEntitiesForce, campaign); } - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), EntityWeightClass.WEIGHT_ASSAULT, 0, + CombatTeam combatTeam = getCombatTeamById(campaign); + int weightClass = combatTeam != null ? combatTeam.getWeightClass(campaign) : EntityWeightClass.WEIGHT_LIGHT; + addEnemyForce(enemyEntities, weightClass, EntityWeightClass.WEIGHT_ASSAULT, 0, -1, campaign); - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), EntityWeightClass.WEIGHT_ASSAULT, 0, + addEnemyForce(enemyEntities, weightClass, EntityWeightClass.WEIGHT_ASSAULT, 0, -1, campaign); BotForce botForce = getEnemyBotForce(getContract(campaign), startEdge, getEnemyHome(), enemyEntities); diff --git a/MekHQ/src/mekhq/campaign/mission/atb/scenario/ExtractionBuiltInScenario.java b/MekHQ/src/mekhq/campaign/mission/atb/scenario/ExtractionBuiltInScenario.java index d81a7b29d2c..b5b5c5efab3 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/scenario/ExtractionBuiltInScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/scenario/ExtractionBuiltInScenario.java @@ -23,8 +23,10 @@ import megamek.common.Board; import megamek.common.Compute; import megamek.common.Entity; +import megamek.common.EntityWeightClass; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.*; import mekhq.campaign.mission.ObjectiveEffect.EffectScalingType; import mekhq.campaign.mission.ObjectiveEffect.ObjectiveEffectType; @@ -98,7 +100,10 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(getAllyBotForce(getContract(campaign), getStartingPos(), playerHome, allyEntities), campaign); } - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), campaign); + CombatTeam combatTeam = getCombatTeamById(campaign); + int weightClass = combatTeam != null ? combatTeam.getWeightClass(campaign) : EntityWeightClass.WEIGHT_LIGHT; + + addEnemyForce(enemyEntities, weightClass, campaign); addBotForce(getEnemyBotForce(getContract(campaign), enemyStart, getEnemyHome(), enemyEntities), campaign); ArrayList otherForce = new ArrayList<>(); diff --git a/MekHQ/src/mekhq/campaign/mission/atb/scenario/HideAndSeekBuiltInScenario.java b/MekHQ/src/mekhq/campaign/mission/atb/scenario/HideAndSeekBuiltInScenario.java index 9f593f1c5a7..4b7c99e56d9 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/scenario/HideAndSeekBuiltInScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/scenario/HideAndSeekBuiltInScenario.java @@ -23,6 +23,7 @@ import megamek.common.Entity; import megamek.common.EntityWeightClass; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.AtBScenario; import mekhq.campaign.mission.CommonObjectiveFactory; @@ -107,12 +108,15 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(getAllyBotForce(getContract(campaign), getStartingPos(), playerHome, allyEntities), campaign); } + CombatTeam combatTeam = getCombatTeamById(campaign); + int weightClass = combatTeam != null ? combatTeam.getWeightClass(campaign) : EntityWeightClass.WEIGHT_LIGHT; + if (isAttacker()) { - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), - EntityWeightClass.WEIGHT_ASSAULT, 2, 0, campaign); + addEnemyForce(enemyEntities, weightClass, EntityWeightClass.WEIGHT_ASSAULT, 2, + 0, campaign); } else { - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), - EntityWeightClass.WEIGHT_HEAVY, 0, 0, campaign); + addEnemyForce(enemyEntities, weightClass, EntityWeightClass.WEIGHT_HEAVY, 0, + 0, campaign); } addBotForce(getEnemyBotForce(getContract(campaign), enemyStart, getEnemyHome(), enemyEntities), campaign); diff --git a/MekHQ/src/mekhq/campaign/mission/atb/scenario/HoldTheLineBuiltInScenario.java b/MekHQ/src/mekhq/campaign/mission/atb/scenario/HoldTheLineBuiltInScenario.java index 36bde062550..187a304bfb9 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/scenario/HoldTheLineBuiltInScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/scenario/HoldTheLineBuiltInScenario.java @@ -23,6 +23,7 @@ import megamek.common.Entity; import megamek.common.EntityWeightClass; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.AtBScenario; import mekhq.campaign.mission.CommonObjectiveFactory; @@ -79,8 +80,11 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(getAllyBotForce(getContract(campaign), getStartingPos(), playerHome, allyEntities), campaign); } - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), EntityWeightClass.WEIGHT_ASSAULT, - isAttacker() ? 0 : 4, 0, campaign); + CombatTeam combatTeam = getCombatTeamById(campaign); + int weightClass = combatTeam != null ? combatTeam.getWeightClass(campaign) : EntityWeightClass.WEIGHT_LIGHT; + + addEnemyForce(enemyEntities, weightClass, EntityWeightClass.WEIGHT_ASSAULT, + isAttacker() ? 0 : 4, 0, campaign); addBotForce(getEnemyBotForce(getContract(campaign), enemyStart, getEnemyHome(), enemyEntities), campaign); } diff --git a/MekHQ/src/mekhq/campaign/mission/atb/scenario/ProbeBuiltInScenario.java b/MekHQ/src/mekhq/campaign/mission/atb/scenario/ProbeBuiltInScenario.java index a18f653bb8e..296ed4f4da9 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/scenario/ProbeBuiltInScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/scenario/ProbeBuiltInScenario.java @@ -22,6 +22,7 @@ import megamek.common.Entity; import megamek.common.EntityWeightClass; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.AtBScenario; import mekhq.campaign.mission.CommonObjectiveFactory; @@ -79,7 +80,10 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(getAllyBotForce(getContract(campaign), getStartingPos(), playerHome, allyEntities), campaign); } - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), EntityWeightClass.WEIGHT_MEDIUM, 0, 0, + CombatTeam combatTeam = getCombatTeamById(campaign); + int weightClass = combatTeam != null ? combatTeam.getWeightClass(campaign) : EntityWeightClass.WEIGHT_LIGHT; + + addEnemyForce(enemyEntities, weightClass, EntityWeightClass.WEIGHT_MEDIUM, 0, 0, campaign); addBotForce(getEnemyBotForce(getContract(campaign), enemyStart, getEnemyHome(), enemyEntities), campaign); diff --git a/MekHQ/src/mekhq/campaign/mission/atb/scenario/ReconRaidBuiltInScenario.java b/MekHQ/src/mekhq/campaign/mission/atb/scenario/ReconRaidBuiltInScenario.java index f547848e724..3c3399b4767 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/scenario/ReconRaidBuiltInScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/scenario/ReconRaidBuiltInScenario.java @@ -20,6 +20,7 @@ import megamek.common.*; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.*; import mekhq.campaign.mission.ObjectiveEffect.ObjectiveEffectType; import mekhq.campaign.mission.ScenarioObjective.ObjectiveCriterion; @@ -76,8 +77,11 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(getAllyBotForce(getContract(campaign), getStartingPos(), playerHome, allyEntities), campaign); } - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), - isAttacker() ? EntityWeightClass.WEIGHT_ASSAULT : EntityWeightClass.WEIGHT_MEDIUM, 0, 0, campaign); + CombatTeam combatTeam = getCombatTeamById(campaign); + int weightClass = combatTeam != null ? combatTeam.getWeightClass(campaign) : EntityWeightClass.WEIGHT_LIGHT; + + addEnemyForce(enemyEntities, weightClass, isAttacker() ? EntityWeightClass.WEIGHT_ASSAULT : EntityWeightClass.WEIGHT_MEDIUM, + 0, 0, campaign); addBotForce(getEnemyBotForce(getContract(campaign), enemyStart, getEnemyHome(), enemyEntities), campaign); } diff --git a/MekHQ/src/mekhq/campaign/mission/atb/scenario/StandUpBuiltInScenario.java b/MekHQ/src/mekhq/campaign/mission/atb/scenario/StandUpBuiltInScenario.java index 8e1ecb0dc7c..a7c9fa7cb2d 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/scenario/StandUpBuiltInScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/scenario/StandUpBuiltInScenario.java @@ -20,7 +20,9 @@ import megamek.common.Compute; import megamek.common.Entity; +import megamek.common.EntityWeightClass; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.AtBScenario; import mekhq.campaign.mission.CommonObjectiveFactory; @@ -64,7 +66,10 @@ public void setExtraScenarioForces(Campaign campaign, ArrayList allyEnti addBotForce(getAllyBotForce(getContract(campaign), getStartingPos(), playerHome, allyEntities), campaign); } - addEnemyForce(enemyEntities, getStrategicFormation(campaign).getWeightClass(campaign), campaign); + CombatTeam combatTeam = getCombatTeamById(campaign); + int weightClass = combatTeam != null ? combatTeam.getWeightClass(campaign) : EntityWeightClass.WEIGHT_LIGHT; + + addEnemyForce(enemyEntities, weightClass, campaign); addBotForce(getEnemyBotForce(getContract(campaign), getEnemyHome(), getEnemyHome(), enemyEntities), campaign); } diff --git a/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java b/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java index 44235bf5e19..0aecd6c02e4 100644 --- a/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java +++ b/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -194,30 +194,41 @@ public int calculatePartsAvailabilityLevel() { } } - public AtBLanceRole getRequiredLanceRole() { - switch (this) { - case CADRE_DUTY: - return AtBLanceRole.TRAINING; - case GARRISON_DUTY: - case SECURITY_DUTY: - case RIOT_DUTY: - return AtBLanceRole.DEFENCE; - case GUERRILLA_WARFARE: - case PIRATE_HUNTING: - case PLANETARY_ASSAULT: - case RELIEF_DUTY: - return AtBLanceRole.FIGHTING; - case DIVERSIONARY_RAID: - case EXTRACTION_RAID: - case OBJECTIVE_RAID: - case RECON_RAID: - return AtBLanceRole.SCOUTING; - default: - return AtBLanceRole.UNASSIGNED; - } + /** + * Determines the required combat role for the current contract type. + * + *

    Each contract type specifies a primary {@link CombatRole} that defines + * the focus of the contract. For example, some contracts may require a patrol role, + * while others require maneuver or frontline support.

    + * + * @return the {@link CombatRole} required for the current contract type. + */ + public CombatRole getRequiredCombatRole() { + return switch (this) { + case CADRE_DUTY -> CombatRole.TRAINING; + case GARRISON_DUTY, SECURITY_DUTY, RIOT_DUTY -> CombatRole.MANEUVER; + case GUERRILLA_WARFARE, PIRATE_HUNTING, PLANETARY_ASSAULT, RELIEF_DUTY -> CombatRole.FRONTLINE; + case DIVERSIONARY_RAID, EXTRACTION_RAID, OBJECTIVE_RAID, RECON_RAID -> CombatRole.PATROL; + }; } - public int generateEventType() { + /** + * Generates an event type for the campaign based on the current contract type. + * + *

    This method calculates a random event, with probabilities defined by + * the type of contract. The result is used to trigger specific in-game scenarios or effects.

    + * + *

    If StratCon is enabled the event is instead generated by the + * {@link #generateStratConEvent()} method.

    + * + * @param campaign the {@link Campaign} instance for which the event is being generated. + * @return an integer representing the event type. + */ + public int generateEventType(Campaign campaign) { + if (campaign.getCampaignOptions().isUseStratCon()) { + return generateStratConEvent(); + } + final int roll = Compute.randomInt(20) + 1; switch (this) { @@ -331,101 +342,208 @@ public int generateEventType() { } } + /** + * Generates an event type based on the current contract type. + * + *

    This method is similar to {@link #generateEventType(Campaign)} but is specifically + * tailored for StratCon-enabled campaigns. It uses a die roll to determine the + * resulting event, with probabilities varying by contract type.

    + * + * @return an integer representing the event type. + */ + public int generateStratConEvent() { + final int roll = Compute.randomInt(20) + 1; + + return switch (this) { + case DIVERSIONARY_RAID, OBJECTIVE_RAID, RECON_RAID, EXTRACTION_RAID -> { + if (roll < 14) { + yield AtBContract.EVT_BONUSROLL; + } else if (roll < 16) { + yield AtBContract.EVT_BETRAYAL; + } else if (roll < 17) { + yield AtBContract.EVT_TREACHERY; + } else if (roll < 18) { + yield AtBContract.EVT_LOGISTICSFAILURE; + } else if (roll < 19) { + yield AtBContract.EVT_REINFORCEMENTS; + } else { + yield AtBContract.EVT_SPECIALEVENTS; + } + } + case GARRISON_DUTY -> { + if (roll < 12) { + yield AtBContract.EVT_BONUSROLL; + } else if (roll < 13) { + yield AtBContract.EVT_CIVILDISTURBANCE; + } else if (roll < 14) { + yield AtBContract.EVT_SPORADICUPRISINGS; + } else if (roll < 15) { + yield AtBContract.EVT_REBELLION; + } else if (roll < 16) { + yield AtBContract.EVT_BETRAYAL; + } else if (roll < 17) { + yield AtBContract.EVT_TREACHERY; + } else if (roll < 18) { + yield AtBContract.EVT_LOGISTICSFAILURE; + } else if (roll < 19) { + yield AtBContract.EVT_REINFORCEMENTS; + } else { + yield AtBContract.EVT_SPECIALEVENTS; + } + } + case RIOT_DUTY -> { + if (roll < 11) { + yield AtBContract.EVT_BONUSROLL; + } else if (roll < 12) { + yield AtBContract.EVT_CIVILDISTURBANCE; + } else if (roll < 13) { + yield AtBContract.EVT_SPORADICUPRISINGS; + } else if (roll < 15) { + yield AtBContract.EVT_REBELLION; + } else if (roll < 16) { + yield AtBContract.EVT_BETRAYAL; + } else if (roll < 17) { + yield AtBContract.EVT_TREACHERY; + } else if (roll < 18) { + yield AtBContract.EVT_LOGISTICSFAILURE; + } else if (roll < 19) { + yield AtBContract.EVT_REINFORCEMENTS; + } else { + yield AtBContract.EVT_SPECIALEVENTS; + } + } + case PIRATE_HUNTING -> { + if (roll < 14) { + yield AtBContract.EVT_BONUSROLL; + } else if (roll < 15) { + yield AtBContract.EVT_CIVILDISTURBANCE; + } else if (roll < 16) { + yield AtBContract.EVT_BETRAYAL; + } else if (roll < 17) { + yield AtBContract.EVT_TREACHERY; + } else if (roll < 18) { + yield AtBContract.EVT_LOGISTICSFAILURE; + } else if (roll < 19) { + yield AtBContract.EVT_REINFORCEMENTS; + } else { + yield AtBContract.EVT_SPECIALEVENTS; + } + } + default -> { + if (roll < 15) { + yield AtBContract.EVT_BONUSROLL; + } else if (roll < 16) { + yield AtBContract.EVT_BETRAYAL; + } else if (roll < 17) { + yield AtBContract.EVT_TREACHERY; + } else if (roll < 18) { + yield AtBContract.EVT_LOGISTICSFAILURE; + } else if (roll < 19) { + yield AtBContract.EVT_REINFORCEMENTS; + } else { + yield AtBContract.EVT_SPECIALEVENTS; + } + } + }; + } + public int generateSpecialScenarioType(final Campaign campaign) { // Our roll is era-based. If it is pre-spaceflight, early spaceflight, or Age of // War there // cannot be Star League Caches as the Star League hasn't formed final int roll = Compute.randomInt(campaign.getEra().hasFlag(EraFlag.PRE_SPACEFLIGHT, EraFlag.EARLY_SPACEFLIGHT, EraFlag.AGE_OF_WAR) ? 12 : 20) + 1; - switch (this) { - case DIVERSIONARY_RAID: - case OBJECTIVE_RAID: - case RECON_RAID: - case EXTRACTION_RAID: + return switch (this) { + case DIVERSIONARY_RAID, OBJECTIVE_RAID, RECON_RAID, EXTRACTION_RAID -> { if (roll <= 1) { - return AtBScenario.OFFICERDUEL; - } else if (roll <= 2) { - return AtBScenario.ACEDUEL; + yield AtBScenario.OFFICERDUEL; + } else if (roll == 2) { + yield AtBScenario.ACEDUEL; } else if (roll <= 6) { - return AtBScenario.AMBUSH; - } else if (roll <= 7) { - return AtBScenario.CIVILIANHELP; - } else if (roll <= 8) { - return AtBScenario.ALLIEDTRAITORS; + yield AtBScenario.AMBUSH; + } else if (roll == 7) { + yield AtBScenario.CIVILIANHELP; + } else if (roll == 8) { + yield AtBScenario.ALLIEDTRAITORS; } else if (roll <= 12) { - return AtBScenario.PRISONBREAK; + yield AtBScenario.PRISONBREAK; } else if (roll <= 16) { - return AtBScenario.STARLEAGUECACHE1; + yield AtBScenario.STARLEAGUECACHE1; } else { - return AtBScenario.STARLEAGUECACHE2; + yield AtBScenario.STARLEAGUECACHE2; } - case GARRISON_DUTY: + } + case GARRISON_DUTY -> { if (roll <= 2) { - return AtBScenario.OFFICERDUEL; + yield AtBScenario.OFFICERDUEL; } else if (roll <= 4) { - return AtBScenario.ACEDUEL; + yield AtBScenario.ACEDUEL; } else if (roll <= 6) { - return AtBScenario.AMBUSH; + yield AtBScenario.AMBUSH; } else if (roll <= 10) { - return AtBScenario.CIVILIANHELP; + yield AtBScenario.CIVILIANHELP; } else if (roll <= 12) { - return AtBScenario.ALLIEDTRAITORS; + yield AtBScenario.ALLIEDTRAITORS; } else if (roll <= 16) { - return AtBScenario.STARLEAGUECACHE1; + yield AtBScenario.STARLEAGUECACHE1; } else { - return AtBScenario.STARLEAGUECACHE2; + yield AtBScenario.STARLEAGUECACHE2; } - case RIOT_DUTY: + } + case RIOT_DUTY -> { if (roll <= 1) { - return AtBScenario.OFFICERDUEL; + yield AtBScenario.OFFICERDUEL; } else if (roll <= 3) { - return AtBScenario.ACEDUEL; + yield AtBScenario.ACEDUEL; } else if (roll <= 7) { - return AtBScenario.AMBUSH; - } else if (roll <= 8) { - return AtBScenario.CIVILIANHELP; + yield AtBScenario.AMBUSH; + } else if (roll == 8) { + yield AtBScenario.CIVILIANHELP; } else if (roll <= 12) { - return AtBScenario.ALLIEDTRAITORS; + yield AtBScenario.ALLIEDTRAITORS; } else if (roll <= 16) { - return AtBScenario.STARLEAGUECACHE1; + yield AtBScenario.STARLEAGUECACHE1; } else { - return AtBScenario.STARLEAGUECACHE2; + yield AtBScenario.STARLEAGUECACHE2; } - case PIRATE_HUNTING: + } + case PIRATE_HUNTING -> { if (roll <= 1) { - return AtBScenario.OFFICERDUEL; + yield AtBScenario.OFFICERDUEL; } else if (roll <= 4) { - return AtBScenario.ACEDUEL; + yield AtBScenario.ACEDUEL; } else if (roll <= 7) { - return AtBScenario.AMBUSH; + yield AtBScenario.AMBUSH; } else if (roll <= 11) { - return AtBScenario.CIVILIANHELP; - } else if (roll <= 12) { - return AtBScenario.ALLIEDTRAITORS; + yield AtBScenario.CIVILIANHELP; + } else if (roll == 12) { + yield AtBScenario.ALLIEDTRAITORS; } else if (roll <= 16) { - return AtBScenario.STARLEAGUECACHE1; + yield AtBScenario.STARLEAGUECACHE1; } else { - return AtBScenario.STARLEAGUECACHE2; + yield AtBScenario.STARLEAGUECACHE2; } - default: + } + default -> { if (roll <= 2) { - return AtBScenario.OFFICERDUEL; + yield AtBScenario.OFFICERDUEL; } else if (roll <= 4) { - return AtBScenario.ACEDUEL; + yield AtBScenario.ACEDUEL; } else if (roll <= 6) { - return AtBScenario.AMBUSH; + yield AtBScenario.AMBUSH; } else if (roll <= 8) { - return AtBScenario.CIVILIANHELP; + yield AtBScenario.CIVILIANHELP; } else if (roll <= 10) { - return AtBScenario.ALLIEDTRAITORS; + yield AtBScenario.ALLIEDTRAITORS; } else if (roll <= 12) { - return AtBScenario.PRISONBREAK; + yield AtBScenario.PRISONBREAK; } else if (roll <= 16) { - return AtBScenario.STARLEAGUECACHE1; + yield AtBScenario.STARLEAGUECACHE1; } else { - return AtBScenario.STARLEAGUECACHE2; + yield AtBScenario.STARLEAGUECACHE2; } - } + } + }; } public int generateBigBattleType() { diff --git a/MekHQ/src/mekhq/campaign/mission/enums/AtBLanceRole.java b/MekHQ/src/mekhq/campaign/mission/enums/AtBLanceRole.java deleted file mode 100644 index d2ab643f14d..00000000000 --- a/MekHQ/src/mekhq/campaign/mission/enums/AtBLanceRole.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2020-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.mission.enums; - -import java.util.ResourceBundle; - -import megamek.logging.MMLogger; -import mekhq.MekHQ; - -public enum AtBLanceRole { - // region Enum Declarations - FIGHTING("AtBLanceRole.FIGHTING.text", "AtBLanceRole.FIGHTING.toolTipText"), - DEFENCE("AtBLanceRole.DEFENCE.text", "AtBLanceRole.DEFENCE.toolTipText"), - SCOUTING("AtBLanceRole.SCOUTING.text", "AtBLanceRole.SCOUTING.toolTipText"), - TRAINING("AtBLanceRole.TRAINING.text", "AtBLanceRole.TRAINING.toolTipText"), - UNASSIGNED("AtBLanceRole.UNASSIGNED.text", "AtBLanceRole.UNASSIGNED.toolTipText"); - // endregion Enum Declarations - - // region Variable Declarations - private final String name; - private final String toolTipText; - // endregion Variable Declarations - - // region Constructors - AtBLanceRole(final String name, final String toolTipText) { - final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Mission", - MekHQ.getMHQOptions().getLocale()); - this.name = resources.getString(name); - this.toolTipText = resources.getString(toolTipText); - } - // endregion Constructors - - // region Getters - public String getToolTipText() { - return toolTipText; - } - // endregion Getters - - // region Boolean Comparison Methods - public boolean isFighting() { - return this == FIGHTING; - } - - public boolean isDefence() { - return this == DEFENCE; - } - - public boolean isScouting() { - return this == SCOUTING; - } - - public boolean isTraining() { - return this == TRAINING; - } - - public boolean isUnassigned() { - return this == UNASSIGNED; - } - // endregion Boolean Comparison Methods - - // region File I/O - public static AtBLanceRole parseFromString(final String text) { - try { - return valueOf(text); - } catch (Exception ignored) { - - } - - try { - switch (Integer.parseInt(text)) { - case 0: - return UNASSIGNED; - case 1: - return FIGHTING; - case 2: - return DEFENCE; - case 3: - return SCOUTING; - case 4: - return TRAINING; - default: - break; - } - } catch (Exception ignored) { - - } - - MMLogger.create(AtBLanceRole.class) - .error("Unable to parse " + text + " into an AtBLanceRole. Returning FIGHTING."); - - return FIGHTING; - } - // endregion File I/O - - @Override - public String toString() { - return name; - } -} diff --git a/MekHQ/src/mekhq/campaign/mission/enums/CombatRole.java b/MekHQ/src/mekhq/campaign/mission/enums/CombatRole.java new file mode 100644 index 00000000000..dc6351c08b0 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/enums/CombatRole.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020-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.mission.enums; + +import megamek.logging.MMLogger; +import mekhq.MekHQ; + +import java.util.ResourceBundle; + +public enum CombatRole { + // region Enum Declarations + MANEUVER("CombatRole.MANEUVER.text", "CombatRole.MANEUVER.toolTipText"), + FRONTLINE("CombatRole.FRONTLINE.text", "CombatRole.FRONTLINE.toolTipText"), + PATROL("CombatRole.PATROL.text", "CombatRole.PATROL.toolTipText"), + TRAINING("CombatRole.TRAINING.text", "CombatRole.TRAINING.toolTipText"), + AUXILIARY("CombatRole.AUXILIARY.text", "CombatRole.AUXILIARY.toolTipText"), + RESERVE("CombatRole.RESERVE.text", "CombatRole.RESERVE.toolTipText"); + // endregion Enum Declarations + + // region Variable Declarations + private final String name; + private final String toolTipText; + // endregion Variable Declarations + + // region Constructors + CombatRole(final String name, final String toolTipText) { + final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Mission", + MekHQ.getMHQOptions().getLocale()); + this.name = resources.getString(name); + this.toolTipText = resources.getString(toolTipText); + } + // endregion Constructors + + // region Getters + public String getToolTipText() { + return toolTipText; + } + // endregion Getters + + // region Boolean Comparison Methods + public boolean isManeuver() { + return this == MANEUVER; + } + + public boolean isFrontline() { + return this == FRONTLINE; + } + + public boolean isPatrol() { + return this == PATROL; + } + + public boolean isTraining() { + return this == TRAINING; + } + + public boolean isAuxiliary() { + return this == AUXILIARY; + } + + public boolean isReserve() { + return this == RESERVE; + } + // endregion Boolean Comparison Methods + + // region File I/O + /** + * Parses a {@link String} into a {@link CombatRole} enum value. + *

    + * This method first attempts to interpret the input string as an integer and then maps it + * to the corresponding {@link CombatRole} based on its ordinal index. If that fails, it + * attempts to match the string to the name of a {@link CombatRole} using {@code Enum.valueOf(String)}. + * If both parsing approaches fail, it logs an error and returns the default value {@code IN_RESERVE}. + *

    + * + * @param text the string to be parsed into a {@link CombatRole}. + * The string can represent either: + *
      + *
    • An integer corresponding to the ordinal index of a {@link CombatRole}.
    • + *
    • The name of a {@link CombatRole}.
    • + *
    + * @return the corresponding {@link CombatRole} if the input is valid; + * otherwise, returns {@code IN_RESERVE}. + */ + public static CombatRole parseFromString(final String text) { + try { + int value = Integer.parseInt(text); + return values()[value]; + } catch (Exception ignored) {} + + try { + return valueOf(text); + } catch (Exception ignored) {} + + // <50.02 compatibility handler + return switch (text) { + case "FIGHTING" -> MANEUVER; + case "SCOUTING", "RECON" -> PATROL; + case "DEFENCE", "GARRISON" -> FRONTLINE; + case "IN_RESERVE", "UNASSIGNED" -> RESERVE; + default -> { + MMLogger.create(CombatRole.class) + .warn(String.format("Unable to parse %s into an CombatRole. Returning RESERVE.", + text)); + + yield RESERVE; + } + + }; + + // To be uncommented once the above compatibility handler is removed. +// MMLogger.create(CombatRole.class) +// .error("Unable to parse " + text + " into an CombatRole. Returning RESERVE."); +// +// return RESERVE; + } + // endregion File I/O + + @Override + public String toString() { + return name; + } +} diff --git a/MekHQ/src/mekhq/campaign/mission/enums/ScenarioStatus.java b/MekHQ/src/mekhq/campaign/mission/enums/ScenarioStatus.java index f2c12217baf..cb7b3e2dec8 100644 --- a/MekHQ/src/mekhq/campaign/mission/enums/ScenarioStatus.java +++ b/MekHQ/src/mekhq/campaign/mission/enums/ScenarioStatus.java @@ -18,11 +18,11 @@ */ package mekhq.campaign.mission.enums; -import java.util.ResourceBundle; - import megamek.logging.MMLogger; import mekhq.MekHQ; +import java.util.ResourceBundle; + public enum ScenarioStatus { // region Enum Declarations CURRENT("ScenarioStatus.CURRENT.text", "ScenarioStatus.CURRENT.toolTipText"), @@ -33,7 +33,8 @@ public enum ScenarioStatus { DRAW("ScenarioStatus.DRAW.text", "ScenarioStatus.DRAW.toolTipText"), MARGINAL_DEFEAT("ScenarioStatus.MARGINAL_DEFEAT.text", "ScenarioStatus.MARGINAL_DEFEAT.toolTipText"), DEFEAT("ScenarioStatus.DEFEAT.text", "ScenarioStatus.DEFEAT.toolTipText"), - DECISIVE_DEFEAT("ScenarioStatus.DECISIVE_DEFEAT.text", "ScenarioStatus.DECISIVE_DEFEAT.toolTipText"); + DECISIVE_DEFEAT("ScenarioStatus.DECISIVE_DEFEAT.text", "ScenarioStatus.DECISIVE_DEFEAT.toolTipText"), + REFUSED_ENGAGEMENT("ScenarioStatus.REFUSED_ENGAGEMENT.text", "ScenarioStatus.REFUSED_ENGAGEMENT.toolTipText"); // endregion Enum Declarations // region Variable Declarations @@ -93,12 +94,16 @@ public boolean isDecisiveDefeat() { return this == DECISIVE_DEFEAT; } + public boolean isRefusedEngagement() { + return this == REFUSED_ENGAGEMENT; + } + public boolean isOverallVictory() { return isDecisiveVictory() || isVictory() || isMarginalVictory() || isPyrrhicVictory(); } public boolean isOverallDefeat() { - return isDecisiveDefeat() || isDefeat() || isMarginalDefeat(); + return isDecisiveDefeat() || isDefeat() || isMarginalDefeat() || isRefusedEngagement(); } // endregion Boolean Comparison Methods diff --git a/MekHQ/src/mekhq/campaign/mission/enums/ScenarioType.java b/MekHQ/src/mekhq/campaign/mission/enums/ScenarioType.java new file mode 100644 index 00000000000..9b0f8ae4b63 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/enums/ScenarioType.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 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.mission.enums; + +import megamek.logging.MMLogger; + +/** + * Represents the type of scenario within MekHQ. + * + *

    This enum defines specific scenario types that can occur in the game. It provides utility + * methods to distinguish between various types and supports parsing from string representations, + * with graceful error handling.

    + * + *

    Currently available scenario types:

    + *
      + *
    • {@code NONE} - Default scenario type.
    • + *
    • {@code SPECIAL_LOSTECH} - Indicates a special LosTech-related scenario.
    • + *
    • {@code SPECIAL_RESUPPLY} - Indicates a resupply-related scenario.
    • + *
    + * + *

    This enum also supports utility methods to determine if a scenario is of a specific + * type, such as {@link #isLosTech()} and {@link #isResupply()}.

    + */ +public enum ScenarioType { + NONE, + SPECIAL_LOSTECH, + SPECIAL_RESUPPLY; + + /** + * @return {@code true} if the scenario is considered a LosTech scenario, {@code false} otherwise. + */ + public boolean isLosTech() { + return this == SPECIAL_LOSTECH; + } + + /** + * @return {@code true} if the scenario is considered a Resupply scenario, {@code false} otherwise. + */ + public boolean isResupply() { + return this == SPECIAL_RESUPPLY; + } + + /** + * Parses a {@code ScenarioType} from a string input. + * + *

    This method attempts to interpret the given string as either:

    + *
      + *
    1. An integer index corresponding to the scenario type values, retrieved using {@link #values()}.
    2. + *
    3. A string matching the name of a specific {@code ScenarioType} constant, case-sensitive.
    4. + *
    + * + *

    If parsing fails for both cases, an error is logged and the method returns the default + * {@code NONE} value.

    + * + *

    Parsing Strategy:

    + *
      + *
    1. First, it tries to parse the input as an integer and use it as an index for {@link #values()}.
    2. + *
    3. If that fails, it tries to match the input to a constant name using {@link #valueOf(String)}.
    4. + *
    5. If both attempts fail, it logs an error and returns {@code NONE}.
    6. + *
    + * + *

    Note: If the input is invalid (e.g., a non-integer string or out-of-bounds index), the error + * is logged via {@link MMLogger} and the fallback {@code NONE} is returned.

    + * + * @param text the string to be parsed into a {@code ScenarioType}, representing either + * an integer index or the enum constant name. + * @return the parsed {@code ScenarioType}, or {@code NONE} if parsing fails. + */ + public static ScenarioType parseFromString(final String text) { + try { + int value = Integer.parseInt(text.trim()); + return values()[value]; + } catch (Exception ignored) {} + + try { + return valueOf(text.trim().toUpperCase()); + } catch (Exception ignored) {} + + MMLogger.create(ScenarioType.class) + .error("Unable to parse " + text + " into an ScenarioType. Returning NONE."); + + return NONE; + } +} diff --git a/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/GenerateResupplyContents.java b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/GenerateResupplyContents.java new file mode 100644 index 00000000000..149514e99f3 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/GenerateResupplyContents.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 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.mission.resupplyAndCaches; + +import megamek.codeUtilities.ObjectUtility; +import megamek.common.Compute; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; +import mekhq.campaign.finances.Money; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.enums.AtBMoraleLevel; +import mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType; +import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.enums.PartQuality; +import mekhq.campaign.universe.Faction; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static java.lang.Math.min; +import static mekhq.campaign.unit.Unit.getRandomUnitQuality; + +/** + * This class is responsible for generating resupply contents for various drop types, + * such as parts, armor, and ammunition, based on specified parameters. It calculates + * the items to be included, considering available resources, negotiator skills, and + * item value constraints. + */ +public class GenerateResupplyContents { + private static final MMLogger logger = MMLogger.create(GenerateResupplyContents.class); + + private static final Money HIGH_VALUE_ITEM = Money.of(250000); + /** + * Some parts are weightless, so we include a nominal weight for those. + *

    + * This minimum value can be hand waved as being the weight of the container the part is in. + */ + public static final double RESUPPLY_MINIMUM_PART_WEIGHT = 0.1; + + + /** + * Enum representing different types of drops that can be generated during resupply. + * - `DROP_TYPE_PARTS`: Represents parts for resupply. + * - `DROP_TYPE_ARMOR`: Represents armor for resupply. + * - `DROP_TYPE_AMMO`: Represents ammunition for resupply. + */ + public enum DropType { + DROP_TYPE_PARTS, DROP_TYPE_ARMOR, DROP_TYPE_AMMO + } + + /** + * Generates the resupply contents based on the given {@link Resupply}, the specified + * {@link DropType}, and whether player convoys should be used. + * + * @param resupply The resupply object containing relevant details like + * pools of parts, armor, ammo, and negotiator skill. + * @param dropType The type of drop to generate (parts, armor, or ammunition). + * @param usePlayerConvoys Indicates whether player convoy cargo capacity should be applied. + */ + static void getResupplyContents(Resupply resupply, DropType dropType, boolean usePlayerConvoys) { + double targetCargoTonnage = resupply.getTargetCargoTonnage(); + if (usePlayerConvoys) { + final int targetCargoTonnagePlayerConvoy = resupply.getTargetCargoTonnagePlayerConvoy(); + final double playerCargoCapacity = resupply.getTotalPlayerCargoCapacity(); + + targetCargoTonnage = min(targetCargoTonnagePlayerConvoy, playerCargoCapacity); + } + + List partsPool = resupply.getPartsPool(); + List armorPool = resupply.getArmorPool(); + List ammoBinPool = resupply.getAmmoBinPool(); + + final int negotiatorSkill = resupply.getNegotiatorSkill(); + + List droppedItems = new ArrayList<>(); + + double availableSpace = switch (dropType) { + case DROP_TYPE_PARTS -> targetCargoTonnage * resupply.getFocusParts(); + case DROP_TYPE_ARMOR -> targetCargoTonnage * resupply.getFocusArmor(); + case DROP_TYPE_AMMO -> targetCargoTonnage * resupply.getFocusAmmo(); + }; + + if (availableSpace == 0) { + return; + } + + List relevantPartsPool = switch(dropType) { + case DROP_TYPE_PARTS -> partsPool; + case DROP_TYPE_ARMOR -> armorPool; + case DROP_TYPE_AMMO -> ammoBinPool; + }; + + double currentLoad = 0; + while ((currentLoad < availableSpace) && (!relevantPartsPool.isEmpty())) { + Part potentialPart = switch(dropType) { + case DROP_TYPE_PARTS -> getRandomDrop(partsPool, negotiatorSkill); + case DROP_TYPE_ARMOR -> getRandomDrop(armorPool, negotiatorSkill); + case DROP_TYPE_AMMO -> getRandomDrop(ammoBinPool, negotiatorSkill); + }; + + // If we failed to get a potential part, it likely means the pool is empty. + // Even if the pool isn't empty, it's highly unlikely we'll get a successful pull on + // future iterations, so we end generation early. + if (potentialPart == null) { + resupply.getConvoyContents().addAll(droppedItems); + calculateConvoyWorth(resupply); + logger.info("Encountered null part while getting resupply contents. Aborting early."); + return; + } + + boolean partFetched = false; + + // For particularly valuable items, we roll a follow-up die to see if the item + // is actually picked, or if the supplier substitutes it with another item. + if (potentialPart.getUndamagedValue().isGreaterThan(HIGH_VALUE_ITEM)) { + if (Compute.d6(1) == 6) { + partFetched = true; + } + + // For really expensive items, the player only has one chance per distinct part. + switch (dropType) { + case DROP_TYPE_PARTS -> partsPool.removeAll(Collections.singleton(potentialPart)); + case DROP_TYPE_ARMOR -> armorPool.removeAll(Collections.singleton(potentialPart)); + case DROP_TYPE_AMMO -> ammoBinPool.removeAll(Collections.singleton(potentialPart)); + } + } else { + partFetched = true; + } + + if (partFetched) { + switch (dropType) { + case DROP_TYPE_PARTS -> partsPool.remove(potentialPart); + case DROP_TYPE_ARMOR -> armorPool.remove(potentialPart); + case DROP_TYPE_AMMO -> ammoBinPool.remove(potentialPart); + } + + // Ammo and Armor are delivered in batches of 5t, + // so we need to make sure we're treating them as 5t no matter their actual weight. + double partWeight = 5; + + if (dropType == DropType.DROP_TYPE_PARTS) { + partWeight = potentialPart.getTonnage(); + partWeight = partWeight == 0 ? RESUPPLY_MINIMUM_PART_WEIGHT : partWeight; + } + + currentLoad += partWeight; + droppedItems.add(potentialPart); + } + } + + resupply.getConvoyContents().addAll(droppedItems); + calculateConvoyWorth(resupply); + } + + /** + * Fetches a random part from the given pool of items and assigns a quality to the part, + * based on the provided negotiator skill. If the pool is empty, returns {@code null}. + * + * @param dropPool The list of potential items to choose from for resupply. + * @param negotiatorSkill The skill level of the negotiator, affecting item quality. + * @return A randomly selected part with an assigned quality, or {@code null} if the pool is empty. + */ + private static @Nullable Part getRandomDrop(List dropPool, int negotiatorSkill) { + if (dropPool.isEmpty()) { + return null; + } + + Part randomPart = ObjectUtility.getRandomItem(dropPool); + randomPart.setQuality(getRandomPartQuality(negotiatorSkill)); + + return randomPart; + } + + /** + * Determines a random part quality based on the input modifier. + * This method uses unit-level quality logic as a placeholder, with planned + * future integration of fame and infamy to influence quality. + * + * @param modifier The value influencing the randomness of part quality. + * @return A randomly generated {@link PartQuality} based on the modifier. + */ + static PartQuality getRandomPartQuality(int modifier) { + // TODO: have fame & infamy influence this value, once that module has been implemented + return getRandomUnitQuality(modifier); + } + + /** + * Calculates the worth of the convoy contents based on the resupply type and various campaign + * conditions. + * + * @param resupply The {@link Resupply} object containing details about the convoy + * and associated resupply operation. + */ + private static void calculateConvoyWorth(Resupply resupply) { + List convoyContents = resupply.getConvoyContents(); + Money sellValue = Money.zero(); + Money buyValue = Money.zero(); + for (Part part : convoyContents) { + sellValue = sellValue.plus(part.getActualValue()); + buyValue = buyValue.plus(part.getStickerPrice()); + } + resupply.setConvoyContentsValueBase(buyValue); + + ResupplyType resupplyType = resupply.getResupplyType(); + if (resupplyType.equals(ResupplyType.RESUPPLY_LOOT)) { + // Calculated value initializes as zero, and looted supplies have no associated cost. + return; + } + + // Smugglers always double the cost of the supplies they're offering + if (resupplyType.equals(ResupplyType.RESUPPLY_SMUGGLER)) { + resupply.setConvoyContentsValueCalculated(buyValue.multipliedBy(2)); + return ; + } + + // If the player faction matches the employer faction (and is not Mercenary, or Pirate), + // then supplies are free. + final Campaign campaign = resupply.getCampaign(); + final Faction campaignFaction = campaign.getFaction(); + + final AtBContract contract = resupply.getContract(); + final Faction employerFaction = contract.getEmployerFaction(); + + if (campaignFaction.equals(employerFaction) && !campaignFaction.isMercenary() + && !campaignFaction.isPirate()) { + // convoy contents initializes with a calculated value of zero, so no need to set it here. + return; + } + + // In all other cases, the value of the supplies is based on enemy morale. The logic is + // that the direr the situation, the harder supplies are to come by, so the less willing + // the employer is to part with them at a discount. + AtBMoraleLevel moraleLevel = contract.getMoraleLevel(); + + double multiplier = switch (moraleLevel) { + case ROUTED -> 0.25; + case CRITICAL -> 0.5; + case WEAKENED -> 0.75; + case STALEMATE -> 1; + case ADVANCING -> 1.25; + case DOMINATING -> 1.5; + case OVERWHELMING -> 1.75; + }; + + resupply.setConvoyContentsValueCalculated(sellValue.multipliedBy(multiplier)); + } +} diff --git a/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/PerformResupply.java b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/PerformResupply.java new file mode 100644 index 00000000000..6db3ff71f9d --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/PerformResupply.java @@ -0,0 +1,583 @@ +/* + * Copyright (c) 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.mission.resupplyAndCaches; + +import megamek.codeUtilities.ObjectUtility; +import megamek.common.Compute; +import megamek.common.Entity; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.AtBDynamicScenario; +import mekhq.campaign.mission.Loot; +import mekhq.campaign.mission.ScenarioTemplate; +import mekhq.campaign.mission.enums.AtBMoraleLevel; +import mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType; +import mekhq.campaign.parts.Armor; +import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.equipment.AmmoBin; +import mekhq.campaign.stratcon.StratconCampaignState; +import mekhq.campaign.stratcon.StratconScenario; +import mekhq.campaign.stratcon.StratconTrackState; +import mekhq.gui.dialog.resupplyAndCaches.*; + +import java.util.*; +import java.util.Map.Entry; + +import static mekhq.campaign.mission.enums.AtBMoraleLevel.CRITICAL; +import static mekhq.campaign.mission.enums.AtBMoraleLevel.DOMINATING; +import static mekhq.campaign.mission.enums.AtBMoraleLevel.STALEMATE; +import static mekhq.campaign.mission.resupplyAndCaches.GenerateResupplyContents.DropType.DROP_TYPE_AMMO; +import static mekhq.campaign.mission.resupplyAndCaches.GenerateResupplyContents.DropType.DROP_TYPE_ARMOR; +import static mekhq.campaign.mission.resupplyAndCaches.GenerateResupplyContents.DropType.DROP_TYPE_PARTS; +import static mekhq.campaign.mission.resupplyAndCaches.GenerateResupplyContents.RESUPPLY_MINIMUM_PART_WEIGHT; +import static mekhq.campaign.mission.resupplyAndCaches.GenerateResupplyContents.getResupplyContents; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.RESUPPLY_AMMO_TONNAGE; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.RESUPPLY_ARMOR_TONNAGE; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType.RESUPPLY_CONTRACT_END; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType.RESUPPLY_LOOT; +import static mekhq.campaign.mission.resupplyAndCaches.ResupplyUtilities.forceContainsMajorityVTOLForces; +import static mekhq.campaign.mission.resupplyAndCaches.ResupplyUtilities.forceContainsOnlyAerialForces; +import static mekhq.campaign.mission.resupplyAndCaches.ResupplyUtilities.forceContainsOnlyVTOLForces; +import static mekhq.campaign.stratcon.StratconRulesManager.generateExternalScenario; +import static mekhq.gui.dialog.resupplyAndCaches.DialogItinerary.itineraryDialog; +import static mekhq.utilities.EntityUtilities.getEntityFromUnitId; +import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; + +/** + * The {@code PerformResupply} class handles the execution and management of resupply operations + * within MekHQ campaigns. It covers various aspects of resupply, including generating convoy contents, + * distributing supplies, resolving convoy interceptions, and facilitating player interaction through + * dialogs tied to specific resupply scenarios. + */ +public class PerformResupply { + private static final int NPC_CONVOY_MULTIPLIER = 10; + private static final double INTERCEPTION_LOAD_INFLUENCE = 50; + public static final String RESUPPLY_LOOT_BOX_NAME = "Resupply"; + + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + private static final MMLogger logger = MMLogger.create(PerformResupply.class); + + /** + * Initiates the resupply process for a specified campaign and active contract. + * + *

    This method provides a simplified entry point to the resupply workflow, using a default value + * of 1 for the supply drop count. It delegates to the overloaded method + * {@link #performResupply(Resupply, AtBContract, int)} for the main execution of the resupply + * process, encompassing supply generation, convoy interaction, and delivery confirmation.

    + * + *

    This entry point is typically used when the exact number of supply drops is not specified or + * defaults to a single drop per invocation.

    + * + * @param resupply the {@link Resupply} instance containing information about the resupply operation, + * such as the supplies to be delivered, convoy setup, and context-specific rules. + * @param contract the {@link AtBContract} representing the current contract, which provides the + * operational context for the resupply, including permissions and restrictions. + */ + public static void performResupply(Resupply resupply, AtBContract contract) { + performResupply(resupply, contract, 1); + } + + /** + * Executes the resupply process for a specified campaign, contract, and supply drop count. + * This method coordinates supply allocation, convoy interaction, potential for interception, + * and player confirmation dialogs, ensuring the resupply process adheres to the campaign's + * context and player decisions. + * + *

    Functionality includes:

    + *
      + *
    • Early-exit handling for invalid cargo tonnage or drop count.
    • + *
    • Displaying dialogs to involve the player in choosing convoys or resupply focus.
    • + *
    • Randomized content generation for armor, ammo, and parts.
    • + *
    • Dialog-based confirmation for resupply delivery and associated costs.
    • + *
    + * + * @param resupply the {@link Resupply} instance that defines the campaign's resupply operation, + * including cargo, player and NPC convoys, and mission-related data. + * @param contract the {@link AtBContract} representing the context of the current contract, + * determining aspects such as independent resupply permissions and guerrilla warfare rules. + * @param dropCount the number of supply drops planned for this resupply operation. If zero, + * the method exits early. + */ + public static void performResupply(Resupply resupply, AtBContract contract, int dropCount) { + // These early exits should only occur if the player literally has no units. + if (dropCount == 0) { + logger.info("Resupply exited early, as DropCount is 0"); + return; + } + + int targetCargoTonnage = resupply.getTargetCargoTonnage(); + if (targetCargoTonnage == 0) { + logger.info("Resupply exited early, as targetCargoTonnage is 0"); + return; + } + + final Campaign campaign = resupply.getCampaign(); + final boolean isIndependent = contract.getCommandRights().isIndependent(); + final boolean isGuerrilla = contract.getContractType().isGuerrillaWarfare(); + final ResupplyType resupplyType = resupply.getResupplyType(); + + // If appropriate, prompt the player to use their own convoys + if (!resupplyType.equals(RESUPPLY_LOOT) && !resupplyType.equals(RESUPPLY_CONTRACT_END)) { + // If we're on a guerrilla contract, the player may be approached by smugglers, instead, + // which won't use player convoys. + if (!isGuerrilla) { + new DialogPlayerConvoyOption(resupply, isIndependent); + + // If the player is on an Independent contract and refuses to use their own transports, + // then no resupply occurs. + if (isIndependent && !resupply.getUsePlayerConvoy()) { + return; + } + } + + // Then allow the player to pick a focus + new DialogResupplyFocus(resupply); + } + + // With the focus chosen, we determine the contents of the convoy + boolean isUsePlayerConvoy = resupply.getUsePlayerConvoy(); + for (int i = 0; i < dropCount; i++) { + getResupplyContents(resupply, DROP_TYPE_ARMOR, isUsePlayerConvoy); + getResupplyContents(resupply, DROP_TYPE_AMMO, isUsePlayerConvoy); + getResupplyContents(resupply, DROP_TYPE_PARTS, isUsePlayerConvoy); + } + + resupply.setConvoyContents(resupply.getConvoyContents()); + + double totalTonnage = 0; + for (Part part : resupply.getConvoyContents()) { + if (part instanceof AmmoBin) { + totalTonnage += RESUPPLY_AMMO_TONNAGE; + } else if (part instanceof Armor) { + totalTonnage += RESUPPLY_ARMOR_TONNAGE; + } else { + totalTonnage += part.getTonnage(); + } + } + + logger.info("totalTonnage: " + totalTonnage); + + + // This shouldn't occur, but we include it as insurance. + if (resupply.getConvoyContents().isEmpty()) { + campaign.addReport(String.format(resources.getString("convoyUnsuccessful.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + return; + } + + // Everything prepared, we present the player with a dialog allowing them to confirm + // whether they are willing to pay for the delivery (if appropriate), or for them to + // confirm delivery. + itineraryDialog(resupply); + } + + /** + * Facilitates the delivery of resupply contents to the campaign's resources. + * + *

    This method categorizes parts like ammunition, armor, and general equipment, placing them + * in the warehouse.

    + * + * @param resupply the {@link Resupply} instance defining the current campaign operation. + * @param contents a list of {@link Part} objects representing the resupply contents to be delivered. + * If {@code null}, fetches convoy contents from the {@link Resupply} instance. + */ + public static void makeDelivery(Resupply resupply, @Nullable List contents) { + final Campaign campaign = resupply.getCampaign(); + + if (contents == null) { + contents = resupply.getConvoyContents(); + } + + for (Part part : contents) { + if (part instanceof AmmoBin) { + campaign.getQuartermaster().addAmmo(((AmmoBin) part).getType(), + ((AmmoBin) part).getFullShots() * RESUPPLY_AMMO_TONNAGE); + } else if (part instanceof Armor) { + int quantity = (int) Math.ceil(((Armor) part).getArmorPointsPerTon() * RESUPPLY_ARMOR_TONNAGE); + ((Armor) part).setAmount(quantity); + campaign.getWarehouse().addPart(part, true); + } else { + campaign.getWarehouse().addPart(part, true); + } + } + } + + /** + * Facilitates the delivery of supplies through smugglers, incorporating chances for smuggler + * swindling. + * + *

    Key behaviors:

    + *
      + *
    • Calculates the chance of being swindled based on the contract's morale level.
    • + *
    • If swindled, invokes a dialog to inform the player; otherwise, schedules delivery of supplies.
    • + *
    + * + * @param resupply the {@link Resupply} instance defining the resupply context. + */ + public static void makeSmugglerDelivery(Resupply resupply) { + final AtBContract contract = resupply.getContract(); + int swindleChance = contract.getMoraleLevel().ordinal(); + + if (Compute.randomInt(10) < swindleChance) { + new DialogSwindled(resupply); + } else { + final Campaign campaign = resupply.getCampaign(); + + campaign.addReport(String.format(resources.getString("convoySuccessfulSmuggler.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); + makeDelivery(resupply, null); + } + } + + /** + * Loads and organizes player convoys for a resupply operation. It calculates available + * convoy capacities, sorts convoys and contents, and assigns parts to convoys based on capacity. + * + * @param resupply the {@link Resupply} instance containing convoy and mission-specific data. + */ + public static void loadPlayerConvoys(Resupply resupply) { + // Ammo and Armor are delivered in batches of 5, so we need to make sure to multiply their + // weight by five when picking these items. + final Campaign campaign = resupply.getCampaign(); + final Map playerConvoys = resupply.getPlayerConvoys(); + + // Sort the player's available convoys according to cargo space, largest -> smallest + List> entryList = new ArrayList<>(playerConvoys.entrySet()); + entryList.sort((entry1, entry2) -> + Double.compare(entry2.getValue(), entry1.getValue())); + + List sortedConvoys = new ArrayList<>(); + for (Entry entry : entryList) { + sortedConvoys.add(entry.getKey()); + } + + final List convoyContents = resupply.getConvoyContents(); + Collections.shuffle(convoyContents); + + // Distribute parts across the convoys + for (Force convoy : sortedConvoys) { + if (convoyContents.isEmpty()) { + break; + } + + Double cargoCapacity = playerConvoys.get(convoy); + List convoyItems = new ArrayList<>(); + + for (Part part : convoyContents) { + double tonnage = part.getTonnage(); + tonnage = tonnage == 0 ? RESUPPLY_MINIMUM_PART_WEIGHT : tonnage; + + if (part instanceof AmmoBin) { + tonnage = RESUPPLY_AMMO_TONNAGE; + } else if (part instanceof Armor) { + tonnage = RESUPPLY_ARMOR_TONNAGE; + } + + if (cargoCapacity - tonnage >= 0) { + convoyItems.add(part); + cargoCapacity -= tonnage; + } + } + + convoyContents.removeAll(convoyItems); + + campaign.addReport(String.format(resources.getString("convoyDispatched.text"), + convoy.getName())); + processConvoy(resupply, convoyItems, convoy); + } + } + + /** + * Processes convoy interactions, resolving outcomes based on player decisions, convoy details, + * and interception chances. This includes factors such as convoy weight and morale influence. + * + *

    Handles logic for:

    + *
      + *
    • Calculating interception chances based on convoy weight and mission context.
    • + *
    • Generating roleplay events for player convoys.
    • + *
    • Triggering convoys and interception scenarios.
    • + *
    + * + * @param resupply the {@link Resupply} instance defining the resupply operation. + * @param convoyContents a list of {@link Part} objects representing the contents of the convoy. + * @param playerConvoy the {@link Force} object representing the player's convoy. + * If {@code null}, the convoy is an NPC-controlled unit. + */ + public static void processConvoy(Resupply resupply, List convoyContents, @Nullable Force playerConvoy) { + final Campaign campaign = resupply.getCampaign(); + final AtBContract contract = resupply.getContract(); + + // First, we need to identify whether the convoy has been intercepted. + AtBMoraleLevel morale = contract.getMoraleLevel(); + + // There isn't any chance of an interception if the enemy is Routed, so early-exit + if (morale.isRouted()) { + completeSuccessfulDelivery(resupply, convoyContents); + return; + } + + int interceptionChance = morale.ordinal(); + + // This chance is modified by convoy weight, for player convoys this is easy - we just + // calculate the weight of all units in the convoy. For NPC convoys, we need to get a bit + // creative, as we have no way to determine their size prior to any interception scenario. + // Instead, we base it on the amount of cargo they are carrying. + double convoyWeight = -200; // Convoys have a weight allowance of 200t before suffering a detection malus + + if (playerConvoy == null) { + // The multiplier we want to apply is identical to if the player was running the convoy. + int npcConvoyWeight = resupply.getTargetCargoTonnage() * NPC_CONVOY_MULTIPLIER; + convoyWeight += npcConvoyWeight; + } else { + for (UUID unitId : playerConvoy.getAllUnits(false)) { + Entity entity = getEntityFromUnitId(campaign, unitId); + + if (entity == null) { + continue; + } + + convoyWeight += entity.getWeight(); + } + } + + interceptionChance += (int) Math.ceil(convoyWeight / INTERCEPTION_LOAD_INFLUENCE); + // There is always a 1in10 chance of Interception, no matter how stealthy the convoy. + interceptionChance = Math.max(0, interceptionChance); + + // With interception chance calculated, we check to see whether an interception or event has occurred. + if (Compute.randomInt(10) < interceptionChance) { + generateInterceptionOrConvoyEvent(resupply, playerConvoy, convoyContents, interceptionChance); + } else { + completeSuccessfulDelivery(resupply, convoyContents); + } + } + + /** + * Handles convoy interceptions and their outcomes. The method determines whether the player receives + * a scenario based on the convoy's state and selects the appropriate scenario template, generating an + * encounter or completing the delivery as necessary. + * + *

    Decision-making includes:

    + *
      + *
    • Determines scenario templates based on convoy types (e.g., VTOL, aerospace).
    • + *
    • Reports critical errors and gracefully completes deliveries if templates fail.
    • + *
    • Generates strategic map scenarios to handle interception events dynamically.
    • + *
    + * + * @param resupply the {@link Resupply} instance containing resupply details. + * @param convoy the {@link Force} representing the player's convoy. Can be {@code null} for NPC convoys. + * @param convoyContents a list of {@link Part} objects representing convoy cargo. + * @param interceptionChance the calculated chance of interception for the convoy. + */ + private static void generateInterceptionOrConvoyEvent(Resupply resupply, @Nullable Force convoy, + @Nullable List convoyContents, + int interceptionChance) { + final Campaign campaign = resupply.getCampaign(); + final AtBContract contract = resupply.getContract(); + + if (Compute.randomInt(10) < interceptionChance) { + processConvoyInterception(resupply, convoy, convoyContents); + } else { + // If it is an NPC convoy, we skip roleplay events + if (convoy == null) { + completeSuccessfulDelivery(resupply, convoyContents); + return; + } + + // Non-ground convoys don't get roleplay events + if (forceContainsOnlyVTOLForces(campaign, convoy) || forceContainsOnlyAerialForces(campaign, convoy)) { + completeSuccessfulDelivery(resupply, convoyContents); + return; + } + + // Generate roleplay event + final String STATUS_FORWARD = "statusUpdate"; + final String STATUS_AFTERWARD = ".text"; + + AtBMoraleLevel morale = contract.getMoraleLevel(); + + String eventText; + if (Compute.d6() <= 2) { + eventText = resources.getString(STATUS_FORWARD + Compute.randomInt(100) + + STATUS_AFTERWARD); + } else { + int roll = Compute.randomInt(2); + + if (morale.isAdvancing() || morale.isWeakened()) { + morale = roll == 0 ? (morale.isAdvancing() ? DOMINATING : CRITICAL) : STALEMATE; + } + + eventText = resources.getString(STATUS_FORWARD + "Enemy" + morale + + Compute.randomInt(50) + STATUS_AFTERWARD); + } + + new DialogRoleplayEvent(campaign, convoy, eventText); + completeSuccessfulDelivery(resupply, convoyContents); + } + } + + /** + * Completes the successful delivery of convoy supplies, adding them to campaign resources + * and providing a positive campaign report. + * + * @param resupply the {@link Resupply} instance describing the mission context. + * @param convoyContents the list of convoy contents to be delivered. + */ + private static void completeSuccessfulDelivery(Resupply resupply, List convoyContents) { + final Campaign campaign = resupply.getCampaign(); + + campaign.addReport(String.format(resources.getString("convoySuccessful.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); + + makeDelivery(resupply, convoyContents); + } + + /** + * Handles the interception of a convoy operation. Based on the convoy's state and type, it determines + * the most appropriate scenario template and resolves the outcome of the interception. + * + *

    Key behaviors:

    + *
      + *
    • Identifies an appropriate scenario template for the convoy (e.g., VTOL, player convoy).
    • + *
    • Randomly selects a strategic track to simulate scenario placement.
    • + *
    • Provides loot or completes the delivery if no valid interception scenario exists.
    • + *
    + * + * @param resupply the {@link Resupply} instance representing the resupply mission. + * @param targetConvoy the {@link Force} representing the player's convoy. Can be {@code null} for NPC convoys. + * @param convoyContents a list of {@link Part} objects representing the resupply cargo. + */ + private static void processConvoyInterception(Resupply resupply, @Nullable Force targetConvoy, + @Nullable List convoyContents) { + final String DIRECTORY = "data/scenariotemplates/"; + final String GENERIC = DIRECTORY + "Emergency Convoy Defense.xml"; + final String PLAYER_AEROSPACE_CONVOY = DIRECTORY + "Emergency Convoy Defense - Player - Low-Atmosphere.xml"; + final String PLAYER_VTOL_CONVOY = DIRECTORY + "Emergency Convoy Defense - Player - VTOL.xml"; + final String PLAYER_CONVOY = DIRECTORY + "Emergency Convoy Defense - Player.xml"; + + final Campaign campaign = resupply.getCampaign(); + final AtBContract contract = resupply.getContract(); + + // Trigger a dialog to inform the user an interception has taken place + new DialogInterception(resupply, targetConvoy); + + // Determine which scenario template to use based on convoy state + String templateAddress = GENERIC; + + if (targetConvoy != null) { + if (forceContainsOnlyAerialForces(campaign, targetConvoy)) { + templateAddress = PLAYER_AEROSPACE_CONVOY; + } else if (forceContainsMajorityVTOLForces(campaign, targetConvoy)) { + templateAddress = PLAYER_VTOL_CONVOY; + } else { + templateAddress = PLAYER_CONVOY; + } + } + ScenarioTemplate template = ScenarioTemplate.Deserialize(templateAddress); + + // If we're not using a player convoy, get all possible parts and put them in a pool + if (targetConvoy == null) { + convoyContents = resupply.getConvoyContents(); + } + + // If we've failed to deserialize the requested template, report the error and make the delivery. + // We report the error in this fashion, instead of hiding it in the log, as we want to + // increase the likelihood the player is aware an error has occurred. + if (template == null) { + campaign.addReport(String.format(resources.getString("convoyErrorTemplate.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + templateAddress, CLOSING_SPAN_TAG)); + + makeDelivery(resupply, convoyContents); + return; + } + + // Pick a random track where the interception will take place. If we fail to get a track, + // we log an error and make the delivery, in the same manner as above. + StratconTrackState track; + try { + final StratconCampaignState campaignState = contract.getStratconCampaignState(); + List tracks = campaignState.getTracks(); + track = ObjectUtility.getRandomItem(tracks); + } catch (NullPointerException e) { + campaign.addReport(String.format(resources.getString("convoyErrorTracks.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + templateAddress, CLOSING_SPAN_TAG)); + + makeDelivery(resupply, convoyContents); + return; + } + + // Generate the scenario, placing it in a random hex that does not currently contain a + // scenario, or a facility. If the player is really lucky, the scenario will spawn on top + // of a force already deployed to the Strategic Map. + StratconScenario scenario = generateExternalScenario(campaign, contract, track, + null, template, false, 0); + + // If we successfully generated a scenario, we need to make a couple of final + // adjustments, including assigning the Resupply contents as loot and + // assigning a player convoy (if appropriate) + if (scenario != null) { + AtBDynamicScenario backingScenario = scenario.getBackingScenario(); + + if (targetConvoy != null) { + String currentName = backingScenario.getName(); + backingScenario.setName(currentName + " - " + targetConvoy.getName()); + + backingScenario.addForce(targetConvoy.getId(), ScenarioTemplate.PRIMARY_PLAYER_FORCE_ID); + targetConvoy.setScenarioId(backingScenario.getId(), campaign); + scenario.commitPrimaryForces(); + } + + Loot loot = new Loot(); + loot.setName(RESUPPLY_LOOT_BOX_NAME); + + if (convoyContents != null) { + for (Part part : convoyContents) { + loot.addPart(part); + } + } + + backingScenario.addLoot(loot); + + // Announce the situation to the player + campaign.addReport(String.format(resources.getString("convoyInterceptedStratCon.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + } else { + // If we failed to generate a scenario, for whatever reason, we don't + // want the player confused why there isn't a scenario, so we offer + // this fluffy response. + campaign.addReport(String.format(resources.getString("convoyEscaped.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + + makeDelivery(resupply, convoyContents); + } + } +} diff --git a/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/Resupply.java b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/Resupply.java new file mode 100644 index 00000000000..561634fe0b2 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/Resupply.java @@ -0,0 +1,952 @@ +package mekhq.campaign.mission.resupplyAndCaches; + +import megamek.common.Entity; +import megamek.common.Mek; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; +import mekhq.campaign.finances.Money; +import mekhq.campaign.force.CombatTeam; +import mekhq.campaign.force.Force; +import mekhq.campaign.market.procurement.Procurement; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.parts.*; +import mekhq.campaign.parts.equipment.*; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.Skill; +import mekhq.campaign.personnel.SkillType; +import mekhq.campaign.unit.Unit; +import mekhq.campaign.universe.Faction; + +import java.math.BigInteger; +import java.time.LocalDate; +import java.util.*; + +import static java.lang.Math.floor; +import static java.lang.Math.min; +import static java.lang.Math.round; +import static megamek.common.MiscType.F_SPONSON_TURRET; +import static megamek.common.enums.SkillLevel.NONE; +import static mekhq.campaign.force.CombatTeam.getStandardForceSize; +import static mekhq.campaign.market.procurement.Procurement.getFactionTechCode; +import static mekhq.utilities.EntityUtilities.getEntityFromUnitId; + +/** + * The {@code Resupply} class manages the resupply process during a campaign. + * It calculates the required resupply resources, organizes parts pools (e.g., parts, armor, ammo), + * and handles player convoy logistics and negotiation skills. + *

    + * It supports functionality such as + * - Calculating target resupply tonnage based on combat unit weight and allowances. + * - Building pools for parts (e.g., spare parts, ammo, and armor). + * - Managing player convoy logistics. + * - Handling procurement and negotiation results for administered resources. + */ +public class Resupply { + private final Campaign campaign; + private final AtBContract contract; + private final ResupplyType resupplyType; + private final Faction employerFaction; + private final int currentYear; + private final int employerTechCode; + private final boolean employerIsClan; + private List ammoBinPool; + private double focusAmmo; + private List armorPool; + private double focusArmor; + private List partsPool; + private double focusParts; + private boolean usePlayerConvoy; + private Map playerConvoys; + private int targetCargoTonnage; + private int targetCargoTonnagePlayerConvoy; + private double totalPlayerCargoCapacity; + private int negotiatorSkill; + private List convoyContents; + private Money convoyContentsValueBase; + private Money convoyContentsValueCalculated; + + public static final int CARGO_MULTIPLIER = 4; + public static final int RESUPPLY_AMMO_TONNAGE = 1; + public static final int RESUPPLY_ARMOR_TONNAGE = 5; + final LocalDate BATTLE_OF_TUKAYYID = LocalDate.of(3052, 5, 21); + + private static final MMLogger logger = MMLogger.create(Resupply.class); + + /** + * Enum representing the various types of resupply methods available during a campaign. + */ + public enum ResupplyType { + RESUPPLY_NORMAL, RESUPPLY_LOOT, RESUPPLY_CONTRACT_END, RESUPPLY_SMUGGLER + } + + /** + * Constructs a new {@link Resupply} instance and initializes resupply parameters for the + * given campaign and contract. This includes setting faction data, calculating target cargo + * tonnage, and building parts pools. + * + * @param campaign The current campaign. + * @param contract The specific contract under which the resupply process is conducted. + */ + public Resupply(Campaign campaign, AtBContract contract, ResupplyType resupplyType) { + this.campaign = campaign; + this.contract = contract; + this.resupplyType = resupplyType; + + employerFaction = contract.getEmployerFaction(); + usePlayerConvoy = contract.getCommandRights().isIndependent(); + targetCargoTonnage = calculateTargetCargoTonnage(campaign, contract); + targetCargoTonnagePlayerConvoy = targetCargoTonnage * CARGO_MULTIPLIER; + + currentYear = campaign.getGameYear(); + + Faction enemyFaction = contract.getEnemy(); + employerIsClan = enemyFaction.isClan(); + + employerTechCode = getFactionTechCode(employerFaction); + + focusAmmo = 0.25; + focusArmor = 0.25; + focusParts = 0.5; + + calculateNegotiationSkill(); + buildPartsPools(collectParts()); + calculatePlayerConvoyValues(); + + convoyContents = new ArrayList<>(); + convoyContentsValueBase = Money.zero(); + convoyContentsValueCalculated = Money.zero(); + } + + /** + * Retrieves the current campaign. + * + * @return A {@link Campaign} representing the current campaign. + */ + public Campaign getCampaign() { + return campaign; + } + + /** + * Retrieves the current contract associated with the resupply operation. + * + * @return An {@link AtBContract} representing the current contract. + */ + public AtBContract getContract() { + return contract; + } + + /** + * Retrieves the current resupply type being used. + * The resupply type indicates the method by which supplies are obtained. + * + * @return A {@link ResupplyType} representing the current method of resupply. + */ + public ResupplyType getResupplyType() { + return resupplyType; + } + + /** + * Retrieves the target cargo tonnage calculated for the resupply process. + * This value represents the expected tonnage of supplies to be delivered + * based on the current campaign and contract conditions. + * + * @return The target cargo tonnage. + */ + public int getTargetCargoTonnage() { + return targetCargoTonnage; + } + + /** + * Retrieves the current focus percentage allocated for ammunition resupply. + * + * @return The percentage of resupply focus allocated to ammo. + */ + public double getFocusAmmo() { + return focusAmmo; + } + /** + * Sets the focus percentage allocated for ammunition resupply. + * + * @param focusAmmo The percentage of resupply focus to allocate to ammo. + */ + public void setFocusAmmo(double focusAmmo) { + this.focusAmmo = focusAmmo; + } + + /** + * Retrieves the current focus percentage allocated for armor resupply. + * + * @return The percentage of resupply focus allocated to armor. + */ + public double getFocusArmor() { + return focusArmor; + } + /** + * Sets the focus percentage allocated for armor resupply. + * + * @param focusArmor The percentage of resupply focus to allocate to armor. + */ + public void setFocusArmor(double focusArmor) { + this.focusArmor = focusArmor; + } + + /** + * Retrieves the current focus percentage allocated for general parts resupply. + * + * @return The percentage of resupply focus allocated to parts. + */ + public double getFocusParts() { + return focusParts; + } + /** + * Sets the focus percentage allocated for general parts resupply. + * + * @param focusParts The percentage of resupply focus to allocate to parts. + */ + public void setFocusParts(double focusParts) { + this.focusParts = focusParts; + } + + /** + * Retrieves the pool of general parts available for resupply. + * This pool includes non-specific parts that have passed all eligibility checks. + * + * @return A list of general parts available for resupply. + */ + public List getPartsPool() { + return partsPool; + } + + /** + * Retrieves the pool of armor parts available for resupply. + * This includes any eligible armor components that have been processed. + * + * @return A list of armor parts available for resupply. + */ + public List getArmorPool() { + return armorPool; + } + + /** + * Retrieves the pool of ammo bins available for resupply. + * This includes eligible ammunition bins that have been processed. + * + * @return A list of ammo bins available for resupply. + */ + public List getAmmoBinPool() { + return ammoBinPool; + } + + /** + * Retrieves the negotiation skill level of the designated negotiator for the resupply process. + * This skill level is used to influence procurement outcomes and quality. + * + * @return The negotiation skill level of the negotiator. + */ + public int getNegotiatorSkill() { + return negotiatorSkill; + } + + /** + * Retrieves the target cargo tonnage used when the player is providing convoy units. + * This value represents the desired amount of cargo that the player's convoy aims to carry. + * + * @return The target cargo tonnage for the player's convoy. + */ + public int getTargetCargoTonnagePlayerConvoy() { + return targetCargoTonnagePlayerConvoy; + } + + /** + * Retrieves the total cargo capacity available in the player's convoy. + * This capacity indicates the maximum amount of cargo that the convoy can hold. + * + * @return The total cargo capacity of the player's convoy. + */ + public double getTotalPlayerCargoCapacity() { + return totalPlayerCargoCapacity; + } + + /** + * Retrieves the current list of parts in the convoy's inventory. + * This method provides access to the parts being transported by the convoy. + * + * @return A list of parts contained within the convoy. + */ + public List getConvoyContents() { + return convoyContents; + } + /** + * Sets the list of parts to be included in the convoy's inventory. + * This method allows the assignment or modification of the parts being transported by the convoy. + * + * @param convoyContents The list of parts to be assigned to the convoy. + */ + public void setConvoyContents(List convoyContents) { + this.convoyContents = convoyContents; + } + + /** + * Retrieves the base value of the contents in the convoy. + * This value typically represents the estimated or predetermined worth of the convoy's cargo. + * + * @return A {@link Money} object representing the base value of the convoy contents. + */ + public Money getConvoyContentsValueBase() { + return convoyContentsValueBase; + } + /** + * Sets the base value of the contents in the convoy. + * This is used to define the predetermined or estimated worth of the convoy's cargo. + * + * @param convoyContentsValueBase A {@link Money} object representing the base value of the + * convoy contents. + */ + public void setConvoyContentsValueBase(Money convoyContentsValueBase) { + this.convoyContentsValueBase = convoyContentsValueBase; + } + + /** + * Retrieves the calculated value of the convoy's contents. + * This value is typically dynamic and may include various adjustments based on gameplay scenarios. + * + * @return A {@link Money} object representing the calculated value of the convoy contents. + */ + public Money getConvoyContentsValueCalculated() { + return convoyContentsValueCalculated; + } + /** + * Sets the calculated value of the convoy's contents. + * This value may be adjusted dynamically during the campaign or based on external factors. + * + * @param convoyContentsValueCalculated A {@link Money} object representing the calculated value + * of the convoy contents. + */ + public void setConvoyContentsValueCalculated(Money convoyContentsValueCalculated) { + this.convoyContentsValueCalculated = convoyContentsValueCalculated; + } + + /** + * Retrieves the player convoys and their corresponding cargo capacities. + * This method provides a mapping of player-controlled forces to their available cargo capacity. + * + * @return A {@link Map} where the key is a {@link Force} representing the player's convoy, + * and the value is a {@link Double} indicating its cargo capacity. + */ + public Map getPlayerConvoys() { + return playerConvoys; + } + + /** + * Checks whether the player's convoy is set to be used for resupply purposes. + * This indicates if resupply operations take into account the player's convoy. + * + * @return A {@code boolean} indicating whether the player's convoy is being used. + * Returns {@code true} if the player's convoy is used, {@code false} otherwise. + */ + public boolean getUsePlayerConvoy() { + return usePlayerConvoy; + } + /** + * Sets whether the player's convoy should be used for resupply purposes. + * This determines if cargo and resupply operations will consider the player's convoy. + * + * @param usePlayerConvoy A {@code boolean} indicating whether the player's convoy should be used. + * {@code true} to use the player's convoy, {@code false} otherwise. + */ + public void setUsePlayerConvoy(boolean usePlayerConvoy) { + this.usePlayerConvoy = usePlayerConvoy; + } + + static int calculateTargetCargoTonnage(Campaign campaign, AtBContract contract) { + double unitTonnage = 0; + + // First, calculate the total tonnage across all combat units in the campaign. + // We define a 'combat unit' as any unit not flagged as non-combat who is both in a Combat + // Team and not in a Force flagged as non-combat + for (CombatTeam formation : campaign.getCombatTeamsTable().values()) { + Force force = campaign.getForce(formation.getForceId()); + + if (force == null) { + continue; + } + + if (!force.isCombatForce()) { + continue; + } + + for (UUID unitId : force.getAllUnits(true)) { + Entity entity = getEntityFromUnitId(campaign, unitId); + + if (entity == null) { + continue; + } + + if (isProhibitedUnitType(entity, false)) { + continue; + } + + unitTonnage += entity.getWeight(); + } + } + + // Next, we determine the tonnage cap. This is the maximum tonnage the employer is willing to support. + final int INDIVIDUAL_TONNAGE_ALLOWANCE = 80; // This is how many tons the employer will budget per unit + final int formationSize = getStandardForceSize(campaign.getFaction()); + final int tonnageCap = contract.getRequiredCombatTeams() * formationSize * INDIVIDUAL_TONNAGE_ALLOWANCE; + + // Then we determine the size of each individual 'drop'. This uses the lowest of + // unitTonnage and tonnageCap and divides that by 100 + final double baseTonnage = min(unitTonnage, tonnageCap); + + final int TONNAGE_DIVIDER = 125; + final double dropSize = baseTonnage / TONNAGE_DIVIDER; + + return (int) round(dropSize); + } + + /** + * Checks whether a unit type is prohibited from resupply based on its characteristics. + * Some units, such as large craft, super-heavy units, and conventional infantry, may + * be excluded. + *

    + * If {@code excludeDropShipsFromCheck} is {@code true} DropShips will not be considered a + * prohibited unit + * + * @param entity The entity being checked. + * @param excludeDropShipsFromCheck {@code true} to exclude DropShips from prohibited checks, + * {@code false} otherwise. + * @return {@code true} if the unit type is prohibited, {@code false} otherwise. + */ + public static boolean isProhibitedUnitType(Entity entity, boolean excludeDropShipsFromCheck) { + if (entity.isDropShip() && excludeDropShipsFromCheck) { + return false; + } + + return entity.isSmallCraft() + || entity.isLargeCraft() + || entity.isSuperHeavy() + || entity.isConventionalInfantry(); + } + + /** + * Builds the pools of parts (e.g., general parts, ammo bins, and armor) for the resupply drop. + * Parts are procured, shuffled, and organized into their respective pools for distribution. + * + * @param potentialParts A map of potential parts to include in the supply drop, keyed by part name. + */ + private void buildPartsPools(Map potentialParts) { + partsPool = new ArrayList<>(); + armorPool = new ArrayList<>(); + ammoBinPool = new ArrayList<>(); + + for (PartDetails potentialPart : potentialParts.values()) { + int weight = (int) Math.round(potentialPart.getWeight()); + for (int entry = 0; entry < weight; entry++) { + Part part = potentialPart.getPart(); + Part preparedPart = preparePart(part); + + // We don't need null protection for 'part' as if 'part' is null preparedPart will + // just return 'null', which we catch here. + if (preparedPart == null) { + continue; + } + + if (preparedPart instanceof Armor) { + armorPool.add(preparedPart); + continue; + } + + if (preparedPart instanceof AmmoBin) { + ammoBinPool.add(preparedPart); + continue; + } + + partsPool.add(preparedPart); + } + } + + // Make procurement checks for each of the items in the individual pools + Procurement procurement = new Procurement(negotiatorSkill, currentYear, employerFaction); + + partsPool = procurement.makeProcurementChecks(partsPool, true, true); + Collections.shuffle(partsPool); + + armorPool = procurement.makeProcurementChecks(armorPool, true, true); + Collections.shuffle(armorPool); + + ammoBinPool = procurement.makeProcurementChecks(ammoBinPool, true, true); + Collections.shuffle(ammoBinPool); + } + + /** + * Prepares a copy of a part for inclusion in the resupply pool. This involves cloning + * the part, marking it as new, and fixing any issues. If part cloning fails or the part + * is invalid for inclusion, {@code null} is returned. + * + * @param originPart The original part to prepare. + * @return The prepared part, or {@code null} if the part cannot be included. + */ + private @Nullable Part preparePart(Part originPart) { + Part clonedPart = originPart.clone(); + + // If we failed to clone a part, it's likely because the part doesn't exist. + // This means it's been destroyed, and what we're detecting is the absence of a part. + // This is a major limitation of cloning parts, and one I've not fathomed a solution to. + if (clonedPart == null) { + return null; + } + + // TODO: Improve handling of missing or destroyed locations and equipment. + // This will likely need to be a >50.02 thing, unfortunately. + + try { + clonedPart.fix(); + } catch (Exception e) { + clonedPart.setHits(0); + } + + clonedPart.setBrandNew(true); + clonedPart.setOmniPodded(false); + + return clonedPart; + } + + /** + * Collects eligible parts from campaign units and organizes them into a map. Each part + * is checked for eligibility using methods such as exclusion lists and location validation. + * Campaign warehouse resources are also factored into the weight of included resources. + * + * @return A map of part names with their corresponding details (e.g., weight). + */ + private Map collectParts() { + final Collection unitIds = campaign.getForce(0).getAllUnits(true); + Map processedParts = new HashMap<>(); + + boolean allowClan = (employerIsClan || campaign.getLocalDate().isAfter(BATTLE_OF_TUKAYYID)) + && campaign.getCampaignOptions().isAllowClanPurchases(); + boolean allowInnerSphere = campaign.getCampaignOptions().isAllowISPurchases(); + + try { + for (UUID unitId : unitIds) { + Unit unit = campaign.getUnit(unitId); + + if (unit == null) { + continue; + } + + Entity entity = unit.getEntity(); + + if (entity == null) { + continue; + } + + if (isProhibitedUnitType(entity, false)) { + logger.info("skipping " + unit.getName() + " as it is prohibited."); + continue; + } + + if (!unit.isSalvage() && (unit.isAvailable() || unit.isDeployed())) { + List parts = unit.getParts(); + for (Part part : parts) { + if (part.isClan()) { + if (!allowClan) { + continue; + } + } else { + if (!allowInnerSphere) { + continue; + } + } + + if (isIneligiblePart(part, unit)) { + continue; + } + + int dropWeight = part instanceof MissingPart ? 10 : 1; + dropWeight = (int) floor(dropWeight * getPartMultiplier(part)); + + PartDetails partDetails = new PartDetails(part, dropWeight); + + processedParts.merge(getPartKey(part), partDetails, (oldValue, newValue) -> { + oldValue.setWeight(oldValue.getWeight() + newValue.getWeight()); + return oldValue; + }); + } + } + } + + applyWarehouseWeightModifiers(processedParts); + } catch (Exception exception) { + logger.error("Aborted parts collection.", exception); + } + + return processedParts; + } + + /** + * Generates a key for the given part based on its name and tonnage. + * + *

    The key is a combination of the part's name and its tonnage, separated by a colon. + * For specific part types such as {@link AmmoBin} and {@link Armor}, the tonnage is + * always set to a set value, regardless of the actual tonnage.

    + * + * @param part The {@link Part} for which the key is generated. Must not be {@code null}. + * @return A unique key in the format {@code "partName:partTonnage"}, where + * {@code partName} is the name of the part and {@code partTonnage} is the + * tonnage of the part or a fixed value for {@link AmmoBin} and {@link Armor}. + */ + private static String getPartKey(Part part) { + String partName = part.getName(); + double partTonnage = part.getTonnage(); + + if (part instanceof AmmoBin) { + partTonnage = RESUPPLY_AMMO_TONNAGE; + } else if (part instanceof Armor) { + partTonnage = RESUPPLY_ARMOR_TONNAGE; + } + + return partName + ':' + partTonnage; + } + + /** + * Checks if a part is ineligible for inclusion in the resupply process. Ineligibility is + * determined based on exclusion lists, unit structure compatibility, and transporter checks. + * + * @param part The part being checked. + * @param unit The unit to which the part belongs. + * @return {@code true} if the part is ineligible, {@code false} otherwise. + */ + private boolean isIneligiblePart(Part part, Unit unit) { + return checkExclusionList(part) + || checkMekLocation(part, unit) + || checkTankLocation(part) + || checkMotiveSystem(part) + || checkTransporter(part); + } + + /** + * Checks if a part is in the exclusion list and should not be considered for resupply. + * Equipment parts are evaluated for specific flags, such as {@code F_SPONSON_TURRET}, + * which would disqualify them from inclusion. + * + * @param part The part to check. + * @return {@code true} if the part is in the exclusion list, {@code false} otherwise. + */ + private boolean checkExclusionList(Part part) { + if (part instanceof EquipmentPart) { + List excludedTypes = List.of(F_SPONSON_TURRET); + for (BigInteger excludedType : excludedTypes) { + if (((EquipmentPart) part).getType().hasFlag(excludedType)) { + return true; + } + } + } + return false; + } + + /** + * Checks if the given part is an instance of {@code MotiveSystem}. + * + * @param part the {@link Part} to be checked. + * @return {@code true} if the part is a {@link MotiveSystem}, {@code false} otherwise. + */ + private boolean checkMotiveSystem(Part part) { + return part instanceof MotiveSystem; + } + + /** + * Checks if a part belonging to a 'Mek' unit is eligible for resupply, based on its location + * or whether the unit is considered extinct. For example, parts located in the center torso + * or parts from extinct units are deemed ineligible. + * + * @param part The part to check. + * @param mek The unit to which the part belongs. + * @return {@code true} if the part is ineligible due to its location or extinction, + * {@code false} otherwise. + */ + private boolean checkMekLocation(Part part, Unit mek) { + return part instanceof MekLocation && + (((MekLocation) part).getLoc() == Mek.LOC_CT + || mek.isExtinct(currentYear, employerIsClan, employerTechCode)); + } + + /** + * Verifies if a vehicle part is eligible for resupply. Parts such as rotors + * and turrets are always eligible, while other tank locations may be disqualified. + * + * @param part The part to check. + * @return {@code true} if the part is ineligible for resupply, {@code false} otherwise. + */ + private boolean checkTankLocation(Part part) { + return part instanceof TankLocation && !(part instanceof Rotor || part instanceof Turret); + } + + /** + * Determines whether a part is a transporter-related part, such as a `TransportBayPart`, + * which makes it ineligible for consideration in the resupply process. + * + * @param part The part to check. + * @return {@code true} if the part is a transporter part, {@code false} otherwise. + */ + private boolean checkTransporter(Part part) { + return part instanceof TransportBayPart; + } + + /** + * Adjusts the provided parts list by applying warehouse weight modifiers. + * + *

    This method compares the in-campaign warehouse's spare parts inventory with the given + * parts list and reduces the weight (quantity) of parts in the list based on the warehouse + * stock. If the warehouse contains enough resources to fully satisfy the demand for a part, + * the part is removed from the parts list.

    + * + *

    The adjustments are performed as follows: + *

      + *
    • For each part in the warehouse: + *
        + *
      • The weight of the part in the part list is reduced by the quantity available + * in the warehouse.
      • + *
      • If the weight becomes zero or negative, the part is flagged for removal.
      • + *
      + *
    • + *
    • All flagged parts are then removed from the part list.
    • + *
    + *

    + * + * @param partsList A map containing part identifiers (keys) and their corresponding {@link PartDetails}. + * The map will be modified to reflect the warehouse adjustments. + */ + private void applyWarehouseWeightModifiers(Map partsList) { + // Adjust based on the quantity in the warehouse + for (Part part : campaign.getWarehouse().getSpareParts()) { + int weight = part.getQuantity(); + + // We don't want empty AmmoStorage to reduce Resupply weighting + if (part instanceof AmmoStorage && (((AmmoStorage) part).getShots() == 0)) { + continue; + } + + // This prevents us accidentally adding new items to the pool + if (!partsList.containsKey(getPartKey(part))) { + continue; + } + + PartDetails partDetails = new PartDetails(part, weight); + partsList.merge(getPartKey(part), partDetails, (oldValue, newValue) -> { + oldValue.setWeight(oldValue.getWeight() - newValue.getWeight()); + return oldValue; + }); + } + + // Remove any items that now have 0 (or negative) tickets left in the pool + List removalList = new ArrayList<>(); + for (PartDetails partDetails : partsList.values()) { + Part part = partDetails.getPart(); + double weight = partDetails.getWeight(); + + if (weight <= 0) { + removalList.add(getPartKey(part)); + } + } + + for (String removalKey : removalList) { + partsList.remove(removalKey); + } + } + + /** + * Retrieves the multiplier value for a specific part type to calculate its priority + * in the resupply process. This multiplier affects how the part is weighted when + * included in the pool of available resupply resources. + * + * @param part The part to determine the multiplier for. + * @return A multiplier value for the given part type. + */ + private static double getPartMultiplier(Part part) { + double multiplier = 1; + + // This is based on the Mishra Method, found in the Company Generator + if (part instanceof HeatSink) { + multiplier = 2.5; + } else if (part instanceof MekLocation) { + if (((MekLocation) part).getLoc() == Mek.LOC_HEAD) { + multiplier = 2; + } + } else if (part instanceof MASC + || part instanceof MekGyro + || part instanceof EnginePart + || checkEquipmentSubType(part)) { + multiplier = 0.5; + } else if (part instanceof AmmoBin || part instanceof Armor) { + multiplier = 5; + } + + return multiplier; + } + + /** + * Determines whether a part is an eligible equipment subtype for the resupply process. + * Excludes certain types such as ammo, ammo storage, heat sinks, and jump jets. + * + * @param part The part to check. + * @return {@code true} if the part is an eligible equipment subtype, {@code false} otherwise. + */ + private static boolean checkEquipmentSubType(Part part) { + if (part instanceof EquipmentPart) { + if (part instanceof AmmoBin) { + return false; + } + + if (part instanceof AmmoStorage) { + return false; + } + + if (part instanceof BattleArmorEquipmentPart) { + return false; + } + + if (part instanceof HeatSink) { + return false; + } + + return !(part instanceof JumpJet); + } + + return true; + } + + /** + * Calculates the negotiation skill level by selecting the most qualified negotiator + * in the current campaign. If the contract type is classified as guerrilla warfare, the + * flagged commander is prioritized. Otherwise, Admin/Logistics personnel are evaluated. + */ + private void calculateNegotiationSkill() { + Person negotiator; + negotiatorSkill = NONE.ordinal(); + + if (contract.getContractType().isGuerrillaWarfare()) { + negotiator = campaign.getFlaggedCommander(); + } else { + negotiator = null; + + for (Person admin : campaign.getAdmins()) { + if (admin.getPrimaryRole().isAdministratorLogistics() + || admin.getSecondaryRole().isAdministratorLogistics()) { + if (negotiator == null + || (admin.outRanksUsingSkillTiebreaker(campaign, negotiator))) { + negotiator = admin; + } + } + } + } + + if (negotiator != null) { + Skill skill = negotiator.getSkill(SkillType.S_NEG); + + if (skill != null) { + int skillLevel = skill.getFinalSkillValue(); + negotiatorSkill = skill.getType().getExperienceLevel(skillLevel); + } + } + } + + /** + * Calculates the total cargo capacity available in player-controlled convoys. + * Convoy forces and their units are evaluated for cargo capacity, and disabled, + * damaged, or uncrewed units are excluded from the totals. + */ + private void calculatePlayerConvoyValues() { + playerConvoys = new HashMap<>(); + totalPlayerCargoCapacity = 0; + + for (Force force : campaign.getAllForces()) { + if (!force.isConvoyForce()) { + continue; + } + + double cargoCapacitySubTotal = 0; + if (force.isConvoyForce()) { + boolean hasCargo = false; + for (UUID unitId : force.getAllUnits(false)) { + try { + Unit unit = campaign.getUnit(unitId); + Entity entity = unit.getEntity(); + + if (unit.isDamaged() + || !unit.isFullyCrewed() + || isProhibitedUnitType(entity, true)) { + continue; + } + + double individualCargo = unit.getCargoCapacity(); + + if (individualCargo > 0) { + hasCargo = true; + } + + cargoCapacitySubTotal += individualCargo; + } catch (Exception ignored) { + // If we run into an exception, it's because we failed to get Unit or Entity. + // In either case, we just ignore that unit. + } + } + + if (hasCargo) { + if (cargoCapacitySubTotal > 0) { + totalPlayerCargoCapacity += cargoCapacitySubTotal; + playerConvoys.put(force, cargoCapacitySubTotal); + } + } + } + } + } + + /** + * Represents details about a part used during the collection and sorting process + * for resupply resources. Contains the part itself and its weight value. + */ + private static class PartDetails { + private final Part part; + private double weight; + + /** + * Constructs a new {@code PartDetails} instance. + * + * @param part The associated part. + * @param weight The weight or priority of the part. + */ + public PartDetails(Part part, double weight) { + this.part = part; + this.weight = weight; + } + + /** + * Gets the associated part. + * + * @return The part. + */ + public Part getPart() { + return part; + } + + /** + * Gets the current weight of the part. + * + * @return The weight. + */ + public double getWeight() { + return weight; + } + + /** + * Sets the current weight of the part. + * + * @param weight The new weight to assign. + */ + public void setWeight(double weight) { + this.weight = weight; + } + } +} diff --git a/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/ResupplyUtilities.java b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/ResupplyUtilities.java new file mode 100644 index 00000000000..deb222ef34e --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/ResupplyUtilities.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 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.mission.resupplyAndCaches; + +import megamek.common.Compute; +import megamek.common.Entity; +import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.AtBDynamicScenario; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.enums.PersonnelStatus; +import mekhq.campaign.unit.Unit; +import mekhq.gui.dialog.resupplyAndCaches.DialogAbandonedConvoy; + +import java.util.UUID; +import java.util.Vector; + +import static java.lang.Math.floor; +import static java.lang.Math.max; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.CARGO_MULTIPLIER; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.RESUPPLY_AMMO_TONNAGE; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.RESUPPLY_ARMOR_TONNAGE; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.calculateTargetCargoTonnage; +import static mekhq.campaign.personnel.enums.PersonnelStatus.KIA; +import static mekhq.utilities.EntityUtilities.getEntityFromUnitId; + +/** + * Utility class for managing resupply operations and events in MekHQ campaigns. + * + *

    The functionalities provided by this class assist in handling various resupply and convoy-related + * scenarios, including: + *

      + *
    • Managing abandoned convoy scenarios by removing units, updating crew statuses, and triggering dialogs.
    • + *
    • Estimating cargo requirements for resupply missions based on campaign context and contract details.
    • + *
    • Determining the outcomes for personnel (e.g., prisoners of war, killed in action) following combat-related events.
    • + *
    + * + *

    The class interacts heavily with the campaign's {@link Campaign} context and dynamic scenarios + * from AtB (Against the Bot) contracts. It also integrates random computations for decision-making, + * ensuring variability in outcomes for immersive campaign simulation.

    + * + *

    This utility is central to the logistics and event-handling systems present in MekHQ's resupply mechanics.

    + */ +public class ResupplyUtilities { + /** + * Processes an abandoned convoy, managing the removal of units and determining the fate of the + * convoy's crew members. + * + *

    This method performs the following tasks: + *

      + *
    • Identifies the player's convoy force from the scenario's template force IDs.
    • + *
    • Resolves each unit and its crew within the convoy:
    • + *
    • - Determines each crew member's fate, either captured or killed in action.
    • + *
    • - Removes units from the campaign force.
    • + *
    • Displays a dialog about the abandoned convoy.
    • + *
    + * + * @param campaign the {@link Campaign} instance to which the convoy belongs. + * @param contract the {@link AtBContract} related to the abandoned convoy scenario. + * @param scenario the {@link AtBDynamicScenario} containing details of the abandoned convoy event. + */ + public static void processAbandonedConvoy(Campaign campaign, AtBContract contract, + AtBDynamicScenario scenario) { + final int scenarioId = scenario.getId(); + + for (Force force : campaign.getAllForces()) { + Force parentForce = force.getParentForce(); + + if (parentForce != null && (force.getParentForce().isConvoyForce())) { + continue; + } + + if (force.isConvoyForce() && force.getScenarioId() == scenarioId) { + new DialogAbandonedConvoy(campaign, contract, force); + + for (UUID unitID : force.getAllUnits(false)) { + Unit unit = campaign.getUnit(unitID); + + for (Person crewMember : unit.getCrew()) { + decideCrewMemberFate(campaign, crewMember); + } + + campaign.removeUnit(unitID); + } + } + } + } + + /** + * Determines the fate of a crew member based on random chance, assigning their status + * to either killed in action (KIA) or prisoner of war (POW). + * + *

    The fate is decided randomly via a 2d6 roll: + *

      + *
    • If the roll is greater than 7, the crew member becomes a POW.
    • + *
    • Otherwise, the crew member is marked as KIA.
    • + *
    + * + * The survival chances are based on the infantry survival rules in Campaign Operations. + * + * @param campaign the {@link Campaign} instance for date tracking and updating crew member status. + * @param person the {@link Person} representing the crew member whose fate is being decided. + */ + private static void decideCrewMemberFate(Campaign campaign, Person person) { + PersonnelStatus status = KIA; + + if (Compute.d6(2) > 7) { + status = PersonnelStatus.POW; + } + + person.changeStatus(campaign, campaign.getLocalDate(), status); + } + + /** + * Estimates the total cargo requirements for a resupply operation based on the campaign + * and the associated contract details. These cargo requirements are specifically modified for + * player-owned convoys. + * + *

    This estimation is calculated as follows: + *

      + *
    • Determines the target cargo tonnage using the {@link Campaign} and {@link AtBContract} data.
    • + *
    • Applies a cargo multiplier defined in {@link Resupply#CARGO_MULTIPLIER}.
    • + *
    + * + * @param campaign the {@link Campaign} instance to calculate cargo requirements for. + * @param contract the {@link AtBContract} defining the parameters of the mission. + * @return the estimated cargo requirement in tons. + */ + public static int estimateCargoRequirements(Campaign campaign, AtBContract contract) { + double targetTonnage = calculateTargetCargoTonnage(campaign, contract) * CARGO_MULTIPLIER; + + // Armor and ammo are always delivered in blocks, so cargo will never be less than the sum + // of those blocks + return max(RESUPPLY_AMMO_TONNAGE + RESUPPLY_ARMOR_TONNAGE, (int) Math.ceil(targetTonnage)); + } + + /** + * Determines if a convoy only contains VTOL or similar units. + * + * @param campaign the {@link Campaign} instance the convoy belongs to. + * @param convoy the {@link Force} representing the convoy to check. + * @return {@code true} if the convoy only contains VTOL units, {@code false} otherwise. + */ + public static boolean forceContainsOnlyVTOLForces(Campaign campaign, Force convoy) { + for (UUID unitId : convoy.getAllUnits(false)) { + Entity entity = getEntityFromUnitId(campaign, unitId); + + if (entity == null) { + continue; + } + + if (!entity.isAirborneVTOLorWIGE()) { + return false; + } + } + + return true; + } + + /** + * Determines if a convoy contains a majority of VTOL (or similar) units. + * + *

    This is calculated by checking if at least half of the units are VTOLs or similarly + * airborne types.

    + * + * @param campaign the {@link Campaign} instance the convoy belongs to. + * @param convoy the {@link Force} representing the convoy being evaluated. + * @return {@code true} if the VTOL units constitute at least half of the units, {@code false} otherwise. + */ + public static boolean forceContainsMajorityVTOLForces(Campaign campaign, Force convoy) { + Vector allUnits = convoy.getAllUnits(false); + int convoySize = allUnits.size(); + int vtolCount = 0; + + for (UUID unitId : convoy.getAllUnits(false)) { + Entity entity = getEntityFromUnitId(campaign, unitId); + + if (entity == null) { + continue; + } + + if (entity.isAirborneVTOLorWIGE()) { + vtolCount++; + } + } + + return vtolCount >= floor((double) convoySize / 2); + } + + /** + * Determines if a convoy only contains aerial units, such as aerospace or conventional fighters. + * + * @param campaign the {@link Campaign} instance the convoy belongs to. + * @param convoy the {@link Force} representing the convoy to check. + * @return {@code true} if the convoy only contains aerial units, {@code false} otherwise. + */ + public static boolean forceContainsOnlyAerialForces(Campaign campaign, Force convoy) { + for (UUID unitId : convoy.getAllUnits(false)) { + Entity entity = getEntityFromUnitId(campaign, unitId); + + if (entity == null) { + continue; + } + + if (!entity.isAerospace() && !entity.isConventionalFighter()) { + return false; + } + } + + return true; + } +} diff --git a/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/StarLeagueCache.java b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/StarLeagueCache.java new file mode 100644 index 00000000000..370298bd0be --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/StarLeagueCache.java @@ -0,0 +1,640 @@ +/* + * Copyright (c) 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.mission.resupplyAndCaches; + +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.Compute; +import megamek.common.Entity; +import megamek.common.MekFileParser; +import megamek.common.MekSummary; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; +import mekhq.campaign.finances.Money; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.parts.MekLocation; +import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.TankLocation; +import mekhq.campaign.parts.enums.PartQuality; +import mekhq.campaign.stratcon.StratconCoords; +import mekhq.campaign.stratcon.StratconScenario; +import mekhq.campaign.stratcon.StratconTrackState; +import mekhq.campaign.unit.Unit; +import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; +import mekhq.campaign.universe.PlanetarySystem; +import org.apache.commons.math3.util.Pair; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.time.LocalDate; +import java.util.List; +import java.util.*; + +import static megamek.common.EntityWeightClass.WEIGHT_ASSAULT; +import static megamek.common.EntityWeightClass.WEIGHT_HEAVY; +import static megamek.common.EntityWeightClass.WEIGHT_LIGHT; +import static megamek.common.EntityWeightClass.WEIGHT_MEDIUM; +import static megamek.common.Mek.LOC_CT; +import static megamek.common.UnitType.AEROSPACEFIGHTER; +import static megamek.common.UnitType.INFANTRY; +import static megamek.common.UnitType.MEK; +import static megamek.common.UnitType.TANK; +import static mekhq.campaign.finances.enums.TransactionType.MISCELLANEOUS; +import static mekhq.campaign.mission.BotForceRandomizer.UNIT_WEIGHT_UNSPECIFIED; +import static mekhq.campaign.unit.Unit.getRandomUnitQuality; +import static mekhq.campaign.universe.Factions.getFactionLogo; + +public class StarLeagueCache { + private final Campaign campaign; + private final AtBContract contract; + private final int cacheType; + private final Random random = new Random(); + private Faction originFaction; + private boolean didGenerationFail = false; + private final int ruinedChance; + private Map partsPool; + private List intactUnits; + + // We use year -1 as otherwise MHQ considers the SL to no longer exist. + private final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + private final LocalDate FALL_OF_STAR_LEAGUE = LocalDate.of( + Factions.getInstance().getFaction("SL").getEndYear() - 1, 1, 1); + + public enum CacheType { + TRASH_CACHE, // The cache contains only trash or roleplay items. + CLUE_CACHE, // The cache contains a clue that will lead the player to another cache. + DATA_CACHE, // The cache contains a memory core + LEGACY_CACHE, // The cache contains a message from the past, improving Loyalty across the campaign + TRAP_CACHE, // The cache is a trap + COMBAT_CACHE // The cache contains units and parts + } + + private final static MMLogger logger = MMLogger.create(StarLeagueCache.class); + + public static int getCacheType() { + return Compute.randomInt(CacheType.values().length); + } + + public Faction getFaction() { + return originFaction; + } + + public StarLeagueCache(Campaign campaign, AtBContract contract, int cacheType) { + this.campaign = campaign; + this.contract = contract; + this.cacheType = cacheType; + + ruinedChance = campaign.getGameYear() - FALL_OF_STAR_LEAGUE.getYear(); + + determineOriginFaction(); + } + + private void generateCombatCacheContents() { + if (!didGenerationFail) { + intactUnits = getCacheContents(); + processUnits(); + } + + if (partsPool.isEmpty() && intactUnits.isEmpty()) { + didGenerationFail = true; + } + } + + public boolean didGenerationFail() { + return didGenerationFail; + } + + private void processUnits() { + int intactUnitCount = 0; + + for (int lance = 0; lance < contract.getRequiredCombatTeams(); lance++) { + // This will generate a number between 1 and 4 with an average roll of 3 + intactUnitCount += Compute.randomInt(3) + Compute.randomInt(3); + } + + intactUnitCount = Math.min(intactUnitCount, intactUnits.size()); + + for (int individualUnit = 0; individualUnit < ruinedChance; individualUnit++) { + if (Compute.randomInt(500) < ruinedChance) { + intactUnitCount--; + } + } + + List actuallyIntactUnits = new ArrayList<>(); + for (int i = 0; i < intactUnitCount; i++) { + int randomIndex = random.nextInt(intactUnits.size()); + actuallyIntactUnits.add(intactUnits.get(randomIndex)); + intactUnits.remove(randomIndex); + } + + // This uses the end state of intact units as the list of units too ruined for salvage + collectParts(); + + // We then replace 'intactUnits' with the actually intact units. + intactUnits = actuallyIntactUnits; + } + + private void collectParts() { + partsPool = new HashMap<>(); + + try { + for (Unit unit : intactUnits) { + List parts = unit.getParts(); + for (Part part : parts) { + if (part instanceof MekLocation) { + if (((MekLocation) part).getLoc() == LOC_CT) { + continue; + } + } + + if (part instanceof TankLocation) { + continue; + } + + // Is the part too damaged to be salvaged? + if (Compute.randomInt(500) < ruinedChance) { + continue; + } + + Pair pair = new Pair<>(unit, part); +// int weight = getDropWeight(pair.getValue()); +// partsPool.merge(part, weight, Integer::sum); + } + } + } catch (Exception exception) { + logger.error("Aborted parts collection.", exception); + } + } + + public void determineOriginFaction() { + final int sphereOfInfluence = 650; + final PlanetarySystem contractSystem = contract.getSystem(); + final double distanceToTerra = contractSystem.getDistanceTo(campaign.getSystemById("Terra")); + + // This is a fallback to better ensure something drops, even if it isn't a SLDF Depot + // This value was reached by 'eye-balling' the map of the Inner Sphere + if (distanceToTerra > sphereOfInfluence) { + List factions = contractSystem.getFactions(FALL_OF_STAR_LEAGUE); + + if (factions.isEmpty()) { + didGenerationFail = true; + } else { + Collections.shuffle(factions); + originFaction = Factions.getInstance().getFaction(factions.get(0)); + } + } else { + originFaction = Factions.getInstance().getFaction("SL"); + } + } + + private List getCacheContents() { + Map> unitsPresent = buildUnitWeightMap(); + List unitSummaries = getUnitSummaries(unitsPresent); + + List units = new ArrayList<>(); + for (MekSummary summary : unitSummaries) { + Entity entity = getEntity(summary); + + if (entity == null) { + continue; + } + + Unit unit = getUnit(entity); + + if (unit != null) { + units.add(unit); + } + } + + return units; + } + + @Nullable + private Entity getEntity(MekSummary unitData) { + try { + return new MekFileParser(unitData.getSourceFile(), unitData.getEntryName()).getEntity(); + } catch (Exception ex) { + logger.error("Unable to load entity: {}: {}", + unitData.getSourceFile(), + unitData.getEntryName(), ex); + return null; + } + } + + public Unit getUnit(Entity entity) { + PartQuality quality = getRandomUnitQuality(0); + Unit unit = new Unit(entity, campaign); + unit.initializeParts(true); + unit.setQuality(quality); + + return unit; + } + + private List getUnitSummaries(Map> unitsPresent) { + final List potentialUnitTypes = List.of(INFANTRY, TANK, MEK, AEROSPACEFIGHTER); + + List unitSummaries = new ArrayList<>(); + for (int unitType : potentialUnitTypes) { + for (int unitWeight : unitsPresent.get(unitType)) { + unitSummaries.add(campaign.getUnitGenerator().generate(originFaction.getShortName(), unitType, unitWeight, + FALL_OF_STAR_LEAGUE.getYear(), getRandomUnitQuality(0).toNumeric())); + } + } + + return unitSummaries; + } + + private Map> buildUnitWeightMap() { + final int COMPANY_COUNT = 3; + Map> unitsPresent = new HashMap<>(); + + for (int company = 0; company < COMPANY_COUNT; company++) { + int unitType = getCompanyUnitType(); + + if (unitType == INFANTRY) { + unitsPresent.put(INFANTRY, List.of(UNIT_WEIGHT_UNSPECIFIED, UNIT_WEIGHT_UNSPECIFIED, + UNIT_WEIGHT_UNSPECIFIED)); + } else { + for (int lance : getCompanyLances()) { + if (unitsPresent.containsKey(unitType)) { + unitsPresent.get(unitType).addAll(getUnitWeights(lance)); + } else { + unitsPresent.put(unitType, getUnitWeights(lance)); + } + } + } + } + return unitsPresent; + } + + private int getCompanyUnitType() { + int roll = Compute.d6(); + // This table is based on the one found on p265 of Total Warfare. + // We increased the chance of rolling 'Meks, because players will expect to get 'Meks out + // of these caches + return switch (roll) { + case 1 -> INFANTRY; + case 2, 3 -> TANK; + case 4, 5 -> MEK; + case 6 -> AEROSPACEFIGHTER; + default -> throw new IllegalStateException("Unexpected value in getCompanyUnitType: " + + roll); + }; + + } + + private List getCompanyLances() { + List companyLances = new ArrayList<>(); + + int roll = Compute.d6(1); + // This table is based on the one found on p265 of Total Warfare + switch (roll) { + case 1 -> { + companyLances.add(WEIGHT_LIGHT); + companyLances.add(WEIGHT_MEDIUM); + companyLances.add(WEIGHT_MEDIUM); + } + case 2 -> { + companyLances.add(WEIGHT_LIGHT); + companyLances.add(WEIGHT_MEDIUM); + companyLances.add(WEIGHT_HEAVY); + } + case 3 -> { + companyLances.add(WEIGHT_MEDIUM); + companyLances.add(WEIGHT_MEDIUM); + companyLances.add(WEIGHT_HEAVY); + } + case 4 -> { + companyLances.add(WEIGHT_LIGHT); + companyLances.add(WEIGHT_HEAVY); + companyLances.add(WEIGHT_HEAVY); + } + case 5 -> { + companyLances.add(WEIGHT_HEAVY); + companyLances.add(WEIGHT_HEAVY); + companyLances.add(WEIGHT_HEAVY); + } + case 6 -> { + companyLances.add(WEIGHT_HEAVY); + companyLances.add(WEIGHT_HEAVY); + companyLances.add(WEIGHT_ASSAULT); + } + default -> throw new IllegalStateException("Unexpected value in getCompanyLances(): " + + roll); + } + + return companyLances; + } + + private List getUnitWeights(int weight) { + List unitWeights = new ArrayList<>(); + final int[] rollOutcome; + + int roll = Compute.d6(2); + // This table is based on the one found on p265 of Total Warfare + switch (roll) { + case 1 -> rollOutcome = switch (weight) { + case WEIGHT_LIGHT -> new int[]{WEIGHT_LIGHT, WEIGHT_LIGHT, WEIGHT_LIGHT, WEIGHT_LIGHT}; + case WEIGHT_MEDIUM -> new int[]{WEIGHT_LIGHT, WEIGHT_MEDIUM, WEIGHT_MEDIUM, WEIGHT_HEAVY}; + case WEIGHT_HEAVY -> new int[]{WEIGHT_MEDIUM, WEIGHT_HEAVY, WEIGHT_HEAVY, WEIGHT_HEAVY}; + case WEIGHT_ASSAULT -> new int[]{WEIGHT_MEDIUM, WEIGHT_HEAVY, WEIGHT_ASSAULT, WEIGHT_ASSAULT}; + default -> throw new IllegalStateException("Unexpected weight: " + weight); + }; + case 2, 3 -> rollOutcome = switch (weight) { + case WEIGHT_LIGHT -> new int[]{WEIGHT_LIGHT, WEIGHT_LIGHT, WEIGHT_LIGHT, WEIGHT_MEDIUM}; + case WEIGHT_MEDIUM, WEIGHT_HEAVY -> new int[]{weight, weight, weight, weight}; + case WEIGHT_ASSAULT -> new int[]{WEIGHT_HEAVY, WEIGHT_HEAVY, WEIGHT_ASSAULT, WEIGHT_ASSAULT}; + default -> throw new IllegalStateException("Unexpected weight: " + weight); + }; + case 4, 5 -> rollOutcome = switch (weight) { + case WEIGHT_LIGHT -> new int[]{WEIGHT_LIGHT, WEIGHT_LIGHT, WEIGHT_MEDIUM, WEIGHT_MEDIUM}; + case WEIGHT_MEDIUM -> new int[]{WEIGHT_MEDIUM, WEIGHT_MEDIUM, WEIGHT_MEDIUM, WEIGHT_HEAVY}; + case WEIGHT_HEAVY -> new int[]{WEIGHT_MEDIUM, WEIGHT_HEAVY, WEIGHT_HEAVY, WEIGHT_ASSAULT}; + case WEIGHT_ASSAULT -> new int[]{WEIGHT_HEAVY, WEIGHT_ASSAULT, WEIGHT_ASSAULT, WEIGHT_ASSAULT}; + default -> throw new IllegalStateException("Unexpected weight: " + weight); + }; + case 6 -> rollOutcome = switch (weight) { + case WEIGHT_LIGHT -> new int[]{WEIGHT_LIGHT, WEIGHT_LIGHT, WEIGHT_MEDIUM, WEIGHT_HEAVY}; + case WEIGHT_MEDIUM -> new int[]{WEIGHT_MEDIUM, WEIGHT_MEDIUM, WEIGHT_HEAVY, WEIGHT_HEAVY}; + case WEIGHT_HEAVY -> new int[]{WEIGHT_HEAVY, WEIGHT_HEAVY, WEIGHT_HEAVY, WEIGHT_ASSAULT}; + case WEIGHT_ASSAULT -> new int[]{WEIGHT_ASSAULT, WEIGHT_ASSAULT, WEIGHT_ASSAULT, WEIGHT_ASSAULT}; + default -> throw new IllegalStateException("Unexpected weight: " + weight); + }; + default -> throw new IllegalStateException("Unexpected value in getlanceWeights(): " + roll); + } + + for (int outcome : rollOutcome) { + unitWeights.add(outcome); + } + + return unitWeights; + } + + public void createDudDialog(StratconTrackState track, StratconScenario scenario) { + StratconCoords stratconCoords = scenario.getCoords(); + + // Dialog dimensions and representative + final int DIALOG_WIDTH = 400; + final int DIALOG_HEIGHT = 200; + + // Creates and sets up the dialog + JDialog dialog = new JDialog(); + dialog.setTitle(resources.getString("dialog.title")); + dialog.setLayout(new BorderLayout()); + dialog.setSize(UIUtil.scaleForGUI(DIALOG_WIDTH, DIALOG_HEIGHT)); + dialog.setLocationRelativeTo(null); + + // Defines the action when the dialog is being dismissed + ActionListener dialogDismissActionListener = e -> dialog.dispose(); + + // Associates the dismiss action to the dialog window close event + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent windowEvent) { + dialogDismissActionListener.actionPerformed(null); + } + }); + + // Prepares and adds the icon of the representative as a label + JLabel iconLabel = new JLabel(); + iconLabel.setHorizontalAlignment(JLabel.CENTER); + + ImageIcon speakerIcon = getSpeakerIcon(false); +// speakerIcon = scaleImageIconToWidth(speakerIcon, UIUtil.scaleForGUI(100)); + iconLabel.setIcon(speakerIcon); + dialog.add(iconLabel, BorderLayout.NORTH); + + // Prepares and adds the description +// JLabel description = new JLabel( +// String.format("
    %s
    ", +// UIUtil.scaleForGUI(DIALOG_WIDTH), getDudDialogText(track, stratconCoords))); +// description.setHorizontalAlignment(JLabel.CENTER); + + JPanel descriptionPanel = new JPanel(); + descriptionPanel.setBorder(BorderFactory.createTitledBorder( + String.format(resources.getString("dialogBorderTitle.text"), "PLACEHOLDER"))); +// descriptionPanel.add(description); + dialog.add(descriptionPanel, BorderLayout.CENTER); + + // Prepares and adds the confirm button + JButton confirmButton = new JButton(resources.getString("confirmDud.text")); + confirmButton.addActionListener(dialogDismissActionListener); + dialog.add(confirmButton, BorderLayout.SOUTH); + + // Pack, position and display the dialog + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + public void createProposalDialog() { + Money proposal = calculateProposal(); + + // Dialog dimensions and representative + final int DIALOG_WIDTH = 400; + final int DIALOG_HEIGHT = 200; + + // Creates and sets up the dialog + JDialog dialog = new JDialog(); + dialog.setTitle(resources.getString("dialog.title")); + dialog.setLayout(new BorderLayout()); + dialog.setSize(UIUtil.scaleForGUI(DIALOG_WIDTH, DIALOG_HEIGHT)); + dialog.setLocationRelativeTo(null); + + // Defines the action when the dialog is being dismissed + ActionListener dialogDismissActionListener = e -> { + dialog.dispose(); + campaign.getFinances().credit(MISCELLANEOUS, campaign.getLocalDate(), proposal, + resources.getString("transaction.text")); + }; + + // Associates the dismiss action to the dialog window close event + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent windowEvent) { + dialogDismissActionListener.actionPerformed(null); + } + }); + + // Prepares and adds the icon of the representative as a label + JLabel iconLabel = new JLabel(); + iconLabel.setHorizontalAlignment(JLabel.CENTER); + + ImageIcon speakerIcon = getSpeakerIcon(true); +// speakerIcon = scaleImageIconToWidth(speakerIcon, UIUtil.scaleForGUI(100)); + iconLabel.setIcon(speakerIcon); + dialog.add(iconLabel, BorderLayout.NORTH); + + // Prepares and adds the description +// JLabel description = new JLabel( +// String.format("
    %s
    ", +// UIUtil.scaleForGUI(DIALOG_WIDTH), getProposalText(proposal))); +// description.setHorizontalAlignment(JLabel.CENTER); + + JPanel descriptionPanel = new JPanel(); + descriptionPanel.setBorder(BorderFactory.createTitledBorder( + String.format(resources.getString("dialogBorderTitle.text"), + resources.getString("senderUnknown.text")))); +// descriptionPanel.add(description); + dialog.add(descriptionPanel, BorderLayout.CENTER); + + // Prepares and adds the accept button + JButton acceptDialog = new JButton(resources.getString("propositionAccept.text")); + acceptDialog.addActionListener(dialogDismissActionListener); + + // Prepares and adds the refuse button + JButton refuseDialog = new JButton(resources.getString("propositionRefuse.text")); + refuseDialog.addActionListener(e -> { + dialog.dispose(); + createProposalRefusalConfirmationDialog(proposal); + }); + + // Creates a panel to house both buttons + JPanel actionsPanel = new JPanel(new FlowLayout()); + actionsPanel.add(acceptDialog); + actionsPanel.add(refuseDialog); + dialog.add(actionsPanel, BorderLayout.SOUTH); + + // Pack, position and display the dialog + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + public void createProposalRefusalConfirmationDialog(Money proposal) { + // Dialog dimensions and representative + final int DIALOG_WIDTH = 300; + final int DIALOG_HEIGHT = 200; + + // Creates and sets up the dialog + JDialog dialog = new JDialog(); + dialog.setTitle(resources.getString("dialog.title")); + dialog.setLayout(new BorderLayout()); + dialog.setSize(UIUtil.scaleForGUI(DIALOG_WIDTH, DIALOG_HEIGHT)); + dialog.setLocationRelativeTo(null); + + // Defines the action when the dialog is being dismissed + ActionListener dialogDismissActionListener = e -> { + dialog.dispose(); + campaign.getFinances().credit(MISCELLANEOUS, campaign.getLocalDate(), proposal, + resources.getString("transaction.text")); + }; + + // Associates the dismiss action to the dialog window close event + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent windowEvent) { + dialogDismissActionListener.actionPerformed(null); + } + }); + + // Prepares and adds the icon of the representative as a label + JLabel iconLabel = new JLabel(); + iconLabel.setHorizontalAlignment(JLabel.CENTER); + + ImageIcon speakerIcon = getSpeakerIcon(true); +// speakerIcon = scaleImageIconToWidth(speakerIcon, UIUtil.scaleForGUI(100)); + iconLabel.setIcon(speakerIcon); + dialog.add(iconLabel, BorderLayout.NORTH); + + // Prepares and adds the description + JLabel description = new JLabel( + String.format("
    %s
    ", + UIUtil.scaleForGUI(DIALOG_WIDTH), resources.getString("warning.text"))); + description.setHorizontalAlignment(JLabel.CENTER); + + JPanel descriptionPanel = new JPanel(); + descriptionPanel.setBorder(BorderFactory.createTitledBorder( + String.format(resources.getString("dialogBorderTitle.text"), + resources.getString("senderUnknown.text")))); + descriptionPanel.add(description); + dialog.add(descriptionPanel, BorderLayout.CENTER); + + // Prepares and adds the accept button + JButton acceptDialog = new JButton(resources.getString("propositionAccept.text")); + acceptDialog.addActionListener(dialogDismissActionListener); + + // Prepares and adds the refuse button + JButton refuseDialog = new JButton(resources.getString("propositionRefuse.text")); + refuseDialog.addActionListener(e -> { + dialog.dispose(); + createProposalRefusalConfirmationDialog(proposal); + }); + + // Creates a panel to house both buttons + JPanel actionsPanel = new JPanel(new FlowLayout()); + actionsPanel.add(acceptDialog); + actionsPanel.add(refuseDialog); + dialog.add(actionsPanel, BorderLayout.SOUTH); + + // Pack, position and display the dialog + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + private Money calculateProposal() { + Money proposal = contract.getTotalAmount(); + double proposalValue = proposal.getAmount().doubleValue(); + double roundedValue = Math.ceil(proposalValue / 1_000_000) * 1_000_000; + return Money.of(roundedValue); + } + +// private String getProposalText(Money proposal) { +// String commanderTitle = getCommanderTitle(campaign, true); +// +// return String.format(resources.getString("proposition" + Compute.randomInt(100) + ".text"), +// commanderTitle) + "

    " + String.format(resources.getString("propositionValue.text"), +// proposal.toAmountAndSymbolString()); +// } + +// private String getDudDialogText(StratconTrackState track, StratconCoords stratconCoords) { +// final String DUD_FORWARD = "dud"; +// final String DUD_AFTERWARD = ".text"; +// +// String commanderTitle = getCommanderTitle(campaign, false); +// String gridReference = track.toString() + '-' + stratconCoords.toBTString(); +// +// int roll = Compute.d6(1); +// if ((roll <= 2) || !(Objects.equals(originFaction.getShortName(), "SL"))) { +// return String.format(resources.getString(DUD_FORWARD + "Generic" + +// Compute.randomInt(100) + DUD_AFTERWARD), commanderTitle, gridReference, +// originFaction.getFullName(campaign.getGameYear())); +// } else { +// return String.format(resources.getString(DUD_FORWARD + "StarLeague" +// + Compute.randomInt(100) + DUD_AFTERWARD), commanderTitle, gridReference); +// } +// } + + @Nullable + private ImageIcon getSpeakerIcon(boolean isAnon) { + if (isAnon) { + return new ImageIcon("data/images/portraits/default.gif"); + } else { + return getFactionLogo(campaign, campaign.getFaction().getShortName(), true); + } + } +} diff --git a/MekHQ/src/mekhq/campaign/parts/AeroSensor.java b/MekHQ/src/mekhq/campaign/parts/AeroSensor.java index 422929ca5ad..d9ad6e50412 100644 --- a/MekHQ/src/mekhq/campaign/parts/AeroSensor.java +++ b/MekHQ/src/mekhq/campaign/parts/AeroSensor.java @@ -47,11 +47,11 @@ public AeroSensor() { this(0, false, null); } - public AeroSensor(int tonnage, boolean lc, Campaign c) { - super(tonnage, c); + public AeroSensor(int tonnage, boolean largeCraft, Campaign campaign) { + super(tonnage, campaign); this.name = "Aerospace Sensors"; - this.largeCraft = lc; - this.unitTonnageMatters = true; + this.largeCraft = largeCraft; + this.unitTonnageMatters = !largeCraft; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingAeroSensor.java b/MekHQ/src/mekhq/campaign/parts/MissingAeroSensor.java index 929698a4bea..9d8e7548a3b 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingAeroSensor.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingAeroSensor.java @@ -45,10 +45,10 @@ public MissingAeroSensor() { this(0, false, null); } - public MissingAeroSensor(int tonnage, boolean drop, Campaign c) { - super(tonnage, c); + public MissingAeroSensor(int tonnage, boolean dropship, Campaign campaign) { + super(tonnage, campaign); this.name = "Aero Sensors"; - this.dropship = drop; + this.dropship = dropship; } @Override @@ -137,4 +137,20 @@ public TechAdvancement getTechAdvancement() { public PartRepairType getMRMSOptionType() { return PartRepairType.ELECTRONICS; } + + /** This function is over-ridden from it's parent because Aero Sensors depend on tonnage for small craft, but not for dropships/warships + * So we have to use regexes to change the acquistion name if the sensors are for spacecraft + * @return description string for the missing part + */ + @Override + public String getAcquisitionName() { + if (dropship) { + //The below regex splits by the () characters but keeps them in the description + String[] sliced = super.getAcquisitionName().split("(?<=\\()|(?=\\))"); + String description = sliced[0] + sliced[2] + sliced[3]; + return description; + } else { + return super.getAcquisitionName(); + } + } } diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index 1ff036c3219..3c1dcbb0fe1 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -92,7 +92,7 @@ public abstract class Part implements IPartWork, ITechnology { protected String name; protected int id; - /** + /** * This is the unitTonnage which needs to be tracked for some parts * even when off the unit. Actual tonnage is returned via the * getTonnage() method @@ -256,7 +256,7 @@ public boolean isPriceAdjustedForAmount() { */ public Money adjustCostsForCampaignOptions(@Nullable Money cost) { return adjustCostsForCampaignOptions(cost, false); - } + } /** * Adjusts the cost of a part based on campaign options @@ -335,8 +335,8 @@ public void setReplacementPart(@Nullable Part part) { public int getUnitTonnage() { return unitTonnage; } - - /** + + /** * @return Is this an item that exists in multiple forms for units of different tonnages? */ public boolean isUnitTonnageMatters() { @@ -554,13 +554,13 @@ public boolean isSameStatus(Part otherPart) { // parts that are reserved for refit or being worked on are never the same // status if (isReservedForRefit() || isBeingWorkedOn() || isReservedForReplacement() || hasParentPart() - || otherPart.isReservedForRefit() || otherPart.isBeingWorkedOn() + || otherPart.isReservedForRefit() || otherPart.isBeingWorkedOn() || otherPart.isReservedForReplacement() || otherPart.hasParentPart()) { return false; } return getQuality() == otherPart.getQuality() && getHits() == otherPart.getHits() - && getSkillMin() == otherPart.getSkillMin() + && getSkillMin() == otherPart.getSkillMin() && getDaysToArrival() == otherPart.getDaysToArrival(); } @@ -568,7 +568,7 @@ protected boolean isClanTechBase() { return getTechBase() == TECH_BASE_CLAN; } - public abstract void writeToXML(final PrintWriter pw, int indent); + public abstract void writeToXML(PrintWriter pw, int indent); protected int writeToXMLBegin(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "part", "id", id, "type", getClass()); @@ -903,7 +903,7 @@ public TargetRoll getAllMods(final @Nullable Person tech) { @Override public TargetRoll getAllModsForMaintenance() { - // according to StratOps you get a -1 mod when checking on individual parts + // according to Campaign Ops [p.197] you get a -1 mod when performing a maintenance check on individual parts // but we will make this user customizable final TargetRoll mods = new TargetRoll(campaign.getCampaignOptions().getMaintenanceBonus(), "maintenance"); mods.addModifier(Availability.getTechModifier(getTechRating()), @@ -1103,7 +1103,7 @@ public String getDetails(boolean includeRepairDetails) { sj.add(getUnitTonnage() + " tons"); } - if (includeRepairDetails && hits > 0) { + if (includeRepairDetails && hits > 0) { sj.add(hits + (hits == 1 ? " hit" : " hits")); if (campaign.getCampaignOptions().isPayForRepairs()) { sj.add(getActualValue().multipliedBy(0.2).toAmountAndSymbolString() + " to repair"); diff --git a/MekHQ/src/mekhq/campaign/parts/PartInUse.java b/MekHQ/src/mekhq/campaign/parts/PartInUse.java index 50012f64e54..69519f57895 100644 --- a/MekHQ/src/mekhq/campaign/parts/PartInUse.java +++ b/MekHQ/src/mekhq/campaign/parts/PartInUse.java @@ -37,6 +37,8 @@ public class PartInUse { private int plannedCount; private Money cost = Money.zero(); private List spares = new ArrayList<>(); + private double requestedStock; + private boolean isBundle; private void appendDetails(StringBuilder sb, Part part) { String details = part.getDetails(false); @@ -58,6 +60,7 @@ public PartInUse(Part part) { this.description = sb.toString(); this.partToBuy = part.getAcquisitionWork(); this.tonnagePerItem = part.getTonnage(); + this.isBundle = false; // AmmoBin are special: They aren't buyable (yet?), but instead buy you the ammo inside // We redo the description based on that if (partToBuy instanceof AmmoStorage) { @@ -75,10 +78,21 @@ public PartInUse(Part part) { if (part instanceof Armor) { // Armor needs different tonnage values this.tonnagePerItem = 1.0 / ((Armor) part).getArmorPointsPerTon(); + this.isBundle = true; } if (null != partToBuy) { this.cost = partToBuy.getBuyCost(); + String descString = partToBuy.getAcquisitionName(); + if( !(descString.contains("(") && descString.contains(")"))) { + descString = descString.split(",")[0]; + descString = descString.split("<")[0]; + } + if(descString.equals("Turret")) { + descString += " " + part.getTonnage() + " tons"; + } + this.description = descString; } + this.requestedStock = 0; } public String getDescription() { @@ -169,6 +183,26 @@ public Money getCost() { return cost; } + public double getRequestedStock() { + return requestedStock; + } + + public void setRequestedStock(double requestedStock) { + this.requestedStock = requestedStock; + } + + public boolean getIsBundle() { + return isBundle; + } + + public void setIsBundle(boolean isBundle) { + this.isBundle = isBundle; + } + + public double getTonnagePerItem() { + return tonnagePerItem; + } + @Override public int hashCode() { return Objects.hashCode(description); diff --git a/MekHQ/src/mekhq/campaign/parts/Refit.java b/MekHQ/src/mekhq/campaign/parts/Refit.java index 12d90beb22a..eddb612788f 100644 --- a/MekHQ/src/mekhq/campaign/parts/Refit.java +++ b/MekHQ/src/mekhq/campaign/parts/Refit.java @@ -28,6 +28,7 @@ import java.util.*; import java.util.stream.Collectors; +import mekhq.campaign.enums.CampaignTransportType; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -238,7 +239,7 @@ public Money getCost() { * only be {@link mekhq.campaign.Campaign Campaign} when merging parts. * * This is only used by RefitTest.java - * + * * @return A mutable {@link List} of old parts in the refit. */ @Deprecated @@ -251,7 +252,7 @@ public List getOldUnitParts() { * only be {@link mekhq.campaign.Campaign Campaign} when merging parts. * * This is only used by RefitTest.java - * + * * @return A mutable {@link List} of new part IDs in the refit. */ @Deprecated @@ -327,13 +328,13 @@ public void calculate() { return; } time = 0; - sameArmorType = newEntity.getArmorType(newEntity.firstArmorIndex()) + sameArmorType = newEntity.getArmorType(newEntity.firstArmorIndex()) == oldUnit.getEntity().getArmorType(oldUnit.getEntity().firstArmorIndex()); // SVs with standard SV armor need to check for change in BAR/tech rating if (newEntity.isSupportVehicle() && (newEntity.getArmorType(newEntity.firstArmorIndex()) == EquipmentType.T_ARMOR_STANDARD)) { - - sameArmorType = (newEntity.getBARRating(newEntity.firstArmorIndex()) + + sameArmorType = (newEntity.getBARRating(newEntity.firstArmorIndex()) == oldUnit.getEntity().getArmorType(oldUnit.getEntity().firstArmorIndex())) && (newEntity.getArmorTechRating() == oldUnit.getEntity().getArmorTechRating()); } @@ -398,14 +399,14 @@ public void calculate() { boolean acceptableReplacement = (oldPart instanceof MissingPart) && ((MissingPart) oldPart).isAcceptableReplacement(newPart, true); - // We're not going to require replacing the life support system just because + // We're not going to require replacing the life support system just because // the number of bay personnel changes. - boolean aeroLifeSupportIssue = (oldPart instanceof AeroLifeSupport) + boolean aeroLifeSupportIssue = (oldPart instanceof AeroLifeSupport) && (newPart instanceof AeroLifeSupport) && !crewSizeChanged(); if (acceptableReplacement || oldPart.isSamePartType(newPart) || aeroLifeSupportIssue) { // need a special check for location and armor amount for armor - if ((oldPart instanceof Armor) && (newPart instanceof Armor) + if ((oldPart instanceof Armor) && (newPart instanceof Armor) && (oldPart.getLocation() != newPart.getLocation() || ((Armor) oldPart).isRearMounted() != ((Armor) newPart).isRearMounted() || ((Armor) oldPart).getTotalAmount() != ((Armor) newPart).getTotalAmount())) { @@ -457,9 +458,9 @@ public void calculate() { boolean acceptableReplacement = (oldPart instanceof MissingPart) && ((MissingPart) oldPart).isAcceptableReplacement(newPart, true); - // We're not going to require replacing the life support system just because + // We're not going to require replacing the life support system just because // the number of bay personnel changes. - boolean aeroLifeSupportIssue = (oldPart instanceof AeroLifeSupport) + boolean aeroLifeSupportIssue = (oldPart instanceof AeroLifeSupport) && (newPart instanceof AeroLifeSupport) && !crewSizeChanged(); if (acceptableReplacement || oldPart.isSamePartType(newPart) || aeroLifeSupportIssue) { @@ -481,7 +482,7 @@ public void calculate() { // save this in case we fail to find equipment in the same location. int loc = newPart.getLocation(); boolean rear = ((EquipmentPart) newPart).isRearFacing(); - boolean oldIsDifferent = (oldPart instanceof EquipmentPart) + boolean oldIsDifferent = (oldPart instanceof EquipmentPart) && ((oldPart.getLocation() != loc) || (((EquipmentPart) oldPart).isRearFacing() != rear)); boolean oldIsDifferentMissing = (oldPart instanceof MissingEquipmentPart) && ((oldPart.getLocation() != loc) || (((MissingEquipmentPart) oldPart).isRearFacing() != rear)); @@ -512,7 +513,7 @@ public void calculate() { // Use equivalent MissingEquipmentPart install time time += movedPart.getMissingPart().getBaseTime(); } - + } else { // it's a new part // dont actually add the part itself but rather its missing equivalent @@ -632,7 +633,7 @@ public void calculate() { if (newPart instanceof MissingEnginePart) { Engine oldEngine = oldUnit.getEntity().getEngine(); Engine newEngine = newUnit.getEntity().getEngine(); - if (oldEngine.getRating() != newEngine.getRating() + if (oldEngine.getRating() != newEngine.getRating() || oldEngine.getEngineType() != newEngine.getEngineType()) { updateRefitClass(customJob ? CLASS_E : CLASS_D); } @@ -1084,11 +1085,11 @@ private void calculateRefurbishment() { time = WORKMONTH * 3; } else if (newEntity instanceof Dropship || newEntity instanceof Jumpship) { time = WORKMONTH; - } else if (newEntity instanceof Mek || newEntity instanceof Aero) { + } else if (newEntity instanceof Mek || newEntity instanceof Aero) { // ConvFighter and SmallCraft are derived from Aero - time = WORKWEEK * 2; + time = WORKWEEK * 2; } else if (newEntity instanceof BattleArmor || newEntity instanceof Tank || newEntity instanceof ProtoMek) { - time = WORKWEEK; + time = WORKWEEK; } else { time = WORKWEEK * 2; // Default to same as Mek logger.error("Unit " + newEntity.getModel() + " did not set its time correctly."); @@ -1111,7 +1112,12 @@ public void begin() throws EntityLoadingException, IOException { oldUnit.setRefit(this); // Bay space might change, and either way all cargo needs to be unloaded while // the refit is in progress - oldUnit.unloadTransportShip(); + for (CampaignTransportType campaignTransportType : CampaignTransportType.values()) { + if (oldUnit.hasTransportedUnits(campaignTransportType)) { + oldUnit.unloadTransport(campaignTransportType); + } + } + newEntity.setOwner(oldUnit.getEntity().getOwner()); // We don't want to require waiting for a refit kit if all that is missing is @@ -1393,10 +1399,10 @@ private void updateRefitClass(int newClass) { } } - + /** - * Aborts this refit and releases the various marked parts to be used for other purposes. - * + * Aborts this refit and releases the various marked parts to be used for other purposes. + * * TODO: WeaverThree - Is it safe to call this on an un-started refit? Looks like no. */ public void cancel() { @@ -1533,10 +1539,10 @@ private void complete() { } // dont forget to switch entities! - // ----------------- from here on oldUnit refers to the new entity ------------------------- + // ----------------- from here on oldUnit refers to the new entity ------------------------- oldUnit.setEntity(newEntity); // Bay capacities might have changed - reset them - oldUnit.initializeBaySpace(); + oldUnit.initializeAllTransportSpace(); // set up new parts ArrayList newParts = new ArrayList<>(); @@ -1554,8 +1560,8 @@ private void complete() { // Only Aerospace Fighters are expected to have heat sink parts (Meks handled // separately) SmallCraft, DropShip, JumpShip, WarShip, and SpaceStation use // SpacecraftCoolingSystem instead - expectedHeatSinkParts = ((Aero) newEntity).getHeatSinks() - - ((Aero) newEntity).getPodHeatSinks() + expectedHeatSinkParts = ((Aero) newEntity).getHeatSinks() + - ((Aero) newEntity).getPodHeatSinks() - untrackedHeatSinkCount(newEntity); } for (Part part : newUnitParts) { @@ -1714,8 +1720,8 @@ private void changeAmmoBinMunitions(final Unit unit) { } /** - * Writes the configuration for the new side of the refit to a - * .mtf or .blk file in the customs directory. + * Writes the configuration for the new side of the refit to a + * .mtf or .blk file in the customs directory. * @throws EntityLoadingException */ public void saveCustomization() throws EntityLoadingException { @@ -1746,13 +1752,13 @@ public void saveCustomization() throws EntityLoadingException { String fileExtension = newEntity instanceof Mek ? ".mtf" : ".blk"; String fileOutName = sCustomsDir + File.separator + fileName + fileExtension; fileNameCampaign = sCustomsDirCampaign + File.separator + fileName + fileExtension; - + // if this file already exists then don't overwrite it or we might break another unit if ((new File(fileOutName)).exists() || (new File(fileNameCampaign)).exists()) { throw new IOException("A file already exists with the custom name " + fileNameCampaign + ". Please choose a different name. (Unit name and/or model)"); } - + if (newEntity instanceof Mek) { try (FileOutputStream out = new FileOutputStream(fileNameCampaign); PrintStream p = new PrintStream(out)) { @@ -1836,7 +1842,7 @@ public Unit getOriginalUnit() { } /** - * @return Have we failed a refit check? If so, the quality of the parts will decrease. + * @return Have we failed a refit check? If so, the quality of the parts will decrease. * unless it's a refurbishment, at least. */ public boolean hasFailedCheck() { @@ -1868,7 +1874,7 @@ public int getDifficulty() { // Refit kit bonus added below in getAllMods } - + /** * @param tech - a Person whose attribute may modify this roll * @return a TargetRoll describing all of our difficulty modifiers @@ -1927,7 +1933,7 @@ public String fail(int rating) { } else { return ReportingUtilities.messageSurroundedBySpanWithColor( MekHQ.getMHQOptions().getFontColorNegativeHexColor(), - "The customization of " + oldUnit.getEntity().getShortName() + "The customization of " + oldUnit.getEntity().getShortName() + " will take " + getTimeLeft() + " additional minutes to complete."); } } @@ -1981,7 +1987,7 @@ public int getSkillMin() { /** * A refit has the same base and actual time - * @return minutes to complete refit + * @return minutes to complete refit */ @Override public int getBaseTime() { @@ -2935,7 +2941,7 @@ public Part clone() { } /** - * Gets a value indicating whether or not this refit is a custom job. + * Gets a value indicating whether or not this refit is a custom job. * If false, this is a Refit Kit (CamOps 212).\ * @return is custom job? */ @@ -2944,7 +2950,7 @@ public boolean isCustomJob() { } /** - * @return Has a refit kit for this refit been found (if applicable). + * @return Has a refit kit for this refit been found (if applicable). */ public boolean kitFound() { return kitFound; diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/HeatSink.java b/MekHQ/src/mekhq/campaign/parts/equipment/HeatSink.java index 83143502e19..d17253fec1b 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/HeatSink.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/HeatSink.java @@ -25,6 +25,8 @@ import mekhq.campaign.finances.Money; import mekhq.campaign.parts.enums.PartRepairType; +import java.util.StringJoiner; + /** * @author Jay Lawson (jaylawson39 at yahoo.com) */ @@ -114,4 +116,29 @@ public PartRepairType getRepairPartType() { public boolean isOmniPoddable() { return true; } + + /** + * Gets a string containing details regarding the part, + * and optionally include information on its repair + * status. + * + * @param includeRepairDetails {@code true} if the details + * should include information such as the number of + * hits or how much it would cost to repair the + * part. + * @return A string containing details regarding the part. + */ + @Override + public String getDetails(boolean includeRepairDetails) { + StringJoiner sj = new StringJoiner(", "); + if (getName() != null && getName().equals("Double Heat Sink")) { + sj.add(getTechBaseName()); + } + + if( !super.getDetails(includeRepairDetails).isEmpty()) { + sj.add(super.getDetails(includeRepairDetails)); + } + + return sj.toString(); + } } diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java index c9285cfaa46..168def2bc5e 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Person.java +++ b/MekHQ/src/mekhq/campaign/personnel/Person.java @@ -47,7 +47,6 @@ import mekhq.campaign.log.ServiceLogger; import mekhq.campaign.mod.am.InjuryUtil; import mekhq.campaign.parts.Part; -import mekhq.campaign.personnel.education.Academy; import mekhq.campaign.personnel.enums.*; import mekhq.campaign.personnel.enums.education.EducationLevel; import mekhq.campaign.personnel.enums.education.EducationStage; @@ -168,8 +167,9 @@ public class Person { private UUID doctorId; private List techUnits; + private int vocationalXPTimer; + // days of rest - private int idleMonths; private int daysToWaitForHealing; // Our rank @@ -216,7 +216,7 @@ public class Person { private int eduEducationTime; private int eduDaysOfTravel; private List eduTagAlongs; - private List eduFailedApplications; + private List eduFailedApplications; // endregion Education // region Personality @@ -895,7 +895,7 @@ public String getSecondaryRoleDesc() { return getSecondaryRole().getName(isClanPersonnel()); } - public boolean canPerformRole(LocalDate today, Person person, final PersonnelRole role, final boolean primary) { + public boolean canPerformRole(LocalDate today, 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 @@ -931,7 +931,7 @@ public boolean canPerformRole(LocalDate today, Person person, final PersonnelRol } } - if (person.isChild(today)) { + if (isChild(today)) { return false; } @@ -944,8 +944,7 @@ public boolean canPerformRole(LocalDate today, Person person, final PersonnelRol case NAVAL_VEHICLE_DRIVER -> hasSkill(SkillType.S_PILOT_NVEE); case VTOL_PILOT -> hasSkill(SkillType.S_PILOT_VTOL); case VEHICLE_GUNNER -> hasSkill(SkillType.S_GUN_VEE); - case MECHANIC -> hasSkill(SkillType.S_TECH_MECHANIC) - && (getSkill(SkillType.S_TECH_MECHANIC).getExperienceLevel() > SkillType.EXP_ULTRA_GREEN); + case MECHANIC -> hasSkill(SkillType.S_TECH_MECHANIC); case VEHICLE_CREW -> Stream.of(SkillType.S_TECH_MEK, SkillType.S_TECH_AERO, SkillType.S_TECH_MECHANIC, SkillType.S_TECH_BA, SkillType.S_DOCTOR, SkillType.S_MEDTECH, SkillType.S_ASTECH) @@ -959,18 +958,11 @@ public boolean canPerformRole(LocalDate today, Person person, final PersonnelRol case VESSEL_CREW -> hasSkill(SkillType.S_TECH_VESSEL); case VESSEL_GUNNER -> hasSkill(SkillType.S_GUN_SPACE); case VESSEL_NAVIGATOR -> hasSkill(SkillType.S_NAV); - case MEK_TECH -> - hasSkill(SkillType.S_TECH_MEK) - && (getSkill(SkillType.S_TECH_MEK).getExperienceLevel() > SkillType.EXP_ULTRA_GREEN); - case AERO_TEK -> - hasSkill(SkillType.S_TECH_AERO) - && (getSkill(SkillType.S_TECH_AERO).getExperienceLevel() > SkillType.EXP_ULTRA_GREEN); - case BA_TECH -> - hasSkill(SkillType.S_TECH_BA) - && (getSkill(SkillType.S_TECH_BA).getExperienceLevel() > SkillType.EXP_ULTRA_GREEN); + case MEK_TECH -> hasSkill(SkillType.S_TECH_MEK); + case AERO_TEK -> hasSkill(SkillType.S_TECH_AERO); + case BA_TECH -> hasSkill(SkillType.S_TECH_BA); case ASTECH -> hasSkill(SkillType.S_ASTECH); - case DOCTOR -> hasSkill(SkillType.S_DOCTOR) - && (getSkill(SkillType.S_DOCTOR).getExperienceLevel() > SkillType.EXP_ULTRA_GREEN); + case DOCTOR -> hasSkill(SkillType.S_DOCTOR); case MEDIC -> hasSkill(SkillType.S_MEDTECH); case ADMINISTRATOR_COMMAND, ADMINISTRATOR_LOGISTICS, ADMINISTRATOR_TRANSPORT, ADMINISTRATOR_HR -> hasSkill(SkillType.S_ADMIN); @@ -1077,13 +1069,13 @@ public void changeStatus(final Campaign campaign, final LocalDate today, setRetirement(today); break; - case PREGNANCY_COMPLICATIONS: - campaign.getProcreation().processPregnancyComplications(campaign, campaign.getLocalDate(), this); - // purposeful fall through case STUDENT: - // log entries & reports are handled by the education package + // log entries and reports are handled by the education package // (mekhq/campaign/personnel/education) break; + case PREGNANCY_COMPLICATIONS: + campaign.getProcreation().processPregnancyComplications(campaign, campaign.getLocalDate(), this); + // purposeful fall through default: campaign.addReport(String.format(status.getReportText(), getHyperlinkedFullTitle())); ServiceLogger.changedStatus(this, campaign.getLocalDate(), status); @@ -1096,7 +1088,7 @@ public void changeStatus(final Campaign campaign, final LocalDate today, setDateOfDeath(today); if ((genealogy.hasSpouse()) && (!genealogy.getSpouse().getStatus().isDead())) { - campaign.getDivorce().widowed(campaign, campaign.getLocalDate(), getGenealogy().getSpouse()); + campaign.getDivorce().widowed(campaign, campaign.getLocalDate(), this); } // log death across genealogy @@ -1316,12 +1308,12 @@ public void setStatus(final PersonnelStatus status) { this.status = status; } - public int getIdleMonths() { - return idleMonths; + public int getVocationalXPTimer() { + return vocationalXPTimer; } - public void setIdleMonths(final int idleMonths) { - this.idleMonths = idleMonths; + public void setVocationalXPTimer(final int vocationalXPTimer) { + this.vocationalXPTimer = vocationalXPTimer; } public int getDaysToWaitForHealing() { @@ -1481,8 +1473,33 @@ public UUID getId() { return id; } + /** + * Checks if the person is considered a child based on their age and today's date. + * + *

    This method uses the default context where the person is not being checked + * for procreation-specific thresholds.

    + * + * @param today the current date to calculate the age against + * @return {@code true} if the person's age is less than 16; {@code false} otherwise + */ public boolean isChild(final LocalDate today) { - return getAge(today) < 18; + return isChild(today, false); + } + + /** + * Checks if the person is considered a child based on their age, today's date, and procreation + * status. + * + * @param today the current date to calculate the age against + * @param use18 if {@code true}, the threshold considers a person a child + * if their age is less than 18; otherwise, the default age threshold of + * 16 applies + * @return {@code true} if the person's age is less than the specified threshold + * (procreation or default), {@code false} otherwise + */ + public boolean isChild(final LocalDate today, boolean use18) { + int age = getAge(today); + return age < (use18 ? 18 : 16); } public Genealogy getGenealogy() { @@ -1722,14 +1739,12 @@ public void addEduTagAlong(final UUID tagAlong) { this.eduTagAlongs.add(tagAlong); } - public List getEduFailedApplications() { + public List getEduFailedApplications() { return eduFailedApplications; } - public void addEduFailedApplications(final Academy eduFailedApplications) { - if (!this.eduFailedApplications.contains(eduFailedApplications)) { - this.eduFailedApplications.add(eduFailedApplications); - } + public void addEduFailedApplications(final String failedApplication) { + eduFailedApplications.add(failedApplication); } /** @@ -1985,8 +2000,8 @@ public void writeToXML(final PrintWriter pw, int indent, final Campaign campaign MHQXMLUtility.writeSimpleXMLTag(pw, indent, "biography", biography); } - if (idleMonths > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "idleMonths", idleMonths); + if (vocationalXPTimer > 0) { + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "vocationalXPTimer", vocationalXPTimer); } if (!genealogy.isEmpty()) { @@ -2174,6 +2189,16 @@ public void writeToXML(final PrintWriter pw, int indent, final Campaign campaign MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "eduTagAlongs"); } + if (!eduTagAlongs.isEmpty()) { + MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "eduFailedApplications"); + + for (String failedApplication : eduFailedApplications) { + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "eduFailedApplication", failedApplication); + } + + MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "eduFailedApplications"); + } + if (eduAcademySystem != null) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "eduAcademySystem", eduAcademySystem); } @@ -2337,8 +2362,10 @@ public static Person generateInstanceFromXML(Node wn, Campaign c, Version versio retVal.secondaryDesignator = ROMDesignation.parseFromString(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("daysToWaitForHealing")) { retVal.daysToWaitForHealing = Integer.parseInt(wn2.getTextContent()); - } else if (wn2.getNodeName().equalsIgnoreCase("idleMonths")) { - retVal.idleMonths = Integer.parseInt(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("vocationalXPTimer") + // <50.03 compatibility handler + || wn2.getNodeName().equalsIgnoreCase("idleMonths")) { + retVal.vocationalXPTimer = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("id")) { retVal.id = UUID.fromString(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("genealogy")) { @@ -2547,6 +2574,18 @@ public static Person generateInstanceFromXML(Node wn, Campaign c, Version versio } } } + } else if (wn2.getNodeName().equalsIgnoreCase("eduFailedApplications")) { + if (wn2.getNodeName().equalsIgnoreCase("eduFailedApplications")) { + NodeList nodes = wn2.getChildNodes(); + + for (int j = 0; j < nodes.getLength(); j++) { + Node node = nodes.item(j); + + if (node.getNodeName().equalsIgnoreCase("eduFailedApplication")) { + retVal.eduFailedApplications.add(node.getTextContent()); + } + } + } } else if (wn2.getNodeName().equalsIgnoreCase("eduAcademySystem")) { retVal.eduAcademySystem = String.valueOf(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("eduAcademyName")) { @@ -2566,44 +2605,61 @@ public static Person generateInstanceFromXML(Node wn, Campaign c, Version versio } else if (wn2.getNodeName().equalsIgnoreCase("aggression")) { try { // <50.01 compatibility handler - retVal.aggression = Aggression.parseFromString(wn2.getTextContent()); - } catch (Exception e) { + retVal.aggression = Aggression.valueOf(wn2.getTextContent() + .toUpperCase() + .replaceAll("-", "_") + .replaceAll(" ", "_")); + } catch (IllegalArgumentException e) { retVal.aggression = Aggression.fromOrdinal(Integer.parseInt(wn2.getTextContent())); } } else if (wn2.getNodeName().equalsIgnoreCase("ambition")) { try { // <50.01 compatibility handler - retVal.ambition = Ambition.parseFromString(wn2.getTextContent()); - } catch (Exception e) { + retVal.ambition = Ambition.valueOf(wn2.getTextContent() + .toUpperCase() + .replaceAll("-", "_") + .replaceAll(" ", "_")); + } catch (IllegalArgumentException e) { retVal.ambition = Ambition.fromOrdinal(Integer.parseInt(wn2.getTextContent())); } } else if (wn2.getNodeName().equalsIgnoreCase("greed")) { try { // <50.01 compatibility handler - retVal.greed = Greed.parseFromString(wn2.getTextContent()); - } catch (Exception e) { + retVal.greed = Greed.valueOf(wn2.getTextContent() + .toUpperCase() + .replaceAll("-", "_") + .replaceAll(" ", "_")); + } catch (IllegalArgumentException e) { retVal.greed = Greed.fromOrdinal(Integer.parseInt(wn2.getTextContent())); } } else if (wn2.getNodeName().equalsIgnoreCase("social")) { try { // <50.01 compatibility handler - retVal.social = Social.parseFromString(wn2.getTextContent()); - } catch (Exception e) { + retVal.social = Social.valueOf(wn2.getTextContent() + .toUpperCase() + .replaceAll("-", "_") + .replaceAll(" ", "_")); + } catch (IllegalArgumentException e) { retVal.social = Social.fromOrdinal(Integer.parseInt(wn2.getTextContent())); } - retVal.social = Social.parseFromString(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("personalityQuirk")) { try { // <50.01 compatibility handler - retVal.personalityQuirk = PersonalityQuirk.parseFromString(wn2.getTextContent()); - } catch (Exception e) { + retVal.personalityQuirk = PersonalityQuirk.valueOf(wn2.getTextContent() + .toUpperCase() + .replaceAll("-", "_") + .replaceAll(" ", "_")); + } catch (IllegalArgumentException e) { retVal.personalityQuirk = PersonalityQuirk.fromOrdinal(Integer.parseInt(wn2.getTextContent())); } } else if (wn2.getNodeName().equalsIgnoreCase("intelligence")) { try { // <50.01 compatibility handler - retVal.intelligence = Intelligence.parseFromString(wn2.getTextContent()); - } catch (Exception e) { + retVal.intelligence = Intelligence.valueOf(wn2.getTextContent() + .toUpperCase() + .replaceAll("-", "_") + .replaceAll(" ", "_")); + } catch (IllegalArgumentException e) { retVal.intelligence = Intelligence.fromOrdinal(Integer.parseInt(wn2.getTextContent())); } } else if (wn2.getNodeName().equalsIgnoreCase("personalityDescription")) { @@ -2679,6 +2735,22 @@ public static Person generateInstanceFromXML(Node wn, Campaign c, Version versio if (retVal.getRecruitment() == null) { retVal.setRecruitment(c.getLocalDate()); } + + // This resolves a bug squashed in 2025 (50.03) but lurked in our codebase + // potentially as far back as 2014. The next two handlers should never be removed. + if (!retVal.canPerformRole(c.getLocalDate(), retVal.getPrimaryRole(), true)) { + retVal.setPrimaryRole(c, PersonnelRole.NONE); + logger.info(String.format("%s was found to be ineligible for their" + + " primary role. That role has been removed and they were assigned the" + + " NONE role.", retVal.getFullTitle())); + } + + if (!retVal.canPerformRole(c.getLocalDate(), retVal.getSecondaryRole(), false)) { + retVal.setSecondaryRole(PersonnelRole.NONE); + logger.info(String.format("%s was found to be ineligible for their" + + " secondary role. That role has been removed and they were assigned the" + + " NONE role.", retVal.getFullTitle())); + } } catch (Exception e) { logger.error("Failed to read person {} from file", retVal.getFullName(), e); retVal = null; @@ -2816,7 +2888,6 @@ private static void updateOptions(String edgeTriggers, Person retVal, List edgeOptionList) { for (String edgeTrigger : edgeOptionList) { - logger.info(edgeTrigger); String advName = Crew.parseAdvantageName(edgeTrigger); try { @@ -2835,22 +2906,17 @@ public Money getTotalEarnings() { } /** - * This is used to pay a person + * This is used to pay a person. Preventing negative payments + * is intentional to ensure we don't accidentally + * change someone when trying to give them money. + * To charge a person, implement a new method. + * (And then add a @see here) * * @param money the amount of money to add to their total earnings */ public void payPerson(final Money money) { - totalEarnings = getTotalEarnings().plus(money); - } - - /** - * This is used to pay a person their salary - * - * @param campaign the campaign the person is a part of - */ - public void payPersonSalary(final Campaign campaign) { - if (getStatus().isActive()) { - payPerson(getSalary(campaign)); + if (money.isPositiveOrZero()) { + totalEarnings = getTotalEarnings().plus((money)); } } @@ -3292,6 +3358,81 @@ public int getExperienceLevel(final Campaign campaign, final boolean secondary) } } + /** + * Retrieves the skills associated with the character's profession. + * The skills returned depend on whether the personnel's primary or secondary role + * is being queried and may also vary based on the campaign's configuration settings, such as + * whether artillery skills are enabled. + * + *

    This method identifies the {@link PersonnelRole} associated with the personnel and returns + * a list of corresponding skills. The resulting skills depend on the profession and specific + * conditions, such as whether artillery is enabled in the campaign options.

    + * + *

    Examples of skill mappings include: + *

      + *
    • MEKWARRIOR: Includes gun and piloting skills for meks, with optional + * artillery skills if enabled in the campaign.
    • + *
    • LAM_PILOT: Covers skills for both meks and aerospace combat.
    • + *
    • GROUND_VEHICLE_DRIVER: Includes piloting skills for ground vehicles.
    • + *
    • VEHICLE_GUNNER: Includes vehicle gunnery skills, with optional artillery skills + * if enabled.
    • + *
    • AEROSPACE_PILOT: Covers skills for aerospace gunnery and piloting.
    • + *
    • ADMINISTRATORS: Includes administrative, negotiation, and scrounging skills.
    • + *
    • DEPENDENT or NONE: Returns no specific skills.
    • + *
    + * + * @param campaign the current {@link Campaign} + * @param secondary a boolean indicating whether to retrieve skills for the secondary ({@code true}) + * or primary ({@code false}) profession of the character + * @return a {@link List} of skill identifiers ({@link String}) associated with the personnel's role, + * possibly modified by campaign settings + */ + public List getProfessionSkills(final Campaign campaign, final boolean secondary) { + final PersonnelRole profession = secondary ? getSecondaryRole() : getPrimaryRole(); + final boolean isUseArtillery = campaign.getCampaignOptions().isUseArtillery(); + + return switch (profession) { + case MEKWARRIOR -> { + if (isUseArtillery) { + yield List.of(SkillType.S_GUN_MEK, SkillType.S_PILOT_MEK, SkillType.S_ARTILLERY); + } else { + yield List.of(SkillType.S_GUN_MEK, SkillType.S_PILOT_MEK); + } + } + case LAM_PILOT -> List.of(SkillType.S_GUN_MEK, SkillType.S_PILOT_MEK, + SkillType.S_GUN_AERO, SkillType.S_PILOT_AERO); + case GROUND_VEHICLE_DRIVER -> List.of(SkillType.S_PILOT_GVEE); + case NAVAL_VEHICLE_DRIVER -> List.of(SkillType.S_PILOT_NVEE); + case VTOL_PILOT -> List.of(SkillType.S_PILOT_VTOL); + case VEHICLE_GUNNER -> { + if (isUseArtillery) { + yield List.of(SkillType.S_GUN_VEE, SkillType.S_ARTILLERY); + } else { + yield List.of(SkillType.S_GUN_VEE); + } + } + case VEHICLE_CREW, MECHANIC -> List.of(SkillType.S_TECH_MECHANIC); + case AEROSPACE_PILOT -> List.of(SkillType.S_GUN_AERO, SkillType.S_PILOT_AERO); + case CONVENTIONAL_AIRCRAFT_PILOT -> List.of(SkillType.S_GUN_JET, SkillType.S_PILOT_JET); + case PROTOMEK_PILOT -> List.of(SkillType.S_GUN_PROTO, SkillType.S_GUN_PROTO); + case BATTLE_ARMOUR -> List.of(SkillType.S_GUN_BA, SkillType.S_ANTI_MEK); + case SOLDIER -> List.of(SkillType.S_SMALL_ARMS); + case VESSEL_PILOT -> List.of(SkillType.S_PILOT_SPACE); + case VESSEL_GUNNER -> List.of(SkillType.S_GUN_SPACE); + case VESSEL_CREW -> List.of(SkillType.S_TECH_VESSEL); + case VESSEL_NAVIGATOR -> List.of(SkillType.S_NAV); + case MEK_TECH -> List.of(SkillType.S_TECH_MEK); + case AERO_TEK -> List.of(SkillType.S_TECH_AERO); + case BA_TECH -> List.of(SkillType.S_TECH_BA); + case ASTECH -> List.of(SkillType.S_ASTECH); + case DOCTOR -> List.of(SkillType.S_DOCTOR); + case MEDIC -> List.of(SkillType.S_MEDTECH); + case ADMINISTRATOR_COMMAND, ADMINISTRATOR_LOGISTICS, ADMINISTRATOR_TRANSPORT, + ADMINISTRATOR_HR -> List.of(SkillType.S_ADMIN, SkillType.S_NEG, SkillType.S_SCROUNGE); + case DEPENDENT, NONE -> List.of(String.valueOf(SkillType.EXP_NONE)); + }; + } + /** * @param campaign the campaign the person is a part of * @return a full description in HTML format that will be used for the graphical @@ -3923,7 +4064,7 @@ public boolean isTech() { public boolean isTechExpanded() { return isTechMek() || isTechAero() || isTechMechanic() || isTechBA() || isTechLargeVessel(); } - + public boolean isTechLargeVessel() { boolean hasSkill = hasSkill(SkillType.S_TECH_VESSEL); return hasSkill && (getPrimaryRole().isVesselCrew() || getSecondaryRole().isVesselCrew()); @@ -3957,6 +4098,10 @@ public boolean isDoctor() { return hasSkill(SkillType.S_DOCTOR) && (getPrimaryRole().isDoctor() || getSecondaryRole().isDoctor()); } + public boolean isDependent() { + return (getPrimaryRole().isDependent() || getSecondaryRole().isDependent()); + } + public boolean isTaskOvertime(final IPartWork partWork) { return (partWork.getTimeLeft() > getMinutesLeft()) && (getOvertimeLeft() > 0); } diff --git a/MekHQ/src/mekhq/campaign/personnel/SkillType.java b/MekHQ/src/mekhq/campaign/personnel/SkillType.java index 1d0262de95e..e30a0a60002 100644 --- a/MekHQ/src/mekhq/campaign/personnel/SkillType.java +++ b/MekHQ/src/mekhq/campaign/personnel/SkillType.java @@ -2,6 +2,7 @@ * SkillType.java * * Copyright (c) 2009 Jay Lawson (jaylawson39 at yahoo.com). All rights reserved. + * Copyright (c) 2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,18 +22,18 @@ package mekhq.campaign.personnel; import megamek.common.*; +import megamek.common.enums.SkillLevel; import megamek.logging.MMLogger; -import megamek.common.enums.*; import mekhq.MekHQ; import mekhq.utilities.MHQXMLUtility; import mekhq.utilities.ReportingUtilities; - import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.PrintWriter; import java.util.Hashtable; +import java.util.List; import java.util.Map; /** @@ -63,9 +64,8 @@ public class SkillType { public static final String S_ARTILLERY = "Artillery"; public static final String S_SMALL_ARMS = "Small Arms"; public static final String S_ANTI_MEK = "Anti-Mek"; - public static final String S_TACTICS = "Tactics"; - // non-combat skills + // support skills public static final String S_TECH_MEK = "Tech/Mek"; public static final String S_TECH_MECHANIC = "Tech/Mechanic"; public static final String S_TECH_AERO = "Tech/Aero"; @@ -80,6 +80,7 @@ public class SkillType { public static final String S_LEADER = "Leadership"; public static final String S_SCROUNGE = "Scrounge"; public static final String S_STRATEGY = "Strategy"; + public static final String S_TACTICS = "Tactics"; public static final int NUM_LEVELS = 11; @@ -187,6 +188,20 @@ public static String getExperienceLevelColor(SkillLevel level) { }; } + /** + * Checks if a given skill is a combat skill. + * + * @param skill The skill to check if it is a combat skill. + * @return {@code true} if the skill is a combat skill, {@code false} otherwise. + */ + public static boolean isCombatSkill(SkillType skill) { + List combatSkills = List.of(S_PILOT_MEK, S_PILOT_AERO, S_PILOT_JET, S_PILOT_GVEE, S_PILOT_VTOL, + S_PILOT_NVEE, S_PILOT_SPACE, S_GUN_MEK, S_GUN_AERO, S_GUN_JET, S_GUN_VEE, S_GUN_SPACE, + S_GUN_BA, S_GUN_PROTO, S_ARTILLERY, S_SMALL_ARMS, S_ANTI_MEK); + + return combatSkills.contains(skill.getName()); + } + /** * @param level - skill level integer to get tagged name for * @return "Skillname" wrapped by coloring span or bare if no color exists @@ -195,7 +210,7 @@ public static String getColoredExperienceLevelName(int level) { if (getExperienceLevelColor(level).isEmpty()) { return getExperienceLevelName(level); } - + return ReportingUtilities.messageSurroundedBySpanWithColor( getExperienceLevelColor(level), getExperienceLevelName(level)); } @@ -319,6 +334,15 @@ public int getCost(int lvl) { } } + /** + * Retrieves the cost values associated with this skill type for different levels. + * + * @return an array of Integer representing the costs for each level of the skill. + */ + public Integer[] getCosts() { + return costs; + } + /** get the cost to acquire this skill at the given level from scratch **/ public int getTotalCost(int lvl) { int totalCost = 0; diff --git a/MekHQ/src/mekhq/campaign/personnel/autoAwards/MiscAwards.java b/MekHQ/src/mekhq/campaign/personnel/autoAwards/MiscAwards.java index 607f3818295..edaeba6fa61 100644 --- a/MekHQ/src/mekhq/campaign/personnel/autoAwards/MiscAwards.java +++ b/MekHQ/src/mekhq/campaign/personnel/autoAwards/MiscAwards.java @@ -279,7 +279,7 @@ private static boolean prisonerOfWar(Campaign campaign, Award award, UUID person */ private static boolean drillInstructor(Campaign campaign, Award award, UUID person) { if (award.canBeAwarded(campaign.getPerson(person))) { - return campaign.getAllStrategicFormations().stream() + return campaign.getAllCombatTeams().stream() .anyMatch(lance -> (lance.getRole().isTraining()) && (lance.getCommanderId().equals(person))); } diff --git a/MekHQ/src/mekhq/campaign/personnel/autoAwards/TheatreOfWarAwards.java b/MekHQ/src/mekhq/campaign/personnel/autoAwards/TheatreOfWarAwards.java index a89b3a431f6..0bcb4906312 100644 --- a/MekHQ/src/mekhq/campaign/personnel/autoAwards/TheatreOfWarAwards.java +++ b/MekHQ/src/mekhq/campaign/personnel/autoAwards/TheatreOfWarAwards.java @@ -52,7 +52,13 @@ public static Map> TheatreOfWarAwardsProcessor(Campaign ca boolean isEligible; List eligibleAwards = new ArrayList<>(); - String employer = ((Contract) mission).getEmployer(); + // if the mission isn't an instance of 'AtBContract' we won't have the information we need, + // so abort processing. + if (!(mission instanceof AtBContract)) { + return AutoAwardsController.prepareAwardData(person, eligibleAwards); + } + + String employer = ((AtBContract) mission).getEmployerCode(); int contractStartYear = ((Contract) mission).getStartDate().getYear(); int currentYear = campaign.getGameYear(); @@ -103,10 +109,10 @@ public static Map> TheatreOfWarAwardsProcessor(Campaign ca } if (belligerents.size() == 1) { - if (!processFaction(belligerents.get(0), employer)) { + if (!processFaction(employer, belligerents.get(0))) { continue; } - } else if ((campaign.getCampaignOptions().isUseAtB()) && (mission instanceof AtBContract)) { + } else if (campaign.getCampaignOptions().isUseAtB()) { String enemy = ((AtBContract) mission).getEnemyCode(); if (hasLoyalty(employer, attackers)) { diff --git a/MekHQ/src/mekhq/campaign/personnel/death/AbstractDeath.java b/MekHQ/src/mekhq/campaign/personnel/death/AbstractDeath.java index 5da721ba6a9..4abac2e98be 100644 --- a/MekHQ/src/mekhq/campaign/personnel/death/AbstractDeath.java +++ b/MekHQ/src/mekhq/campaign/personnel/death/AbstractDeath.java @@ -180,7 +180,7 @@ public boolean processNewDay(final Campaign campaign, final LocalDate today, * @param gender the person's gender * @return true if the person is selected to randomly die, otherwise false */ - public abstract boolean randomlyDies(final int age, final Gender gender); + public abstract boolean randomlyDies(int age, Gender gender); // endregion Random Death // endregion New Day diff --git a/MekHQ/src/mekhq/campaign/personnel/divorce/AbstractDivorce.java b/MekHQ/src/mekhq/campaign/personnel/divorce/AbstractDivorce.java index d1aa784ce5a..5fd433fcd53 100644 --- a/MekHQ/src/mekhq/campaign/personnel/divorce/AbstractDivorce.java +++ b/MekHQ/src/mekhq/campaign/personnel/divorce/AbstractDivorce.java @@ -199,56 +199,50 @@ public void divorce(final Campaign campaign, final LocalDate today, final Person style.apply(campaign, origin, spouse); - final FormerSpouseReason reason; + final FormerSpouseReason reasonOrigin; + final FormerSpouseReason reasonSpouse; - if (spouse.getStatus().isDeadOrMIA() == origin.getStatus().isDeadOrMIA()) { - reason = FormerSpouseReason.DIVORCE; + if (!origin.getStatus().isDead()) { + reasonOrigin = FormerSpouseReason.DIVORCE; + reasonSpouse = FormerSpouseReason.DIVORCE; PersonalLogger.divorcedFrom(origin, spouse, today); + PersonalLogger.divorcedFrom(spouse, origin, today); - if (origin.getStatus().isDead()) { - PersonalLogger.widowedBy(spouse, origin, today); + campaign.addReport(String.format(resources.getString("divorce.report"), + origin.getHyperlinkedName(), spouse.getHyperlinkedName())); + } else { + reasonOrigin = FormerSpouseReason.DIVORCE; + reasonSpouse = FormerSpouseReason.WIDOWED; - campaign.addReport(String.format(resources.getString("widowed.report"), - origin.getHyperlinkedName(), spouse.getHyperlinkedName())); + if (origin.getStatus().isKIA()) { + PersonalLogger.spouseKia(spouse, origin, today); } else { - PersonalLogger.divorcedFrom(spouse, origin, today); - - campaign.addReport(String.format(resources.getString("divorce.report"), - origin.getHyperlinkedName(), spouse.getHyperlinkedName())); + PersonalLogger.widowedBy(spouse, origin, today); } - spouse.setMaidenName(null); - origin.setMaidenName(null); - - spouse.getGenealogy().setSpouse(null); - origin.getGenealogy().setSpouse(null); - } else if (spouse.getStatus().isDeadOrMIA()) { - reason = FormerSpouseReason.WIDOWED; + PersonalLogger.divorcedFrom(origin, spouse, today); - if (spouse.getStatus().isKIA()) { - PersonalLogger.spouseKia(origin, spouse, today); - } - origin.setMaidenName(null); - origin.getGenealogy().setSpouse(null); - } else { - // Origin is Dead or MIA - reason = FormerSpouseReason.WIDOWED; - if (origin.getStatus().isKIA()) { - PersonalLogger.spouseKia(spouse, origin, today); - } - spouse.setMaidenName(null); - spouse.getGenealogy().setSpouse(null); + campaign.addReport(String.format(resources.getString("widowed.report"), + origin.getHyperlinkedName(), spouse.getHyperlinkedName())); } // Add to the former spouse list - spouse.getGenealogy().addFormerSpouse(new FormerSpouse(origin, today, reason)); - origin.getGenealogy().addFormerSpouse(new FormerSpouse(spouse, today, reason)); + spouse.getGenealogy().addFormerSpouse(new FormerSpouse(origin, today, reasonOrigin)); + origin.getGenealogy().addFormerSpouse(new FormerSpouse(spouse, today, reasonSpouse)); - // Clear origin spouses + // Clear spouse data origin.getGenealogy().setOriginSpouse(null); + origin.getGenealogy().setSpouse(null); + spouse.getGenealogy().setOriginSpouse(null); + spouse.getGenealogy().setSpouse(null); + + + // Clear maiden names + spouse.setMaidenName(null); + origin.setMaidenName(null); // roll for removal of marriageable flag if (Compute.d6(1) <= 2) { @@ -261,11 +255,11 @@ public void divorce(final Campaign campaign, final LocalDate today, final Person List departingPartners = new ArrayList<>(); - if (origin.getPrimaryRole().isDependent()) { + if (origin.isDependent() && !origin.getStatus().isDead()) { departingPartners.add(origin); } - if (spouse.getPrimaryRole().isDependent()) { + if (spouse.isDependent() && !spouse.getStatus().isDead()) { departingPartners.add(spouse); } diff --git a/MekHQ/src/mekhq/campaign/personnel/education/Academy.java b/MekHQ/src/mekhq/campaign/personnel/education/Academy.java index 2573891f4b9..12cb049e528 100644 --- a/MekHQ/src/mekhq/campaign/personnel/education/Academy.java +++ b/MekHQ/src/mekhq/campaign/personnel/education/Academy.java @@ -659,7 +659,7 @@ public boolean isQualified(Person person) { * false otherwise */ public boolean hasRejectedApplication(Person person) { - return person.getEduFailedApplications().contains(this); + return person.getEduFailedApplications().contains(name + "::" + getEducationLevel(person)); } /** @@ -813,6 +813,12 @@ public String getTooltip(Campaign campaign, List personnel, int courseIn tooltip.append("
    "); // with the skill content resolved, we can move onto the rest of the tooltip + if (!isLocal && !isHomeSchool) { + int targetNumber = campaign.getCampaignOptions().getEntranceExamBaseTargetNumber() - facultySkill; + tooltip.append("").append(resources.getString("entranceExam.text")) + .append(" ").append(' ').append(targetNumber).append("+
    "); + } + if (personnel.size() == 1) { tooltip.append("").append(resources.getString("tuition.text")).append(" ") .append(getTuitionAdjusted(person) * getFactionDiscountAdjusted(campaign, person)).append(" CSB") diff --git a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java index b6125465bd4..4e3537e7edc 100644 --- a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java +++ b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java @@ -28,16 +28,21 @@ import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.log.ServiceLogger; import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.enums.PersonnelStatus; import mekhq.campaign.personnel.enums.education.EducationLevel; import mekhq.campaign.personnel.enums.education.EducationStage; +import mekhq.campaign.personnel.familyTree.Genealogy; +import mekhq.campaign.personnel.randomEvents.enums.personalities.Intelligence; import mekhq.utilities.ReportingUtilities; import java.time.DayOfWeek; import java.time.LocalDate; import java.util.*; +import static megamek.common.Compute.d6; +import static mekhq.campaign.personnel.SkillType.EXP_REGULAR; +import static mekhq.campaign.personnel.SkillType.EXP_VETERAN; + /** * The EducationController class is responsible for managing the education * process. It provides methods to begin the education process, calculate @@ -79,7 +84,7 @@ public static boolean makeEnrollmentCheck(Campaign campaign, Person person, Stri } // has the character already failed to apply to this academy? - if (person.getEduFailedApplications().contains(academy)) { + if (person.getEduFailedApplications().contains(academyNameInSet + "::" + academy.getEducationLevel(person))) { campaign.addReport(String.format(resources.getString("secondApplication.text"), person.getHyperlinkedFullTitle(), academyNameInSet, @@ -91,7 +96,7 @@ public static boolean makeEnrollmentCheck(Campaign campaign, Person person, Stri CampaignOptions campaignOptions = campaign.getCampaignOptions(); // Calculate the roll based on Intelligence if necessary - int roll = Compute.d6(2); + int roll = d6(2); if (campaignOptions.isUseRandomPersonalities()) { roll += (person.getIntelligence().getIntelligenceScore() / 4); } @@ -103,9 +108,9 @@ public static boolean makeEnrollmentCheck(Campaign campaign, Person person, Stri if (roll >= targetNumber) { return true; } else { - // Mark the academy in the person's list of failed applications preventing - // re-application - person.addEduFailedApplications(academy); + // Mark the academy in the person's list of failed applications preventing re-application + person.addEduFailedApplications(academyNameInSet + "::" + academy.getEducationLevel(person)); + campaign.addReport(String.format(resources.getString("applicationFailure.text"), person.getHyperlinkedFullTitle(), ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), @@ -243,11 +248,29 @@ public static void enrollPerson(Campaign campaign, Person person, Academy academ person.setEduJourneyTime(campaign.getSimplifiedTravelTime(campaign.getSystemById(campus))); person.setEduAcademySystem(campus); } + } - for (Person child : person.getGenealogy().getChildren()) { - if ((child.getStatus().isActive()) && (child.isChild(campaign.getLocalDate()))) { - person.addEduTagAlong(child.getId()); - child.changeStatus(campaign, campaign.getLocalDate(), PersonnelStatus.ON_LEAVE); + Genealogy genealogy = person.getGenealogy(); + Person spouse = genealogy.getSpouse(); + List children = genealogy.getChildren(); + + boolean hasActiveParent = false; + if (spouse != null) { + if (spouse.getStatus().isActive() && spouse.isDependent()) { + person.addEduTagAlong(spouse.getId()); + spouse.changeStatus(campaign, campaign.getLocalDate(), PersonnelStatus.ON_LEAVE); + } + + hasActiveParent = spouse.getStatus().isActive(); + } + + if (!hasActiveParent) { + for (Person child : children) { + if (child.getStatus().isActive()) { + if (child.isChild(campaign.getLocalDate())) { + person.addEduTagAlong(child.getId()); + child.changeStatus(campaign, campaign.getLocalDate(), PersonnelStatus.ON_LEAVE); + } } } } @@ -322,7 +345,7 @@ public static String generateName(Academy academy, String campus) { MekHQ.getMHQOptions().getLocale()); if (academy.isPrepSchool()) { - if (Compute.d6(1) <= 3) { + if (d6(1) <= 3) { return campus + ' ' + generateTypeChild(resources) + ' ' + resources.getString("conjoinerOf.text") + ' ' + generateSuffix(resources); } else { @@ -330,7 +353,7 @@ public static String generateName(Academy academy, String campus) { } } - if (Compute.d6(1) <= 3) { + if (d6(1) <= 3) { if (academy.isMilitary()) { return campus + ' ' + generateMilitaryPrefix(resources) + ' ' + generateTypeAdult(resources) + ' ' + resources.getString("conjoinerOf.text") + ' ' + generateSuffix(resources); @@ -357,7 +380,7 @@ public static String generateName(Academy academy, String campus) { * @throws IllegalStateException if an unexpected roll occurs */ private static String generateMilitaryPrefix(ResourceBundle resources) { - return switch (Compute.d6(1)) { + return switch (d6(1)) { case 1 -> resources.getString("prefixCombinedArms.text"); case 2 -> resources.getString("prefixCombinedForces.text"); case 3 -> resources.getString("prefixMilitary.text"); @@ -376,7 +399,7 @@ private static String generateMilitaryPrefix(ResourceBundle resources) { * @throws IllegalStateException if the random roll is unexpected. */ private static String generateSuffix(ResourceBundle resources) { - return switch (Compute.d6(1)) { + return switch (d6(1)) { case 1 -> resources.getString("suffixTechnology.text"); case 2 -> resources.getString("suffixTechnologyAdvanced.text"); case 3 -> resources.getString("suffixScience.text"); @@ -397,7 +420,7 @@ private static String generateSuffix(ResourceBundle resources) { * @throws IllegalStateException if the generated roll is unexpected */ private static String generateTypeAdult(ResourceBundle resources) { - return switch (Compute.d6(1)) { + return switch (d6(1)) { case 1 -> resources.getString("typeAcademy.text"); case 2 -> resources.getString("typeCollege.text"); case 3 -> resources.getString("typeInstitute.text"); @@ -418,7 +441,7 @@ private static String generateTypeAdult(ResourceBundle resources) { * @throws IllegalStateException if the generated roll is unexpected */ private static String generateTypeChild(ResourceBundle resources) { - return switch (Compute.d6(1)) { + return switch (d6(1)) { case 1 -> resources.getString("typeAcademy.text"); case 2 -> resources.getString("typePreparatorySchool.text"); case 3 -> resources.getString("typeInstitute.text"); @@ -741,7 +764,7 @@ private static void checkForTrainingAccidents(Campaign campaign, Academy academy if (roll == 0) { if ((!person.isChild(campaign.getLocalDate())) || (campaign.getCampaignOptions().isAllAges())) { - if (Compute.d6(2) >= 5) { + if (d6(2) >= 5) { processTrainingInjury(campaign, academy, person, resources); } else { String resultString = String.format(resources.getString("eventTrainingAccidentKilled.text"), @@ -774,7 +797,7 @@ private static void checkForTrainingAccidents(Campaign campaign, Academy academy */ private static void processTrainingInjury(Campaign campaign, Academy academy, Person person, ResourceBundle resources) { - int roll = Compute.d6(3); + int roll = d6(3); String resultString = String.format(resources.getString("eventTrainingAccidentWounded.text"), ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), @@ -810,7 +833,9 @@ private static boolean checkForDropout(Campaign campaign, Academy academy, Perso int adultDiceSize = campaign.getCampaignOptions().getAdultDropoutChance(); int childDiceSize = campaign.getCampaignOptions().getChildrenDropoutChance(); - if (person.isChild(campaign.getLocalDate())) { + // Under 18s don't generally have the same capacity for self-determination as 18+ + // characters, so we treat them as children - even though at 16 they can take Roles. + if (person.isChild(campaign.getLocalDate(), true)) { if (childDiceSize > 1) { roll = Compute.randomInt(childDiceSize); } else { @@ -973,8 +998,13 @@ private static boolean checkForAcademyDestruction(Campaign campaign, Academy aca // been destroyed too. if ((campaign.getLocalDate().getYear() >= academy.getDestructionYear()) || (campaign.getSystemById(person.getEduAcademySystem()).getPopulation(campaign.getLocalDate()) == 0)) { - if ((!person.isChild(campaign.getLocalDate())) || (campaign.getCampaignOptions().isAllAges())) { - if (Compute.d6(2) >= 5) { + + // We use the 'use18' clause here because we don't want to upset players by having + // children killed when their academy is attacked unless the player has explicitly + // opted in. While players can assign 16-year-olds to combat roles and have them killed + // there, that doesn't have the same connotations. + if ((!person.isChild(campaign.getLocalDate(), true)) || (campaign.getCampaignOptions().isAllAges())) { + if (d6(2) >= 5) { String reportMessage = String.format(resources.getString("eventDestruction.text"), person.getHyperlinkedFullTitle(), ReportingUtilities @@ -1083,7 +1113,7 @@ private static boolean graduateAdult(Campaign campaign, Person person, Academy a // class resits required if (graduationRoll < 20) { - roll = Compute.d6(3); + roll = d6(3); String reportMessage = String.format(resources.getString("graduatedClassNeeded.text"), person.getHyperlinkedFullTitle(), @@ -1099,7 +1129,7 @@ private static boolean graduateAdult(Campaign campaign, Person person, Academy a } if (graduationRoll >= 99) { - if (Compute.d6(1) >= 5) { + if (d6(1) >= 5) { if (academy.isHomeSchool()) { String reportMessage = String.format(resources.getString("graduatedHomeSchooled.text"), person.getHyperlinkedFullTitle(), @@ -1160,7 +1190,7 @@ private static boolean graduateAdult(Campaign campaign, Person person, Academy a // graduated with honors if (graduationRoll >= 90) { - if (Compute.d6(1) >= 5) { + if (d6(1) >= 5) { if (academy.isHomeSchool()) { String reportMessage = String.format(resources.getString("graduatedHomeSchooled.text"), @@ -1221,7 +1251,7 @@ private static boolean graduateAdult(Campaign campaign, Person person, Academy a } // default graduation - if (Compute.d6(1) >= 5) { + if (d6(1) >= 5) { if (academy.isHomeSchool()) { String reportMessage = String.format(resources.getString("graduatedHomeSchooled.text"), person.getHyperlinkedFullTitle(), @@ -1419,7 +1449,7 @@ private static void graduateChild(Campaign campaign, Person person, Academy acad * @param person the person whose loyalty is to be adjusted */ private static void adjustLoyalty(Person person) { - int roll = Compute.d6(1); + int roll = d6(1); if (roll == 1) { person.setLoyalty(person.getLoyalty() - 1); @@ -1463,7 +1493,7 @@ private static void processGraduation(Campaign campaign, Person person, Academy List rolls = new ArrayList<>(); for (int roll = 0; roll < 4; roll++) { - rolls.add(Compute.d6(1)); + rolls.add(d6(1)); } Collections.sort(rolls); @@ -1647,46 +1677,6 @@ private static String graduationEventPicker() { return graduationEventTable.get(Compute.randomInt(graduationEventTable.size())); } - /** - * Sets the initial education level for a person based on their age and experience level. - * - * @param campaign the current campaign - * @param person the person whose education level needs to be set - */ - public static void setInitialEducation(Campaign campaign, Person person) { - LocalDate today = campaign.getLocalDate(); - - // If the person is younger than 16, set their highest education as early childhood - if (person.getAge(today) < 16) { - person.setEduHighestEducation(EducationLevel.EARLY_CHILDHOOD); - return; - } - - int experienceLevel = person.getExperienceLevel(campaign, false); - - // If the person's primary role is support - if (person.getPrimaryRole().isSupport(true)) { - if (experienceLevel == SkillType.EXP_VETERAN) { - // If experience level is Veteran, set Education level as Post Graduate - person.setEduHighestEducation(EducationLevel.POST_GRADUATE); - return; - } else if (experienceLevel == SkillType.EXP_ELITE) { - // If experience level is Elite, assign Doctorate level qualification and prefix Dr. - person.setEduHighestEducation(EducationLevel.DOCTORATE); - person.setPreNominal("Dr"); - return; - } - } - - // If the person's experience level is above Ultra-Green, assign College level education - // Else, assign them High School level education - if (experienceLevel > SkillType.EXP_ULTRA_GREEN) { - person.setEduHighestEducation(EducationLevel.COLLEGE); - } else { - person.setEduHighestEducation(EducationLevel.HIGH_SCHOOL); - } - } - /** * Returns a list of graduation event table entries. The list contains various * graduation event table entries such as addresses, admirer @@ -1919,4 +1909,140 @@ private static List graduationEventTable() { "surpriseMisunderstanding.text", "surpriseRejection.text"); } + + /** + * Sets the initial education level for a person based on their age, experience level, intelligence, + * a random pass/fail system, and their primary role. + * Additionally, assigns a pre-nominal ("Dr") for individuals who achieve a doctorate-level education. + * + *

    The method applies the following rules:

    + *
      + *
    • Persons younger than 16 are automatically assigned the "Early Childhood" education level.
    • + *
    • For individuals aged 16 or older, a pass/fail rate (based on intelligence and default thresholds) + * determines whether they flunk or succeed at further education.
    • + *
    • The assigned education level depends on their experience level and whether they have a combat or non-combat role.
    • + *
    • If the person's education reaches the doctorate level, they are granted the pre-nominal "Dr".
    • + *
    + * + *

    The pass rate is influenced by the campaign's settings. When using random personalities, the person's + * intelligence modifies the base pass rate (defaulting to average intelligence if personalities are disabled).

    + * + * @param campaign the campaign context, used to retrieve the current date, options, and calculate the person's experience. + * @param person the person whose initial education level is being set. + */ + public static void setInitialEducationLevel(final Campaign campaign, final Person person) { + // Retrieve the current date and the person's age + final LocalDate today = campaign.getLocalDate(); + final int age = person.getAge(today); + + // Assign "Early Childhood" education if the person is younger than 16 + if (age < 16) { + person.setEduHighestEducation(EducationLevel.EARLY_CHILDHOOD); + return; + } + + // Get the person's experience level and role (combat or non-combat) + final int experienceLevel = person.getExperienceLevel(campaign, false); + final boolean isCombatRole = person.getPrimaryRole().isCombat(); + + // We base passRate on US averages + int passRate = 60 - (Intelligence.values().length / 2); + final int intelligenceModifier = campaign.getCampaignOptions().isUseRandomPersonalities() + ? person.getIntelligence().getIntelligenceScore() + : Intelligence.values().length / 2; + passRate += intelligenceModifier; + + final boolean flunked = Compute.randomInt(100) <= passRate; + + EducationLevel educationLevel; + if (isCombatRole) { + // Determine education levels for combat roles + educationLevel = getCombatEducationLevel(experienceLevel, flunked, passRate); + } else { + // Determine education levels for non-combat roles + educationLevel = getNonCombatEducationLevel(experienceLevel, flunked, passRate); + } + + // Assign the determined education level + person.setEduHighestEducation(educationLevel); + + // Optionally assign a pre-nominal for doctorate-level education + if (educationLevel.isDoctorate()) { + person.setPreNominal("Dr"); + } + } + + /** + * Determines the education level for individuals in combat roles based on their experience level, + * intelligence-based pass/fail rate, and whether they flunked previously. + * + *

    The following rules are applied:

    + *
      + *
    • If the experience level is below "regular", the education level is either:
    • + *
    • -- "Early Childhood" if the individual flunked (with a second-chance roll based on the pass rate), or
    • + *
    • -- "High School" if they succeed.
    • + *
    • If the experience level is "regular" or higher, the education level is either:
    • + *
    • -- "High School" if the individual flunked, or
    • + *
    • -- "College" if they succeed.
    • + *
    + * + * @param experienceLevel the person's experience level (e.g., below regular, regular, or higher). + * @param flunked whether the person initially flunked based on the pass rate. + * @param passRate the calculated pass rate based on default thresholds and intelligence modifiers. + * @return the appropriate {@link EducationLevel} based on the person's experience level and final success status. + */ + private static EducationLevel getCombatEducationLevel(final int experienceLevel, boolean flunked, final int passRate) { + if (experienceLevel < EXP_REGULAR) { + // Second-chance roll for High School + if (flunked) { + flunked = Compute.randomInt(100) < passRate; + } + + return flunked ? EducationLevel.EARLY_CHILDHOOD : EducationLevel.HIGH_SCHOOL; + } + return flunked ? EducationLevel.HIGH_SCHOOL : EducationLevel.COLLEGE; + } + + /** + * Determines the education level for individuals in non-combat roles based on their experience level, + * intelligence-based pass/fail rate, and whether they flunked previously. + * + *

    The following rules are applied:

    + *
      + *
    • If the experience level is below "regular", the education level is either:
    • + *
    • -- "Early Childhood" if the individual flunked (with a second-chance roll based on the pass rate), or
    • + *
    • -- "High School" if they succeed.
    • + *
    • If the experience level is "regular", the education level is either:
    • + *
    • -- "High School" if the individual flunked, or
    • + *
    • -- "College" if they succeed.
    • + *
    • If the experience level is "veteran", the education level is either:
    • + *
    • -- "College" if the individual flunked, or
    • + *
    • -- "Post-Graduate" if they succeed.
    • + *
    • If the experience level is above "veteran", the education level is either:
    • + *
    • -- "Post-Graduate" if the individual flunked, or
    • + *
    • -- "Doctorate" if they succeed.
    • + *
    + * + * @param experienceLevel the person's experience level (e.g., below regular, regular, veteran, or higher). + * @param flunked whether the person initially flunked based on the pass rate. + * @param passRate the calculated pass rate based on default thresholds and intelligence modifiers. + * @return the appropriate {@link EducationLevel} based on the person's experience level and final success status. + */ + private static EducationLevel getNonCombatEducationLevel(final int experienceLevel, + boolean flunked, final int passRate) { + if (experienceLevel < EXP_REGULAR) { + // Second-chance roll for High School + if (flunked) { + flunked = Compute.randomInt(100) < passRate; + } + + return flunked ? EducationLevel.EARLY_CHILDHOOD : EducationLevel.HIGH_SCHOOL; + } else if (experienceLevel == EXP_REGULAR) { + return flunked ? EducationLevel.HIGH_SCHOOL : EducationLevel.COLLEGE; + } else if (experienceLevel == EXP_VETERAN) { + return flunked ? EducationLevel.COLLEGE : EducationLevel.POST_GRADUATE; + } else { + return flunked ? EducationLevel.POST_GRADUATE : EducationLevel.DOCTORATE; + } + } } diff --git a/MekHQ/src/mekhq/campaign/personnel/education/TrainingCombatTeams.java b/MekHQ/src/mekhq/campaign/personnel/education/TrainingCombatTeams.java new file mode 100644 index 00000000000..9b5505ebb21 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/personnel/education/TrainingCombatTeams.java @@ -0,0 +1,287 @@ +package mekhq.campaign.personnel.education; + +import megamek.logging.MMLogger; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.force.CombatTeam; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.Skill; +import mekhq.campaign.unit.Unit; + +import java.time.LocalDate; +import java.util.*; + +import static java.lang.Math.round; +import static mekhq.campaign.personnel.SkillType.EXP_GREEN; +import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; + +/** + * Handles the training of combat teams within the campaign. + * + *

    This class is responsible for managing the process of skill improvement and education for + * training combat teams and their associated personnel. It identifies eligible combat teams, + * validates their contracts and current conditions, and processes training for each team member.

    + * + *

    Key functionality includes tracking education time for trainees, determining skills eligible + * for training, generating skill improvement reports, and handling both individual and group + * training scenarios.

    + * + *

    Main methods: + *

      + *
    • {@link #processTrainingCombatTeams(Campaign)}: Entry point for processing all combat + * team training in the campaign.
    • + *
    • {@link #processTraining(Campaign, CombatTeam)}: Applies training logic to a specific + * combat team.
    • + *
    • {@link #performTraining(Campaign, Force, Person, Map)}: Handles training for individual + * trainees in a force.
    • + *
    • {@link #processEducationTime(Campaign, Person, Person, List)}: Updates a trainee's + * education progression and improves skills.
    • + *
    • {@link #createSkillsList(Campaign, Set)}: Collects the skill levels of educators to + * determine skills eligible for training.
    • + *
    + */ +public class TrainingCombatTeams { + private static final MMLogger logger = MMLogger.create(TrainingCombatTeams.class); + + private static final String BUNDLE_NAME = "mekhq.resources.Education"; + private static ResourceBundle resources = ResourceBundle.getBundle(BUNDLE_NAME, MekHQ.getMHQOptions().getLocale()); + + /** + * Processes all training combat teams in the campaign. + * + *

    This method iterates through all combat teams in the campaign and processes training for + * those whose role includes training. It ensures that combat teams are eligible for training + * by checking that their contracts are active and valid on the current date. If StratCon is + * used, it also verifies that the teams are deployed in their appropriate sectors.

    + * + * @param campaign the {@link Campaign} instance managing the combat teams and their operations + */ + public static void processTrainingCombatTeams(final Campaign campaign) { + final LocalDate today = campaign.getLocalDate(); + final List combatTeams = campaign.getAllCombatTeams(); + + for (CombatTeam combatTeam : combatTeams) { + if (!combatTeam.getRole().isTraining()) { + continue; + } + + AtBContract contract = combatTeam.getContract(campaign); + if (contract == null || !contract.isActiveOn(today, true)) { + continue; + } + + if (campaign.getCampaignOptions().isUseStratCon() + && !contract.getStratconCampaignState().isForceDeployedHere(combatTeam.getForceId())) { + continue; + } + + processTraining(campaign, combatTeam); + } + } + + /** + * Handles the training progression for an individual combat team. + * + *

    This method identifies the combat team's commander and educators within the unit, + * collects their skills, and compares them to the skills of the trainees in the team. Eligible + * trainees undergo training to improve their skills, which is simulated through education + * time tracking and skill level progression.

    + * + *

    If educators or trainees lack eligible skills, appropriate reports are generated. + * Training updates for skills and progression are logged within the campaign.

    + * + * @param campaign the {@link Campaign} managing the combat team and its associated personnel + * @param combatTeam the {@link CombatTeam} undergoing training during this session + */ + private static void processTraining(final Campaign campaign, final CombatTeam combatTeam) { + // First, identify the Combat Team's commander + combatTeam.refreshCommander(campaign); + Person commander = combatTeam.getCommander(campaign); + + if (commander == null) { + logger.info(String.format("Failed to fetch commander for Combat Team: %s", + combatTeam.getForceId())); + return; + } + + // Second, fetch all active crew in the commander's unit. + // If the commander's unit is not multi-crewed, only the commander will be returned. + Set educators = new HashSet<>(commander.getUnit().getActiveCrew()); + + // Then build a set of their skills + Map educatorSkills = createSkillsList(campaign, educators); + + // Next cycle through each character in the force + Force force = campaign.getForceFor(commander); + + performTraining(campaign, force, commander, educatorSkills); + } + + /** + * Handles training for all trainees within a force. + * + *

    This method iterates over each unit in the specified force and processes training + * for all active personnel within each unit. Eligible skills are identified by comparing + * the educator's abilities to those of the trainees, and skill improvement is simulated.

    + * + * @param campaign the current {@link Campaign} + * @param force the {@link Force} containing the units to train + * @param commander the {@link Person} commanding the combat team + * @param educatorSkills a map of skills and their experience levels available for teaching + */ + private static void performTraining(Campaign campaign, Force force, Person commander, + Map educatorSkills) { + for (UUID unitId : force.getAllUnits(true)) { + Unit unit = campaign.getUnit(unitId); + + if (unit == null) { + continue; + } + + for (Person trainee : unit.getActiveCrew()) { + if (commander.getUnit().getActiveCrew().contains(trainee)) { + continue; + } + + List skillsBeingTrained = new ArrayList<>(); + for (String commanderSkill : educatorSkills.keySet()) { + Skill traineeSkill = trainee.getSkill(commanderSkill); + + if (traineeSkill != null) { + int traineeExperienceLevel = traineeSkill.getExperienceLevel(); + + if (traineeExperienceLevel > EXP_GREEN) { + continue; + } + + // The commander is required to be one step above the experience level they + // are teaching. + if (traineeExperienceLevel < (educatorSkills.get(commanderSkill) - 1)) { + skillsBeingTrained.add(traineeSkill); + } + } + } + + if (educatorSkills.isEmpty() || skillsBeingTrained.isEmpty()) { + campaign.addReport(String.format(resources.getString("notLearningAnything.text"), + trainee.getHyperlinkedFullTitle(), commander.getFullTitle(), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + trainee.setEduAcademyName(""); + trainee.setEduEducationTime(0); + continue; + } + + // We piggyback on the education module here. + // If the character ever enters actual education, this will be overwritten. + processEducationTime(campaign, commander, trainee, skillsBeingTrained); + } + } + } + + /** + * Progresses a trainee's education time and improves skills if the required threshold is met. + * + *

    This method calculates the education time required for the next experience level and compares + * it with the trainee's accumulated education time. If the required time is met or exceeded, the + * skill level is increased, and education time is reset or reduced as needed.

    + * + * @param campaign the current {@link Campaign} + * @param commander the {@link Person} acting as the educator for the trainee + * @param trainee the {@link Person} receiving training + * @param skillsBeingTrained a list of eligible {@link Skill} objects for training + */ + private static void processEducationTime(Campaign campaign, Person commander, + Person trainee, List skillsBeingTrained) { + final CampaignOptions campaignOptions = campaign.getCampaignOptions(); + final String EDUCATION_STRING = "TRAINING_COMBAT_TEAM"; // Never change this + final int WEEK_DURATION = 7; // days + final int EDUCATION_TIME_MULTIPLIER = 28; // days + + if (!Objects.equals(trainee.getEduAcademyName(), EDUCATION_STRING)) { + trainee.setEduAcademyName(EDUCATION_STRING); + trainee.setEduEducationTime(0); + } else { + int newEducationTime = trainee.getEduEducationTime() + WEEK_DURATION; + trainee.setEduEducationTime(newEducationTime); + + if (skillsBeingTrained.isEmpty()) { + return; + } + + // The lowest skill is improved first + skillsBeingTrained.sort(Comparator.comparingInt(Skill::getExperienceLevel)); + Skill targetSkill = skillsBeingTrained.get(0); + // The +1 is to account for the next experience level to be gained + int currentExperienceLevel = targetSkill.getExperienceLevel() + 1; + + int perExperienceLevelMultiplier = EDUCATION_TIME_MULTIPLIER; + double experienceMultiplier = campaignOptions.getXpCostMultiplier(); + double intelligenceCostMultiplier = trainee.getIntelligenceXpCostMultiplier(campaignOptions); + + perExperienceLevelMultiplier = (int) round(perExperienceLevelMultiplier * experienceMultiplier * intelligenceCostMultiplier); + + int educationTimeReduction = currentExperienceLevel * perExperienceLevelMultiplier; + if (newEducationTime >= educationTimeReduction) { + trainee.setEduEducationTime(newEducationTime - educationTimeReduction); + targetSkill.setLevel(targetSkill.getLevel() + 1); + + + campaign.addReport(String.format(resources.getString("learnedNewSkill.text"), + commander.getFullTitle(), trainee.getHyperlinkedFullTitle(), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG, targetSkill.getType().getName(), targetSkill.getFinalSkillValue())); + } + } + } + + /** + * Creates a list of skills available for education based on the educators' abilities. + * + *

    This method aggregates the skills of all given educators, taking the highest experience + * level available for each skill among the provided educators.

    + * + * @param campaign the current {@link Campaign} + * @param educators a {@link Set} of {@link Person} objects acting as educators + * @return a {@link Map} of skill names to experience levels representing the available skills + * for teaching + */ + private static Map createSkillsList(Campaign campaign, Set educators) { + Map educatorSkills = new HashMap<>(); + for (Person educator : educators) { + // First, collect all the skills. We use a Set, as we don't want duplicates + Set professionSkills = new HashSet<>(); + + if (educator.getPrimaryRole().isCombat()) { + professionSkills.addAll(educator.getProfessionSkills(campaign, false)); + } + + if (educator.getSecondaryRole().isCombat()) { + professionSkills.addAll(educator.getProfessionSkills(campaign, true)); + } + + // Then, find the best experience level available among educators in the commander's unit + for (String professionSkill : professionSkills) { + Skill skill = educator.getSkill(professionSkill); + + if (skill != null) { + if (!educatorSkills.containsKey(professionSkill)) { + educatorSkills.put(professionSkill, skill.getExperienceLevel()); + } else { + int educatorSkillLevel = educatorSkills.get(professionSkill); + + if (educatorSkillLevel < skill.getExperienceLevel()) { + educatorSkills.put(professionSkill, skill.getExperienceLevel()); + } + } + } + } + } + + return educatorSkills; + } +} diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractPersonnelGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractPersonnelGenerator.java index 7ee60f39dc0..eb1fb7b3d3d 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractPersonnelGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractPersonnelGenerator.java @@ -207,20 +207,32 @@ protected void generatePhenotype(Campaign campaign, Person person) { } /** - * Generates the birthday for a {@link Person}. - * @param campaign The {@link Campaign} which tracks the person. - * @param person The {@link Person} being generated. - * @param expLvl The experience level of {@code person}. - * @param isClanPersonnel A value indicating if {@code person} is from the Clans. + * Generates the birthday for a {@link Person} based on their experience level and affiliation. + *

    + * The method calculates the person's age using {@link Utilities#getAgeByExpLevel(int, boolean)} + * and subtracts it from the current campaign date to determine their year of birth. + * A random day within that year is then selected, ensuring the generated birthday is + * always on or before the current campaign date, so the person's age is accurate. + *

    + * + * @param campaign The {@link Campaign} containing metadata such as the current local date. + * @param person The {@link Person} whose birthday is being generated. + * @param expLvl The experience level of the {@code person}, which determines their age. + * @param isClanPersonnel Indicates whether the {@code person} belongs to the Clans, + * which affects the calculated age. */ protected void generateBirthday(Campaign campaign, Person person, int expLvl, boolean isClanPersonnel) { - LocalDate birthday = campaign.getLocalDate(); - birthday = birthday.minusYears(Utilities.getAgeByExpLevel(expLvl, isClanPersonnel)); + LocalDate currentDate = campaign.getLocalDate(); + int age = Utilities.getAgeByExpLevel(expLvl, isClanPersonnel); + + // Subtract age to get the target year + LocalDate birthday = currentDate.minusYears(age); - // choose a random day and month - int nDays = birthday.isLeapYear() ? 366 : 365; + // Constrain the random day to ensure the birthday is on or before the current date + int daysInYear = birthday.isLeapYear() ? 366 : 365; + int maxDay = Math.min(currentDate.getDayOfYear(), daysInYear); + int randomDay = Compute.randomInt(maxDay) + 1; - int randomDay = Compute.randomInt(nDays) + 1; person.setDateOfBirth(birthday.withDayOfYear(randomDay)); } } diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSkillGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSkillGenerator.java index 5c1c45118a3..cdf833dc105 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSkillGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSkillGenerator.java @@ -65,7 +65,7 @@ public void setSkillPreferences(RandomSkillPreferences skillPreferences) { * @param expLvl The experience level of the person (e.g. * {@link SkillType#EXP_GREEN}). */ - public abstract void generateSkills(final Campaign campaign, final Person person, final int expLvl); + public abstract void generateSkills(Campaign campaign, Person person, int expLvl); /** * Generates the default skills for a {@link Person} based on their primary diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSpecialAbilityGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSpecialAbilityGenerator.java index 397faf24d76..04c6652fb5c 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSpecialAbilityGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSpecialAbilityGenerator.java @@ -57,5 +57,5 @@ public void setSkillPreferences(RandomSkillPreferences skillPreferences) { * @param expLvl The experience level of the person (e.g. {@link SkillType#EXP_GREEN}). * @return A value indicating whether or not a special ability was assigned. */ - public abstract boolean generateSpecialAbilities(final Campaign campaign, final Person person, final int expLvl); + public abstract boolean generateSpecialAbilities(Campaign campaign, Person person, int expLvl); } diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java index bd2c216742e..cf29ec73895 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java @@ -24,9 +24,7 @@ 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; @@ -35,6 +33,8 @@ import java.util.Objects; +import static mekhq.campaign.personnel.education.EducationController.setInitialEducationLevel; + /** * Creates {@link Person} instances using the default MekHQ algorithm. */ @@ -133,12 +133,8 @@ public Person generate(Campaign campaign, PersonnelRole primaryRole, PersonnelRo person.setLoyalty(Compute.d6(3)); } - // set education based on age - if (age < 16) { - person.setEduHighestEducation(EducationLevel.EARLY_CHILDHOOD); - } else { - person.setEduHighestEducation(EducationLevel.HIGH_SCHOOL); - } + // set starting education + setInitialEducationLevel(campaign, person); // generate personality PersonalityController.generatePersonality(person); diff --git a/MekHQ/src/mekhq/campaign/personnel/marriage/AbstractMarriage.java b/MekHQ/src/mekhq/campaign/personnel/marriage/AbstractMarriage.java index b2e0183c50c..ff7be2c4977 100644 --- a/MekHQ/src/mekhq/campaign/personnel/marriage/AbstractMarriage.java +++ b/MekHQ/src/mekhq/campaign/personnel/marriage/AbstractMarriage.java @@ -112,19 +112,34 @@ public void setUseRandomPrisonerMarriages(final boolean useRandomPrisonerMarriag 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()) { + } + + if (person.getGenealogy().hasSpouse()) { return resources.getString("cannotMarry.AlreadyMarried.text"); - } else if (!person.getStatus().isActive()) { + } + + if (!person.getStatus().isActive()) { return resources.getString("cannotMarry.Inactive.text"); - } else if (person.isDeployed()) { + } + + if (person.isDeployed()) { return resources.getString("cannotMarry.Deployed.text"); - } else if (person.isChild(today)) { + } + + // Not allowing under-18s to marry is project policy + if (person.isChild(today, true)) { return resources.getString("cannotMarry.TooYoung.text"); - } else if (!isUseClanPersonnelMarriages() && person.isClanPersonnel()) { + } + + if (!isUseClanPersonnelMarriages() && person.isClanPersonnel()) { return resources.getString("cannotMarry.ClanPersonnel.text"); - } else if (!isUsePrisonerMarriages() && person.getPrisonerStatus().isCurrentPrisoner()) { + } + + if (!isUsePrisonerMarriages() && person.getPrisonerStatus().isCurrentPrisoner()) { return resources.getString("cannotMarry.Prisoner.text"); - } else if (randomMarriage) { + } + + if (randomMarriage) { if (!isUseRandomClanPersonnelMarriages() && person.isClanPersonnel()) { return resources.getString("cannotMarry.RandomClanPersonnel.text"); } else if (!isUseRandomPrisonerMarriages() && person.getPrisonerStatus().isCurrentPrisoner()) { @@ -255,8 +270,9 @@ public static void performMarriageChanges(Campaign campaign, LocalDate today, Pe * @param campaign the campaign to process * @param today the current day * @param person the person to process + * @param isBackground whether the marriage occurred in a character's background */ - public void processNewWeek(final Campaign campaign, final LocalDate today, final Person person) { + public void processNewWeek(final Campaign campaign, final LocalDate today, final Person person, boolean isBackground) { if (canMarry(today, person, true) != null) { return; } @@ -282,7 +298,7 @@ public void processNewWeek(final Campaign campaign, final LocalDate today, final isInterUnit = true; } - marryRandomSpouse(campaign, today, person, isSameSex, isInterUnit, true); + marryRandomSpouse(campaign, today, person, isSameSex, isInterUnit, isBackground); } } @@ -391,7 +407,7 @@ Person createExternalSpouse(final Campaign campaign, final LocalDate today, fina externalSpouse.setDateOfBirth(externalSpouse.getDateOfBirth().minusYears(difference)); } else if (externalSpouseAge > externalSpouseMaxAge) { - int difference = externalSpouseMaxAge - externalSpouseAge; + int difference = externalSpouseAge - externalSpouseMaxAge; externalSpouse.setDateOfBirth(externalSpouse.getDateOfBirth().plusYears(difference)); } diff --git a/MekHQ/src/mekhq/campaign/personnel/procreation/AbstractProcreation.java b/MekHQ/src/mekhq/campaign/personnel/procreation/AbstractProcreation.java index 260489665e4..4891a960b2a 100644 --- a/MekHQ/src/mekhq/campaign/personnel/procreation/AbstractProcreation.java +++ b/MekHQ/src/mekhq/campaign/personnel/procreation/AbstractProcreation.java @@ -33,7 +33,6 @@ 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 mekhq.campaign.universe.Faction; @@ -43,6 +42,8 @@ import java.time.temporal.ChronoUnit; import java.util.*; +import static mekhq.campaign.personnel.education.EducationController.setInitialEducationLevel; + /** * AbstractProcreation is the baseline class for procreation and birth in MekHQ. It holds all the * common logic for procreation, and is implemented by classes defining how to determine if a female @@ -193,43 +194,84 @@ public int determinePregnancyWeek(final LocalDate today, final Person person) { final boolean randomProcreation) { if (person.getGender().isMale()) { return resources.getString("cannotProcreate.Gender.text"); - } else if (!person.isTryingToConceive()) { + } + + if (!person.isTryingToConceive()) { return resources.getString("cannotProcreate.NotTryingForABaby.text"); - } else if (person.isPregnant()) { + } + + if (person.isPregnant()) { return resources.getString("cannotProcreate.AlreadyPregnant.text"); - } else if (!person.getStatus().isActive()) { + } + + if (!person.getStatus().isActive()) { return resources.getString("cannotProcreate.Inactive.text"); - } else if (person.isDeployed()) { + } + + if (person.isDeployed()) { return resources.getString("cannotProcreate.Deployed.text"); - } else if (person.isChild(today)) { + } + + // Not allowing under-18s to procreate is project policy + if (person.isChild(today, true)) { return resources.getString("cannotProcreate.Child.text"); - } else if (person.getAge(today) >= 51) { + } + + if (person.getAge(today) >= 51) { return resources.getString("cannotProcreate.TooOld.text"); - } else if (!isUseClanPersonnelProcreation() && person.isClanPersonnel()) { + } + + if (!isUseClanPersonnelProcreation() && person.isClanPersonnel()) { return resources.getString("cannotProcreate.ClanPersonnel.text"); - } else if (!isUsePrisonerProcreation() && person.getPrisonerStatus().isCurrentPrisoner()) { + } + + if (!isUsePrisonerProcreation() && person.getPrisonerStatus().isCurrentPrisoner()) { return resources.getString("cannotProcreate.Prisoner.text"); - } else if (randomProcreation) { + } + + if (randomProcreation) { if (!isUseRelationshiplessProcreation() && !person.getGenealogy().hasSpouse()) { return resources.getString("cannotProcreate.NoSpouse.text"); - } else if (!isUseRandomClanPersonnelProcreation() && person.isClanPersonnel()) { + } + + if (!isUseRandomClanPersonnelProcreation() && person.isClanPersonnel()) { return resources.getString("cannotProcreate.RandomClanPersonnel.text"); - } else if (!isUseRandomPrisonerProcreation() && person.getPrisonerStatus().isCurrentPrisoner()) { + } + + if (!isUseRandomPrisonerProcreation() && person.getPrisonerStatus().isCurrentPrisoner()) { return resources.getString("cannotProcreate.RandomPrisoner.text"); - } else if (person.getGenealogy().hasSpouse()) { + } + + if (person.getGenealogy().hasSpouse()) { if (person.getGenealogy().getSpouse().getGender().isFemale()) { return resources.getString("cannotProcreate.FemaleSpouse.text"); - } else if (!person.getGenealogy().getSpouse().isTryingToConceive()) { + } + + if (!person.getGenealogy().getSpouse().isTryingToConceive()) { return resources.getString("cannotProcreate.SpouseNotTryingForABaby.text"); - } else if (!person.getGenealogy().getSpouse().getStatus().isActive()) { + } + + if (!person.getGenealogy().getSpouse().getStatus().isActive()) { return resources.getString("cannotProcreate.InactiveSpouse.text"); - } else if (person.getGenealogy().getSpouse().isDeployed()) { + } + + if (person.getGenealogy().getSpouse().isDeployed()) { return resources.getString("cannotProcreate.DeployedSpouse.text"); - } else if (person.getGenealogy().getSpouse().isChild(today)) { + } + + // Not allowing under-18s to procreate is project policy + // This conditional shouldn't be relevant in 2025, due to changes made in 2024. + // However, we're keeping it as we don't want campaigns from pre-policy + // implementation being able to circumnavigate project policy. + if (person.getGenealogy().getSpouse().isChild(today, true)) { return resources.getString("cannotProcreate.ChildSpouse.text"); - } else if (!isUseRandomClanPersonnelProcreation() && person.getGenealogy().getSpouse().isClanPersonnel()) { + } + + if (!isUseRandomClanPersonnelProcreation() && person.getGenealogy().getSpouse().isClanPersonnel()) { return resources.getString("cannotProcreate.ClanPersonnelSpouse.text"); - } else if (!isUseRandomPrisonerProcreation() + } + + if (!isUseRandomPrisonerProcreation() && person.getGenealogy().getSpouse().getPrisonerStatus().isCurrentPrisoner()) { return resources.getString("cannotProcreate.PrisonerSpouse.text"); } @@ -369,9 +411,7 @@ public void birth(final Campaign campaign, final LocalDate today, final Person m campaign.recruitPerson(baby, prisonerStatus, true, true); // if the mother is at school, add the baby to the list of tag alongs - if ((mother.getEduAcademyName() != null) - && (!EducationController.getAcademy(mother.getEduAcademySet(), mother.getEduAcademyNameInSet()).isHomeSchool())) { - + if (mother.getStatus().isStudent()) { mother.addEduTagAlong(baby.getId()); baby.changeStatus(campaign, today, PersonnelStatus.ON_LEAVE); } @@ -391,6 +431,11 @@ public void birth(final Campaign campaign, final LocalDate today, final Person m // Cleanup Data removePregnancy(mother); + + // Return from Maternity leave + if (mother.getStatus().isOnMaternityLeave()) { + mother.changeStatus(campaign, today, PersonnelStatus.ACTIVE); + } } /** @@ -466,11 +511,7 @@ public List birthHistoric(final Campaign campaign, final LocalDate today baby.setLoyalty(Compute.d6(3) + 2); // set education based on age - if(baby.getAge(today) < 16) { - baby.setEduHighestEducation(EducationLevel.EARLY_CHILDHOOD); - } else { - baby.setEduHighestEducation(EducationLevel.HIGH_SCHOOL); - } + setInitialEducationLevel(campaign, baby); // Create reports and log the birth logAndUpdateFamily(campaign, today, mother, baby, father); @@ -558,7 +599,17 @@ public void processNewWeek(final Campaign campaign, final LocalDate today, final // They give birth if the due date has passed if ((today.isAfter(person.getDueDate())) || (today.isEqual(person.getDueDate()))) { birth(campaign, today, person); + + return; } + + if (campaign.getCampaignOptions().isUseMaternityLeave()) { + if (person.getStatus().isActive() + && (person.getDueDate().minusWeeks(20).isAfter(today.minusDays(1)))) { + person.changeStatus(campaign, today, PersonnelStatus.ON_MATERNITY_LEAVE); + } + } + return; } @@ -602,5 +653,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 procreation(final Person person); + protected abstract boolean procreation(Person person); } diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java index 296b994f750..220543d9d97 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java @@ -110,58 +110,6 @@ public boolean isNone() { // endregion Boolean Comparison Methods // region File I/O - /** - * Parses a given string and returns the corresponding Aggression enum. - * Accepts either the ENUM ordinal value or its name - * - * @param aggression the string to be parsed - * @return the Aggression enum that corresponds to the given string - * @throws IllegalStateException if the given string does not match any valid - * Aggression - */ - @Deprecated - public static Aggression parseFromString(final String aggression) { - return switch (aggression) { - case "0", "None" -> NONE; - // Minor Characteristics - case "1", "Bold" -> BOLD; - case "2", "Aggressive" -> AGGRESSIVE; - case "3", "Assertive" -> ASSERTIVE; - case "4", "Belligerent" -> BELLIGERENT; - case "5", "Brash" -> BRASH; - case "6", "Confident" -> CONFIDENT; - case "7", "Courageous" -> COURAGEOUS; - case "8", "Daring" -> DARING; - case "9", "Decisive" -> DECISIVE; - case "10", "Determined" -> DETERMINED; - case "11", "Domineering" -> DOMINEERING; - case "12", "Fearless" -> FEARLESS; - case "13", "Hostile" -> HOSTILE; - case "14", "Hot-Headed" -> HOT_HEADED; - case "15", "Impetuous" -> IMPETUOUS; - case "16", "Impulsive" -> IMPULSIVE; - case "17", "Inflexible" -> INFLEXIBLE; - case "18", "Intrepid" -> INTREPID; - case "19", "Overbearing" -> OVERBEARING; - case "20", "Reckless" -> RECKLESS; - case "21", "Resolute" -> RESOLUTE; - case "22", "Stubborn" -> STUBBORN; - case "23", "Tenacious" -> TENACIOUS; - case "24", "Vigilant" -> VIGILANT; - // Major Characteristics - case "25", "Bloodthirsty" -> BLOODTHIRSTY; - case "26", "Diplomatic" -> DIPLOMATIC; - case "27", "Murderous" -> MURDEROUS; - case "28", "Pacifistic" -> PACIFISTIC; - case "29", "Sadistic" -> SADISTIC; - case "30", "Savage" -> SAVAGE; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/Aggression.java/parseFromString: " - + aggression); - }; - } - /** * Returns the {@link Aggression} associated with the given ordinal. * @@ -170,13 +118,11 @@ public static Aggression parseFromString(final String aggression) { * {@code NONE} if not found */ public static Aggression fromOrdinal(int ordinal) { - for (Aggression aggression : values()) { - if (aggression.ordinal() == ordinal) { - return aggression; - } + if ((ordinal >= 0) && (ordinal < values().length)) { + return values()[ordinal]; } - final MMLogger logger = MMLogger.create(Aggression.class); + MMLogger logger = MMLogger.create(Aggression.class); logger.error(String.format("Unknown Aggression ordinal: %s - returning NONE.", ordinal)); return NONE; diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java index 7f68f04fad1..dcc91418daf 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java @@ -108,58 +108,6 @@ public boolean isNone() { // endregion Boolean Comparison Methods // region File I/O - /** - * Parses a given string and returns the corresponding Ambition enum. - * Accepts either the ENUM ordinal value or its name - * - * @param ambition the string to be parsed - * @return the Ambition enum that corresponds to the given string - * @throws IllegalStateException if the given string does not match any valid - * Ambition - */ - @Deprecated - public static Ambition parseFromString(final String ambition) { - return switch (ambition) { - case "0", "None" -> NONE; - // Minor Characteristics - case "1", "Ambitious" -> AMBITIOUS; - case "2", "Arrogant" -> ARROGANT; - case "3", "Aspiring" -> ASPIRING; - case "4", "Calculating" -> CALCULATING; - case "5", "Conniving" -> CONNIVING; - case "6", "Controlling" -> CONTROLLING; - case "7", "Cutthroat" -> CUTTHROAT; - case "8", "Diligent" -> DILIGENT; - case "9", "Driven" -> DRIVEN; - case "10", "Energetic" -> ENERGETIC; - case "11", "Excessive" -> EXCESSIVE; - case "12", "Focused" -> FOCUSED; - case "13", "Goal-Oriented" -> GOAL_ORIENTED; - case "14", "Motivated" -> MOTIVATED; - case "15", "Opportunistic" -> OPPORTUNISTIC; - case "16", "Overconfident" -> OVERCONFIDENT; - case "17", "Persistent" -> PERSISTENT; - case "18", "Proactive" -> PROACTIVE; - case "19", "Resilient" -> RESILIENT; - case "20", "Ruthless" -> RUTHLESS; - case "21", "Selfish" -> SELFISH; - case "22", "Strategic" -> STRATEGIC; - case "23", "Unambitious" -> UNAMBITIOUS; - case "24", "Unscrupulous" -> UNSCRUPULOUS; - // Major Characteristics - case "25", "Dishonest" -> DISHONEST; - case "26", "Innovative" -> INNOVATIVE; - case "27", "Manipulative" -> MANIPULATIVE; - case "28", "Resourceful" -> RESOURCEFUL; - case "29", "Tyrannical" -> TYRANNICAL; - case "30", "Visionary" -> VISIONARY; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/Ambition.java/parseFromString: " - + ambition); - }; - } - /** * Returns the {@link Ambition} associated with the given ordinal. * @@ -168,13 +116,11 @@ public static Ambition parseFromString(final String ambition) { * {@code NONE} if not found */ public static Ambition fromOrdinal(int ordinal) { - for (Ambition ambition : values()) { - if (ambition.ordinal() == ordinal) { - return ambition; - } + if ((ordinal >= 0) && (ordinal < values().length)) { + return values()[ordinal]; } - final MMLogger logger = MMLogger.create(Ambition.class); + MMLogger logger = MMLogger.create(Ambition.class); logger.error(String.format("Unknown Ambition ordinal: %s - returning NONE.", ordinal)); return NONE; diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java index e63bc71c945..bd713a15848 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java @@ -108,58 +108,6 @@ public boolean isNone() { // endregion Boolean Comparison Methods // region File I/O - /** - * Parses a given string and returns the corresponding Greed enum. - * Accepts either the ENUM ordinal value, or its name - * - * @param greed the string to be parsed - * @return the Greed enum that corresponds to the given string - * @throws IllegalStateException if the given string does not match any valid - * Greed - */ - @Deprecated - public static Greed parseFromString(final String greed) { - return switch (greed) { - case "0", "None" -> NONE; - // Minor Characteristics - case "1", "Astute" -> ASTUTE; - case "2", "Adept" -> ADEPT; - case "3", "Avaricious" -> AVARICIOUS; - case "4", "Dynamic" -> DYNAMIC; - case "5", "Eager" -> EAGER; - case "6", "Exploitative" -> EXPLOITATIVE; - case "7", "Fraudulent" -> FRAUDULENT; - case "8", "Generous" -> GENEROUS; - case "9", "Greedy" -> GREEDY; - case "10", "Hoarding" -> HOARDING; - case "11", "Insatiable" -> INSATIABLE; - case "12", "Insightful" -> INSIGHTFUL; - case "13", "Judicious" -> JUDICIOUS; - case "14", "Lustful" -> LUSTFUL; - case "15", "Mercenary" -> MERCENARY; - case "16", "Overreaching" -> OVERREACHING; - case "17", "Profitable" -> PROFITABLE; - case "18", "Savvy" -> SAVVY; - case "19", "Self-Serving" -> SELF_SERVING; - case "20", "Shameless" -> SHAMELESS; - case "21", "Shrewd" -> SHREWD; - case "22", "Tactical" -> TACTICAL; - case "23", "Unprincipled" -> UNPRINCIPLED; - case "24", "Voracious" -> VORACIOUS; - // Major Characteristics - case "25", "Corrupt" -> CORRUPT; - case "26", "Enterprising" -> ENTERPRISING; - case "27", "Intuitive" -> INTUITIVE; - case "28", "Meticulous" -> METICULOUS; - case "29", "Nefarious" -> NEFARIOUS; - case "30", "Thief" -> THIEF; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/Greed.java/parseFromString: " - + greed); - }; - } - /** * Returns the {@link Greed} associated with the given ordinal. * @@ -168,13 +116,11 @@ public static Greed parseFromString(final String greed) { * {@code NONE} if not found */ public static Greed fromOrdinal(int ordinal) { - for (Greed greed : values()) { - if (greed.ordinal() == ordinal) { - return greed; - } + if ((ordinal >= 0) && (ordinal < values().length)) { + return values()[ordinal]; } - final MMLogger logger = MMLogger.create(Greed.class); + MMLogger logger = MMLogger.create(Greed.class); logger.error(String.format("Unknown Greed ordinal: %s - returning NONE.", ordinal)); return NONE; diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Intelligence.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Intelligence.java index f05c9b31b54..83cb6a9f104 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Intelligence.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Intelligence.java @@ -80,50 +80,6 @@ public boolean isAverage() { // endregion Boolean Comparison Methods // region File I/O - /** - * Parses a given string and returns the corresponding Quirk enum. - * Accepts either the ENUM ordinal value, or its name - * - * @param quirk the string to be parsed - * @return the Greed enum that corresponds to the given string - * @throws IllegalStateException if the given string does not match any valid - * Quirk - */ - @Deprecated - public static Intelligence parseFromString(final String quirk) { - return switch (quirk) { - case "0", "Brain Dead" -> BRAIN_DEAD; - case "1", "Unintelligent" -> UNINTELLIGENT; - case "2", "Feeble Minded", "Foolish" -> FOOLISH; - case "3", "Simple" -> SIMPLE; - case "4", "Slow to Comprehend", "Slow" -> SLOW; - case "5", "Uninspired" -> UNINSPIRED; - case "6", "Dull" -> DULL; - case "7", "Dimwitted" -> DIMWITTED; - case "8", "Obtuse" -> OBTUSE; - case "9", "Below Average" -> BELOW_AVERAGE; - case "10", "Under Performing" -> UNDER_PERFORMING; - case "11", "Limited Insight" -> LIMITED_INSIGHT; - case "12", "Average" -> AVERAGE; - case "13", "Above Average" -> ABOVE_AVERAGE; - case "14", "Studious" -> STUDIOUS; - case "15", "Discerning" -> DISCERNING; - case "16", "Sharp" -> SHARP; - case "17", "Quick-Witted" -> QUICK_WITTED; - case "18", "Perceptive" -> PERCEPTIVE; - case "19", "Bright" -> BRIGHT; - case "20", "Clever" -> CLEVER; - case "21", "Intellectual" -> INTELLECTUAL; - case "22", "Brilliant" -> BRILLIANT; - case "23", "Exceptional" -> EXCEPTIONAL; - case "24", "Genius" -> GENIUS; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/PersonalityQuirk.java/parseFromString: " - + quirk); - }; - } - /** * Returns the {@link Intelligence} associated with the given ordinal. * @@ -132,13 +88,11 @@ public static Intelligence parseFromString(final String quirk) { * {@code AVERAGE} if not found */ public static Intelligence fromOrdinal(int ordinal) { - for (Intelligence intelligence : values()) { - if (intelligence.ordinal() == ordinal) { - return intelligence; - } + if ((ordinal >= 0) && (ordinal < values().length)) { + return values()[ordinal]; } - final MMLogger logger = MMLogger.create(Intelligence.class); + MMLogger logger = MMLogger.create(Intelligence.class); logger.error(String.format("Unknown Intelligence ordinal: %s - returning AVERAGE.", ordinal)); return AVERAGE; diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/PersonalityQuirk.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/PersonalityQuirk.java index b78e4c42c23..84b5df107cd 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/PersonalityQuirk.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/PersonalityQuirk.java @@ -273,127 +273,6 @@ public boolean isNone() { // endregion Boolean Comparison Methods // region File I/O - /** - * Parses a given string and returns the corresponding Quirk enum. - * Accepts either the ENUM ordinal value, or its name - * - * @param quirk the string to be parsed - * @return the Greed enum that corresponds to the given string - * @throws IllegalStateException if the given string does not match any valid - * Quirk - */ - @Deprecated - public static PersonalityQuirk parseFromString(final String quirk) { - return switch (quirk) { - case "0", "none" -> NONE; - case "1", "Constantly Adjusting Clothes" -> ADJUSTS_CLOTHES; - case "2", "Overly Affectionate" -> AFFECTIONATE; - case "3", "Overly Apologetic" -> APOLOGETIC; - case "4", "Always Reading" -> BOOKWORM; - case "5", "Keeps a Personal Calendar" -> CALENDAR; - case "6", "Fond of Scented Candles" -> CANDLES; - case "7", "Always Chewing Gum" -> CHEWING_GUM; - case "8", "Chronically Late" -> CHRONIC_LATENESS; - case "9", "Compulsive Cleaner" -> CLEANER; - case "10", "Collects Odd Items" -> COLLECTOR; - case "11", "Competitive Nature" -> COMPETITIVE_NATURE; - case "12", "Excessive Complimenter" -> COMPLIMENTS; - case "13", "Prone to Daydreaming" -> DAYDREAMER; - case "14", "Compulsive Doodler" -> DOODLER; - case "15", "Talks to Animals" -> DOOLITTLE; - case "16", "Overly Dramatic" -> DRAMATIC; - case "17", "Unpredictable Eating Habits" -> EATING_HABITS; - case "18", "Extreme Environmental Sensitivity" -> ENVIRONMENTAL_SENSITIVITY; - case "19", "Excessive Caution" -> EXCESSIVE_CAUTION; - case "20", "Over-the-Top Greetings" -> EXCESSIVE_GREETING; - case "21", "Intense Eye Contact" -> EYE_CONTACT; - case "22", "Eccentric Fashion Choices" -> FASHION_CHOICES; - case "23", "Constantly Fidgeting" -> FIDGETS; - case "24", "Extreme Personal Fitness Routine" -> FITNESS; - case "25", "Fixates on One Topic" -> FIXATES; - case "26", "Carries a Flask" -> FLASK; - case "27", "Always Tapping Foot" -> FOOT_TAPPER; - case "28", "Chronically Forgetful" -> FORGETFUL; - case "29", "Overly Formal Speech" -> FORMAL_SPEECH; - case "30", "Constantly Rearranges Furniture" -> FURNITURE; - case "31", "Constantly Adjusts Glasses" -> GLASSES; - case "32", "Always Wearing Gloves" -> GLOVES; - case "33", "Excessive Hand Gestures" -> HAND_GESTURES; - case "34", "Compulsive Hand-Wringer" -> HAND_WRINGER; - case "35", "Overly Enthusiastic Handshake" -> HANDSHAKE; - case "36", "Always Wearing Headphones" -> HEADPHONES; - case "37", "Frequently Snacking on Healthy Foods" -> HEALTHY_SNACKS; - case "38", "Passionate about History" -> HISTORIAN; - case "39", "Habitual Hummer" -> HUMMER; - case "40", "Obsessed with Hygiene" -> HYGIENIC; - case "41", "Unusual Sleep Patterns" -> IRREGULAR_SLEEPER; - case "42", "Fond of Puns" -> JOKER; - case "43", "Compulsive List Maker" -> LISTS; - case "44", "Overly Literal" -> LITERAL; - case "45", "Checks Locks Repeatedly" -> LOCKS; - case "46", "Tends to Speak in a Measured Pace" -> MEASURED_TALKER; - case "47", "Extreme Minimalism" -> MINIMALIST; - case "48", "Prefers Using a Specific Mug" -> MUG; - case "49", "Constant Nail Biter" -> NAIL_BITER; - case "50", "Frequent Nicknaming" -> NICKNAMING; - case "51", "Night Owl" -> NIGHT_OWL; - case "52", "Compulsive Note-Taking" -> NOTE_TAKER; - case "53", "Always Carrying a Notebook" -> NOTEBOOK; - case "54", "Carries a Personal Object" -> OBJECT; - case "55", "Obsessive Organizational Tendencies" -> ORGANIZATIONAL_TENDENCIES; - case "56", "Always Organizing" -> ORGANIZER; - case "57", "Fond of Origami" -> ORIGAMI; - case "58", "Obsessive Over-Planner" -> OVER_PLANNER; - case "59", "Chronic Overexplainer" -> OVEREXPLAINER; - case "60", "abitual Pen Clicker" -> PEN_CLICKER; - case "61", "Habitual Pen Twirler" -> PEN_TWIRLER; - case "62", "Overly Friendly with Equipment" -> PERSONIFICATION; - case "63", "Habitual Pessimist" -> PESSIMIST; - case "64", "Tends to Use Specific Phrases" -> PHRASES; - case "65", "Loves Plants" -> PLANTS; - case "66", "Excessive Politeness" -> POLITE; - case "67", "Loves Practical Jokes" -> PRACTICAL_JOKER; - case "68", "Always Prepared" -> PREPARED; - case "69", "Overly Punctual" -> PUNCTUAL; - case "70", "Obsessed with Puzzles" -> PUZZLES; - case "71", "Collects Quotes" -> QUOTES; - case "72", "Rarely Sleeps" -> RARELY_SLEEPS; - case "73", "Has a Routine for Small Tasks" -> ROUTINE; - case "74", "Constantly Seeking Approval" -> SEEKS_APPROVAL; - case "75", "Overly Sentimental" -> SENTIMENTAL; - case "76", "Compulsive Sharpening" -> SHARPENING; - case "77", "Sings to Themselves" -> SINGS; - case "78", "Chronically Skeptical" -> SKEPTICAL; - case "79", "Talks in Sleep" -> SLEEP_TALKER; - case "80", "Compulsive Smiler" -> SMILER; - case "81", "Always Has a Snack" -> SNACKS; - case "82", "Compulsive Storytelling" -> STORYTELLING; - case "83", "Constantly Stretching" -> STRETCHING; - case "84", "Superstitious Rituals" -> SUPERSTITIOUS_RITUALS; - case "85", "Highly Supervised Habits" -> SUPERVISED_HABITS; - case "86", "Incessant Tech Talk" -> TECH_TALK; - case "87", "Phobia of Technology" -> TECHNOPHOBIA; - case "88", "Uses Obscure Words" -> THESAURUS; - case "89", "Speaks in Third Person" -> THIRD_PERSON; - case "90", "Obsessed with Time Management" -> TIME_MANAGEMENT; - case "91", "Compulsive Tinkerer" -> TINKERER; - case "92", "Habitual Truth-Teller" -> TRUTH_TELLER; - case "93", "Unnecessary Caution" -> UNNECESSARY_CAUTION; - case "94", "Unpredictable Speech" -> UNPREDICTABLE_SPEECH; - case "95", "Unusual Hobbies" -> UNUSUAL_HOBBIES; - case "96", "Constantly Checking the Time" -> WATCH; - case "97", "Obsessed with Weather" -> WEATHERMAN; - case "98", "Frequent Whistler" -> WHISTLER; - case "99", "Persistent Worrier" -> WORRIER; - case "100", "Writes Everything Down" -> WRITER; - case "101", "Constantly Reminiscing" -> BATTLEFIELD_NOSTALGIA; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/PersonalityQuirk.java/parseFromString: " - + quirk); - }; - } - /** * Returns the {@link PersonalityQuirk} associated with the given ordinal. * @@ -402,13 +281,11 @@ public static PersonalityQuirk parseFromString(final String quirk) { * {@code NONE} if not found */ public static PersonalityQuirk fromOrdinal(int ordinal) { - for (PersonalityQuirk quirk : values()) { - if (quirk.ordinal() == ordinal) { - return quirk; - } + if ((ordinal >= 0) && (ordinal < values().length)) { + return values()[ordinal]; } - final MMLogger logger = MMLogger.create(PersonalityQuirk.class); + MMLogger logger = MMLogger.create(PersonalityQuirk.class); logger.error(String.format("Unknown PersonalityQuirk ordinal: %s - returning NONE.", ordinal)); return NONE; diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Social.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Social.java index 0c958983fe6..f375515ef89 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Social.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Social.java @@ -108,58 +108,6 @@ public boolean isNone() { // endregion Boolean Comparison Methods // region File I/O - /** - * Parses a given string and returns the corresponding Social enum. - * Accepts either the ENUM ordinal value or its name - * - * @param social the string to be parsed - * @return the Social enum that corresponds to the given string - * @throws IllegalStateException if the given string does not match any valid - * Social - */ - @Deprecated - public static Social parseFromString(final String social) { - return switch (social) { - case "0", "None" -> NONE; - // Minor Characteristics - case "1", "Apathetic" -> APATHETIC; - case "2", "Authentic" -> AUTHENTIC; - case "3", "Blunt" -> BLUNT; - case "4", "Callous" -> CALLOUS; - case "5", "Condescending" -> CONDESCENDING; - case "6", "Considerate" -> CONSIDERATE; - case "7", "Disingenuous" -> DISINGENUOUS; - case "8", "Dismissive" -> DISMISSIVE; - case "9", "Encouraging" -> ENCOURAGING; - case "10", "Erratic" -> ERRATIC; - case "11", "Empathetic" -> EMPATHETIC; - case "12", "Friendly" -> FRIENDLY; - case "13", "Inspiring" -> INSPIRING; - case "14", "Indifferent" -> INDIFFERENT; - case "15", "Introverted" -> INTROVERTED; - case "16", "Irritable" -> IRRITABLE; - case "17", "Neglectful" -> NEGLECTFUL; - case "18", "Petty" -> PETTY; - case "19", "Persuasive" -> PERSUASIVE; - case "20", "Receptive" -> RECEPTIVE; - case "21", "Sincere" -> SINCERE; - case "22", "Supportive" -> SUPPORTIVE; - case "23", "Tactful" -> TACTFUL; - case "24", "Untrustworthy" -> UNTRUSTWORTHY; - // Major Characteristics - case "25", "Altruistic" -> ALTRUISTIC; - case "26", "Compassionate" -> COMPASSIONATE; - case "27", "Gregarious" -> GREGARIOUS; - case "28", "Narcissistic" -> NARCISSISTIC; - case "29", "Pompous" -> POMPOUS; - case "30", "Scheming" -> SCHEMING; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/Social.java/parseFromString: " - + social); - }; - } - /** * Returns the {@link Social} associated with the given ordinal. * @@ -168,13 +116,11 @@ public static Social parseFromString(final String social) { * {@code NONE} if not found */ public static Social fromOrdinal(int ordinal) { - for (Social social : values()) { - if (social.ordinal() == ordinal) { - return social; - } + if ((ordinal >= 0) && (ordinal < values().length)) { + return values()[ordinal]; } - final MMLogger logger = MMLogger.create(Social.class); + MMLogger logger = MMLogger.create(Social.class); logger.error(String.format("Unknown Social ordinal: %s - returning NONE.", ordinal)); return NONE; diff --git a/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/AverageExperienceRating.java b/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/AverageExperienceRating.java index 74e33129045..fd7c71a82e3 100644 --- a/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/AverageExperienceRating.java +++ b/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/AverageExperienceRating.java @@ -239,6 +239,10 @@ private static double calculateRegularExperience(Person person, Entity entity, U skillCount++; } + if (skillCount == 0) { + return 0; + } + return (double) skillValue / skillCount; } } diff --git a/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/ReputationController.java b/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/ReputationController.java index 04f7694ed22..ff49c62dfaa 100644 --- a/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/ReputationController.java +++ b/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/ReputationController.java @@ -615,7 +615,8 @@ public ReputationController generateInstanceFromXML(final Node workingNode) { if (workingNode2.getNodeName().equalsIgnoreCase("averageSkillLevel")) { this.averageSkillLevel = SkillLevel.valueOf(workingNode2.getTextContent().toUpperCase()); } else if (workingNode2.getNodeName().equalsIgnoreCase("averageExperienceRating")) { - this.averageExperienceRating = Integer.parseInt(workingNode2.getTextContent()); + this.averageExperienceRating = Integer.parseInt( + workingNode2.getTextContent().replaceAll("-", "_")); } else if (workingNode2.getNodeName().equalsIgnoreCase("atbModifier")) { this.atbModifier = Integer.parseInt(workingNode2.getTextContent()); } else if (workingNode2.getNodeName().equalsIgnoreCase("commanderMap")) { diff --git a/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/TransportationRating.java b/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/TransportationRating.java index 663dd065c9e..b19a052b653 100644 --- a/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/TransportationRating.java +++ b/MekHQ/src/mekhq/campaign/rating/CamOpsReputation/TransportationRating.java @@ -71,8 +71,8 @@ public static List> calculateTransportationRating(Campaign // The above logic is repeated for the remaining entities // ASF - capacity = transportationCapacities.get("asfBays"); - requirements = Math.max(0, transportationRequirements.get("asfCount") - spareCapacity); + capacity = transportationCapacities.get("asfBays") + spareCapacity; + requirements = transportationRequirements.get("asfCount"); rating = calculateRating(capacity, requirements); capacityRating = getCapacityRating(rating, capacityRating); @@ -94,20 +94,19 @@ public static List> calculateTransportationRating(Campaign spareCapacity = Math.max(0, capacity - requirements); // Heavy Vehicles - capacity = transportationCapacities.get("heavyVehicleBays"); - requirements = Math.max(0, transportationRequirements.get("heavyVehicleCount") - spareCapacity); + capacity = transportationCapacities.get("heavyVehicleBays") + spareCapacity; + requirements = transportationRequirements.get("heavyVehicleCount"); rating = calculateRating(capacity, requirements); capacityRating = getCapacityRating(rating, capacityRating); // as this spare capacity can also be used by light vehicles, // we need to track the remaining spare capacity - spareCapacity -= Math.max(0, transportationRequirements.get("heavyVehicleCount")); spareCapacity += Math.max(0, capacity - requirements); // Light Vehicles - capacity = transportationCapacities.get("lightVehicleBays"); - requirements = Math.max(0, transportationRequirements.get("lightVehicleCount") - spareCapacity); + capacity = transportationCapacities.get("lightVehicleBays") + spareCapacity; + requirements = transportationRequirements.get("lightVehicleCount"); rating = calculateRating(capacity, requirements); capacityRating = getCapacityRating(rating, capacityRating); @@ -174,7 +173,8 @@ public static List> calculateTransportationRating(Campaign // Docking Collar Requirements int dockingCollarCount = transportationCapacities.get("dockingCollars"); - if ((dockingCollarCount > 0) && (dockingCollarCount >= transportationRequirements.get("dropShipCount"))) { + if ((dockingCollarCount >= 0) + && (dockingCollarCount >= transportationRequirements.get("dropShipCount"))) { transportationRating += 5; logger.debug("Exceeding docking collar requirements: +5"); } @@ -228,7 +228,7 @@ protected static int calculateRating(int capacity, int requirements) { if (usage < 0) { return BELOW_CAPACITY; - } else if (usage > requirements * 2) { + } else if (usage >= requirements * 2) { return DOUBLE_CAPACITY; } else { return AT_CAPACITY; @@ -261,8 +261,7 @@ private static Map calculateTransportationCapacities(Campaign c Entity entity = unit.getEntity(); // Skip the unit if it doesn't meet the specific criteria - if (!(entity.isDropShip()) && !(entity.isJumpShip()) - && !(entity.isWarShip()) && !(entity.isSmallCraft())) { + if (!entity.isSmallCraft() && !entity.isLargeCraft()) { continue; } @@ -304,6 +303,8 @@ private static Map calculateTransportationCapacities(Campaign c passengerCapacity += bay.getPersonnel(entity.isClan()); } + + passengerCapacity += entity.getNPassenger(); } // Map the capacity of each bay type diff --git a/MekHQ/src/mekhq/campaign/storyarc/storypoint/CreateCharacterStoryPoint.java b/MekHQ/src/mekhq/campaign/storyarc/storypoint/CreateCharacterStoryPoint.java index 599bfe6201d..def9a7364fe 100644 --- a/MekHQ/src/mekhq/campaign/storyarc/storypoint/CreateCharacterStoryPoint.java +++ b/MekHQ/src/mekhq/campaign/storyarc/storypoint/CreateCharacterStoryPoint.java @@ -33,7 +33,6 @@ import mekhq.campaign.personnel.PersonnelOptions; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.backgrounds.BackgroundsController; -import mekhq.campaign.personnel.education.EducationController; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.Phenotype; import mekhq.campaign.personnel.generator.AbstractSkillGenerator; @@ -54,6 +53,8 @@ import java.util.Enumeration; import java.util.UUID; +import static mekhq.campaign.personnel.education.EducationController.setInitialEducationLevel; + /** * This StoryPoint opens a {@link CreateCharacterDialog CreateCharacterDialog} * which allows a player to create a new @@ -137,28 +138,28 @@ public Person createPerson() { if (null == faction) { faction = campaign.getFaction(); } - Person p = new Person(campaign, faction.getShortName()); + Person person = new Person(campaign, faction.getShortName()); if (null != primaryRole) { - p.setPrimaryRole(campaign, primaryRole); + person.setPrimaryRole(campaign, primaryRole); } - p.setClanPersonnel(clan); - if (p.isClanPersonnel() && null != phenotype) { - p.setPhenotype(phenotype); + person.setClanPersonnel(clan); + if (person.isClanPersonnel() && null != phenotype) { + person.setPhenotype(phenotype); } - p.setCommander(commander); - p.setGivenName(firstname); - p.setSurname(surname); - p.setBloodname(bloodname); - p.setBiography(biography); - p.setRank(rank); + person.setCommander(commander); + person.setGivenName(firstname); + person.setSurname(surname); + person.setBloodname(bloodname); + person.setBiography(biography); + person.setRank(rank); if (edge > 0) { - p.changeEdge(edge); - setEdgeTriggers(p); + person.changeEdge(edge); + setEdgeTriggers(person); } if (null != personId) { - p.setId(personId); + person.setId(personId); } // We need to generate basic skills in order to get any phenotype bonuses @@ -177,20 +178,20 @@ public Person createPerson() { skillPrefs.setCombatSmallArmsBonus(-12); skillPrefs.setSupportSmallArmsBonus(-12); AbstractSkillGenerator skillGenerator = new DefaultSkillGenerator(skillPrefs); - skillGenerator.generateSkills(getCampaign(), p, SkillType.EXP_ULTRA_GREEN); - - p.setDateOfBirth(getCampaign().getLocalDate().minusYears(age)); + skillGenerator.generateSkills(getCampaign(), person, SkillType.EXP_ULTRA_GREEN); - // set education - EducationController.setInitialEducation(campaign, p); + person.setDateOfBirth(getCampaign().getLocalDate().minusYears(age)); // generate background - BackgroundsController.generateBackground(campaign, p); + BackgroundsController.generateBackground(campaign, person); // generate personality - PersonalityController.generatePersonality(p); + PersonalityController.generatePersonality(person); + + // set education + setInitialEducationLevel(campaign, person); - return p; + return person; } @Override diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconCampaignState.java b/MekHQ/src/mekhq/campaign/stratcon/StratconCampaignState.java index 17eaa6c5b9f..8a65904e1ef 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconCampaignState.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconCampaignState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2019-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -28,8 +28,11 @@ import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.adapters.XmlAdapter; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import megamek.common.annotations.Nullable; import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.AtBScenario; import org.w3c.dom.Node; import javax.xml.namespace.QName; @@ -176,6 +179,15 @@ public void useSupportPoint() { supportPoints--; } + /** + * Decreases the number of support points by the specified decrement. + * + * @param decrement The number of support points to use/decrease. + */ + public void useSupportPoints(int decrement) { + supportPoints -= decrement; + } + /** * Convenience/speed method of determining whether or not a force with the given * ID has been deployed to a track in this campaign. @@ -203,6 +215,48 @@ public void removeStratconScenario(int scenarioID) { } } + /** + * Retrieves the {@link StratconScenario} associated with a given {@link AtBScenario}. + * + *

    + * This method searches through all {@link StratconTrackState} objects in the {@link StratconCampaignState} + * to find the first {@link StratconScenario} whose backing scenario matches the specified {@link AtBScenario}. + * If no such scenario is found, it returns {@code null}. + *

    + * + * Usage: + *

    + * Use this method to easily fetch the {@link StratconScenario} associated with the provided + * {@link AtBScenario}. + *

    + * + * @param campaign The {@link Campaign} containing the data to search through. + * @param scenario The {@link AtBScenario} to find the corresponding {@link StratconScenario} for. + * @return The matching {@link StratconScenario}, or {@code null} if no corresponding scenario is found. + */ + public static @Nullable StratconScenario getStratconScenarioFromAtBScenario(Campaign campaign, + AtBScenario scenario) { + AtBContract contract = scenario.getContract(campaign); + if (contract == null) { + return null; + } + + StratconCampaignState campaignState = contract.getStratconCampaignState(); + if (campaignState == null) { + return null; + } + + for (StratconTrackState track : campaignState.getTracks()) { + for (StratconScenario stratConScenario : track.getScenarios().values()) { + if (scenario.equals(stratConScenario.getBackingScenario())) { + return stratConScenario; // Return the first matching scenario if found + } + } + } + + return null; + } + /** * Serialize this instance of a campaign state to a PrintWriter * Omits initial xml declaration diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 1918ce15e04..72f0b0d5b23 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -38,6 +38,7 @@ import java.util.Random; import static java.lang.Math.min; +import static mekhq.campaign.stratcon.SupportPointNegotiation.negotiateInitialSupportPoints; /** * This class handles StratCon state initialization when a contract is signed. @@ -74,7 +75,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // scenarios // when objective is allied/hostile facility, place those facilities - int maximumTrackIndex = Math.max(0, contract.getRequiredLances() / NUM_LANCES_PER_TRACK); + int maximumTrackIndex = Math.max(0, contract.getRequiredCombatTeams() / NUM_LANCES_PER_TRACK); int planetaryTemperature = campaign.getLocation().getPlanet().getTemperature(campaign.getLocalDate()); for (int x = 0; x < maximumTrackIndex; x++) { @@ -92,7 +93,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // a campaign will have X tracks going at a time, where // X = # required lances / 3, rounded up. The last track will have fewer // required lances. - int oddLanceCount = contract.getRequiredLances() % NUM_LANCES_PER_TRACK; + int oddLanceCount = contract.getRequiredCombatTeams() % NUM_LANCES_PER_TRACK; if (oddLanceCount > 0) { int scenarioOdds = contractDefinition.getScenarioOdds() .get(Compute.randomInt(contractDefinition.getScenarioOdds().size())); @@ -108,7 +109,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // now seed the tracks with objectives and facilities for (ObjectiveParameters objectiveParams : contractDefinition.getObjectiveParameters()) { int objectiveCount = objectiveParams.objectiveCount > 0 ? (int) objectiveParams.objectiveCount - : (int) Math.max(1, -objectiveParams.objectiveCount * contract.getRequiredLances()); + : (int) Math.max(1, -objectiveParams.objectiveCount * contract.getRequiredCombatTeams()); List trackObjects = trackObjectDistribution(objectiveCount, campaignState.getTracks().size()); @@ -165,7 +166,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // non-objective allied facilities int facilityCount = contractDefinition.getAlliedFacilityCount() > 0 ? (int) contractDefinition.getAlliedFacilityCount() - : (int) (-contractDefinition.getAlliedFacilityCount() * contract.getRequiredLances()); + : (int) (-contractDefinition.getAlliedFacilityCount() * contract.getRequiredCombatTeams()); List trackObjects = trackObjectDistribution(facilityCount, campaignState.getTracks().size()); @@ -179,7 +180,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // non-objective hostile facilities facilityCount = contractDefinition.getHostileFacilityCount() > 0 ? (int) contractDefinition.getHostileFacilityCount() - : (int) (-contractDefinition.getHostileFacilityCount() * contract.getRequiredLances()); + : (int) (-contractDefinition.getHostileFacilityCount() * contract.getRequiredCombatTeams()); trackObjects = trackObjectDistribution(facilityCount, campaignState.getTracks().size()); @@ -202,7 +203,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai } // Determine starting morale - if (contract.getContractType().isGarrisonType()) { + if (contract.getContractType().isGarrisonDuty()) { contract.setMoraleLevel(AtBMoraleLevel.ROUTED); LocalDate routEnd = contract.getStartDate().plusMonths(Math.max(1, Compute.d6() - 3)).minusDays(1); @@ -222,7 +223,27 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai } // Determine starting Support Points - campaign.negotiateAdditionalSupportPoints(); + negotiateInitialSupportPoints(campaign, contract); + + // Roll to see if a hidden cache is present + if (campaign.getLocalDate().isAfter(LocalDate.of(2900, 1, 1))) { +// if (Compute.randomInt(100) == 0) { +// ScenarioTemplate template = ScenarioTemplate.Deserialize( +// "data/scenariotemplates/Chasing a Rumor.xml"); +// +// if (template != null) { +// StratconScenario hiddenCache = addHiddenExternalScenario(campaign, contract, +// null, template, false); +// +// if (hiddenCache != null) { +// logger.info(String.format("A secret cache has been spawned for contract %s", +// contract.getName())); +// } +// } else { +// logger.error("'Chasing a Rumor' scenario failed to deserialize"); +// } +// } + } // now we're done } @@ -346,8 +367,31 @@ private static void initializeTrackFacilities(StratconTrackState trackState, int } /** - * Worker function that takes a trackstate and plops down the given number of - * facilities owned by the given faction + * Initializes and populates a StratCon track with a specified number of objective scenarios. + * This method selects scenario templates, places them on the track in unoccupied coordinates, + * and optionally assigns facilities and objectives based on predefined rules. + * + *

    The key steps of this method include: + *

      + *
    • Selecting scenario templates from the provided list of objective scenarios.
    • + *
    • Identifying unoccupied coordinates on the track to place each scenario.
    • + *
    • Adding facilities if the scenario template requires them (hostile or allied).
    • + *
    • Generating and configuring scenarios with relevant attributes and modifiers:
    • + *
        + *
      • Clearing scenario dates to maintain persistence.
      • + *
      • Marking scenarios as strategic objectives.
      • + *
      • Adding optional modifiers to provide additional effects or conditions.
      • + *
      + *
    • Tracking newly added scenarios as strategic objectives for gameplay purposes.
    • + *
    + * + * @param campaign the {@link Campaign} managing the state of the overall gameplay + * @param contract the {@link AtBContract} related to the current StratCon campaign + * @param trackState the {@link StratconTrackState} representing the track where objectives are placed + * @param numScenarios the number of objective scenarios to generate + * @param objectiveScenarios a list of {@link String} identifiers for potential scenarios that can be generated + * @param objectiveModifiers a list of optional {@link String} modifiers to apply to the generated scenarios; + * can be {@code null} if no modifiers are required */ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract contract, StratconTrackState trackState, @@ -389,13 +433,14 @@ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract // create scenario - don't assign a force yet StratconScenario scenario = StratconRulesManager.generateScenario(campaign, contract, trackState, - Force.FORCE_NONE, coords, template); + Force.FORCE_NONE, coords, template, null); // clear dates, because we don't want the scenario disappearing on us scenario.setDeploymentDate(null); scenario.setActionDate(null); scenario.setReturnDate(null); scenario.setStrategicObjective(true); + scenario.setTurningPoint(true); scenario.getBackingScenario().setCloaked(true); // apply objective mods if (objectiveModifiers != null) { diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconFacility.java b/MekHQ/src/mekhq/campaign/stratcon/StratconFacility.java index a644e03d577..c059e2badff 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconFacility.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconFacility.java @@ -47,7 +47,7 @@ public enum FacilityType { TankBase, AirBase, ArtilleryBase, - SupplyDepot, + SpacePort, DataCenter, IndustrialFacility, CommandCenter, @@ -66,6 +66,7 @@ public enum FacilityType { private List localModifiers = new ArrayList<>(); private String capturedDefinition; private boolean revealTrack; + private boolean increaseScanRange; private int scenarioOddsModifier; private int monthlySPModifier; private boolean preventAerospace; @@ -94,6 +95,7 @@ public StratconFacility clone() { clone.localModifiers = new ArrayList<>(localModifiers); clone.setCapturedDefinition(capturedDefinition); clone.revealTrack = revealTrack; + clone.increaseScanRange = increaseScanRange; clone.scenarioOddsModifier = scenarioOddsModifier; clone.monthlySPModifier = monthlySPModifier; clone.preventAerospace = preventAerospace; @@ -113,6 +115,7 @@ public void copyRulesDataFrom(StratconFacility facility) { setSharedModifiers(new ArrayList<>(facility.getSharedModifiers())); setOwner(facility.getOwner()); setRevealTrack(facility.getRevealTrack()); + setIncreaseScanRange(facility.getIncreaseScanRange()); setScenarioOddsModifier(facility.getScenarioOddsModifier()); setMonthlySPModifier(facility.getMonthlySPModifier()); setPreventAerospace(facility.preventAerospace()); @@ -245,6 +248,14 @@ public void setRevealTrack(boolean revealTrack) { this.revealTrack = revealTrack; } + public boolean getIncreaseScanRange() { + return increaseScanRange; + } + + public void setIncreaseScanRange(boolean increaseScanRange) { + this.increaseScanRange = increaseScanRange; + } + public int getScenarioOddsModifier() { return scenarioOddsModifier; } diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index a87ad379854..ded4b3eebcc 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -18,11 +18,8 @@ */ package mekhq.campaign.stratcon; -import megamek.codeUtilities.ObjectUtility; import megamek.common.Minefield; import megamek.common.TargetRoll; -import megamek.common.TargetRollModifier; -import megamek.common.UnitType; import megamek.common.annotations.Nullable; import megamek.common.event.Subscribe; import megamek.logging.MMLogger; @@ -33,21 +30,27 @@ import mekhq.campaign.event.NewDayEvent; import mekhq.campaign.event.ScenarioChangedEvent; import mekhq.campaign.event.StratconDeploymentEvent; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.force.Force; -import mekhq.campaign.force.StrategicFormation; import mekhq.campaign.mission.*; import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; import mekhq.campaign.mission.ScenarioForceTemplate.ForceGenerationMethod; import mekhq.campaign.mission.ScenarioMapParameters.MapLocation; import mekhq.campaign.mission.atb.AtBScenarioModifier; import mekhq.campaign.mission.enums.AtBMoraleLevel; +import mekhq.campaign.mission.enums.CombatRole; import mekhq.campaign.mission.enums.ContractCommandRights; +import mekhq.campaign.mission.enums.ScenarioStatus; +import mekhq.campaign.mission.resupplyAndCaches.StarLeagueCache; +import mekhq.campaign.mission.resupplyAndCaches.StarLeagueCache.CacheType; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.Skill; +import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.turnoverAndRetention.Fatigue; import mekhq.campaign.stratcon.StratconContractDefinition.StrategicObjectiveType; import mekhq.campaign.stratcon.StratconScenario.ScenarioState; import mekhq.campaign.unit.Unit; +import org.apache.commons.math3.util.Pair; import java.time.DayOfWeek; import java.time.LocalDate; @@ -57,10 +60,14 @@ import static java.lang.Math.max; import static java.lang.Math.min; import static java.lang.Math.round; +import static megamek.codeUtilities.ObjectUtility.getRandomItem; import static megamek.common.Compute.d6; import static megamek.common.Compute.randomInt; +import static megamek.common.Coords.ALL_DIRECTIONS; +import static megamek.common.UnitType.*; import static mekhq.campaign.force.Force.FORCE_NONE; -import static mekhq.campaign.icons.enums.LayeredForceIconOperationalStatus.determineLayeredForceIconOperationalStatus; +import static mekhq.campaign.icons.enums.OperationalStatus.determineLayeredForceIconOperationalStatus; +import static mekhq.campaign.mission.AtBDynamicScenarioFactory.finalizeScenario; import static mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment.Allied; import static mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment.Opposing; import static mekhq.campaign.mission.ScenarioMapParameters.MapLocation.AllGroundTerrain; @@ -69,8 +76,9 @@ import static mekhq.campaign.mission.ScenarioMapParameters.MapLocation.SpecificGroundTerrain; import static mekhq.campaign.personnel.SkillType.S_ADMIN; import static mekhq.campaign.personnel.SkillType.S_TACTICS; +import static mekhq.campaign.personnel.SkillType.getSkillHash; import static mekhq.campaign.stratcon.StratconContractInitializer.getUnoccupiedCoords; -import static mekhq.campaign.stratcon.StratconRulesManager.ReinforcementEligibilityType.FIGHT_LANCE; +import static mekhq.campaign.stratcon.StratconRulesManager.ReinforcementEligibilityType.AUXILIARY; import static mekhq.campaign.stratcon.StratconRulesManager.ReinforcementResultsType.DELAYED; import static mekhq.campaign.stratcon.StratconRulesManager.ReinforcementResultsType.FAILED; import static mekhq.campaign.stratcon.StratconRulesManager.ReinforcementResultsType.INTERCEPTED; @@ -86,6 +94,7 @@ */ public class StratconRulesManager { public final static int BASE_LEADERSHIP_BUDGET = 1000; + private static final MMLogger logger = MMLogger.create(StratconRulesManager.class); /** @@ -98,7 +107,7 @@ public enum ReinforcementEligibilityType { NONE, /** - * Lance is already deployed to the track + * Combat Team is already deployed to the track */ CHAINED_SCENARIO, @@ -108,9 +117,10 @@ public enum ReinforcementEligibilityType { REGULAR, /** - * The lance's deployment orders are "Fight". We pay a support point and make an enhanced roll + * The Combat Team's deployment orders are "Frontline" or "Auxiliary". + * We pay a support point and make an enhanced roll */ - FIGHT_LANCE + AUXILIARY } /** @@ -141,37 +151,44 @@ public enum ReinforcementResultsType { /** * This method generates scenario dates for each week of the StratCon campaign. *

    - * The method first determines the number of required scenario rolls based on the required - * lance count from the track, then multiplies that count depending on the contract's morale level. + * The method first determines the number of turning point scenario rolls based + * on the required + * lance count from the track, then multiplies that count depending on the + * contract's morale level. *

    - * If auto-assign for lances is enabled, and either there are no available forces or the number of - * weekly scenarios equals or exceeds the number of available forces, it breaks from the scenario + * If auto-assign for lances is enabled, and either there are no available + * forces or the number of + * weekly scenarios equals or exceeds the number of available forces, it breaks + * from the scenario * generation loop. *

    - * For each scenario, a scenario odds target number is calculated, and a roll is made against - * this target. If the roll is less than the target number, a new weekly scenario is created + * For each scenario, a scenario odds target number is calculated, and a roll is + * made against + * this target. If the roll is less than the target number, a new weekly + * scenario is created * with a random date within the week. * - * @param campaign The campaign. + * @param campaign The campaign. * @param campaignState The state of the StratCon campaign. - * @param contract The AtBContract for the campaign. - * @param track The StratCon campaign track. + * @param contract The AtBContract for the campaign. + * @param track The StratCon campaign track. */ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCampaignState campaignState, - AtBContract contract, StratconTrackState track) { + AtBContract contract, StratconTrackState track) { // maps scenarios to force IDs final boolean autoAssignLances = contract.getCommandRights().isIntegrated(); - List availableForceIDs = getAvailableForceIDs(campaign); + List availableForceIDs = getAvailableForceIDs(campaign, contract); int scenarioRolls = track.getRequiredLanceCount(); AtBMoraleLevel moraleLevel = contract.getMoraleLevel(); switch (moraleLevel) { - case STALEMATE -> scenarioRolls = (int) round(scenarioRolls * 1.25); - case ADVANCING -> scenarioRolls = (int) round(scenarioRolls * 1.5); - case DOMINATING -> scenarioRolls = scenarioRolls * 2; - case OVERWHELMING -> scenarioRolls = scenarioRolls * 3; + case ADVANCING -> scenarioRolls = (int) round(scenarioRolls * 1.33); + case DOMINATING -> scenarioRolls = (int) round(scenarioRolls * 1.66); + case OVERWHELMING -> scenarioRolls = scenarioRolls * 2; + default -> { + } } for (int scenarioIndex = 0; scenarioIndex < scenarioRolls; scenarioIndex++) { @@ -179,7 +196,7 @@ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCamp break; } - if (autoAssignLances && (campaignState.getWeeklyScenarios().size() >= availableForceIDs.size())) { + if (autoAssignLances && (scenarioIndex >= availableForceIDs.size())) { break; } @@ -189,7 +206,8 @@ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCamp if (roll < targetNum) { LocalDate scenarioDate = campaign.getLocalDate().plusDays(randomInt(7)); campaignState.addWeeklyScenario(scenarioDate); - logger.info(String.format("StratCon Weekly Scenario Roll: %s vs. %s (%s)", roll, targetNum, scenarioDate)); + logger.info( + String.format("StratCon Weekly Scenario Roll: %s vs. %s (%s)", roll, targetNum, scenarioDate)); } else { logger.info(String.format("StratCon Weekly Scenario Roll: %s vs. %s", roll, targetNum)); } @@ -199,18 +217,24 @@ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCamp /** * This method generates a weekly scenario for a specific track. *

    - * First, it initializes empty collections for generated scenarios and available forces, and + * First, it initializes empty collections for generated scenarios and available + * forces, and * determines whether lances are auto-assigned. *

    - * Then it generates a requested number of scenarios. If auto-assign is enabled and there + * Then it generates a requested number of scenarios. If auto-assign is enabled + * and there * are no available forces, it breaks from the scenario generation loop. *

    - * For each scenario, it first tries to create a scenario for existing forces on the track. - * If that is not possible, it selects random force, removes it from available forces, and - * creates a scenario for it. For any scenario, if it is under liaison command, it may set the + * For each scenario, it first tries to create a scenario for existing forces on + * the track. + * If that is not possible, it selects random force, removes it from available + * forces, and + * creates a scenario for it. For any scenario, if it is under liaison command, + * it may set the * scenario as required and attaches the liaison. *

    - * After scenarios are generated, OpFors, events, etc. are finalized for each scenario. + * After scenarios are generated, OpFors, events, etc. are finalized for each + * scenario. * * @param campaign The current campaign. * @param campaignState The relevant StratCon campaign state. @@ -218,13 +242,14 @@ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCamp * @param scenarioCount The number of scenarios to generate. */ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCampaignState campaignState, - AtBContract contract, int scenarioCount) { + AtBContract contract, int scenarioCount) { final boolean autoAssignLances = contract.getCommandRights().isIntegrated(); // get this list just so we have it available - List availableForceIDs = getAvailableForceIDs(campaign); + List availableForceIDs = getAvailableForceIDs(campaign, contract); - // Build the available force pool - this ensures operational forces have an increased + // Build the available force pool - this ensures operational forces have an + // increased // chance of being picked if (autoAssignLances && !availableForceIDs.isEmpty()) { List availableForcePool = new ArrayList<>(); @@ -236,6 +261,10 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam continue; } + if (force.isDeployed()) { + continue; + } + int operationalStatus = 0; int unitCount = 0; @@ -249,7 +278,8 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam } } - int calculatedOperationStatus = (int) round(Math.pow((3 - (double) operationalStatus / unitCount), 2.0)); + int calculatedOperationStatus = (int) round( + Math.pow((3 - (double) operationalStatus / unitCount), 2.0)); for (int i = 0; i < calculatedOperationStatus; i++) { availableForcePool.add(forceId); @@ -260,7 +290,6 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam availableForceIDs = availableForcePool; } - Map> sortedAvailableForceIDs = sortForcesByMapType(availableForceIDs, campaign); for (int scenarioIndex = 0; scenarioIndex < scenarioCount; scenarioIndex++) { @@ -272,7 +301,7 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam StratconTrackState track = campaignState.getTracks().get(0); if (tracks.size() > 1) { - track = ObjectUtility.getRandomItem(tracks); + track = getRandomItem(tracks); } if (autoAssignLances && availableForceIDs.isEmpty()) { @@ -286,18 +315,20 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam continue; } - // if forces are already assigned to these coordinates, use those instead of randomly + // if forces are already assigned to these coordinates, use those instead of + // randomly // selected ones StratconScenario scenario; if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { scenario = generateScenarioForExistingForces(scenarioCoords, - track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track); - // otherwise, pick a random force from the avail + track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track); + // otherwise, pick a random force from the avail } else { int randomForceIndex = randomInt(availableForceIDs.size()); int randomForceID = availableForceIDs.get(randomForceIndex); - // remove the force from the available lists, so we don't designate it as primary + // remove the force from the available lists, so we don't designate it as + // primary // twice if (autoAssignLances) { availableForceIDs.removeIf(id -> id.equals(randomForceIndex)); @@ -312,7 +343,7 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam if (track.getScenarios().containsKey(scenarioCoords)) { track.getScenarios().get(scenarioCoords).incrementRequiredPlayerLances(); assignAppropriateExtraForceToScenario(track.getScenarios().get(scenarioCoords), - sortedAvailableForceIDs); + sortedAvailableForceIDs); continue; } @@ -320,13 +351,6 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam } if (scenario != null) { - // if under liaison command, pick a random scenario from the ones generated - // to set as required and attach liaison - if (contract.getCommandRights().isLiaison() && (randomInt(4) == 0)) { - scenario.setRequiredScenario(true); - setAttachedUnitsModifier(scenario, contract); - } - finalizeBackingScenario(campaign, contract, track, autoAssignLances, scenario); } } @@ -334,174 +358,215 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam /** * Generates a StratCon scenario. - * This is a utility method that allows us to generate a scenario quickly without specifying + * This is a utility method that allows us to generate a scenario quickly + * without specifying * track state and scenario template. * * @param campaign The current campaign. * @param contract The contract associated with the scenario. - * @return A newly generated {@link StratconScenario}, or {@code null} if scenario creation fails. + * @return A newly generated {@link StratconScenario}, or {@code null} if + * scenario creation fails. */ public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract) { return generateExternalScenario(campaign, contract, null, null, - null, false); + null, false, null); } /** * Generates a new StratCon scenario using advanced configuration. * It provides a scenario based on a given campaign, contract, track, template. - * This is meant for scenario control on a higher level than the overloading methods. + * This is meant for scenario control on a higher level than the overloading + * methods. * - * @param campaign The current campaign. - * @param contract The contract associated with the scenario. - * @param track The {@link StratconTrackState} the scenario should be assigned to, or - * {@code null} to select a random track. - * @param scenarioCoords The {@link StratconCoords} where in the track to place the scenario, or - * {@code null} to select a random hex. If populated, {@code track} cannot be - * {@code null} - * @param template A specific {@link ScenarioTemplate} to use for scenario generation, - * or {@code null} to select scenario template randomly. - * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top of - * player-allied facilities. - * @return A newly generated {@link StratconScenario}, or {@code null} if scenario creation fails. + * @param campaign The current campaign. + * @param contract The contract associated with the scenario. + * @param track The {@link StratconTrackState} the scenario + * should be assigned to, or + * {@code null} to select a random track. + * @param scenarioCoords The {@link StratconCoords} where in the track to + * place the scenario, or + * {@code null} to select a random hex. If + * populated, {@code track} cannot be + * {@code null} + * @param template A specific {@link ScenarioTemplate} to use for + * scenario generation, + * or {@code null} to select scenario template + * randomly. + * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top + * of + * player-allied facilities. + * @param daysTilDeployment How many days beyond current date until the + * scenario, or {@code null} + * to pick a random date within the next 7 days. + * @return A newly generated {@link StratconScenario}, or {@code null} if + * scenario creation fails. */ - public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract, - @Nullable StratconTrackState track, @Nullable StratconCoords scenarioCoords, - @Nullable ScenarioTemplate template, boolean allowPlayerFacilities) { - // If we're not generating for a specific track, randomly pick one. - if (track == null) { - track = getRandomTrack(contract); - - if (track == null) { - logger.error("Failed to generate a random track, aborting scenario generation."); - return null; - } - } - - // Are we automatically assigning lances? - boolean autoAssignLances = contract.getCommandRights().isIntegrated(); - - // Grab the available lances and sort them by map type - List availableForceIDs = getAvailableForceIDs(campaign); - Map> sortedAvailableForceIDs = sortForcesByMapType(availableForceIDs, campaign); - - // Select the target coords. - if (scenarioCoords == null) { - scenarioCoords = getUnoccupiedCoords(track, allowPlayerFacilities); - } - - if (scenarioCoords == null) { - logger.warn("Target track is full, aborting scenario generation."); - return null; - } - - // If forces are already assigned to the target coordinates, use those instead of randomly - // selected a new force - StratconScenario scenario = null; - if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { - scenario = generateScenarioForExistingForces(scenarioCoords, - track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track, - template); - } - - // Otherwise, pick a random force from those available - // If a template has been specified, remove forces that aren't appropriate for the - // template. - if (template != null) { - MapLocation location = template.mapParameters.getMapLocation(); - - switch (location) { - case AllGroundTerrain, SpecificGroundTerrain -> { - sortedAvailableForceIDs.get(LowAtmosphere).clear(); - sortedAvailableForceIDs.get(Space).clear(); - } - case LowAtmosphere -> { - sortedAvailableForceIDs.get(AllGroundTerrain).clear(); - sortedAvailableForceIDs.get(Space).clear(); - } - case Space -> { - sortedAvailableForceIDs.get(AllGroundTerrain).clear(); - sortedAvailableForceIDs.get(LowAtmosphere).clear(); - } - } - } - - // If we haven't generated a scenario yet, it's because we need to pick a random force. - if (scenario == null) { - int availableForces = availableForceIDs.size(); - int randomForceID = FORCE_NONE; - - if (availableForces > 0) { - int randomForceIndex = randomInt(availableForces); - randomForceID = availableForceIDs.get(randomForceIndex); - } - - scenario = setupScenario(scenarioCoords, randomForceID, campaign, contract, track, template, false); - } - - if (scenario == null) { - return null; - } - - // We end by finalizing the scenario - finalizeBackingScenario(campaign, contract, track, autoAssignLances, scenario); - - // We return the scenario in case we want to make specific changes. - return scenario; - } + public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract, + @Nullable StratconTrackState track, + @Nullable StratconCoords scenarioCoords, + @Nullable ScenarioTemplate template, + boolean allowPlayerFacilities, + @Nullable Integer daysTilDeployment) { + // If we're not generating for a specific track, randomly pick one. + if (track == null) { + track = getRandomTrack(contract); + + if (track == null) { + logger.error("Failed to generate a random track, aborting scenario generation."); + return null; + } + } + + // Are we automatically assigning lances? + boolean autoAssignLances = contract.getCommandRights().isIntegrated(); + + // Grab the available lances and sort them by map type + List availableForceIDs = getAvailableForceIDs(campaign, contract); + Map> sortedAvailableForceIDs = sortForcesByMapType(availableForceIDs, campaign); + + // Select the target coords. + if (scenarioCoords == null) { + scenarioCoords = getUnoccupiedCoords(track, allowPlayerFacilities); + } + + if (scenarioCoords == null) { + logger.warn("Target track is full, aborting scenario generation."); + return null; + } + + // If forces are already assigned to the target coordinates, use those instead + // of randomly + // selected a new force + StratconScenario scenario = null; + if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { + scenario = generateScenarioForExistingForces(scenarioCoords, + track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track, + template, daysTilDeployment); + } + + // Otherwise, pick a random force from those available + // If a template has been specified, remove forces that aren't appropriate for + // the + // template. + if (template != null) { + MapLocation location = template.mapParameters.getMapLocation(); + + switch (location) { + case AllGroundTerrain, SpecificGroundTerrain -> { + sortedAvailableForceIDs.get(LowAtmosphere).clear(); + sortedAvailableForceIDs.get(Space).clear(); + } + case LowAtmosphere -> { + sortedAvailableForceIDs.get(AllGroundTerrain).clear(); + sortedAvailableForceIDs.get(Space).clear(); + } + case Space -> { + sortedAvailableForceIDs.get(AllGroundTerrain).clear(); + sortedAvailableForceIDs.get(LowAtmosphere).clear(); + } + } + } + + // If we haven't generated a scenario yet, it's because we need to pick a random + // force. + if (scenario == null) { + int availableForces = availableForceIDs.size(); + int randomForceID = FORCE_NONE; + + if (availableForces > 0) { + int randomForceIndex = randomInt(availableForces); + randomForceID = availableForceIDs.get(randomForceIndex); + } + + scenario = setupScenario(scenarioCoords, randomForceID, campaign, contract, track, + template, false, daysTilDeployment); + } + + if (scenario == null) { + return null; + } + + // We end by finalizing the scenario + finalizeBackingScenario(campaign, contract, track, autoAssignLances, scenario); + + // We return the scenario in case we want to make specific changes. + return scenario; + } /** * Generates a reinforcement interception scenario for a given StratCon track. * An interception scenario is set up at unoccupied coordinates on the track. - * If the scenario setup is successful, it is finalized and the deployment date for the + * If the scenario setup is successful, it is finalized and the deployment date + * for the * scenario is set as the current date. * - * @param campaign the current campaign - * @param contract the {@link AtBContract} for which the scenario is created - * @param track the {@link StratconTrackState} where the scenario is located, or {@code null} - * if not located on a track - * @param template the {@link ScenarioTemplate} used to create the scenario - * @param interceptedForce the {@link Force} that's being intercepted in the scenario + * @param campaign the current campaign + * @param contract the {@link AtBContract} for which the scenario is + * created + * @param track the {@link StratconTrackState} where the scenario is + * located, or {@code null} + * if not located on a track + * @param template the {@link ScenarioTemplate} used to create the + * scenario + * @param interceptedForce the {@link Force} that's being intercepted in the + * scenario */ - public static @Nullable void generateReinforcementInterceptionScenario( - Campaign campaign, AtBContract contract, - StratconTrackState track, ScenarioTemplate template, Force interceptedForce) { - StratconCoords scenarioCoords = getUnoccupiedCoords(track, false); + public static @Nullable void generateReinforcementInterceptionScenario( + Campaign campaign, StratconScenario linkedScenario, AtBContract contract, + StratconTrackState track, ScenarioTemplate template, Force interceptedForce) { + StratconCoords scenarioCoords = getUnoccupiedCoords(track, false); - StratconScenario scenario = setupScenario(scenarioCoords, interceptedForce.getId(), campaign, - contract, track, template, true); + StratconScenario scenario = setupScenario(scenarioCoords, interceptedForce.getId(), campaign, + contract, track, template, true, 0); - if (scenario == null) { - logger.error("Failed to generate a random interception scenario, aborting scenario generation."); - return; - } + if (scenario == null) { + logger.error("Failed to generate a random interception scenario, aborting scenario generation."); + return; + } - finalizeBackingScenario(campaign, contract, track, true, scenario); - scenario.setDeploymentDate(campaign.getLocalDate()); - } + finalizeBackingScenario(campaign, contract, track, true, scenario); + scenario.setActionDate(campaign.getLocalDate()); + scenario.getBackingScenario().setStatus(ScenarioStatus.CURRENT); + scenario.getBackingScenario().setLinkedScenarioID(linkedScenario.getBackingScenario().getId()); + linkedScenario.getBackingScenario().setLinkedScenarioID(scenario.getBackingScenario().getId()); + } /** - * Adds a {@link StratconScenario} to the specified contract. This scenario is cloaked so will + * Adds a {@link StratconScenario} to the specified contract. This scenario is + * cloaked so will * not be visible until the player uncovers it. - * If no {@link StratconTrackState} or {@link ScenarioTemplate} is provided, random one will be + * If no {@link StratconTrackState} or {@link ScenarioTemplate} is provided, + * random one will be * picked. * - * @param campaign The current campaign. - * @param contract The {@link AtBContract} associated with the scenario. - * @param trackState The {@link StratconTrackState} in which the scenario occurs. - * If {@code null}, a random trackState is selected. - * @param template The {@link ScenarioTemplate} for the scenario. - * If {@code null}, the default template is used. - * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top of - * player-allied facilities. + * @param campaign The current campaign. + * @param contract The {@link AtBContract} associated with the + * scenario. + * @param trackState The {@link StratconTrackState} in which the + * scenario occurs. + * If {@code null}, a random trackState is + * selected. + * @param template The {@link ScenarioTemplate} for the scenario. + * If {@code null}, the default template is used. + * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top + * of + * player-allied facilities. + * @param daysTilDeployment How many days until the scenario takes place, or + * {@code null} to + * pick a random day within the next 7 days. * * @return The created {@link StratconScenario} or @code null}, - * if no {@link ScenarioTemplate} is found or if all coordinates in the provided - * {@link StratconTrackState} are occupied (and therefore, scenario placement is not possible). + * if no {@link ScenarioTemplate} is found or if all coordinates in the + * provided + * {@link StratconTrackState} are occupied (and therefore, scenario + * placement is not possible). */ - public static @Nullable StratconScenario addHiddenExternalScenario(Campaign campaign, AtBContract contract, - @Nullable StratconTrackState trackState, - @Nullable ScenarioTemplate template, - boolean allowPlayerFacilities) { + public static @Nullable StratconScenario addHiddenExternalScenario(Campaign campaign, + AtBContract contract, + @Nullable StratconTrackState trackState, + @Nullable ScenarioTemplate template, + boolean allowPlayerFacilities, + @Nullable Integer daysTilDeployment) { // If we're not generating for a specific track, randomly pick one. if (trackState == null) { trackState = getRandomTrack(contract); @@ -517,13 +582,13 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam if (coords == null) { logger.error(String.format("Unable to place objective scenario on track %s," + " as all coords were occupied. Aborting.", - trackState.getDisplayableName())); + trackState.getDisplayableName())); return null; } // create scenario - don't assign a force yet StratconScenario scenario = StratconRulesManager.generateScenario(campaign, contract, - trackState, FORCE_NONE, coords, template); + trackState, FORCE_NONE, coords, template, daysTilDeployment); if (scenario == null) { return null; @@ -534,6 +599,7 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam scenario.setActionDate(null); scenario.setReturnDate(null); scenario.setStrategicObjective(true); + scenario.setTurningPoint(true); scenario.getBackingScenario().setCloaked(true); trackState.addScenario(scenario); @@ -542,57 +608,184 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam } /** - * Fetches a random {@link StratconTrackState} from the {@link StratconCampaignState}. + * Fetches a random {@link StratconTrackState} from the + * {@link StratconCampaignState}. * If no tracks are present, it logs an error message and returns {@code null}. * - * @param contract The {@link AtBContract} from which the track state will be fetched. - * @return The randomly chosen {@link StratconTrackState}, or {@code null} if no tracks are available. + * @param contract The {@link AtBContract} from which the track state will be + * fetched. + * @return The randomly chosen {@link StratconTrackState}, or {@code null} if no + * tracks are available. */ - public static @Nullable StratconTrackState getRandomTrack(AtBContract contract) { - List tracks = contract.getStratconCampaignState().getTracks(); - Random rand = new Random(); - - if (!tracks.isEmpty()) { - return tracks.get(rand.nextInt(tracks.size())); - } else { - logger.error("No tracks available. Unable to fetch random track"); - return null; - } - } + public static @Nullable StratconTrackState getRandomTrack(AtBContract contract) { + List tracks = contract.getStratconCampaignState().getTracks(); + Random rand = new Random(); + + if (!tracks.isEmpty()) { + return tracks.get(rand.nextInt(tracks.size())); + } else { + logger.error("No tracks available. Unable to fetch random track"); + return null; + } + } /** - * Finalizes the backing scenario, setting up the OpFor, scenario parameters, and other + * Finalizes the backing scenario, setting up the OpFor, scenario parameters, + * and other * necessary steps. * - * @param campaign The current campaign. - * @param contract The contract associated with the scenario. - * @param track The relevant {@link StratconTrackState}. - * @param autoAssignLances Flag indicating whether lances are to be auto-assigned. - * @param scenario The {@link StratconScenario} scenario to be finalized. + * @param campaign The current campaign. + * @param contract The contract associated with the scenario. + * @param track The relevant {@link StratconTrackState}. + * @param autoAssignLances Flag indicating whether lances are to be + * auto-assigned. + * @param scenario The {@link StratconScenario} scenario to be + * finalized. */ private static void finalizeBackingScenario(Campaign campaign, AtBContract contract, - @Nullable StratconTrackState track, boolean autoAssignLances, - StratconScenario scenario) { - AtBDynamicScenarioFactory.finalizeScenario(scenario.getBackingScenario(), contract, campaign); + @Nullable StratconTrackState track, boolean autoAssignLances, + StratconScenario scenario) { + final AtBDynamicScenario backingScenario = scenario.getBackingScenario(); + + // First determine if the scenario is a Turning Point (that win/lose will affect + // CVP) + determineIfTurningPointScenario(contract, scenario); + + // Then add any Cadre Duty units + if (contract.getContractType().isCadreDuty()) { + addCadreDutyTrainees(backingScenario); + } + + // Finally, finish scenario set up + finalizeScenario(backingScenario, contract, campaign); setScenarioParametersFromBiome(track, scenario); swapInPlayerUnits(scenario, campaign, FORCE_NONE); if (!autoAssignLances && !scenario.ignoreForceAutoAssignment()) { for (int forceID : scenario.getPlayerTemplateForceIDs()) { - scenario.getBackingScenario().removeForce(forceID); + backingScenario.removeForce(forceID); } scenario.setCurrentState(ScenarioState.UNRESOLVED); track.addScenario(scenario); } else { commitPrimaryForces(campaign, scenario, track); - // if we're auto-assigning lances, deploy all assigned forces to the track as well + // if we're auto-assigning lances, deploy all assigned forces to the track as + // well for (int forceID : scenario.getPrimaryForceIDs()) { processForceDeployment(scenario.getCoords(), forceID, campaign, track, false); } } } + /** + * Adds a Cadre Duty trainees modifier to the given scenario based on the + * location of the battle. + * + *

    + * This method determines the type of trainees to be added to the scenario by + * evaluating the map + * location parameter of the scenario's template. Depending on whether the + * battle is an air or + * space battle versus a ground battle, the appropriate Cadre Duty trainees + * scenario modifier + * is applied to the backing scenario. + *

    + * + *

    + * The logic is as follows: + *

      + *
    • If the battle occurs in low atmosphere or space, the air trainees + * modifier is added.
    • + *
    • If the battle occurs on the ground at any other map location, the ground + * trainees + * modifier is added.
    • + *
    + * + * @param backingScenario The {@link AtBDynamicScenario} representing the + * current scenario to which the modifier will be + * applied. + */ + private static void addCadreDutyTrainees(AtBDynamicScenario backingScenario) { + final ScenarioTemplate template = backingScenario.getTemplate(); + final MapLocation mapLocation = template.mapParameters.getMapLocation(); + boolean isAirBattle = (mapLocation == LowAtmosphere) || (mapLocation == Space); + + if (isAirBattle) { + backingScenario.addScenarioModifier( + AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_AIR)); + } else { + backingScenario.addScenarioModifier( + AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_GROUND)); + } + } + + /** + * Determines if a given StratCon scenario should be marked as critical within + * the context of a + * contract. + *

    + * This method evaluates the scenario's template, type, and the contract's + * command rights to decide + * if the scenario should be flagged as a "turning point." Turning Point + * scenarios can cause CVP + * to be increased or decreased. + *

    + * + *

    + * The logic follows these rules: + *

      + *
    • If the scenario template or its type is not related to resupply + * operations, the + * method evaluates the contract's command rights.
    • + *
    • For INTEGRATED or HOUSE command rights: + * non-resupply scenarios are always marked as required.
    • + *
    • For LIAISON or INDEPENDENT command + * rights: + * non-resupply scenarios have a 25% chance (1 in 4) to be marked as required. + * An attached + * units modifier is also set if the scenario becomes required.
    • + *
    + * + * @param contract The {@link AtBContract} representing the current contract. + * @param scenario The {@link StratconScenario} being evaluated to determine if + * it is a Turning Point. + */ + private static void determineIfTurningPointScenario(AtBContract contract, StratconScenario scenario) { + ScenarioTemplate template = scenario.getScenarioTemplate(); + boolean isResupply = scenario.getBackingScenario().getStratConScenarioType().isResupply(); + + if (isResupply) { + scenario.setTurningPoint(false); + return; + } + + boolean isObjective = scenario.isStrategicObjective(); + + if (template == null || !template.getStratConScenarioType().isResupply()) { + ContractCommandRights commandRights = contract.getCommandRights(); + switch (commandRights) { + case INTEGRATED -> { + scenario.setTurningPoint(true); + if (randomInt(3) == 0 || isObjective) { + setAttachedUnitsModifier(scenario, contract); + } + } + case HOUSE, LIAISON -> { + if (randomInt(3) == 0 || isObjective) { + scenario.setTurningPoint(true); + setAttachedUnitsModifier(scenario, contract); + } + } + case INDEPENDENT -> { + if (randomInt(3) == 0 || isObjective) { + scenario.setTurningPoint(true); + } + } + } + } + } + /** * Picks the scenario terrain based on the scenario coordinates' biome * Note that "finalizeScenario" currently wipes out temperature/map info so this @@ -682,17 +875,18 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai Collection potentialUnits = new HashSet<>(); - // find units in player's campaign by default, all units in the TO&E are eligible + // find units in player's campaign by default, all units in the TO&E are + // eligible if (explicitForceID == FORCE_NONE) { for (UUID unitId : campaign.getForces().getUnits()) { try { potentialUnits.add(campaign.getUnit(unitId)); } catch (Exception exception) { logger.error(String.format("Error retrieving unit (%s): %s", - unitId, exception.getMessage())); + unitId, exception.getMessage())); } } - // if we're using a seed force, then units transporting this force are eligible + // if we're using a seed force, then units transporting this force are eligible } else { Force force = campaign.getForce(explicitForceID); for (UUID unitID : force.getUnits()) { @@ -709,7 +903,7 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai // if it's the right type of unit and is around if (forceCompositionMatchesDeclaredUnitType(unit.getEntity().getUnitType(), - sft.getAllowedUnitType(), false) && + sft.getAllowedUnitType()) && unit.isAvailable() && unit.isFunctional()) { // add the unit to the scenario and bench the appropriate bot unit if one is @@ -730,47 +924,63 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai } /** - * Generates a StratCon scenario for forces already existing at the given coordinates on the + * Generates a StratCon scenario for forces already existing at the given + * coordinates on the * provided track. * - * @param scenarioCoords The coordinates where the scenario will be placed on the track. - * @param forceIDs The set of force IDs (ideally for the forces already at the - * specified location). - * @param contract The contract associated with the current scenario. - * @param campaign The current campaign. - * @param track The relevant StratCon track. + * @param scenarioCoords The coordinates where the scenario will be placed on + * the track. + * @param forceIDs The set of force IDs (ideally for the forces already at + * the + * specified location). + * @param contract The contract associated with the current scenario. + * @param campaign The current campaign. + * @param track The relevant StratCon track. * @return The newly generated {@link StratconScenario}. */ public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, - Set forceIDs, AtBContract contract, Campaign campaign, - StratconTrackState track) { + Set forceIDs, AtBContract contract, Campaign campaign, + StratconTrackState track) { return generateScenarioForExistingForces(scenarioCoords, forceIDs, contract, campaign, - track, null); + track, null, null); } /** - * Generates a StratCon scenario for forces already existing at the given coordinates on the - * provided track. This method allows us to specify a specific scenario template. + * Generates a StratCon scenario for forces already existing at the given + * coordinates on the + * provided track. This method allows us to specify a specific scenario + * template. * - * @param scenarioCoords The coordinates where the scenario will be placed on the track. - * @param forceIDs The set of force IDs (ideally for the forces already at the + * @param scenarioCoords The coordinates where the scenario will be placed on + * the track. + * @param forceIDs The set of force IDs (ideally for the forces already + * at the * specified location). * @param contract The contract associated with the current scenario. * @param campaign The current campaign. * @param track The relevant StratCon track. - * @param template A specific {@link ScenarioTemplate} to use, or {@code null} to + * @param template A specific {@link ScenarioTemplate} to use, or + * {@code null} to * select a random template. + * @param daysTilDeployment How many days until the scenario takes place, or + * {@code null} to + * pick a random day within the next 7 days. * @return The newly generated {@link StratconScenario}. */ public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, - Set forceIDs, AtBContract contract, Campaign campaign, - StratconTrackState track, @Nullable ScenarioTemplate template) { + Set forceIDs, + AtBContract contract, + Campaign campaign, + StratconTrackState track, + @Nullable ScenarioTemplate template, + @Nullable Integer daysTilDeployment) { boolean firstForce = true; StratconScenario scenario = null; for (int forceID : forceIDs) { if (firstForce) { - scenario = setupScenario(scenarioCoords, forceID, campaign, contract, track, template, false); + scenario = setupScenario(scenarioCoords, forceID, campaign, contract, track, + template, false, daysTilDeployment); firstForce = false; if (scenario == null) { @@ -792,105 +1002,284 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai } /** - * Deploys a force to the given coordinates on the given track as a result of - * explicit player - * action. + * Deploys a combat team (force) to a specified coordinate within the strategic + * track and performs the + * associated deployment activities, including handling scenarios, facilities, + * scouting behavior, + * and fog of war updates. + * + *

    + * The method processes the deployment as follows: + *

      + *
    1. Reveals the fog of war at or near the deployment coordinates based on the + * force's role, using + * {@code processForceDeployment}.
    2. + *
    3. If the deployment coordinates contain an existing hostile facility, a + * scenario involving + * that facility is created.
    4. + *
    5. If the deployment coordinates are empty, a chance-based scenario may be + * created depending + * on the scenario odds.
    6. + *
    7. If a scenario is revealed (either from the facility or randomly):
    8. + *
    9. - The deployed force is assigned to that scenario.
    10. + *
    11. - The scenario is finalized and parameters are adjusted accordingly.
    12. + *
    13. If a deploying force is performing a scouting mission:
    14. + *
    15. - The target coordinates may be shifted to an unoccupied adjacent + * coordinate if available.
    16. + *
    17. If the coordinates contain a non-allied facility or qualify for a new + * scenario, a + * scenario is generated:
    18. + *
    19. - If forces are already deployed at the location, generate a scenario + * involving + * these forces.
    20. + *
    21. - If no forces are present, assign available forces from the campaign or + * randomly + * select a suitable combat team for the scenario.
    22. + *
    23. - If applicable, determine whether the scenario is under liaison command + * based on + * contract command rights, and update the scenario requirements.
    24. + *
    + * + * @param coords the {@link StratconCoords} representing the deployment + * coordinates. + * @param forceID the unique identifier of the combat team (force) being + * deployed. + * @param campaign the current {@link Campaign} context, which provides access + * to combat teams, facilities, + * and other campaign-level data. + * @param contract the {@link AtBContract} associated with the campaign, which + * determines rules + * and command rights for the deployment. + * @param track the {@link StratconTrackState} representing the strategic + * track, including details + * about scenarios, facilities, and force assignments. + * @param sticky a {@code boolean} flag indicating whether the deployment is + * "sticky," meaning + * the forces remain at the deployment location without + * automatically updating + * their position. */ - public static void deployForceToCoords(StratconCoords coords, int forceID, Campaign campaign, AtBContract contract, - StratconTrackState track, boolean sticky) { + public static void deployForceToCoords(StratconCoords coords, int forceID, Campaign campaign, + AtBContract contract, StratconTrackState track, boolean sticky) { + CombatTeam combatTeam = campaign.getCombatTeamsTable().get(forceID); + + // This shouldn't be possible, but never hurts to have a little insurance + if (combatTeam == null) { + return; + } + + boolean isPatrol = combatTeam.getRole().isPatrol(); + // the following things should happen: // 1. call to "process force deployment", which reveals fog of war in or around - // the coords, depending on force role + // the coords, + // depending on force role // 2. if coords are a hostile facility, we get a facility scenario // 3. if coords are empty, we *may* get a scenario - processForceDeployment(coords, forceID, campaign, track, sticky); // we may stumble on a fixed objective scenario - in that case assign the force - // to it and finalize - // we also will not be encountering any of the other stuff so bug out afterwards + // to it and finalize we also will not be encountering any of the other stuff so + // bug out + // afterward StratconScenario revealedScenario = track.getScenario(coords); if (revealedScenario != null) { + if (!revealedScenario.getBackingScenario().isFinalized()) { + finalizeScenario(revealedScenario.getBackingScenario(), contract, campaign); + setScenarioParametersFromBiome(track, revealedScenario); + } revealedScenario.addPrimaryForce(forceID); - AtBDynamicScenarioFactory.finalizeScenario(revealedScenario.getBackingScenario(), contract, campaign); - setScenarioParametersFromBiome(track, revealedScenario); commitPrimaryForces(campaign, revealedScenario, track); return; } - // don't create a scenario on top of allied facilities StratconFacility facility = track.getFacility(coords); boolean isNonAlliedFacility = (facility != null) && (facility.getOwner() != Allied); + int targetNum = calculateScenarioOdds(track, contract, true); boolean spawnScenario = (facility == null) && (randomInt(100) <= targetNum); if (isNonAlliedFacility || spawnScenario) { - StratconScenario scenario = setupScenario(coords, forceID, campaign, contract, track); - // we deploy immediately in this case, since we deployed the force manually - setScenarioDates(0, track, campaign, scenario); - AtBDynamicScenarioFactory.finalizeScenario(scenario.getBackingScenario(), contract, campaign); - setScenarioParametersFromBiome(track, scenario); + StratconScenario scenario; + + // If we're not deploying on top of an enemy facility, migrate the scenario + if (!isNonAlliedFacility && isPatrol) { + StratconCoords newCoords = getUnoccupiedAdjacentCoords(coords, track); - // if we wound up with a field scenario, we may sub in dropships carrying - // units of the force in question - if (spawnScenario && !isNonAlliedFacility) { - swapInPlayerUnits(scenario, campaign, forceID); + if (newCoords != null) { + coords = newCoords; + } } - commitPrimaryForces(campaign, scenario, track); + // Patrols only get autoAssigned to the scenario if they're dropped on top of a + // non-allied facility + boolean autoAssignLances = !isPatrol || isNonAlliedFacility; + + // Do we already have forces deployed to the target coordinates? + // If so, assign them to the scenario. + Set preDeployedForce = track.getAssignedCoordForces().get(coords); + + if (preDeployedForce != null && !preDeployedForce.isEmpty()) { + scenario = generateScenarioForExistingForces(coords, + track.getAssignedCoordForces().get(coords), contract, campaign, track); + // Otherwise, pick a random force from those available + } else { + List availableForceIDs = getAvailableForceIDs(campaign, contract); + Collections.shuffle(availableForceIDs); + + // If the player doesn't have any available forces, we grab a force at random to + // seed the scenario + if (availableForceIDs.isEmpty()) { + ArrayList combatTeams = campaign.getAllCombatTeams(); + if (!combatTeams.isEmpty()) { + combatTeam = getRandomItem(combatTeams); + + forceID = combatTeam.getForceId(); + } else { + // If the player doesn't have any combat teams (somehow), they get a free pass + return; + } + } + + scenario = setupScenario(coords, forceID, campaign, contract, track); + } + + finalizeBackingScenario(campaign, contract, track, autoAssignLances, scenario); } } + /** + * Finds an unoccupied coordinates adjacent to the given origin coordinates. + * + *

    + * Adjacent coordinates are determined based on all possible directions defined + * by {@code ALL_DIRECTIONS}. + * A coordinate is considered "unoccupied" if the following conditions are met: + *

      + *
    • No scenario is assigned to the coordinate (using + * {@link StratconTrackState#getScenario})
    • + *
    • No facility exists at the coordinate (using + * {@link StratconTrackState#getFacility})
    • + *
    • The coordinate is not occupied by any assigned forces (using + * {@link StratconTrackState#getAssignedForceCoords})
    • + *
    • The coordinate is on the map
    • + *
    + * If multiple suitable coordinates are found, one is selected at random and + * returned. + * If no suitable coordinates are available, the method returns {@code null}. + * + * @param originCoords the coordinate from which to search for unoccupied + * adjacent ones + * @param trackState the state of the track containing information about + * scenarios, facilities, and forces + * @return a randomly selected unoccupied adjacent coordinate, or {@code null} + * if none are available + */ + private static @Nullable StratconCoords getUnoccupiedAdjacentCoords(StratconCoords originCoords, + StratconTrackState trackState) { + // We need to reduce width/height by one because coordinates index from 0, not 1 + final int trackWidth = trackState.getWidth() - 1; + final int trackHeight = trackState.getHeight() - 1; + + List suitableCoords = new ArrayList<>(); + for (int direction : ALL_DIRECTIONS) { + StratconCoords newCoords = originCoords.translate(direction); + + if (trackState.getScenario(newCoords) != null) { + continue; + } + + if (trackState.getFacility(newCoords) != null) { + continue; + } + + if (trackState.getAssignedForceCoords().containsValue(newCoords)) { + continue; + } + + // This is to ensure we're not trying to place a scenario off the map + if ((newCoords.getX() < 0) + || (newCoords.getX() > trackWidth) + || (newCoords.getY() < 0) + || (newCoords.getY() > trackHeight)) { + continue; + } + + suitableCoords.add(newCoords); + } + + if (suitableCoords.isEmpty()) { + return null; + } + + return getRandomItem(suitableCoords); + } + /** * Sets up a StratCon scenario with the given parameters. * - * @param coords The coordinates where the scenario is to be placed on the track. - * @param forceID The ID of the forces involved in the scenario. - * @param campaign The current campaign. - * @param contract The contract associated with the current scenario. - * @param track The relevant StratCon track. + * @param coords The coordinates where the scenario is to be placed on the + * track. + * @param forceID The ID of the forces involved in the scenario. + * @param campaign The current campaign. + * @param contract The contract associated with the current scenario. + * @param track The relevant StratCon track. * @return The newly set up {@link StratconScenario}. */ private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, - AtBContract contract, StratconTrackState track) { - return setupScenario(coords, forceID, campaign, contract, track, null, false); + AtBContract contract, StratconTrackState track) { + return setupScenario(coords, forceID, campaign, contract, track, null, false, null); } /** - * Sets up a Stratcon scenario with the given parameters optionally allowing use a specific scenario template. + * Sets up a Stratcon scenario with the given parameters optionally allowing use + * a specific scenario template. *

    - * If a facility is already present at the provided coordinates, the scenario will be setup for that facility. - * If there is no facility, a new scenario will be generated; if the ScenarioTemplate argument provided was non-null, + * If a facility is already present at the provided coordinates, the scenario + * will be setup for that facility. + * If there is no facility, a new scenario will be generated; if the + * ScenarioTemplate argument provided was non-null, * it will be used, else a randomly selected scenario will be generated. - * In case the generated scenario turns out to be a facility scenario, a new facility will be added to the track at + * In case the generated scenario turns out to be a facility scenario, a new + * facility will be added to the track at * the provided coordinates and setup for that facility. * - * @param coords The coordinates where the scenario is to be placed on the track. - * @param forceID The ID of the forces involved in the scenario. - * @param campaign The current campaign. - * @param contract The contract associated with the current scenario. - * @param track The relevant StratCon track. - * @param template A specific {@link ScenarioTemplate} to use for scenario setup, or - * {@code null} to select the scenario template randomly. - * @param ignoreFacilities Whether we should ignore any facilities at the selected location + * @param coords The coordinates where the scenario is to be placed + * on the track. + * @param forceID The ID of the forces involved in the scenario. + * @param campaign The current campaign. + * @param contract The contract associated with the current scenario. + * @param track The relevant StratCon track. + * @param template A specific {@link ScenarioTemplate} to use for + * scenario setup, or + * {@code null} to select the scenario template + * randomly. + * @param ignoreFacilities Whether we should ignore any facilities at the + * selected location + * @param daysTilDeployment How many days until the scenario takes place, or + * {@code null} to + * pick a random day within the next 7 days. * @return The newly set up {@link StratconScenario}. */ - private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, - AtBContract contract, StratconTrackState track, - @Nullable ScenarioTemplate template, boolean ignoreFacilities) { + private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, + Campaign campaign, AtBContract contract, + StratconTrackState track, + @Nullable ScenarioTemplate template, + boolean ignoreFacilities, + @Nullable Integer daysTilDeployment) { StratconScenario scenario; if (track.getFacilities().containsKey(coords) && !ignoreFacilities) { StratconFacility facility = track.getFacility(coords); boolean alliedFacility = facility.getOwner() == Allied; template = StratconScenarioFactory.getFacilityScenario(alliedFacility); - scenario = generateScenario(campaign, contract, track, forceID, coords, template); + scenario = generateScenario(campaign, contract, track, forceID, coords, template, daysTilDeployment); setupFacilityScenario(scenario, facility); } else { if (template != null) { - scenario = generateScenario(campaign, contract, track, forceID, coords, template); + scenario = generateScenario(campaign, contract, track, forceID, coords, template, daysTilDeployment); } else { - scenario = generateScenario(campaign, contract, track, forceID, coords); + scenario = generateScenario(campaign, contract, track, forceID, coords, daysTilDeployment); } if (scenario == null) { @@ -961,23 +1350,78 @@ private static void processFacilityEffects(StratconTrackState track, } /** - * Process the deployment of a force to the given coordinates on the given + * Processes the deployment of a force to the specified coordinates on the given * track. - * This does not include assigning the force to any scenarios + * + *

    + * This includes revealing the deployed coordinates, identifying and revealing + * facilities + * and scenarios within the scan range, and updating necessary game states such + * as fatigue + * and force assignments. It does not include assigning the force to specific + * scenarios. + *

    + * + * Behavior: + *
      + *
    • If the force's deployment coordinates are unrevealed, fatigue is + * increased for the force.
    • + *
    • Ensures that fatigue is increased only once during the deployment + * process.
    • + *
    • Reveals all coordinates, facilities, and scenarios within the force's + * scan range.
    • + *
    • Handles cloaked scenarios by activating them and updating game states as + * necessary.
    • + *
    • Updates the track's revealed coordinates to include the deployment and + * adjacent areas within range.
    • + *
    • Assigns the deployed force to the specified coordinates and clears their + * previous track assignments.
    • + *
    + * + * Notes: + *
      + *
    • Scout or patrol roles may increase the scan range.
    • + *
    • The method uses a breadth-first search (BFS) approach to traverse the hex + * grid and reveal neighbors + * within the scan range efficiently, avoiding redundant processing using a + * visited set.
    • + *
    + * + * @param coords The coordinates where the force is being deployed. + * @param forceID The ID of the force being deployed. + * @param campaign The current campaign context, used to retrieve combat teams + * and update game events. + * @param track The current track state where the deployment is happening. + * @param sticky Whether the force should be persistently assigned to the + * track. + * + * @throws IllegalStateException if the force or the associated combat team is + * missing or invalid. */ public static void processForceDeployment(StratconCoords coords, int forceID, Campaign campaign, StratconTrackState track, boolean sticky) { - // plan of action: - // increase fatigue if the coordinates are not currently unrevealed - // reveal deployed coordinates - // reveal facility in deployed coordinates (and all adjacent coordinates for - // scout lances) - // reveal scenario in deployed coordinates (and all adjacent coordinates for - // scout lances) - // we want to ensure we only increase Fatigue once boolean hasFatigueIncreased = false; + // BFS queue for coordinates, tracks distance from the starting point + Queue> queue = new LinkedList<>(); + // Keep a set of visited coordinates to avoid redundancy + Set visited = new HashSet<>(); + + // Start with the initial deployment coordinate at distance 0 + queue.add(new Pair<>(coords, 0)); + visited.add(coords); + + // Determine scan range + int scanRange = track.getScanRangeIncrease(); + + CombatTeam combatTeam = campaign.getCombatTeamsTable().get(forceID); + + if (combatTeam != null && combatTeam.getRole().isPatrol()) { + scanRange++; + } + + // Process starting point if (!track.getRevealedCoords().contains(coords)) { increaseFatigue(forceID, campaign); hasFatigueIncreased = true; @@ -985,44 +1429,64 @@ public static void processForceDeployment(StratconCoords coords, int forceID, Ca track.getRevealedCoords().add(coords); - StratconFacility facility = track.getFacility(coords); - if (facility != null) { - facility.setVisible(true); + StratconFacility targetFacility = track.getFacility(coords); + if (targetFacility != null) { + targetFacility.setVisible(true); } StratconScenario scenario = track.getScenario(coords); - // if we're deploying on top of a scenario and it's "cloaked" - // then we have to activate it - if ((scenario != null) && scenario.getBackingScenario().isCloaked()) { - scenario.getBackingScenario().setCloaked(false); - setScenarioDates(0, track, campaign, scenario); // must be called before commitPrimaryForces - MekHQ.triggerEvent(new ScenarioChangedEvent(scenario.getBackingScenario())); - } - if (campaign.getStrategicFormationsTable().get(forceID).getRole().isScouting()) { - for (int direction = 0; direction < 6; direction++) { - StratconCoords checkCoords = coords.translate(direction); + if (scenario != null) { + AtBDynamicScenario backingScenario = scenario.getBackingScenario(); - facility = track.getFacility(checkCoords); - if (facility != null) { - facility.setVisible(true); + if (backingScenario != null) { + if (backingScenario.isCloaked()) { + backingScenario.setCloaked(false); } - scenario = track.getScenario(checkCoords); - // if we've revealed a scenario and it's "cloaked" - // we have to activate it - if ((scenario != null) && scenario.getBackingScenario().isCloaked()) { - scenario.getBackingScenario().setCloaked(false); + if (backingScenario.getDate() == null) { setScenarioDates(0, track, campaign, scenario); - MekHQ.triggerEvent(new ScenarioChangedEvent(scenario.getBackingScenario())); } - if ((!track.getRevealedCoords().contains(checkCoords)) && (!hasFatigueIncreased)) { - increaseFatigue(forceID, campaign); - hasFatigueIncreased = true; - } + MekHQ.triggerEvent(new ScenarioChangedEvent(backingScenario)); + } + } + + // Traverse neighboring coordinates up to the specified distance + while (!queue.isEmpty()) { + Pair current = queue.poll(); + StratconCoords currentCoords = current.getKey(); + int distance = current.getValue(); + + // Only process neighbors if they're within the max distance + if (distance < scanRange) { + for (int direction = 0; direction < 6; direction++) { + StratconCoords checkCoords = currentCoords.translate(direction); - track.getRevealedCoords().add(coords.translate(direction)); + // Skip already visited coordinates + if (visited.contains(checkCoords)) { + continue; + } + + // Mark as visited + visited.add(checkCoords); + queue.add(new Pair<>(checkCoords, distance + 1)); // Add the neighbor with incremented distance + + // Process facilities + targetFacility = track.getFacility(checkCoords); + if (targetFacility != null) { + targetFacility.setVisible(true); + } + + // Increase fatigue only once + if (!track.getRevealedCoords().contains(checkCoords) && !hasFatigueIncreased) { + increaseFatigue(forceID, campaign); + hasFatigueIncreased = true; + } + + // Mark the current coordinate as revealed + track.getRevealedCoords().add(checkCoords); + } } } @@ -1051,19 +1515,67 @@ private static void increaseFatigue(int forceID, Campaign campaign) { } /** - * Worker function that processes the effects of deploying a reinforcement force to a scenario + * Processes the effects of deploying a reinforcement force to a scenario. + * Based on the reinforcement type, the campaign state, and the results dice + * rolls, skills, + * and intercept odds), this method determines whether the reinforcement + * deployment succeeds, + * fails, is delayed, or is intercepted. + * + *

    + * Key steps include: + *

      + *
    • Checking if the reinforcement type is + * {@link ReinforcementEligibilityType#CHAINED_SCENARIO}, + * which automatically succeeds.
    • + *
    • Calculating the results of dice rolls, optionally adjusted for skills + * such as Tactics, + * and comparing it against the target number to determine success or + * failure.
    • + *
    • Handling critical failures, interception attempts, and enemy + * routing.
    • + *
    • Generating follow-up scenarios for intercepted reinforcements or handling + * delays.
    • + *
    * - * @param reinforcementType the type of reinforcement being deployed - * @param campaignState the state of the campaign - * @param scenario the current scenario - * @param campaign the campaign instance - * @return {@code true} if the reinforcement deployment is successful, {@code false} otherwise + * @param force the {@link Force} being deployed as a + * reinforcement + * @param reinforcementType the type of reinforcement (e.g., auxiliary + * or chained scenario) + * @param campaignState the current state of the campaign + * @param scenario the scenario to which the reinforcements are + * being deployed + * @param campaign the overarching campaign instance managing + * the scenario + * @param reinforcementTargetNumber the target number that the reinforcement + * roll must meet or exceed + * @param isGMReinforcement {@code true} if the player is using GM + * powers to bypass the + * reinforcement check, {@code false} + * otherwise. + * @return a {@link ReinforcementResultsType} indicating the result of the + * reinforcement deployment: + *
      + *
    • {@link ReinforcementResultsType#SUCCESS} - The reinforcement is + * deployed successfully.
    • + *
    • {@link ReinforcementResultsType#FAILED} - The reinforcement + * deployment fails.
    • + *
    • {@link ReinforcementResultsType#DELAYED} - The reinforcement is + * delayed.
    • + *
    • {@link ReinforcementResultsType#INTERCEPTED} - The reinforcement + * is intercepted, + * possibly resulting in a new scenario.
    • + *
    */ - public static ReinforcementResultsType processReinforcementDeployment( - Force force, ReinforcementEligibilityType reinforcementType, StratconCampaignState campaignState, - StratconScenario scenario, Campaign campaign) { + public static ReinforcementResultsType processReinforcementDeployment(Force force, + ReinforcementEligibilityType reinforcementType, + StratconCampaignState campaignState, + StratconScenario scenario, + Campaign campaign, + int reinforcementTargetNumber, + boolean isGMReinforcement) { final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", - MekHQ.getMHQOptions().getLocale()); + MekHQ.getMHQOptions().getLocale()); if (reinforcementType.equals(ReinforcementEligibilityType.CHAINED_SCENARIO)) { return SUCCESS; @@ -1071,48 +1583,7 @@ public static ReinforcementResultsType processReinforcementDeployment( AtBContract contract = campaignState.getContract(); - // Start by determining who will be making the attempt - Person commandLiaison = campaign.getSeniorAdminCommandPerson(); - - if (commandLiaison == null) { - campaign.addReport(String.format(resources.getString("reinforcementsNoAdmin.text"), - scenario.getName(), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); - return FAILED; - } - - // Assuming we found a relevant character, spend the support point required for the attempt - if (campaignState.getSupportPoints() >= 1) { - campaignState.useSupportPoint(); - } else { - campaign.addReport(String.format(resources.getString("reinforcementsNoSupportPoints.text"), - scenario.getName(), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); - return FAILED; - } - - // Then calculate the target number and modifiers - - Skill skill = commandLiaison.getSkill(S_ADMIN); - - if (skill == null) { - campaign.addReport(String.format(resources.getString("reinforcementsNoAdminSkill.text"), - scenario.getName(), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG), commandLiaison.getHyperlinkedFullTitle()); - return FAILED; - } - - int skillTargetNumber = skill.getFinalSkillValue(); - - TargetRoll reinforcementTargetNumber = new TargetRoll(); - - // Base Target Number - reinforcementTargetNumber.addModifier(skillTargetNumber, "Base TN"); - - // Facilities Modifier + // Determine StratCon Track and other context for recalculation StratconTrackState track = null; for (StratconTrackState trackState : campaignState.getTracks()) { if (trackState.getScenarios().containsValue(scenario)) { @@ -1121,82 +1592,50 @@ public static ReinforcementResultsType processReinforcementDeployment( } } - int facilityModifier = 0; - if (track != null) { - for (StratconFacility facility : track.getFacilities().values()) { - if (facility.getOwner().equals(ForceAlignment.Player) || facility.getOwner().equals(Allied)) { - facilityModifier--; - } else { - facilityModifier++; - } - } - } - - reinforcementTargetNumber.addModifier(facilityModifier, "Facilities"); - - // Skill Modifier - int skillModifier = -contract.getAllySkill().getAdjustedValue(); - - ContractCommandRights commandRights = contract.getCommandRights(); - if (commandRights.isIndependent()) { - if (campaign.getCampaignOptions().getUnitRatingMethod().isCampaignOperations()) { - skillModifier = -campaign.getReputation().getAverageSkillLevel().getAdjustedValue(); - } - } - - skillModifier += contract.getEnemySkill().getAdjustedValue(); - - reinforcementTargetNumber.addModifier(skillModifier, "Skill"); - - // Liaison Modifier - int liaisonModifier = 0; - if (commandRights.isLiaison()) { - liaisonModifier -= 1; - } else if (commandRights.isHouse() || commandRights.isIntegrated()) { - liaisonModifier -= 2; - } - - reinforcementTargetNumber.addModifier(liaisonModifier, "Command Rights"); - // Make the roll int roll = d6(2); - // If the formation is in Fight Stance, use the highest of two rolls - String fightStanceReport = ""; - if (reinforcementType == FIGHT_LANCE) { + // If the formation is set to Maneuver or Auxiliary, use the highest of two + // rolls + String maneuverRoleReport = ""; + if (reinforcementType == AUXILIARY) { int secondRoll = d6(2); roll = max(roll, secondRoll); - fightStanceReport = String.format(" (%s)", roll); + maneuverRoleReport = String.format(" (%s)", roll); } - StringBuilder modifierString = new StringBuilder(); + StringBuilder reportStatus = new StringBuilder(); - for (TargetRollModifier modifier : reinforcementTargetNumber.getModifiers()) { - modifierString.append(modifier.getDesc()).append(' ').append(modifier.getValue()).append(' '); + if (isGMReinforcement) { + reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text.gm"), + scenario.getName())); + reportStatus.append(' '); + reportStatus.append(String.format(resources.getString("reinforcementsAutomaticSuccess.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); + campaign.addReport(reportStatus.toString()); + return SUCCESS; + } else { + reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text"), + scenario.getName(), roll, maneuverRoleReport, reinforcementTargetNumber)); } - logger.info(String.format("Reinforcement Roll Modifiers: %s", modifierString)); - - StringBuilder reportStatus = new StringBuilder(); - reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text"), - scenario.getName(), roll, fightStanceReport, reinforcementTargetNumber.getValue())); - // Critical Failure if (roll == 2) { reportStatus.append(' '); reportStatus.append(String.format(resources.getString("reinforcementsCriticalFailure.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return FAILED; } // Reinforcement successful - if (roll >= reinforcementTargetNumber.getValue()) { + if (roll >= reinforcementTargetNumber) { reportStatus.append(' '); reportStatus.append(String.format(resources.getString("reinforcementsSuccess.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - CLOSING_SPAN_TAG)); + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return SUCCESS; } @@ -1209,18 +1648,18 @@ public static ReinforcementResultsType processReinforcementDeployment( if (interceptionRoll >= interceptionOdds) { reportStatus.append(' '); reportStatus.append(String.format(resources.getString("reinforcementsCommandFailure.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); - return FAILED; + return DELAYED; } // Check failed, but enemy is routed if (contract.getMoraleLevel().isRouted()) { reportStatus.append(' '); reportStatus.append(String.format(resources.getString("reinforcementsSuccessRouted.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - CLOSING_SPAN_TAG)); + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return SUCCESS; } @@ -1228,8 +1667,8 @@ public static ReinforcementResultsType processReinforcementDeployment( // Check failed, enemy attempt interception reportStatus.append(' '); reportStatus.append(String.format(resources.getString("reinforcementsInterceptionAttempt.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), - CLOSING_SPAN_TAG)); + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), + CLOSING_SPAN_TAG)); UUID commanderId = force.getForceCommanderID(); @@ -1238,8 +1677,8 @@ public static ReinforcementResultsType processReinforcementDeployment( reportStatus.append(' '); reportStatus.append(String.format(resources.getString("reinforcementsErrorNoCommander.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return FAILED; } @@ -1251,45 +1690,31 @@ public static ReinforcementResultsType processReinforcementDeployment( reportStatus.append(' '); reportStatus.append(String.format(resources.getString("reinforcementsErrorUnableToFetchCommander.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return FAILED; } + roll = d6(2); + int targetNumber = 9; Skill tactics = commander.getSkill(S_TACTICS); - if (tactics == null) { - reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementCommanderNoSkill.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); - campaign.addReport(reportStatus.toString()); - - MapLocation mapLocation = scenario.getScenarioTemplate().mapParameters.getMapLocation(); - - String templateString = "data/scenariotemplates/%sReinforcements Intercepted.xml"; - - ScenarioTemplate scenarioTemplate = switch (mapLocation) { - case AllGroundTerrain, SpecificGroundTerrain -> ScenarioTemplate.Deserialize(String.format(templateString, "")); - case Space -> ScenarioTemplate.Deserialize(String.format(templateString, "Space ")); - case LowAtmosphere -> ScenarioTemplate.Deserialize(String.format(templateString, "Low-Atmosphere ")); - }; - - generateReinforcementInterceptionScenario(campaign, contract, track, scenarioTemplate, force); - - return INTERCEPTED; + if (tactics != null) { + targetNumber -= tactics.getFinalSkillValue(); + } else { + // Effectively a -1 penalty for being unskilled + targetNumber++; } - roll = d6(2); - int baseTargetNumber = 9; - int targetNumber = baseTargetNumber - tactics.getFinalSkillValue(); - if (roll >= targetNumber) { reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementEvasionSuccessful.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - CLOSING_SPAN_TAG, roll, targetNumber)); + String reportString = tactics != null + ? resources.getString("reinforcementEvasionSuccessful.text") + : resources.getString("reinforcementEvasionSuccessful.noSkill"); + reportStatus.append(String.format(reportString, + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG, roll, targetNumber)); campaign.addReport(reportStatus.toString()); @@ -1302,23 +1727,189 @@ public static ReinforcementResultsType processReinforcementDeployment( reportStatus.append(' '); reportStatus.append(String.format(resources.getString("reinforcementEvasionUnsuccessful.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG, roll, targetNumber)); + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG, roll, targetNumber)); campaign.addReport(reportStatus.toString()); - MapLocation mapLocation = scenario.getScenarioTemplate().mapParameters.getMapLocation(); + ScenarioTemplate scenarioTemplate = getInterceptionScenarioTemplate(force, campaign); + + generateReinforcementInterceptionScenario(campaign, scenario, contract, track, scenarioTemplate, force); + return INTERCEPTED; + } + + /** + * Retrieves the appropriate {@link ScenarioTemplate} for an interception + * scenario based on the + * provided {@link Force} and {@link Campaign}. + *

    + * The method determines which scenario template file should be used by + * analyzing the primary unit + * type of the {@link Force} within the given {@link Campaign}. It then + * deserializes the template + * file into a {@link ScenarioTemplate} object. + *

    + * Special cases: + *

      + *
    • If the primary unit type is `CONV_FIGHTER` or `AEROSPACEFIGHTER` (and a + * random check passes), + * a "Low-Atmosphere" template is selected.
    • + *
    • If the primary unit type qualifies as an `AEROSPACEFIGHTER` or higher, + * a "Space" template is selected.
    • + *
    • Otherwise, the default template is used.
    • + *
    + * + * @param force The {@link Force} instance that the scenario is based on. + * This is used to determine the primary unit type. + * @param campaign The {@link Campaign} in which the interception is taking + * place. + * Provides context for evaluating the {@link Force}. + * @return A {@link ScenarioTemplate} instance based on the template file + * matching the logic above, + * or a default template if no specific case is matched. + * @see ScenarioTemplate#Deserialize(String) + */ + private static ScenarioTemplate getInterceptionScenarioTemplate(Force force, Campaign campaign) { String templateString = "data/scenariotemplates/%sReinforcements Intercepted.xml"; - ScenarioTemplate scenarioTemplate = switch (mapLocation) { - case AllGroundTerrain, SpecificGroundTerrain -> ScenarioTemplate.Deserialize(String.format(templateString, "")); - case Space -> ScenarioTemplate.Deserialize(String.format(templateString, "Space ")); - case LowAtmosphere -> ScenarioTemplate.Deserialize(String.format(templateString, "Low-Atmosphere ")); - }; + ScenarioTemplate scenarioTemplate = ScenarioTemplate.Deserialize(String.format(templateString, "")); - generateReinforcementInterceptionScenario(campaign, contract, track, scenarioTemplate, force); + int primaryUnitType = force.getPrimaryUnitType(campaign); - return INTERCEPTED; + if ((primaryUnitType == CONV_FIGHTER) + || (primaryUnitType == AEROSPACEFIGHTER) && (randomInt(3) == 0)) { + scenarioTemplate = ScenarioTemplate.Deserialize(String.format(templateString, "Low-Atmosphere ")); + } else if (primaryUnitType >= AEROSPACEFIGHTER) { + scenarioTemplate = ScenarioTemplate.Deserialize(String.format(templateString, "Space ")); + } + return scenarioTemplate; + } + + /** + * Calculates the target roll required for determining reinforcements in a + * specific campaign scenario. + * + *

    + * This method evaluates the reinforcement target number by considering various + * factors + * such as the administrative skill of the command liaison, facility ownership + * influence, + * contract-related skill levels, and command rights configurations. Multiple + * modifiers + * are applied step-by-step to generate the final {@link TargetRoll}. + *

    + * + * Steps in Calculation: + *
      + *
    1. Base Target Number:
    2. + *
    3. -- If the {@code commandLiaison} is provided and has administrative + * skill, + * it replaces the default base target number with their skill value.
    4. + *
    5. -- If no liaison is provided, or they lack administrative skill, the base + * target + * number remains at the default value.
    6. + *
    7. Facilities Modifier:
    8. + *
    9. -- Iterates through the facilities in the relevant track to determine + * their + * ownership.
    10. + *
    11. -- If a facility is owned by the player or allied forces, a negative + * modifier + * is applied, reducing the target number.
    12. + *
    13. -- If a facility is owned by non-allied forces, a positive modifier is + * applied, + * increasing the target number.
    14. + *
    15. Skill Modifier:
    16. + *
    17. -- The skill modifier reflects the ally and enemy skill adjustments from + * the contract.
    18. + *
    19. -- If the campaign is operating under an "Independent" rights condition, + * additional + * checks and adjustments are made based on ally and enemy skill levels.
    20. + *
    21. Liaison Command Modifier:
    22. + *
    23. -- If command rights indicate that a liaison is required, the modifier is + * adjusted.
    24. + *
    + * + * @param campaign the {@link Campaign} instance representing the current + * operational campaign. + * @param scenario the {@link StratconScenario} for which reinforcement + * details are being determined. + * @param commandLiaison the {@link Person} acting as the command liaison, or + * {@code null} if no liaison exists. + * @param campaignState the {@link StratconCampaignState} representing the + * state of the overarching campaign. + * @param contract the {@link AtBContract} defining the terms of the + * contract for this scenario. + * @return a {@link TargetRoll} object representing the calculated reinforcement + * target number, + * with appropriate modifiers applied. + */ + public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, + StratconScenario scenario, + @Nullable Person commandLiaison, + StratconCampaignState campaignState, + AtBContract contract) { + // Create Target Roll + TargetRoll reinforcementTargetNumber = new TargetRoll(); + + // Base Target Number + int skillTargetNumber = 12; + SkillType skillType = getSkillHash().get(S_ADMIN); + if (skillType != null) { + skillTargetNumber = getSkillHash().get(S_ADMIN).getTarget(); + } + + if (commandLiaison != null) { + Skill skill = commandLiaison.getSkill(S_ADMIN); + + if (skill != null) { + skillTargetNumber = skill.getFinalSkillValue(); + } + + reinforcementTargetNumber.addModifier(skillTargetNumber, + "Administration (" + commandLiaison.getFullTitle() + ')'); + } else { + reinforcementTargetNumber.addModifier(skillTargetNumber, + "Administration (Unskilled)"); + } + + // Facilities Modifier + StratconTrackState track = scenario.getTrackForScenario(campaign, campaignState); + + int facilityModifier = 0; + if (track != null) { + for (StratconFacility facility : track.getFacilities().values()) { + if (facility.getOwner().equals(ForceAlignment.Player) || facility.getOwner().equals(Allied)) { + facilityModifier--; + } else { + facilityModifier++; + } + } + } + + reinforcementTargetNumber.addModifier(facilityModifier, "Facilities"); + + // Skill Modifier + int skillModifier = -contract.getAllySkill().getAdjustedValue(); + + ContractCommandRights commandRights = contract.getCommandRights(); + if (commandRights.isIndependent()) { + if (campaign.getCampaignOptions().getUnitRatingMethod().isCampaignOperations()) { + skillModifier = -campaign.getReputation().getAverageSkillLevel().getAdjustedValue(); + } + } + + skillModifier += contract.getEnemySkill().getAdjustedValue(); + + reinforcementTargetNumber.addModifier(skillModifier, "Skill Modifier"); + + // Liaison Modifier + if (commandRights.isLiaison()) { + int liaisonModifier = -1; + reinforcementTargetNumber.addModifier(liaisonModifier, "Liaison Command Rights"); + } + + // Return final value + return reinforcementTargetNumber; } /** @@ -1400,12 +1991,12 @@ public static void commitPrimaryForces(Campaign campaign, StratconScenario scena */ private static boolean commanderLanceHasDefensiveAssignment(AtBDynamicScenario scenario, Campaign campaign) { Person lanceCommander = scenario.getLanceCommander(campaign); - if (lanceCommander != null){ + if (lanceCommander != null) { Unit commanderUnit = lanceCommander.getUnit(); if (commanderUnit != null) { - StrategicFormation lance = campaign.getStrategicFormationsTable().get(commanderUnit.getForceId()); + CombatTeam lance = campaign.getCombatTeamsTable().get(commanderUnit.getForceId()); - return (lance != null) && lance.getRole().isDefence(); + return (lance != null) && lance.getRole().isFrontline(); } } @@ -1431,18 +2022,18 @@ private static Map> sortForcesByMapType(List for (int forceID : forceIDs) { switch (campaign.getForce(forceID).getPrimaryUnitType(campaign)) { - case UnitType.BATTLE_ARMOR: - case UnitType.INFANTRY: - case UnitType.MEK: - case UnitType.TANK: - case UnitType.PROTOMEK: - case UnitType.VTOL: + case BATTLE_ARMOR: + case INFANTRY: + case MEK: + case TANK: + case PROTOMEK: + case VTOL: retVal.get(AllGroundTerrain).add(forceID); break; - case UnitType.AEROSPACEFIGHTER: + case AEROSPACEFIGHTER: retVal.get(Space).add(forceID); // intentional fallthrough here, ASFs can go to atmospheric maps too - case UnitType.CONV_FIGHTER: + case CONV_FIGHTER: retVal.get(LowAtmosphere).add(forceID); break; } @@ -1451,33 +2042,103 @@ private static Map> sortForcesByMapType(List } /** - * Worker function that generates stratcon scenario at the given coords, for the - * given force, on the - * given track. Also registers it with the track and campaign. + * Generates a StratCon scenario at the specified coordinates for the given + * force on the specified track. + * The scenario is determined based on a random template suitable for the unit + * type of the specified force, + * and it is optionally configured with a deployment delay. + * + *

    + * This method selects a random scenario template based on the primary unit type + * of the force, + * then delegates the scenario creation and configuration to another overloaded + * {@code generateScenario} method + * which handles specific template-based scenario generation. + *

    + * + * @param campaign the {@link Campaign} managing the overall gameplay + * state + * @param contract the {@link AtBContract} governing the StratCon + * campaign + * @param track the {@link StratconTrackState} where the scenario is + * placed + * @param forceID the ID of the force for which the scenario is + * generated + * @param coords the {@link StratconCoords} specifying where the + * scenario will be generated + * @param daysTilDeployment the number of days until the scenario is deployed; + * if {@code null}, + * deployment dates are determined dynamically + * @return the generated {@link StratconScenario}, or {@code null} if scenario + * generation fails */ - private static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, StratconTrackState track, - int forceID, StratconCoords coords) { + private static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, + StratconTrackState track, int forceID, + StratconCoords coords, + @Nullable Integer daysTilDeployment) { int unitType = campaign.getForce(forceID).getPrimaryUnitType(campaign); ScenarioTemplate template = StratconScenarioFactory.getRandomScenario(unitType); // useful for debugging specific scenario types // template = StratconScenarioFactory.getSpecificScenario("Defend Grounded // Dropship.xml"); - return generateScenario(campaign, contract, track, forceID, coords, template); + return generateScenario(campaign, contract, track, forceID, coords, template, daysTilDeployment); } /** - * Worker function that generates stratcon scenario at the given coords, for the - * given force, on the - * given track, using the given template. Also registers it with the campaign. + * Generates a StratCon scenario at the specified coordinates for the given + * force on the specified track, + * using the provided scenario template. The scenario is customized and + * registered with the campaign. + * + *

    + * The generated scenario is configured as follows: + *

      + *
    • If no template is provided, a random template is chosen based on the unit + * type of the given force.
    • + *
    • If provided, deployment dates are explicitly set. Otherwise, dates are + * determined dynamically.
    • + *
    • Global modifiers, facility modifiers, attached unit modifiers, and allied + * force modifiers + * are applied as appropriate.
    • + *
    • The scenario is marked as unresolved and is registered with the campaign + * and track.
    • + *
    + * This method also handles special conditions: + *
      + *
    • Forces with specific command rights (House or Integrated) will mark the + * scenario as required.
    • + *
    • If no force is provided, the scenario is treated as part of contract + * initialization (e.g., allied forces).
    • + *
    + * + * @param campaign the {@link Campaign} managing the gameplay state + * @param contract the {@link AtBContract} governing the StratCon + * campaign + * @param track the {@link StratconTrackState} to which the scenario + * belongs + * @param forceID the ID of the force for which the scenario is + * generated, or + * {@link Force#FORCE_NONE} if none + * @param coords the {@link StratconCoords} specifying where the + * scenario will be placed + * @param template the {@link ScenarioTemplate} to use for scenario + * generation; if + * {@code null}, a random one is selected + * @param daysTilDeployment the number of days until the scenario is deployed; + * if {@code null}, + * dates will be dynamically set + * @return the generated {@link StratconScenario}, or {@code null} if scenario + * generation failed */ static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, - StratconTrackState track, int forceID, - StratconCoords coords, ScenarioTemplate template) { + StratconTrackState track, int forceID, + StratconCoords coords, ScenarioTemplate template, + @Nullable Integer daysTilDeployment) { StratconScenario scenario = new StratconScenario(); if (template == null) { - int unitType = UnitType.MEK; + int unitType = MEK; try { unitType = campaign.getForce(forceID).getPrimaryUnitType(campaign); @@ -1501,20 +2162,19 @@ private static Map> sortForcesByMapType(List // by default, certain conditions may make this bigger scenario.setRequiredPlayerLances(1); - // do an appropriate allied force if the contract calls for it - // do any attached or integrated units - setAlliedForceModifier(scenario, contract); - setAttachedUnitsModifier(scenario, contract); + // do any facility or global modifiers applyFacilityModifiers(scenario, track, coords); applyGlobalModifiers(scenario, contract.getStratconCampaignState()); - if (contract.getCommandRights().isHouse() || contract.getCommandRights().isIntegrated()) { - scenario.setRequiredScenario(true); - } - - AtBDynamicScenarioFactory.setScenarioModifiers(campaign.getCampaignOptions(), scenario.getBackingScenario()); + AtBDynamicScenarioFactory.setScenarioModifiers(campaign.getCampaignOptions(), + scenario.getBackingScenario()); scenario.setCurrentState(ScenarioState.UNRESOLVED); - setScenarioDates(track, campaign, scenario); + + if (daysTilDeployment == null) { + setScenarioDates(track, campaign, scenario); + } else { + setScenarioDates(daysTilDeployment, track, campaign, scenario); + } // the backing scenario ID must be updated after registering the backing // scenario @@ -1582,58 +2242,13 @@ private static void applyFacilityModifiers(StratconScenario scenario, StratconTr continue; } - modifier.setAdditionalBriefingText( - "(from " + facility.getDisplayableName() + ") " + - modifier.getAdditionalBriefingText()); + modifier.setAdditionalBriefingText('(' + facility.getDisplayableName() + ") " + + modifier.getAdditionalBriefingText()); scenario.getBackingScenario().addScenarioModifier(modifier); } } } - /** - * Set up the appropriate primary allied force modifier, if any - * - * @param contract The scenario's contract. - */ - private static void setAlliedForceModifier(StratconScenario scenario, AtBContract contract) { - int alliedUnitOdds = 0; - - // first, we determine the odds of having an allied unit present - // TODO: move this override out to the contract definition - if (contract.getContractType().isReliefDuty()) { - alliedUnitOdds = 50; - } else { - switch (contract.getCommandRights()) { - case INTEGRATED: - alliedUnitOdds = 50; - break; - case HOUSE: - alliedUnitOdds = 30; - break; - case LIAISON: - alliedUnitOdds = 10; - break; - default: - break; - } - } - - AtBDynamicScenario backingScenario = scenario.getBackingScenario(); - - // if an allied unit is present, then we want to make sure that - // it's ground units for ground battles - if (randomInt(100) <= alliedUnitOdds) { - if ((backingScenario.getTemplate().mapParameters.getMapLocation() == LowAtmosphere) - || (backingScenario.getTemplate().mapParameters.getMapLocation() == Space)) { - backingScenario.addScenarioModifier( - AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_ALLIED_AIR_UNITS)); - } else { - backingScenario.addScenarioModifier( - AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_ALLIED_GROUND_UNITS)); - } - } - } - /** * Set the 'attached' units modifier for the current scenario (integrated, * house, liaison), and make @@ -1645,19 +2260,6 @@ public static void setAttachedUnitsModifier(StratconScenario scenario, AtBContra AtBDynamicScenario backingScenario = scenario.getBackingScenario(); boolean airBattle = (backingScenario.getTemplate().mapParameters.getMapLocation() == LowAtmosphere) || (backingScenario.getTemplate().mapParameters.getMapLocation() == Space); - - // if we're on cadre duty, we're getting three trainees, period - if (contract.getContractType().isCadreDuty()) { - if (airBattle) { - backingScenario.addScenarioModifier( - AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_AIR)); - } else { - backingScenario.addScenarioModifier( - AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_GROUND)); - } - return; - } - // if we're under non-independent command rights, a supervisor may come along switch (contract.getCommandRights()) { case INTEGRATED: @@ -1671,7 +2273,7 @@ public static void setAttachedUnitsModifier(StratconScenario scenario, AtBContra : MHQConstants.SCENARIO_MODIFIER_HOUSE_CO_GROUND)); break; case LIAISON: - if (scenario.isRequiredScenario()) { + if (scenario.isTurningPoint()) { backingScenario.addScenarioModifier( AtBScenarioModifier .getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_LIAISON_AIR @@ -1685,8 +2287,7 @@ public static void setAttachedUnitsModifier(StratconScenario scenario, AtBContra /** * Worker function that sets scenario deploy/battle/return dates based on the - * track's properties and - * current campaign date + * track's properties and current campaign date */ private static void setScenarioDates(StratconTrackState track, Campaign campaign, StratconScenario scenario) { int deploymentDay = track.getDeploymentTime() < 7 ? randomInt(7 - track.getDeploymentTime()) : 0; @@ -1695,7 +2296,8 @@ private static void setScenarioDates(StratconTrackState track, Campaign campaign /** * Worker function that sets scenario deploy/battle/return dates based on the - * track's properties and current campaign date. Takes a fixed deployment day of X days from + * track's properties and current campaign date. Takes a fixed deployment day of + * X days from * campaign's today date. */ private static void setScenarioDates(int deploymentDay, StratconTrackState track, Campaign campaign, @@ -1724,9 +2326,9 @@ private static void setScenarioDates(int deploymentDay, StratconTrackState track private static boolean unitTypeIsAirborne(ScenarioForceTemplate template) { int unitType = template.getAllowedUnitType(); - return ((unitType == UnitType.AEROSPACEFIGHTER) || - (unitType == UnitType.CONV_FIGHTER) || - (unitType == UnitType.DROPSHIP) || + return ((unitType == AEROSPACEFIGHTER) || + (unitType == CONV_FIGHTER) || + (unitType == DROPSHIP) || (unitType == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX)) && (template.getStartingAltitude() > 0); } @@ -1737,65 +2339,117 @@ private static boolean unitTypeIsAirborne(ScenarioForceTemplate template) { * * @return Whether or not the unit types match. */ - public static boolean forceCompositionMatchesDeclaredUnitType(int primaryUnitType, int unitType, - boolean reinforcements) { + public static boolean forceCompositionMatchesDeclaredUnitType(int primaryUnitType, int unitType) { // special cases are "ATB_MIX" and "ATB_AERO_MIX", which encompass multiple unit // types if (unitType == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX) { - // "AtB mix" is usually ground units, but air units can sub in - return (primaryUnitType == UnitType.MEK) || (primaryUnitType == UnitType.TANK) - || (primaryUnitType == UnitType.INFANTRY) - || (primaryUnitType == UnitType.BATTLE_ARMOR) - || (primaryUnitType == UnitType.PROTOMEK) - || (primaryUnitType == UnitType.VTOL) - || (primaryUnitType == UnitType.AEROSPACEFIGHTER) && reinforcements - || (primaryUnitType == UnitType.CONV_FIGHTER) && reinforcements; + return primaryUnitType < JUMPSHIP; } else if (unitType == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_AERO_MIX) { - return (primaryUnitType == UnitType.AEROSPACEFIGHTER) || (primaryUnitType == UnitType.CONV_FIGHTER); + return primaryUnitType >= CONV_FIGHTER; } else { return primaryUnitType == unitType; } } /** - * This is a set of all force IDs for forces that can be deployed to a scenario. + * Retrieves a list of force IDs for all combat teams that are both available + * and suitable for + * deployment under a specific contract. * - * @param campaign Current campaign - * @return List of available force IDs. + *

    + * This method filters out combat teams that do not meet the following criteria: + *

      + *
    • The combat team must be assigned to the specified contract.
    • + *
    • The combat team must not currently be deployed.
    • + *
    • The combat team must have a role other than "In Reserve".
    • + *
    + * + * @param campaign The {@link Campaign} object containing all contracts, + * formations, and states. + * @param contract The {@link AtBContract} under which the combat teams are + * evaluated for deployment. + * @return A {@link List} of force IDs ({@link Integer}) corresponding to all + * suitable combat teams ready for deployment. */ - public static List getAvailableForceIDs(Campaign campaign) { - // first, we gather a set of all forces that are already deployed to a track so - // we eliminate those later - Set forcesInTracks = campaign.getActiveAtBContracts().stream() - .flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()) - .flatMap(track -> track.getAssignedForceCoords().keySet().stream()) - .collect(Collectors.toSet()); + public static List getAvailableForceIDs(Campaign campaign, AtBContract contract) { + // First, build a list of all combat teams in the campaign + ArrayList combatTeams = campaign.getAllCombatTeams(); + + if (combatTeams.isEmpty()) { + // If we don't have any combat teams, there is no point in continuing, so we + // exit early + return Collections.emptyList(); + } + + // Finally, loop through the available combat teams adding those found to be + // suitable to + // the appropriate list. + List suitableForces = new ArrayList<>(); + for (CombatTeam combatTeam : combatTeams) { + // If the combat team isn't assigned to the current contract, it isn't eligible + // to be deployed + if (!Objects.equals(contract, combatTeam.getContract(campaign))) { + continue; + } - // now, we get all the forces that qualify as "lances", and filter out those - // that are - // deployed to a scenario and not in a track already - - return campaign.getStrategicFormationsTable().keySet().stream() - .mapToInt(key -> key) - .mapToObj(campaign::getForce).filter(force -> (force != null) - && !force.isDeployed() - && force.isCombatForce() - && !forcesInTracks.contains(force.getId())) - .map(Force::getId) - .collect(Collectors.toList()); + // So long as the combat team isn't In Reserve or Auxiliary, they are eligible + // to be deployed + CombatRole combatRole = combatTeam.getRole(); + if (!combatRole.isReserve() && !combatRole.isAuxiliary()) { + + if (!combatRole.isTraining() || contract.getContractType().isCadreDuty()) { + suitableForces.add(combatTeam.getForceId()); + } + } + } + + return suitableForces; } /** - * This is a list of all force IDs for forces that can be deployed to a scenario - * in the given force template a) have not been assigned to a track b) are combat-capable c) are - * not deployed to a scenario d) if attempting to deploy as reinforcements, haven't already failed - * to deploy + * Retrieves a list of all force IDs eligible for deployment to a scenario. + *

    + * This method evaluates all forces in the specified {@link Campaign} and + * identifies those that + * meet the criteria for deployment. + *

    + * The criteria ensure that the forces: + *

      + *
    • Are combat-capable (i.e., not auxiliary or in reserve).
    • + *
    • Are not currently assigned to a track (except for the current track if + * deploying as + * reinforcements).
    • + *
    • Are not already deployed to a scenario.
    • + *
    • Have not previously failed to deploy (if deploying as + * reinforcements).
    • + *
    • Match the specified unit type.
    • + *
    + * Forces that meet all conditions are returned as a list of unique force IDs. + * + * @param unitType the desired type of unit to evaluate for deployment + * eligibility. + * @param campaign the {@link Campaign} containing the forces to + * evaluate. + * @param currentTrack the {@link StratconTrackState} representing the + * current track, used to + * filter eligible forces. + * @param reinforcements {@code true} if the forces are being deployed as + * reinforcements; + * otherwise {@code false}. + * @param currentScenario the current {@link StratconScenario}, if any, used to + * exclude failed + * reinforcements. Can be {@code null}. + * @param campaignState the current {@link StratconCampaignState} representing + * the campaign + * state for further filtering of eligible forces. + * @return a {@link List} of unique force IDs that meet all deployment criteria. */ public static List getAvailableForceIDs(int unitType, Campaign campaign, StratconTrackState currentTrack, boolean reinforcements, @Nullable StratconScenario currentScenario, StratconCampaignState campaignState) { List retVal = new ArrayList<>(); - // assemble a set of all force IDs that are currently assigned to tracks that are not this one + // assemble a set of all force IDs that are currently assigned to tracks that + // are not this one Set forcesInTracks = campaign.getActiveAtBContracts().stream() .flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()) .filter(track -> (!Objects.equals(track, currentTrack)) || !reinforcements) @@ -1808,23 +2462,36 @@ public static List getAvailableForceIDs(int unitType, Campaign campaign forcesInTracks.addAll(currentScenario.getFailedReinforcements()); } - for (StrategicFormation formation : campaign.getStrategicFormationsTable().values()) { + for (CombatTeam formation : campaign.getCombatTeamsTable().values()) { Force force = campaign.getForce(formation.getForceId()); if (force == null) { continue; } + if (force.isDeployed()) { + continue; + } + + if (formation.getRole().isReserve()) { + continue; + } + + if (formation.getRole().isAuxiliary() && !reinforcements) { + continue; + } + int primaryUnitType = force.getPrimaryUnitType(campaign); boolean noReinforcementRestriction = !reinforcements || - (getReinforcementType(force.getId(), currentTrack, campaign, campaignState) != ReinforcementEligibilityType.NONE); + (getReinforcementType(force.getId(), currentTrack, campaign, + campaignState) != ReinforcementEligibilityType.NONE); if ((force.getScenarioId() <= 0) - && !force.getAllUnits(true).isEmpty() - && !forcesInTracks.contains(force.getId()) - && forceCompositionMatchesDeclaredUnitType(primaryUnitType, unitType, reinforcements) - && noReinforcementRestriction - && !subElementsOrSelfDeployed(force, campaign)) { + && !force.getAllUnits(true).isEmpty() + && !forcesInTracks.contains(force.getId()) + && forceCompositionMatchesDeclaredUnitType(primaryUnitType, unitType) + && noReinforcementRestriction + && !subElementsOrSelfDeployed(force, campaign)) { retVal.add(force.getId()); } @@ -1865,10 +2532,10 @@ public static List getEligibleDefensiveUnits(Campaign campaign) { // "defensive" units are infantry, battle armor and (Weisman help you) gun // emplacements // and also said unit should be intact/alive/etc - boolean isEligibleInfantry = ((u.getEntity().getUnitType() == UnitType.INFANTRY) - || (u.getEntity().getUnitType() == UnitType.BATTLE_ARMOR)) && !u.isUnmanned(); + boolean isEligibleInfantry = ((u.getEntity().getUnitType() == INFANTRY) + || (u.getEntity().getUnitType() == BATTLE_ARMOR)) && !u.isUnmanned(); - boolean isEligibleGunEmplacement = u.getEntity().getUnitType() == UnitType.GUN_EMPLACEMENT; + boolean isEligibleGunEmplacement = u.getEntity().getUnitType() == GUN_EMPLACEMENT; if ((isEligibleInfantry || isEligibleGunEmplacement) && !u.isDeployed() @@ -1898,7 +2565,8 @@ public static List getEligibleDefensiveUnits(Campaign campaign) { * * @return List of unit IDs. */ - public static List getEligibleLeadershipUnits(Campaign campaign, Set forceIDs, int leadershipSkill) { + public static List getEligibleLeadershipUnits(Campaign campaign, ArrayList forceIDs, + int leadershipSkill) { List eligibleUnits = new ArrayList<>(); // If there is no leadership skill, we shouldn't continue @@ -1927,17 +2595,18 @@ public static List getEligibleLeadershipUnits(Campaign campaign, Set forceIDs) { + private static int getPrimaryUnitType(Campaign campaign, ArrayList forceIDs) { Map unitTypeBuckets = new TreeMap<>(); int biggestBucketID = -1; int biggestBucketCount = 0; @@ -2012,18 +2681,19 @@ public static ReinforcementEligibilityType getReinforcementType(int forceID, Str // TODO: If the force has completed a scenario which allows it, // it can deploy "for free" (ReinforcementEligibilityType.ChainedScenario) - // if the force is in 'fight' stance, it'll be able to deploy using 'fight lance' rules - if (campaign.getStrategicFormationsTable().containsKey(forceID)) { - Hashtable strategicFormations = campaign.getStrategicFormationsTable(); - StrategicFormation formation = strategicFormations.get(forceID); + // if the force is in 'fight' stance, it'll be able to deploy using 'fight + // lance' rules + if (campaign.getCombatTeamsTable().containsKey(forceID)) { + Hashtable combatTeamsTable = campaign.getCombatTeamsTable(); + CombatTeam formation = combatTeamsTable.get(forceID); if (formation == null) { return ReinforcementEligibilityType.NONE; } if (campaignState.getSupportPoints() > 0) { - if (formation.getRole().isFighting()) { - return FIGHT_LANCE; + if (formation.getRole().isManeuver() || formation.getRole().isAuxiliary()) { + return AUXILIARY; } else { return ReinforcementEligibilityType.REGULAR; } @@ -2176,7 +2846,7 @@ public static void processScenarioCompletion(ResolveScenarioTracker tracker) { StratconFacility facility = track.getFacility(scenario.getCoords()); - if (scenario.isRequiredScenario() && !backingScenario.getStatus().isDraw()) { + if (scenario.isTurningPoint() && !backingScenario.getStatus().isDraw()) { campaignState.updateVictoryPoints(victory ? 1 : -1); } @@ -2187,16 +2857,53 @@ public static void processScenarioCompletion(ResolveScenarioTracker tracker) { if ((facility != null) && (facility.getOwnershipChangeScore() > 0)) { switchFacilityOwner(facility); } - - processTrackForceReturnDates(track, campaign); - track.removeScenario(scenario); + + if (backingScenario.getStratConScenarioType().isLosTech()) { + if (victory) { + int roll = randomInt(10); + StarLeagueCache cache = new StarLeagueCache(campaign, ((AtBContract) mission), + CacheType.TRASH_CACHE.ordinal()); + + // The rumor is a dud + // if (false) { // TODO replace placeholder value + // cache.createDudDialog(track, scenario); + // } else { + // if (Objects.equals(cache.getFaction().getShortName(), "SL")) { + // cache.createProposalDialog(); + // } + // } + } + } break; } } } } + public static void linkedScenerioProcessing(ResolveScenarioTracker tracker, List forces) { + Scenario nextScenario = tracker.getCampaign().getScenario(tracker.getScenario().getLinkedScenario()); + + if (nextScenario instanceof AtBScenario) { + StratconCampaignState campaignState = ((AtBScenario)nextScenario).getContract(tracker.getCampaign()).getStratconCampaignState(); + if (campaignState == null) { + return; + } + for (StratconTrackState track : campaignState.getTracks()) { + if (track.getBackingScenariosMap().containsKey(nextScenario.getId())) { + StratconScenario scenario = track.getBackingScenariosMap().get(nextScenario.getId()); + for (int forceID : forces) { + track.unassignForce(forceID); + nextScenario.addForces(forceID); + } + + } + + } + + } + } + /** * Worker function that updates strategic objectives relevant to the passed in * scenario, track and campaign state. For example, "win scenario A" or "win X @@ -2257,6 +2964,9 @@ public static void switchFacilityOwner(StratconFacility facility) { * return date is on or before the given date. */ public static void processTrackForceReturnDates(StratconTrackState track, Campaign campaign) { + final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", + MekHQ.getMHQOptions().getLocale()); + List forcesToUndeploy = new ArrayList<>(); LocalDate date = campaign.getLocalDate(); @@ -2271,6 +2981,9 @@ public static void processTrackForceReturnDates(StratconTrackState track, Campai && (force != null) && !track.getBackingScenariosMap().containsKey(force.getScenarioId()) && !track.getStickyForces().contains(forceID)) { forcesToUndeploy.add(forceID); + + campaign.addReport(String.format(resources.getString("force.undeployed"), + force.getName())); } } @@ -2307,12 +3020,16 @@ public static boolean processIgnoredScenario(StratconScenario scenario, Stratcon for (StratconTrackState track : campaignState.getTracks()) { if (track.getScenarios().containsKey(scenario.getCoords())) { // subtract VP if scenario is 'required' - if (scenario.isRequiredScenario()) { + if (scenario.isTurningPoint()) { campaignState.updateVictoryPoints(-1); } track.removeScenario(scenario); + if (scenario.getBackingScenario().getStratConScenarioType().isResupply()) { + return true; + } + StratconFacility localFacility = track.getFacility(scenario.getCoords()); if (localFacility != null) { // if the ignored scenario was on top of an allied facility diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconScenario.java b/MekHQ/src/mekhq/campaign/stratcon/StratconScenario.java index 4298d8226cd..9ac725602f0 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconScenario.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconScenario.java @@ -13,6 +13,8 @@ */ package mekhq.campaign.stratcon; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlElementWrapper; import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import megamek.common.annotations.Nullable; @@ -21,14 +23,14 @@ import mekhq.campaign.Campaign; import mekhq.campaign.event.DeploymentChangedEvent; import mekhq.campaign.force.Force; -import mekhq.campaign.mission.AtBDynamicScenario; -import mekhq.campaign.mission.ScenarioForceTemplate; -import mekhq.campaign.mission.ScenarioTemplate; +import mekhq.campaign.mission.*; import mekhq.campaign.unit.Unit; import java.time.LocalDate; import java.util.*; +import static mekhq.campaign.stratcon.StratconScenario.ScenarioState.UNRESOLVED; + /** * Class that handles scenario metadata and interaction at the StratCon level * @author NickAragua @@ -41,6 +43,7 @@ public enum ScenarioState { NONEXISTENT, UNRESOLVED, PRIMARY_FORCES_COMMITTED, + AWAITING_REINFORCEMENTS, REINFORCEMENTS_COMMITTED, COMPLETED, IGNORED, @@ -53,6 +56,7 @@ public enum ScenarioState { scenarioStateNames.put(ScenarioState.NONEXISTENT, "Shouldn't be seen"); scenarioStateNames.put(ScenarioState.UNRESOLVED, "Unresolved"); scenarioStateNames.put(ScenarioState.PRIMARY_FORCES_COMMITTED, "Primary forces committed"); + scenarioStateNames.put(ScenarioState.AWAITING_REINFORCEMENTS, "Forces committed reinforcement interception not resolved"); scenarioStateNames.put(ScenarioState.COMPLETED, "Victory"); scenarioStateNames.put(ScenarioState.IGNORED, "Ignored"); scenarioStateNames.put(ScenarioState.DEFEATED, "Defeat"); @@ -68,7 +72,7 @@ public String getScenarioStateName() { private int backingScenarioID; private ScenarioState currentState = ScenarioState.UNRESOLVED; private int requiredPlayerLances; - private boolean requiredScenario; + private boolean turningPoint; private boolean isStrategicObjective; private LocalDate deploymentDate; private LocalDate actionDate; @@ -78,7 +82,7 @@ public String getScenarioStateName() { private boolean ignoreForceAutoAssignment; private int leadershipPointsUsed; private Set failedReinforcements = new HashSet<>(); - private Set primaryForceIDs = new HashSet<>(); + private ArrayList primaryForceIDs = new ArrayList<>(); /** * Add a force to the backing scenario. Do our best to add the force as a "primary" force, as defined in the scenario template. @@ -136,11 +140,23 @@ public List getPlayerTemplateForceIDs() { * These are all the "primary" force IDs, meaning forces that have been used * by the scenario to drive the generation of the OpFor. */ - public Set getPrimaryForceIDs() { + @XmlElementWrapper(name = "primaryForceIDs") + @XmlElement(name = "primaryForceID") + public ArrayList getPrimaryForceIDs() { + // <50.02 compatibility handler + if (primaryForceIDs == null) { + primaryForceIDs = new ArrayList<>(); + } + return primaryForceIDs; } - public void setPrimaryForceIDs(Set primaryForceIDs) { + public void setPrimaryForceIDs(ArrayList primaryForceIDs) { + // <50.02 compatibility handler + if (primaryForceIDs == null) { + primaryForceIDs = new ArrayList<>(); + } + this.primaryForceIDs = primaryForceIDs; } @@ -151,7 +167,6 @@ public void setPrimaryForceIDs(Set primaryForceIDs) { public void commitPrimaryForces() { currentState = ScenarioState.PRIMARY_FORCES_COMMITTED; getPrimaryForceIDs().clear(); - for (int forceID : backingScenario.getPlayerTemplateForceIDs()) { getPrimaryForceIDs().add(forceID); } @@ -167,66 +182,68 @@ public void setCurrentState(ScenarioState state) { @Override public String getInfo() { - return getInfo(null, true); + return getInfo(null); } - public String getInfo(@Nullable Campaign campaign, boolean html) { + public String getInfo(@Nullable Campaign campaign) { StringBuilder stateBuilder = new StringBuilder(); if (isStrategicObjective()) { - stateBuilder.append("Contract objective located") - .append(html ? "
    " : ""); + stateBuilder.append("Contract objective located
    "); } - stateBuilder.append("Scenario: ") - .append(backingScenario.getName()) - .append(html ? "
    " : ""); - - if (backingScenario.getTemplate() != null) { - stateBuilder.append(backingScenario.getTemplate().shortBriefing) - .append(html ? "
    " : ""); - } + if (backingScenario != null) { + stateBuilder.append("Scenario: ") + .append(backingScenario.getName()) + .append("
    "); - if (isRequiredScenario()) { - stateBuilder.append("Deployment required by contract") - .append(html ? "
    " : "").append("-1 VP if lost/ignored; +1 VP if won") - .append(html ? "
    " : ""); - } + if (backingScenario.getTemplate() != null) { + stateBuilder.append("").append(backingScenario.getTemplate().shortBriefing).append("") + .append("
    "); + } - stateBuilder.append("Status: ") - .append(currentState.getScenarioStateName()) - .append("
    "); + if (isTurningPoint()) { + stateBuilder.append("Turning Point
    "); + } - stateBuilder.append("Terrain: ") - .append(backingScenario.getMap()) + stateBuilder.append("Status: ") + .append(currentState.getScenarioStateName()) .append("
    "); - if (deploymentDate != null) { - stateBuilder.append("Deployment Date: ") - .append(deploymentDate) - .append("
    "); - } - if (actionDate != null) { - stateBuilder.append("Battle Date: ") - .append(actionDate) - .append("
    "); - } + stateBuilder.append("Terrain: ") + .append(backingScenario.getMap()) + .append("
    "); - if (returnDate != null) { - stateBuilder.append("Return Date: ") - .append(returnDate) - .append("
    "); - } + if (deploymentDate != null) { + stateBuilder.append("Deployment Date: ") + .append(deploymentDate) + .append("
    "); + } - if (campaign != null) { - AtBDynamicScenario backingScenario = getBackingScenario(); + if (actionDate != null) { + stateBuilder.append("Battle Date: ") + .append(actionDate) + .append("
    "); + } - if (backingScenario != null) { - stateBuilder.append(String.format("Hostile BV: %d
    ", - backingScenario.getTeamTotalBattleValue(campaign, false))); - stateBuilder.append(String.format("Allied BV: %d", - backingScenario.getTeamTotalBattleValue(campaign, true))); + if (returnDate != null) { + stateBuilder.append("Return Date: ") + .append(returnDate) + .append("
    "); + } + + int hostileBV = backingScenario.getTeamTotalBattleValue(campaign, false); + int alliedBV = backingScenario.getTeamTotalBattleValue(campaign, true); + + if (campaign != null) { + stateBuilder.append(String.format("Hostile BV: %s
    ", + hostileBV == 0 && alliedBV == 0 ? "UNKNOWN" : hostileBV)); + stateBuilder.append(String.format("Allied BV: %s", + hostileBV == 0 && alliedBV == 0 ? "UNKNOWN" : alliedBV + )); } } @@ -255,12 +272,12 @@ public void incrementRequiredPlayerLances() { requiredPlayerLances++; } - public boolean isRequiredScenario() { - return requiredScenario; + public boolean isTurningPoint() { + return turningPoint; } - public void setRequiredScenario(boolean requiredScenario) { - this.requiredScenario = requiredScenario; + public void setTurningPoint(boolean turningPoint) { + this.turningPoint = turningPoint; } @XmlTransient @@ -354,6 +371,10 @@ public void addFailedReinforcements(int forceID) { failedReinforcements.add(forceID); } + public void removeFailedReinforcements(int forceID) { + failedReinforcements.remove(forceID); + } + public boolean ignoreForceAutoAssignment() { return ignoreForceAutoAssignment; } @@ -369,4 +390,146 @@ public int getLeadershipPointsUsed() { public void setAvailableLeadershipBudget(int leadershipPointsUsed) { this.leadershipPointsUsed = leadershipPointsUsed; } + + /** + * Retrieves the {@link StratconTrackState} that contains this {@link StratconScenario} + * within the given {@link StratconCampaignState} or derives the campaign state if not provided. + * + *

    + * If a {@link StratconCampaignState} is not provided, the method attempts to derive it using + * information from the backing scenario associated with this {@link StratconScenario}. It uses + * the campaign and contract details to fetch the {@link StratconCampaignState}. Once the + * campaign state is obtained (or provided as input), it searches for the track that contains + * this scenario. + *

    + * + *

    + * If no matching track is found, or if the input or derived data is incomplete (such as + * missing tracks or scenarios), the method returns {@code null}. + *

    + * + * Usage: + *

    + * Use this method to locate the {@link StratconTrackState} that contains this scenario, either by + * directly providing a {@link StratconCampaignState} or allowing the method to derive one using + * the campaign and available scenario details. + *

    + * + * @param campaign The {@link Campaign} containing the data needed to derive the campaign state + * if none is provided. + * @param campaignState The {@link StratconCampaignState} to search for the track containing this scenario. + * Can be {@code null}, in which case the method attempts to determine the campaign state. + * @return The {@link StratconTrackState} that contains this scenario, or {@code null} if no matching track + * is found or if enough data to derive the campaign state is unavailable. + */ + public @Nullable StratconTrackState getTrackForScenario(Campaign campaign, + @Nullable StratconCampaignState campaignState) { + // If a campaign state hasn't been provided, we try to derive it from the available + // scenario information. + if (campaignState == null) { + backingScenario = getBackingScenario(); + + if (backingScenario == null) { + return null; + } + + AtBContract contract = backingScenario.getContract(campaign); + + campaignState = contract.getStratconCampaignState(); + + if (campaignState == null) { + return null; + } + } + + // If we have been provided a campaign state, or have derived one, we can start tracking + // down the associated track. + List tracks = campaignState.getTracks(); + + if (tracks == null) { + return null; + } + + for (StratconTrackState track : tracks) { + Map scenarios = track.getScenarios(); + + if (scenarios == null) { + return null; + } + + if (scenarios.containsValue(this)) { + return track; + } + } + + return null; + } + + /** + * Resets the state of the current scenario for the given campaign. This includes updating the + * scenario state, clearing associated forces and units, and detaching them from the scenario. + * It also ensures that the scenario's backing contract and campaign state remain consistent. + * + * @param campaign The {@link Campaign} object for which the scenario needs to be reset. + *

    + * The method performs the following: + *

      + *
    • Resets the scenario's state to {@code UNRESOLVED}.
    • + *
    • Clears any leadership budget and failed reinforcements associated with the scenario.
    • + *
    • Resets the list of primary forces linked to the scenario.
    • + *
    • If the scenario has a backing {@link AtBDynamicScenario}, it fetches the corresponding contract and + * {@link StratconCampaignState} to handle associated track and force assignments:
    • + *
    • -- Clears all forces and units assigned to the scenario, detaching them appropriately.
    • + *
    • -- Undeploys all units and clears scenario IDs for the forces and units associated with the scenario.
    • + *
    • -- Unassigns the force from the {@link StratconTrackState} and triggers a + * {@link DeploymentChangedEvent} for updates.
    • + *
    + * + * Note: If the backing scenario ID is invalid or the contract is null, the + * method exits early and performs no further actions. + */ + public void resetScenario(Campaign campaign) { + setCurrentState(UNRESOLVED); + setAvailableLeadershipBudget(0); + setFailedReinforcements(new HashSet<>()); + setPrimaryForceIDs(new ArrayList<>()); + + int backingScenarioId = getBackingScenarioID(); + Scenario backingScenario = campaign.getScenario(backingScenarioId); + + if (backingScenarioId != -1 && backingScenario instanceof AtBDynamicScenario) { + AtBContract contract = ((AtBDynamicScenario) backingScenario).getContract(campaign); + if (contract == null) { + return; + } + StratconCampaignState campaignState = contract.getStratconCampaignState(); + + StratconTrackState track = getTrackForScenario(campaign, campaignState); + for (Force force : campaign.getAllForces()) { + if (force.getScenarioId() == backingScenarioId) { + force.clearScenarioIds(campaign, true); + backingScenario.removeForce(force.getId()); + + for (UUID uid : force.getAllUnits(false)) { + Unit unit = campaign.getUnit(uid); + if (unit != null) { + backingScenario.removeUnit(unit.getId()); + unit.undeploy(); + } + } + + track.unassignForce(force.getId()); + MekHQ.triggerEvent(new DeploymentChangedEvent(force, backingScenario)); + } + } + + for (Unit unit : campaign.getUnits()) { + if (unit.getScenarioId() == backingScenarioId) { + backingScenario.removeUnit(unit.getId()); + unit.undeploy(); + MekHQ.triggerEvent(new DeploymentChangedEvent(unit, backingScenario)); + } + } + } + } } diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java b/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java index 322bea7a4f3..ab29ecbed87 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java @@ -281,6 +281,19 @@ public void unassignForce(int forceID) { } } + /** + * Handles the unassignment of a force from this track. + */ + public void unassignUnit(int forceID) { + if (assignedForceCoords.containsKey(forceID)) { + assignedCoordForces.get(assignedForceCoords.get(forceID)).remove(forceID); + assignedForceCoords.remove(forceID); + assignedForceReturnDates.remove(forceID); + removeStickyForce(forceID); + getAssignedForceReturnDatesForStorage().remove(forceID); + } + } + /** * Restores the look up table of force IDs to return dates */ @@ -441,6 +454,26 @@ public boolean hasActiveTrackReveal() { return getFacilities().values().stream().anyMatch(StratconFacility::getRevealTrack); } + /** + * Determines the number of facilities on this track that actively reveal the track. + * + *

    This method iterates through all facilities associated with the track and counts + * how many of them have the ability to reveal the track, as determined by the facility's + * {@link StratconFacility#getIncreaseScanRange()} method.

    + * + * @return an integer representing the total number of facilities on this track + * that are actively revealing it. + */ + public int getScanRangeIncrease() { + int scanRange = 0; + for (StratconFacility facility : getFacilities().values()) { + if (facility.getIncreaseScanRange()) { + scanRange++; + } + } + return scanRange; + } + /** * Count of all the scenario odds adjustments from facilities * (and potentially other sources) on this track. diff --git a/MekHQ/src/mekhq/campaign/stratcon/SupportPointNegotiation.java b/MekHQ/src/mekhq/campaign/stratcon/SupportPointNegotiation.java new file mode 100644 index 00000000000..4691bf29bab --- /dev/null +++ b/MekHQ/src/mekhq/campaign/stratcon/SupportPointNegotiation.java @@ -0,0 +1,277 @@ +package mekhq.campaign.stratcon; + +import megamek.common.Compute; +import megamek.common.annotations.Nullable; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.Skill; + +import java.util.*; + +import static mekhq.campaign.personnel.SkillType.S_ADMIN; +import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; + +/** + * This class handles Support Point negotiations for StratCon. + *

    + * It includes functionality to negotiate both initial and weekly support points for contracts, + * based on the skill levels of available Admin/Transport personnel. + * + *

    The workflow includes:

    + *
      + *
    • Filtering and sorting Admin/Transport personnel by their skill levels.
    • + *
    • Negotiating support points for either a single contract (initial negotiation) or all + * active contracts (weekly negotiation).
    • + *
    • Calculating support points based on dice rolls and personnel skill levels.
    • + *
    • Generating appropriate campaign reports reflecting the success or failure of negotiations.
    • + *
    + */ +public class SupportPointNegotiation { + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", + MekHQ.getMHQOptions().getLocale()); + + /** + * Negotiates weekly additional support points for all active AtB contracts. + * + *

    Uses available Admin/Transport personnel to negotiate support points for contracts, with older contracts + * being processed first. Personnel are removed from the available pool as they are assigned to contracts. + * If no Admin/Transport personnel are available, an error report is generated, and the method exits early.

    + * + *

    Calculated support points are added to the contract if successful, and reports detailing the + * outcome are appended to the campaign reports.

    + * + * @param campaign The {@link Campaign} instance managing the current game state. + */ + public static void negotiateAdditionalSupportPoints(Campaign campaign) { + // Fetch all active contracts and sort them by start date (oldest -> newest) + List activeContracts = campaign.getActiveAtBContracts(); + + if (activeContracts.isEmpty()) { + return; + } + + List sortedContracts = getSortedContractsByStartDate(activeContracts); + + // Get sorted Admin/Transport personnel + List adminTransport = getSortedAdminTransportPersonnel(campaign); + + // If no Admin/Transport personnel, exit early + if (adminTransport.isEmpty()) { + addReportNoPersonnel(campaign, null); + return; + } + + // Iterate over contracts and negotiate support points + for (AtBContract contract : sortedContracts) { + if (adminTransport.isEmpty()) { + break; + } + + processContractSupportPoints(campaign, contract, adminTransport, false); + } + } + + /** + * Negotiates initial support points for a specific AtB contract. + * + *

    This method processes a single contract and uses available Admin/Transport personnel to negotiate + * support points. If no Admin/Transport personnel are available, an error report is generated, and + * the method exits early.

    + * + *

    Calculated support points are added to the contract if successful, and a report detailing the + * outcome is appended to the campaign reports.

    + * + * @param campaign The {@link Campaign} instance managing the current game state. + * @param contract The {@link AtBContract} instance representing the contract for which initial support + * points are being negotiated. + */ + public static void negotiateInitialSupportPoints(Campaign campaign, AtBContract contract) { + // Get sorted Admin/Transport personnel + List adminTransport = getSortedAdminTransportPersonnel(campaign); + + // If no Admin/Transport personnel, exit early + if (adminTransport.isEmpty()) { + addReportNoPersonnel(campaign, contract); + return; + } + + // Negotiate support points for the specific contract + processContractSupportPoints(campaign, contract, adminTransport, true); + } + + /** + * Processes the negotiation of support points for a given AtB contract. + * + *

    Rolls dice for assigned personnel to determine successful negotiations. Support points + * are calculated based on skill levels and the success of the dice rolls. Personnel are + * removed from the pool once assigned, and support points are added to the contract if + * successfully negotiated.

    + * + * @param campaign The {@link Campaign} instance managing the current game state. + * @param contract The {@link AtBContract} instance for which support points are being processed. + * @param adminTransport A {@link List} of available {@link Person} objects representing Admin/Transport personnel. + * @param isInitialNegotiation {@code true} if the negotiation took place at the beginning of + * the contract, otherwise {@code false} + */ + private static void processContractSupportPoints(Campaign campaign, AtBContract contract, + List adminTransport, boolean isInitialNegotiation) { + int negotiatedSupportPoints = 0; + int maxSupportPoints = isInitialNegotiation + ? contract.getRequiredCombatTeams() * 3 + : contract.getRequiredCombatTeams(); + + StratconCampaignState campaignState = contract.getStratconCampaignState(); + + if (campaignState == null) { + return; + } + + if (campaignState.getSupportPoints() >= maxSupportPoints) { + String pluralizer = (maxSupportPoints > 1) || (maxSupportPoints == 0) ? "s" : ""; + + campaign.addReport(String.format( + resources.getString("supportPoints.maximum"), + contract.getName(), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), + CLOSING_SPAN_TAG, + maxSupportPoints, + pluralizer)); + + return; + } + + Iterator iterator = adminTransport.iterator(); + + while (iterator.hasNext() && negotiatedSupportPoints < maxSupportPoints) { + Person admin = iterator.next(); + int rollResult = Compute.d6(2); + negotiatedSupportPoints += calculateSupportPoints(admin, rollResult); + iterator.remove(); + } + + // Determine font color based on success or failure + String fontColor = (negotiatedSupportPoints > 0) + ? MekHQ.getMHQOptions().getFontColorPositiveHexColor() + : MekHQ.getMHQOptions().getFontColorNegativeHexColor(); + + // Add points to the contract if positive + if (negotiatedSupportPoints > 0) { + campaignState.addSupportPoints(negotiatedSupportPoints); + } + + // Add a report + String pluralizer = (negotiatedSupportPoints > 1) || (negotiatedSupportPoints == 0) ? "s" : ""; + if (isInitialNegotiation) { + campaign.addReport(String.format( + resources.getString("supportPoints.initial"), + contract.getName(), + spanOpeningWithCustomColor(fontColor), + negotiatedSupportPoints, + CLOSING_SPAN_TAG, + pluralizer)); + } else { + campaign.addReport(String.format( + resources.getString("supportPoints.weekly"), + spanOpeningWithCustomColor(fontColor), + negotiatedSupportPoints, + CLOSING_SPAN_TAG, + pluralizer, + contract.getName())); + } + } + + /** + * Filters and sorts Admin/Transport personnel from the campaign by their skill levels in descending order. + * + * @param campaign The {@link Campaign} instance containing personnel to be filtered and sorted. + * @return A {@link List} of {@link Person} objects representing Admin/Transport personnel, sorted by skill. + */ + private static List getSortedAdminTransportPersonnel(Campaign campaign) { + List adminTransport = new ArrayList<>(); + for (Person person : campaign.getAdmins()) { + if (person.getPrimaryRole().isAdministratorTransport() + || person.getSecondaryRole().isAdministratorTransport()) { + // Each character gets to roll three times, so we add them to the list three times. + adminTransport.add(person); + adminTransport.add(person); + adminTransport.add(person); + } + } + + adminTransport.sort((p1, p2) -> Integer.compare(getSkillValue(p2), getSkillValue(p1))); + return adminTransport; + } + + /** + * Sorts all active AtB contracts by their start date in ascending order. + * + * @return A {@link List} of {@link AtBContract} instances, sorted by start date. + */ + private static List getSortedContractsByStartDate(List activeContracts) { + activeContracts.sort(Comparator.comparing(AtBContract::getStartDate)); + return activeContracts; + } + + /** + * Calculates the number of support points based on a die roll and the skill level of a given Admin/Transport person. + * + *

    If the dice roll meets or exceeds the admin's skill level, at least one support point is awarded, + * with additional support points depending on the margin of success.

    + * + * @param admin The {@link Person} representing the Admin/Transport personnel rolling for support points. + * @param rollResult The result of rolling two six-sided dice (2d6). + * @return The number of support points awarded based on the roll and skill level. + */ + private static int calculateSupportPoints(Person admin, int rollResult) { + int adminSkill = admin.getSkill(S_ADMIN).getFinalSkillValue(); + if (rollResult < adminSkill) { + return 0; + } + + int points = 1; // Base success + int marginOfSuccess = (rollResult - adminSkill) / 4; + points += marginOfSuccess; + return points; + } + + /** + * Adds a report to the campaign log indicating the absence of Admin/Transport personnel for support point negotiations. + * + *

    If a contract is specified, the report is related to that contract. Otherwise, the report is general + * (e.g., for weekly negotiations).

    + * + * @param campaign The {@link Campaign} instance managing the current game state. + * @param contract An optional {@link AtBContract} instance representing the affected contract (can be {@code null}). + */ + private static void addReportNoPersonnel(Campaign campaign, @Nullable AtBContract contract) { + String reportKey = String.format("supportPoints.%s.noAdministrators", + contract == null ? "weekly" : "initial"); + + if (contract == null) { + campaign.addReport(String.format(resources.getString(reportKey), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG + )); + } else { + campaign.addReport(String.format(resources.getString(reportKey), + contract.getName(), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG + )); + } + } + + /** + * Calculates the total skill value for a given person by summing their skill level and bonuses. + * + * @param person The {@link Person} whose skill value is being calculated. + * @return An {@code int} representing the total skill value (level + bonus). + */ + private static int getSkillValue(Person person) { + Skill skill = person.getSkill(S_ADMIN); + return skill.getTotalSkillLevel(); + } +} diff --git a/MekHQ/src/mekhq/campaign/unit/AbstractTransportedUnitsSummary.java b/MekHQ/src/mekhq/campaign/unit/AbstractTransportedUnitsSummary.java new file mode 100644 index 00000000000..c9b7b351d50 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/unit/AbstractTransportedUnitsSummary.java @@ -0,0 +1,260 @@ +/** + * Copyright (c) 2025-2025 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.unit; + +import megamek.common.*; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; +import mekhq.campaign.unit.enums.TransporterType; + +import java.util.*; + +/** + * Tracks what units this transport is transporting, and its current capacity for its different transporter types. + */ +public abstract class AbstractTransportedUnitsSummary implements ITransportedUnitsSummary { + private static final MMLogger logger = MMLogger.create(AbstractTransportedUnitsSummary.class); + protected Unit transport; + private Set transportedUnits = new HashSet<>(); + private Map transportCapacity = new HashMap<>(); + + AbstractTransportedUnitsSummary(Unit transport) { + this.transport = transport; + if (transport.getEntity() != null) { + recalculateTransportCapacity(transport.getEntity().getTransports()); + } + } + + /** + * Main method to be used for unloading units from a transport + * + * @param transportedUnits Units we wish to unload + */ + @Override + public void unloadTransport(Set transportedUnits) { + for (Unit transportedUnit : transportedUnits) { + unloadTransport(transportedUnit); + } + } + + protected void unloadTransport(Unit transportedUnit) { + Objects.requireNonNull(transportedUnit); + + // Remove this unit from our collection of transported units. + removeTransportedUnit(transportedUnit); + } + + /** + * Recalculates transport capacity - make sure you pass in all the transporters + * of a given type (class), or just all the transporters an entity has. + * @param transporters What transporters are we recalculating? + * @see Entity#getTransports() + */ + @Override + public void recalculateTransportCapacity(@Nullable Vector transporters) { + if (transporters == null || transporters.isEmpty()) { + return; + } + + // First let's clear the transport capacity for each transport type in transporters + for (Transporter transporter : transporters) { + TransporterType transporterType = TransporterType.getTransporterType(transporter); + if (hasTransportCapacity(transporterType)) { + transportCapacity.remove(transporterType); + } + } + + // Then we make sure the transport entity is empty, and then let's load + // our transport entity so we can use the Transporter's getUnused method + clearTransportedEntities(); + loadTransportedEntities(); + + // Now we can update our transport capacities using the unused space of each transporter + for (Transporter transporter : transporters) { + TransporterType transporterType = TransporterType.getTransporterType(transporter); + if (transportCapacity.containsKey(transporterType)) { + transportCapacity.replace(transporterType, transportCapacity.get(transporterType) + transporter.getUnused()); + } else { + transportCapacity.put(transporterType, transporter.getUnused()); + } + } + + // Finally clear the transport entity again + clearTransportedEntities(); + } + + /** + * If this unit is capable of transporting another unit, return true + * + * @return true if the unit can transport another unit + */ + @Override + public boolean hasTransportCapacity() { + return !transportCapacity.isEmpty(); + } + + /** + * Gets the different kinds of transporters the transport has + * + * @return Set of Transporter classes + */ + @Override + public Set getTransportCapabilities() { + return transportCapacity.keySet(); + } + + /** + * Returns true if the unit has capacity left for a transporter type + * + * @param transporterType Does the unit have free capacity in this type? + * @return True if the unit has capacity, false if not + */ + @Override + public boolean hasTransportCapacity(TransporterType transporterType) { + return transportCapacity.containsKey(transporterType); + } + + /** + * Returns the current capacity of a transporter type + * + * @param transporterType What kind of transporter types are we checking? + * @return The current capacity of the transporter, or 0 + */ + @Override + public double getCurrentTransportCapacity(TransporterType transporterType) { + return transportCapacity.getOrDefault(transporterType, 0.0); + } + + /** + * Sets the current transport capacity for the provided transport type + * + * @param transporterType What kind of transporter are we changing the capacity of? + * @param capacity What is the new capacity? + */ + @Override + public void setCurrentTransportCapacity(TransporterType transporterType, double capacity) { + transportCapacity.replace(transporterType, capacity); + } + + /** + * Gets a value indicating whether or not this unit is + * transporting units. + * + * @return true if the unit has any transported units + */ + @Override + public boolean hasTransportedUnits() { + return !transportedUnits.isEmpty(); + } + + /** + * @return the set of units being transported by this unit. + */ + @Override + public Set getTransportedUnits() { + return Collections.unmodifiableSet(transportedUnits); + } + + /** + * Adds a unit to our set of transported units. + * + * @param unit The unit being transported by this instance. + */ + @Override + public void addTransportedUnit(Unit unit) { + transportedUnits.add(Objects.requireNonNull(unit)); + } + + /** + * Removes a unit from our set of transported units. + * + * @param unit The unit to remove from our set of transported units. + * @return True if the unit was removed, otherwise false. + */ + @Override + public boolean removeTransportedUnit(Unit unit) { + return transportedUnits.remove(unit); + } + + /** + * Clears the set of units being transported by this unit. + */ + @Override + public void clearTransportedUnits() { + if (!transportedUnits.isEmpty()) { + transportedUnits.clear(); + } + + clearTransportedEntities(); + } + + protected Set clearTransportedEntities() { + Set transportedEntities = new HashSet<>(); + if (transport.getEntity() != null) { + if (transport.getEntity().hasUnloadedUnitsFromBays()) { + for (Entity transportedEntity : transport.getEntity().getUnitsUnloadableFromBays()) { + transport.getEntity().unload(transportedEntity); + transportedEntities.add(transportedEntity); + } + } // We can't just use Entity::getUnloadableUnits(); getUnloadableFromBays() throws NPE in that flow + for (Entity transportedEntity : transport.getEntity().getUnitsUnloadableFromNonBays()) { + transport.getEntity().unload(transportedEntity); + transportedEntities.add(transportedEntity); + } + + transport.getEntity().resetTransporter(); + } + return transportedEntities; + } + + protected void loadTransportedEntities() { + if (transport.getEntity() != null) { + for (Unit transportedUnit : getTransportedUnits()) { + if (transportedUnit.getEntity() != null) { + transport.getEntity().resetBays(); + loadEntity(transportedUnit.getEntity()); + } + } + } + } + + protected void loadEntity(Entity transportedEntity) { + if (transport.getEntity() != null && transportedEntity != null) { + if (transport.getEntity().canLoad(transportedEntity, false)) { + transport.getEntity().load(transportedEntity, false); + } + else { + logger.error(String.format("Could not load entity %s onto unit %s", transportedEntity.getDisplayName(), transport.getName())); + } + } + } + + /** + * When fixing references we need to replace the transported units + * @param newTransportedUnits The units that should be transported + */ + @Override + public void replaceTransportedUnits(Set newTransportedUnits) { + clearTransportedUnits(); + for (Unit newUnit : newTransportedUnits) { + addTransportedUnit(newUnit); + } + } +} diff --git a/MekHQ/src/mekhq/campaign/unit/CargoStatistics.java b/MekHQ/src/mekhq/campaign/unit/CargoStatistics.java index 00890936aa0..2b85c340c1a 100644 --- a/MekHQ/src/mekhq/campaign/unit/CargoStatistics.java +++ b/MekHQ/src/mekhq/campaign/unit/CargoStatistics.java @@ -24,6 +24,7 @@ import megamek.common.*; import mekhq.campaign.Campaign; import mekhq.campaign.Hangar; +import mekhq.campaign.parts.Part; /** * Provides methods to gather statistics on cargo in a campaign. @@ -90,9 +91,13 @@ public double getCargoTonnage(final boolean inTransit, final boolean mothballed) double cargoTonnage = 0; double mothballedTonnage = 0; - cargoTonnage += getCampaign().getWarehouse().streamSpareParts().filter(p -> inTransit || p.isPresent()) - .mapToDouble(p -> p.getQuantity() * p.getTonnage()) - .sum(); + // if we're in transit or the part is present and has a meaningful tonnage, accumulate it + // not sure what the "in transit" flag is for, but I'm leaving it to retain current behavior + for (Part part : getCampaign().getWarehouse().getSpareParts()) { + if ((inTransit || part.isPresent()) && !Double.isNaN(part.getTonnage())) { + cargoTonnage += part.getQuantity() * part.getTonnage(); + } + } // place units in bays // FIXME: This has been temporarily disabled. It really needs DropShip assignments done to fix it correctly. diff --git a/MekHQ/src/mekhq/campaign/unit/ITransportAssignment.java b/MekHQ/src/mekhq/campaign/unit/ITransportAssignment.java new file mode 100644 index 00000000000..643860cf9b0 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/unit/ITransportAssignment.java @@ -0,0 +1,85 @@ +/* + * ITransportAssignment.java + * + * Copyright (c) 2020-2025 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.unit; + +import megamek.common.Transporter; +import mekhq.campaign.Campaign; +import mekhq.campaign.unit.enums.TransporterType; + +import java.util.Optional; + +/** + * Represents an assignment on a transport. + * @see ITransportedUnitsSummary + * @see mekhq.campaign.enums.CampaignTransportType + */ +public interface ITransportAssignment { + + /** + * The transport that is assigned + * @return + */ + Unit getTransport(); + + boolean hasTransport(); + + TransporterType getTransporterType(); + + boolean hasTransporterType(); + + /** + * Where is this unit being transported? + * @return The transporter this unit is in + */ + Transporter getTransportedLocation(); + + /** + * Is this unit in a specific location? + * @return true if it is + */ + boolean hasTransportedLocation(); + + /** + * Convert location to hash to assist with saving/loading + * @return hash int, or null if none + */ + Optional hashTransportedLocation(); + + /** + * After loading UnitRefs need converted to Units + * @see Unit#fixReferences(Campaign campaign) + * @param campaign Campaign we need to fix references for + * @param unit Unit we need to fix references for + */ + void fixReferences(Campaign campaign, Unit unit); + + /** + * Bays have some extra functionality other transporters don't have, like + * having a tech crew, which will matter for boarding actions against + * dropships and other Ship Transports. This method determines if this + * transport assignment is for a Bay. + * @return true if the unit is transported in a Bay or a subclass + * @see megamek.common.Bay + */ + boolean isTransportedInBay(); + +} diff --git a/MekHQ/src/mekhq/campaign/unit/ITransportedUnitsSummary.java b/MekHQ/src/mekhq/campaign/unit/ITransportedUnitsSummary.java new file mode 100644 index 00000000000..73af23387f6 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/unit/ITransportedUnitsSummary.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2025-2025 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.unit; + +import megamek.common.Entity; +import megamek.common.Infantry; +import megamek.common.InfantryBay; +import megamek.common.Transporter; +import mekhq.campaign.Campaign; +import mekhq.campaign.unit.enums.TransporterType; + +import java.util.Set; +import java.util.Vector; + +public interface ITransportedUnitsSummary { + + /** + * Gets a value indicating whether or not this unit is + * transporting units. + * @return true if the unit has any transported units + */ + boolean hasTransportedUnits(); + + /** + * @return the set of units being transported by this unit. + */ + Set getTransportedUnits(); + + /** + * Adds a unit to our set of transported units. + * + * @param unit The unit being transported by this instance. + */ + void addTransportedUnit(Unit unit); + + /** + * Removes a unit from our set of transported units. + * + * @param unit The unit to remove from our set of transported units. + * @return True if the unit was removed, otherwise false. + */ + boolean removeTransportedUnit(Unit unit); + + /** + * Clears the set of units being transported by this unit. + */ + void clearTransportedUnits(); + + /** + * If this unit is capable of transporting another unit, return true + * @return true if the unit can transport another unit + */ + boolean hasTransportCapacity(); + + /** + * Gets the different kinds of transporters the transport has + * @return Set of Transporter types + */ + Set getTransportCapabilities(); + + /** + * Returns true if the unit has capacity left for a transporter type + * @param transporterType Does the unit have free capacity in this type? + * @return True if the unit has capacity, false if not + */ + boolean hasTransportCapacity(TransporterType transporterType); + + /** + * Returns the current capacity of a transporter type + * @param transporterType What kind of transporter types are we checking? + * @return The current capacity of the transporter + */ + double getCurrentTransportCapacity(TransporterType transporterType); + + /** + * Sets the current transport capacity for the provided transport type + * @param transporterType What kind of transporter are we changing the capacity of? + * @param capacity What is the new capacity? + */ + void setCurrentTransportCapacity(TransporterType transporterType, double capacity); + + /** + * Recalculates transport capacity + * @param transporters What transporters are we tracking the details of? + */ + void recalculateTransportCapacity(Vector transporters); + + /** + * When fixing references we need to replace the transported units + * @param newTransportedUnits The units that should be transported + */ + void replaceTransportedUnits(Set newTransportedUnits); + + /** + * Bay unloading utility used when removing a bay-equipped Transport unit + * This removes all units assigned to the transport from it + * + * @param campaign used to remove this unit as a transport from any other units in the campaign + */ + void clearTransportedUnits(Campaign campaign); + + /** + * Main method to be used for loading units onto a transport + * @param transportedUnits Units we wish to load + * @return the old transports the transportedUnits were assigned to, or an empty set + */ + //Set loadTransport(Unit... transportedUnits); + + /** + * Main method to be used for unloading units from a transport + * @param transportedUnits Units we wish to unload + */ + void unloadTransport(Set transportedUnits); + + /** + * Fixes references after loading + */ + void fixReferences(Campaign campaign, Unit unit); + +} diff --git a/MekHQ/src/mekhq/campaign/unit/MothballInfo.java b/MekHQ/src/mekhq/campaign/unit/MothballInfo.java index 469d753500a..598f91e4432 100644 --- a/MekHQ/src/mekhq/campaign/unit/MothballInfo.java +++ b/MekHQ/src/mekhq/campaign/unit/MothballInfo.java @@ -20,19 +20,19 @@ */ package mekhq.campaign.unit; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.Version; +import megamek.common.force.Force; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; import mekhq.campaign.personnel.Person; import mekhq.utilities.MHQXMLUtility; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** * This class is used to store information about a particular unit that is @@ -56,6 +56,7 @@ public class MothballInfo { * Parameterless constructor, used for deserialization. */ private MothballInfo() { + forceID = Force.NO_FORCE; drivers = new ArrayList<>(); gunners = new ArrayList<>(); vesselCrew = new ArrayList<>(); @@ -138,9 +139,7 @@ public void writeToXML(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "techId", tech.getId()); } - if (forceID > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "forceID", forceID); - } + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "forceID", forceID); for (Person driver : drivers) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "driverId", driver.getId()); diff --git a/MekHQ/src/mekhq/campaign/unit/ShipTransportedUnitsSummary.java b/MekHQ/src/mekhq/campaign/unit/ShipTransportedUnitsSummary.java new file mode 100644 index 00000000000..36f188d6afe --- /dev/null +++ b/MekHQ/src/mekhq/campaign/unit/ShipTransportedUnitsSummary.java @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2025-2025 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.unit; + +import megamek.common.*; +import megamek.logging.MMLogger; +import mekhq.Utilities; +import mekhq.campaign.Campaign; +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.unit.enums.TransporterType; +import mekhq.campaign.utilities.CampaignTransportUtilities; + +import java.util.*; + +/** + * Tracks what units this transport is transporting, and its current capacity for its different transporter types. + * @see AbstractTransportedUnitsSummary + * @see CampaignTransportType#SHIP_TRANSPORT + * + */ +public class ShipTransportedUnitsSummary extends AbstractTransportedUnitsSummary { + private static final MMLogger logger = MMLogger.create(ShipTransportedUnitsSummary.class); + /** + * Initialize the transport details for a transport ship + * @param transport unit this summary is about + */ + public ShipTransportedUnitsSummary(Unit transport) { + super(transport); + } + + /** + * Main method to be used for unloading units from a transport + * + * @param transportedUnits Units we wish to unload + */ + @Override + public void unloadTransport(Set transportedUnits) { + super.unloadTransport(transportedUnits); + + } + + @Override + protected void unloadTransport(Unit transportedUnit) { + super.unloadTransport(transportedUnit); + + // And if the unit is being transported by us, + // then update its transport ship assignment (provided the + // assignment is actually to us!). + if (transportedUnit.hasTransportShipAssignment() + && transportedUnit.getTransportShipAssignment().getTransportShip().equals(transport)) { + transportedUnit.setTransportShipAssignment(null); + + } + } + + + /** + * Bay loading utility used when assigning units to bay-equipped transport units + * For each passed-in unit, this will find the first available, transport bay + * and set + * both the target bay and the transport ship. Once in the MM lobby, + * this data + * will be used to actually load the unit into a bay on the transport. + * + * @param transportedUnits units being loaded + * @param transporterType type (Enum) of bay or Transporter + * @return old transports; what were the units' previous transport, if they had one? + */ + public Set loadTransportShip(Vector transportedUnits, TransporterType transporterType) { + Set oldTransports = new HashSet<>(); + for (Unit transportedUnit : transportedUnits) { + Unit oldTransport = loadTransport(transporterType, transportedUnit); + if (oldTransport != null) { + oldTransports.add(oldTransport); + } + } + + //Let's get matching transporters and then recalculate our transport capacity for this transporter type + Vector transporters = new Vector<>(transport.getEntity().getTransports().stream() + .filter(transporter -> TransporterType.getTransporterType(transporter) == transporterType).toList()); + recalculateTransportCapacity(transporters); + + return oldTransports; + } + + private Unit loadTransport(TransporterType transporterType, Unit transportedUnit) { + Unit oldTransport = null; + int bayNumber = Utilities.selectBestBayFor(transportedUnit.getEntity(), transport.getEntity()); + + TransporterType oldTransporterType = null; + if(transportedUnit.hasTransportShipAssignment()) { + oldTransport = transportedUnit.getTransportShipAssignment().getTransportShip(); + oldTransporterType = transportedUnit.getTransportShipAssignment().getTransporterType(); + if (oldTransport.getEntity() != null) { + oldTransport.unloadFromTransportShip(transportedUnit); + } + } + + transportedUnit.setTransportShipAssignment(new TransportShipAssignment(transport, bayNumber)); + + if ((transportedUnit.getEntity() != null)) { + // This shouldn't happen, but it'd be really annoying to debug if it did + if ((transportedUnit.getEntity().getBayById(bayNumber) != null && TransporterType.getTransporterType(transportedUnit.getEntity().getBayById(bayNumber)) != transporterType)) { + logger.warn(String.format("Unit was assigned a bay number for a different transport type than the unit is assigned! " + + "Transport: %s Unit: %s Assigned Transporter: %s Assigned Bay ID: %s", + transport.getName(), transportedUnit.getName(), transporterType, bayNumber)); + } + } + + addTransportedUnit(transportedUnit); + if (!Objects.equals(oldTransport, transport) + && (transportedUnit.getTransportShipAssignment().getTransporterType() != oldTransporterType)) { + setCurrentTransportCapacity(transporterType, + getCurrentTransportCapacity(transporterType) - CampaignTransportUtilities.transportCapacityUsage(transporterType, transportedUnit.getEntity())); + } + return oldTransport; + } + + /** + * Bay unloading utility used when removing units from bay-equipped transport + * units + * and/or moving them to a new transport + * + * @param transportedUnit The unit that we wish to unload from this transport + */ + public void unloadFromTransportShip(Unit transportedUnit) { + unloadTransport(transportedUnit); + } + + /** + * Bay unloading utility used when removing a bay-equipped Transport unit + * This removes all units assigned to the transport from it + */ + @Override + public void clearTransportedUnits(Campaign campaign) { + clearTransportedUnits(); + + // And now reset the Transported values for all the units we just booted + campaign.getHangar().forEachUnit(u -> { + if (u.hasTransportShipAssignment() + && Objects.equals(transport, u.getTransportShipAssignment().getTransportShip())) { + u.setTransportShipAssignment(null); + } + }); + + recalculateTransportCapacity(transport.getEntity().getTransports()); + } + + /** + * Fixes references after loading + * + * @param campaign + * @param unit + */ + @Override + public void fixReferences(Campaign campaign, Unit unit) { + Set newTransportedUnits = new HashSet<>(); + for (Unit transportedUnit : getTransportedUnits()) { + if (transportedUnit instanceof Unit.UnitRef) { + Unit realUnit = campaign.getHangar().getUnit(transportedUnit.getId()); + if (realUnit != null) { + newTransportedUnits.add(realUnit); + } else { + logger.error( + String.format("Unit %s ('%s') references missing transported unit %s", + unit.getId(), unit.getName(), transportedUnit.getId())); + } + } else { + newTransportedUnits.add(transportedUnit); + } + } + replaceTransportedUnits(newTransportedUnits); + } +} diff --git a/MekHQ/src/mekhq/campaign/unit/TacticalTransportedUnitsSummary.java b/MekHQ/src/mekhq/campaign/unit/TacticalTransportedUnitsSummary.java new file mode 100644 index 00000000000..62f670f0256 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/unit/TacticalTransportedUnitsSummary.java @@ -0,0 +1,214 @@ +/** + * Copyright (c) 2025-2025 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.unit; + +import megamek.common.*; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.unit.enums.TransporterType; +import mekhq.campaign.utilities.CampaignTransportUtilities; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * Tracks what units this transport is transporting, and its current capacity for its different transporter types. + * @see AbstractTransportedUnitsSummary + * @see CampaignTransportType#TACTICAL_TRANSPORT + * + */ +public class TacticalTransportedUnitsSummary extends AbstractTransportedUnitsSummary { + private static final MMLogger logger = MMLogger.create(TacticalTransportedUnitsSummary.class); + + public TacticalTransportedUnitsSummary(Unit transport) { + super(transport); + } + + /** + * Main method to be used for unloading units from a transport + * + * @param transportedUnits Units we wish to unload + */ + @Override + public void unloadTransport(Set transportedUnits) { + super.unloadTransport(transportedUnits); + + } + + @Override + protected void unloadTransport(Unit transportedUnit) { + super.unloadTransport(transportedUnit); + + // And if the unit is being transported by us, + // then update its transport assignment (provided the + // assignment is actually to us!). + if (transportedUnit.hasTacticalTransportAssignment() + && transportedUnit.getTacticalTransportAssignment().getTransport().equals(transport)) { + transportedUnit.setTacticalTransportAssignment(null); + } + } + + /** + * Transporter loading utility used when assigning units to transport units + * For each passed-in unit, this will assign the unit to the type of + * Transporter if one isn't provided. Once in the MM lobby, + * will be used to actually load the unit into a bay on the transport. + * + * @param transporterType type (Enum) of bay or Transporter + * @param units units being loaded + * @return old transports; what were the units' previous transport, if they had one + */ + public Set loadTransport(TransporterType transporterType, Set units) { + return loadTransport(units, null, transporterType); + } + + + /** + * Transporter loading utility used when assigning units to transport units + * For each passed-in unit, this will assign the unit to the specified bay, + * or the type of Transporter if one isn't provided. Once in the MM lobby, + * will be used to actually load the unit into a bay on the transport. + * + * @param units units being loaded + * @param transportedLocation specific bay (Transporter), or null + * @param transporterType type (Enum) of bay or Transporter + * @return old transports; what were the units' previous transport, if they had one + */ + public Set loadTransport(Set units, @Nullable Transporter transportedLocation, TransporterType transporterType) { + Set oldTransports = new HashSet<>(); + //Set oldTransportedEntities = clearTransportedEntities(); + for (Unit transportedUnit : units) { + Unit oldTransport = loadTransport(transportedLocation, transporterType, transportedUnit); + if (oldTransport != null) { + oldTransports.add(oldTransport); + } + } + transport.initializeTacticalTransportSpace(); + return oldTransports; + } + + + /** + * Transporter loading utility used when assigning units to transport units + * For the passed in unit, this will assign the unit to the specified bay, + * or the type of Transporter if one isn't provided. Once in the MM lobby, + * will be used to actually load the unit into a bay on the transport. + * + * @param transportedLocation specific bay, or null + * @param transporterType type (Enum) of bay or Transporter + * @param transportedUnit unit being loaded + * @return old transport; what was the unit's previous transport, if it had one + */ + public Unit loadTransport(@Nullable Transporter transportedLocation, TransporterType transporterType, Unit transportedUnit) { + Unit oldTransport = null; + TransporterType oldTransporterType = null; + + if (transportedUnit.hasTacticalTransportAssignment()) { + oldTransport = transportedUnit.getTacticalTransportAssignment().getTransport(); + oldTransporterType = transportedUnit.getTacticalTransportAssignment().getTransporterType(); + if (oldTransport.getEntity() != null) { + oldTransport.unloadTacticalTransport(transportedUnit); + } + } + if (transportedLocation != null) { + transportedUnit.setTacticalTransportAssignment(new TransportAssignment(transport, transportedLocation)); + } + else if (transporterType != null){ + transportedUnit.setTacticalTransportAssignment(new TransportAssignment(transport, transporterType)); + } else { + logger.error(String.format("Cannot load transport (%s) with unit (%s) without a transported location or transporter!", transport.getId(), transportedUnit.getId())); + return oldTransport; + } + addTransportedUnit(Objects.requireNonNull(transportedUnit)); + + // Update Transport Capacities + if (!Objects.equals(oldTransport, transport) + && (transportedUnit.getTacticalTransportAssignment().getTransporterType() != oldTransporterType)) { + setCurrentTransportCapacity(transporterType, + getCurrentTransportCapacity(transporterType) - CampaignTransportUtilities.transportCapacityUsage(transporterType,transportedUnit.getEntity())); + } + return oldTransport; + } + + /** + * Bay unloading utility used when removing units from bay-equipped transport + * units + * and/or moving them to a new transport + * + * @param transportedUnit The unit that we wish to unload from this transport + */ + public void unloadFromTransport(Unit transportedUnit) { + unloadTransport(transportedUnit); + } + + /** + * Bay unloading utility used when removing a bay-equipped Transport unit + * This removes all units assigned to the transport from it + * + * @param campaign used to remove this unit as a transport from any other units in the campaign + */ + @Override + public void clearTransportedUnits(Campaign campaign) { + clearTransportedUnits(); + + // And now reset the Transported values for all the units we just booted + campaign.getHangar().forEachUnit(u -> { + if (u.hasTacticalTransportAssignment() + && Objects.equals(transport, u.getTacticalTransportAssignment().getTransport())) { + u.setTacticalTransportAssignment(null); + } + }); + + recalculateTransportCapacity(transport.getEntity().getTransports()); + } + + /** + * Fixes references after loading + */ + @Override + public void fixReferences(Campaign campaign, Unit unit) { + Set oldTransportedUnits = new HashSet<>(getTransportedUnits()); + clearTransportedUnits(); + for (Unit tacticalTransportedUnit : oldTransportedUnits) { + if (tacticalTransportedUnit instanceof Unit.UnitRef) { + Unit realUnit = campaign.getHangar().getUnit(tacticalTransportedUnit.getId()); + if (realUnit != null) { + if (realUnit.hasTacticalTransportAssignment()) { + loadTransport(realUnit.getTacticalTransportAssignment().getTransporterType(), new HashSet<>(Collections.singleton(realUnit))); + } else { + logger.error( + String.format("Unit %s ('%s') references tactical transported unit %s which has no transport assignment", + unit.getId(), unit.getName(), tacticalTransportedUnit.getId())); + } + } else { + logger.error( + String.format("Unit %s ('%s') references missing tactical transported unit %s", + unit.getId(), unit.getName(), tacticalTransportedUnit.getId())); + } + } else { + loadTransport(tacticalTransportedUnit.getTacticalTransportAssignment().getTransporterType(), new HashSet<>(Collections.singleton(tacticalTransportedUnit))); + } + } + } +} diff --git a/MekHQ/src/mekhq/campaign/unit/TransportAssignment.java b/MekHQ/src/mekhq/campaign/unit/TransportAssignment.java new file mode 100644 index 00000000000..921f8690d8e --- /dev/null +++ b/MekHQ/src/mekhq/campaign/unit/TransportAssignment.java @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2025-2025 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.unit; + +import megamek.common.Bay; +import megamek.common.Transporter; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.unit.enums.TransporterType; + +import java.util.Optional; + +/** + * Represents an assignment on a transport. Currently only used by TACTICAL_TRANSPORT + * but this could be used by other transport types. + * @see TacticalTransportedUnitsSummary + * @see CampaignTransportType#TACTICAL_TRANSPORT + */ +public class TransportAssignment implements ITransportAssignment { + protected final MMLogger logger = MMLogger.create(this.getClass()); + + Unit transport; + Transporter transportedLocation; + TransporterType transporterType; + + public TransportAssignment(Unit transport) { + this(transport, (TransporterType) null); + } + + public TransportAssignment(Unit transport, TransporterType transporterType) { + setTransport(transport); + setTransporterType(transporterType); + } + + public TransportAssignment(Unit transport, @Nullable Transporter transportedLocation) { + setTransport(transport); + setTransportedLocation(transportedLocation); + setTransporterType(hasTransportedLocation() ? TransporterType.getTransporterType(getTransportedLocation()) : null); + } + + public TransportAssignment(Unit transport, int hashedTransportedLocation) { + setTransport(transport); + for (Transporter location : transport.getEntity().getTransports()) { + if (location.hashCode() == hashedTransportedLocation) { + setTransportedLocation(transportedLocation); + break; + } + } + + } + + + /** + * The transport that is assigned + * + * @return + */ + @Override + public Unit getTransport() { + return transport; + } + + @Override + public boolean hasTransport() { + return transport != null; + } + + /** + * Change the transport assignment to have a new transport + * + * @param newTransport transport, or null if none + * @return true if a unit was provided, false if it was null + */ + protected boolean setTransport(Unit newTransport) { + this.transport = newTransport; + return hasTransport(); + } + + @Override + public TransporterType getTransporterType() { + return transporterType; + } + + @Override + public boolean hasTransporterType() { + return transporterType != null; + } + + protected boolean setTransporterType(TransporterType transporterType) { + this.transporterType = transporterType; + return hasTransporterType(); + } + + @Override + public Transporter getTransportedLocation() { + return transportedLocation; + } + + /** + * Is this unit in a specific location? + * + * @return true if it is + */ + @Override + public boolean hasTransportedLocation() { + return transportedLocation != null; + } + + /** + * Set where this unit should be transported + * + * @return true if a location was provided, false if it was null + */ + protected boolean setTransportedLocation(@Nullable Transporter transportedLocation) { + this.transportedLocation = transportedLocation; + return hasTransportedLocation(); + } + + /** + * Convert location to hash to assist with saving/loading + * + * @return hash int, or null if none + */ + @Override + public Optional hashTransportedLocation() { + if (hasTransportedLocation()) { + return Optional.of(getTransportedLocation().hashCode()); + } + return Optional.empty(); + } + + /** + * After loading UnitRefs need converted to Units + * + * @param campaign Campaign we need to fix references for + * @param unit the unit that needs references fixed + * @see Unit#fixReferences(Campaign campaign) + */ + @Override + public void fixReferences(Campaign campaign, Unit unit) { + if (getTransport() instanceof Unit.UnitRef) { + Unit transport = campaign.getHangar().getUnit(getTransport().getId()); + if (transport != null) { + if (hasTransportedLocation()) { + setTransport(transport); + + } else if (hasTransporterType()) { + setTransport(transport); + } + else { + setTransport(transport); + logger.warn( + String.format("Unit %s ('%s') is missing a transportedLocation " + + "and transporterType for tactical transport %s", + unit.getId(), unit.getName(), getTransport().getId())); + } + } else { + logger.error( + String.format("Unit %s ('%s') references missing transport %s", + unit.getId(), unit.getName(), getTransport().getId())); + + unit.setTacticalTransportAssignment(null); + } + } + } + + /** + * Bays have some extra functionality other transporters don't have, like + * having a tech crew, which will matter for boarding actions against + * dropships and other Ship Transports. This method determines if this + * transport assignment is for a Bay. + * + * @return true if the unit is transported in a Bay or a subclass + * @see Bay + */ + @Override + public boolean isTransportedInBay() { + return (hasTransporterType() && Bay.class.isAssignableFrom(getTransporterType().getTransporterClass())); + } +} diff --git a/MekHQ/src/mekhq/campaign/unit/TransportShipAssignment.java b/MekHQ/src/mekhq/campaign/unit/TransportShipAssignment.java index afd6892c2f3..10ac32a8491 100644 --- a/MekHQ/src/mekhq/campaign/unit/TransportShipAssignment.java +++ b/MekHQ/src/mekhq/campaign/unit/TransportShipAssignment.java @@ -1,7 +1,7 @@ /* * TransportShipAssignment.java * - * Copyright (c) 2020 The Megamek Team. All rights reserved. + * Copyright (c) 2020-2025 The Megamek Team. All rights reserved. * * This file is part of MekHQ. * @@ -21,13 +21,19 @@ package mekhq.campaign.unit; +import mekhq.campaign.Campaign; +import mekhq.campaign.enums.CampaignTransportType; + import java.util.Objects; /** * Represents an assignment to a specific bay on a transport ship. + * Currently only used by SHIP_TRANSPORT + * but this could be used by other transport types. + * @see ShipTransportedUnitsSummary + * @see CampaignTransportType#SHIP_TRANSPORT */ -public class TransportShipAssignment { - private final Unit transportShip; +public class TransportShipAssignment extends TransportAssignment{ private final int bayNumber; /** @@ -36,15 +42,19 @@ public class TransportShipAssignment { * @param bayNumber The bay number on the transport ship. */ public TransportShipAssignment(Unit transportShip, int bayNumber) { - this.transportShip = Objects.requireNonNull(transportShip); + super(transportShip); this.bayNumber = bayNumber; + + if (getTransportShip().getEntity() != null) { + setTransportedLocation(transportShip.getEntity().getBayById(bayNumber)); + } } /** * Gets the transport ship for this assignment. */ public Unit getTransportShip() { - return transportShip; + return transport; } /** @@ -54,6 +64,29 @@ public int getBayNumber() { return bayNumber; } + /** + * After loading UnitRefs need converted to Units + * + * @param campaign Campaign we need to fix references for + * @param unit the unit that needs references fixed + * @see Unit#fixReferences(Campaign campaign) + */ + @Override + public void fixReferences(Campaign campaign, Unit unit) { + if (getTransportShip() instanceof Unit.UnitRef){ + Unit transportShip = campaign.getHangar().getUnit(getTransportShip().getId()); + if (transportShip != null) { + setTransport(transportShip); + } else { + logger.error( + String.format("Unit %s ('%s') references missing transport ship %s", + unit.getId(), unit.getName(), getTransportShip().getId())); + + unit.setTransportShipAssignment(null); + } + } + } + @Override public boolean equals(Object o) { if (o == null) { @@ -67,6 +100,7 @@ public boolean equals(Object o) { } } + @Override public int hashCode() { return Objects.hash(getTransportShip(), getBayNumber()); diff --git a/MekHQ/src/mekhq/campaign/unit/Unit.java b/MekHQ/src/mekhq/campaign/unit/Unit.java index 2eb4e33569c..40fb142f630 100644 --- a/MekHQ/src/mekhq/campaign/unit/Unit.java +++ b/MekHQ/src/mekhq/campaign/unit/Unit.java @@ -2,7 +2,7 @@ * Unit.java * * Copyright (c) 2009 Jay Lawson (jaylawson39 at yahoo.com). All rights reserved. - * Copyright (c) 2016-2024 The MegaMek Team. All Rights Reserved. + * Copyright (c) 2016-2025 The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -25,7 +25,6 @@ import megamek.client.ui.swing.tileset.EntityImage; import megamek.codeUtilities.MathUtility; import megamek.common.*; -import megamek.common.InfantryBay.PlatoonType; import megamek.common.annotations.Nullable; import megamek.common.equipment.AmmoMounted; import megamek.common.equipment.ArmorType; @@ -42,6 +41,7 @@ import mekhq.MekHQ; import mekhq.Utilities; import mekhq.campaign.Campaign; +import mekhq.campaign.enums.CampaignTransportType; import mekhq.campaign.event.PersonCrewAssignmentEvent; import mekhq.campaign.event.PersonTechAssignmentEvent; import mekhq.campaign.event.UnitArrivedEvent; @@ -59,6 +59,7 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.unit.enums.CrewAssignmentState; +import mekhq.campaign.unit.enums.TransporterType; import mekhq.campaign.work.IAcquisitionWork; import mekhq.campaign.work.IPartWork; import mekhq.utilities.MHQXMLUtility; @@ -70,6 +71,8 @@ import javax.swing.*; import java.awt.*; import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.math.BigInteger; import java.util.List; import java.util.*; @@ -77,7 +80,12 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import static mekhq.campaign.enums.CampaignTransportType.SHIP_TRANSPORT; +import static mekhq.campaign.enums.CampaignTransportType.TACTICAL_TRANSPORT; +import static java.lang.Math.max; +import static megamek.common.MiscType.F_CARGO; import static mekhq.campaign.parts.enums.PartQuality.*; +import static mekhq.campaign.unit.enums.TransporterType.*; /** * This is a wrapper class for entity, so that we can add some functionality to @@ -105,27 +113,22 @@ public class Unit implements ITechnology { private boolean salvaged; private UUID id; private String fluffName; + + // This is the large craft assigned to transport this unit private TransportShipAssignment transportShipAssignment; - // If this unit is a transport, list all other units assigned to it - private Set transportedUnits = new HashSet<>(); - private double aeroCapacity = 0.0; - private double baCapacity = 0.0; - private int dockCapacity = 0; - private double hVeeCapacity = 0.0; - private double infCapacity = 0.0; - private double lVeeCapacity = 0.0; - private double mekCapacity = 0.0; - private double protoCapacity = 0.0; - private double shVeeCapacity = 0.0; - private double scCapacity = 0.0; + // This is the transport assigned for scenario deployments + private ITransportAssignment tacticalTransportAssignment; + //Contains what kind of transport it is, what units it's carrying, and the remaining capacity + Set transportedUnitsSummaries = new HashSet<>(); + // assignments private int forceId; protected int scenarioId; private List drivers; - private List gunners; + private Set gunners; private List vesselCrew; // Contains unique Id of each Infantry/BA Entity assigned to this unit as // marines @@ -180,7 +183,7 @@ public Unit(Entity en, Campaign c) { this.parts = new ArrayList<>(); this.podSpace = new ArrayList<>(); this.drivers = new ArrayList<>(); - this.gunners = new ArrayList<>(); + this.gunners = new HashSet<>(); this.vesselCrew = new ArrayList<>(); forceId = Force.FORCE_NONE; scenarioId = Scenario.S_DEFAULT_ID; @@ -188,6 +191,7 @@ public Unit(Entity en, Campaign c) { this.lastMaintenanceReport = ""; this.fluffName = ""; this.maintenanceMultiplier = 4; + initializeAllTransportSpace(); reCalc(); } @@ -299,18 +303,115 @@ public void reCalc() { // Do Nothing } - public void initializeBaySpace() { + /** + * Initializes the transport capacity. If the campaign transport + * capacity type doesn't exist yet, try to create it. If it does + * already exist, let's recalculate the transport capacity instead + * for all transporters that this Unit has + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + public void initializeShipTransportSpace() { // Initialize the bay capacity - this.aeroCapacity = getASFCapacity(); - this.baCapacity = getBattleArmorCapacity(); - this.dockCapacity = getDocks(); - this.hVeeCapacity = getHeavyVehicleCapacity(); - this.infCapacity = getInfantryCapacity(); - this.lVeeCapacity = getLightVehicleCapacity(); - this.mekCapacity = getMekCapacity(); - this.protoCapacity = getProtoMekCapacity(); - this.shVeeCapacity = getSuperHeavyVehicleCapacity(); - this.scCapacity = getSmallCraftCapacity(); + initializeTransportSpace(SHIP_TRANSPORT); + } + + /** + * Initializes the transport capacity. If the campaign transport + * capacity type doesn't exist yet, try to create it. If it does + * already exist, let's recalculate the transport capacity instead + * for all transporters that this Unit has + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see CampaignTransportType#TACTICAL_TRANSPORT + */ + public void initializeTacticalTransportSpace() { + initializeTransportSpace(TACTICAL_TRANSPORT); + } + + /** + * For each CampaignTransportType, initialize the transport capacity. + * If the campaign transport + * capacity type doesn't exist yet, try to create it. If it does + * already exist, let's recalculate the transport capacity instead + * for all transporters that this Unit has + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see CampaignTransportType + */ + public void initializeAllTransportSpace() { + for(CampaignTransportType campaignTransportType : CampaignTransportType.values()) { + initializeTransportSpace(campaignTransportType); + } + } + + + private ShipTransportedUnitsSummary getShipTransportedUnitsSummary() { + return (ShipTransportedUnitsSummary) getTransportedUnitsSummary(SHIP_TRANSPORT); + } + + private TacticalTransportedUnitsSummary getTacticalTransportedUnitsSummary() { + return (TacticalTransportedUnitsSummary) getTransportedUnitsSummary(TACTICAL_TRANSPORT); + } + + /** + * Initializes the transport capacity. If the campaign transport capacity type doesn't exist yet, + * try to create it. If it does already exist, let's recalculate the transport capacity instead for + * all transporters that this Unit has + * @param campaignTransportType transport type we want to prepare + */ + public void initializeTransportSpace(CampaignTransportType campaignTransportType) { + // Initialize the capacity + if (hasTransportedUnitsType(campaignTransportType)) { + getTransportedUnitsSummary(campaignTransportType).recalculateTransportCapacity(getEntity().getTransports()); + } else { + try { + Constructor constructor = campaignTransportType.getTransportedUnitsSummaryType().getConstructor(new Class[]{Unit.class}); + addTransportedUnitType(constructor.newInstance(this)); + } catch (NoSuchMethodException e) { + logger.error(String.format("Could not find constructor to initialize transport space for %s Error: %s Cause: %s", campaignTransportType.name(), e.toString(), e.getCause())); + } catch (InvocationTargetException e) { + logger.error(String.format("Could not find constructor to initialize transport space for %s Error: %s Cause: %s", campaignTransportType.name(), e.toString(), e.getCause())); + } catch (InstantiationException e) { + logger.error(String.format("Could not find constructor to initialize transport space for %s Error: %s Cause: %s", campaignTransportType.name(), e.toString(), e.getCause())); + } catch (IllegalAccessException e) { + logger.error(String.format("Could not find constructor to initialize transport space for %s Error: %s Cause: %s", campaignTransportType.name(), e.toString(), e.getCause())); + } + } + } + + /** + * check to make sure the transported unit summary type exists + * @param campaignTransportType the transported unit type we're checking + * @return true if it exists, false if it doesn't + */ + private boolean hasTransportedUnitsType(CampaignTransportType campaignTransportType) { + for(AbstractTransportedUnitsSummary transportedUnitsSummary : transportedUnitsSummaries) { + if (transportedUnitsSummary.getClass() == campaignTransportType.getTransportedUnitsSummaryType()) { + return true; + } + } + return false; + } + + /** + * For the provided campaign transport type, what's this unit's transported units summary + * @param campaignTransportType what kind of transport type are we checking + * @return transported units summary of that type, or null + */ + public AbstractTransportedUnitsSummary getTransportedUnitsSummary(CampaignTransportType campaignTransportType) { + for(AbstractTransportedUnitsSummary transportedUnitSummary : transportedUnitsSummaries) { + if (transportedUnitSummary.getClass() == campaignTransportType.getTransportedUnitsSummaryType()) { + return transportedUnitSummary; + } + } + return null; + } + + private void addTransportedUnitType(AbstractTransportedUnitsSummary transportedUnitType) { + transportedUnitsSummaries.add(transportedUnitType); + } + + private void fixTransportedUnitReferences(AbstractTransportedUnitsSummary currentTransportedUnits, Set newTransportedUnits) { + currentTransportedUnits.replaceTransportedUnits(newTransportedUnits); } public void setEntity(Entity en) { @@ -337,6 +438,118 @@ public void setId(UUID i) { this.id = i; } + // Generic Transport Methods + + /** + * For the given transport type, is this unit + * transporting any other units? + * @param campaignTransportType Transport Type (Enum) we're checking + * @return true if it has transported units + * @see CampaignTransportType + */ + public boolean hasTransportedUnits(CampaignTransportType campaignTransportType) { + if (hasTransportedUnitsType(campaignTransportType)) { + return getTransportedUnitsSummary(campaignTransportType).hasTransportedUnits(); + } + return false; + } + + /** + * For the given transport type, return the + * set of units it's transporting, or an + * empty set + * @param campaignTransportType Transport Type (Enum) we're checking + * @return Set of Units this transport is carrying, or an empty set + */ + public Set getTransportedUnits(CampaignTransportType campaignTransportType) { + if (hasTransportedUnits(campaignTransportType)) { + return getTransportedUnitsSummary(campaignTransportType).getTransportedUnits(); + } + return new HashSet(); + } + + /** + * For the given campaign transport type, add a unit to our transported units summary + * @param campaignTransportType Transport Type (Enum) we're checking + * @param unit transported unit we're adding + */ + void addTransportedUnit(CampaignTransportType campaignTransportType, Unit unit) { + getTransportedUnitsSummary(campaignTransportType).addTransportedUnit(unit); + } + + /** + * For the given campaign transport type, remove a unit to our transported units summary + * @param campaignTransportType Transport Type (Enum) we're checking + * @param unit transported unit we're adding + */ + boolean removeTransportedUnit(CampaignTransportType campaignTransportType, Unit unit) { + return getTransportedUnitsSummary(campaignTransportType).removeTransportedUnit(unit); + } + + /** + * Clears the set of units being transported by this unit. + */ + public void clearTransportedUnits(CampaignTransportType campaignTransportType) { + getTransportedUnitsSummary(campaignTransportType).clearTransportedUnits(); + } + + /** + * Does this unit have a transport assignment for this campaign transport type? + * @param campaignTransportType the transport type (enum) we're interested in + * @return true if there is a transport assignment of that type, false if not + */ + public boolean hasTransportAssignment(CampaignTransportType campaignTransportType) { + if (campaignTransportType.isShipTransport()) { + return hasTransportShipAssignment(); + } else if (campaignTransportType.isTacticalTransport()) { + return hasTacticalTransportAssignment(); + } + return false; + } + + /** + * Returns the transport assignment for the given transport type, or null if none is provided + * @param campaignTransportType the transport type (enum) we're interested in + * @return corresponding transport assignment, or null if there isn't one + */ + public ITransportAssignment getTransportAssignment(CampaignTransportType campaignTransportType) { + if (campaignTransportType.isShipTransport()) { + return transportShipAssignment; + } else if (campaignTransportType.isTacticalTransport()) { + return tacticalTransportAssignment; + } + return null; + } + + /** + * Set this unit's transport assignment to the provided assignment, if possible + * @param campaignTransportType type (enum) of transport type + * @param assignment the assignment we're setting for this unit + * @see CampaignTransportType + */ + public void setTransportAssignment(CampaignTransportType campaignTransportType, ITransportAssignment assignment) { + if (campaignTransportType.isShipTransport()) { + if (assignment.getClass().isAssignableFrom(campaignTransportType.getTransportAssignmentType())) { + setTransportShipAssignment((TransportShipAssignment) assignment); + } + } else if (campaignTransportType.isTacticalTransport()) { + setTacticalTransportAssignment(assignment); + } + } + + /** + * Unloads a unit from a transport of the provided campaign transport type + * @param campaignTransportType type (enum) of transport type we want to unload from + * @return transport the unit was assigned to + */ + public Unit unloadFromTransport(CampaignTransportType campaignTransportType) { + Unit oldTransport = getTransportAssignment(campaignTransportType).getTransport(); + oldTransport.getTransportedUnitsSummary(campaignTransportType).unloadTransport(this); + return oldTransport; + } + + // End Generic Transport Methods + // A set of methods for working with transport ship assignment for this unit /** @@ -369,15 +582,15 @@ public void setTransportShipAssignment(@Nullable TransportShipAssignment assignm * Gets a value indicating whether or not this unit is * transporting units. */ - public boolean hasTransportedUnits() { - return !transportedUnits.isEmpty(); + public boolean hasShipTransportedUnits() { + return hasTransportedUnits(SHIP_TRANSPORT); } /** * @return the set of units being transported by this unit. */ - public Set getTransportedUnits() { - return Collections.unmodifiableSet(transportedUnits); + public Set getShipTransportedUnits() { + return getTransportedUnits(SHIP_TRANSPORT); } /** @@ -385,8 +598,8 @@ public Set getTransportedUnits() { * * @param unit The unit being transported by this instance. */ - public void addTransportedUnit(Unit unit) { - transportedUnits.add(Objects.requireNonNull(unit)); + public void addShipTransportedUnit(Unit unit) { + addTransportedUnit(SHIP_TRANSPORT, unit); } /** @@ -395,11 +608,11 @@ public void addTransportedUnit(Unit unit) { * @param unit The unit being transported by this instance. * @param bayNumber The bay which will contain the unit. */ - public void addTransportedUnit(Unit unit, int bayNumber) { + public void addShipTransportedUnit(Unit unit, int bayNumber) { Objects.requireNonNull(unit); unit.setTransportShipAssignment(new TransportShipAssignment(this, bayNumber)); - addTransportedUnit(unit); + addShipTransportedUnit(unit); } /** @@ -408,15 +621,15 @@ public void addTransportedUnit(Unit unit, int bayNumber) { * @param unit The unit to remove from our set of transported units. * @return True if the unit was removed from our bays, otherwise false. */ - public boolean removeTransportedUnit(Unit unit) { - return transportedUnits.remove(unit); + public boolean removeShipTransportedUnit(Unit unit) { + return getShipTransportedUnitsSummary().removeTransportedUnit(unit); } /** * Clears the set of units being transported by this unit. */ - public void clearTransportedUnits() { - transportedUnits.clear(); + public void clearShipTransportedUnits() { + getShipTransportedUnitsSummary().clearTransportedUnits(); } /** @@ -424,7 +637,7 @@ public void clearTransportedUnits() { * units */ public boolean isCarryingSmallerAero() { - return transportedUnits.stream().anyMatch(u -> u.getEntity().isAero() + return getShipTransportedUnitsSummary().getTransportedUnits().stream().anyMatch(u -> u.getEntity().isAero() && !u.getEntity().isLargeCraft() && (u.getEntity().getUnitType() != UnitType.SMALL_CRAFT)); } @@ -433,7 +646,7 @@ public boolean isCarryingSmallerAero() { * Gets a value indicating whether or not we are transporting any ground units. */ public boolean isCarryingGround() { - return transportedUnits.stream().anyMatch(u -> !u.getEntity().isAero()); + return getShipTransportedUnitsSummary().getTransportedUnits().stream().anyMatch(u -> !u.getEntity().isAero()); } public int getSite() { @@ -1012,7 +1225,7 @@ public void damageSystem(int type, int equipmentNum, int hits) { // make sure we take note of existing hits to start and as we cycle through // locations int existingHits = getHitCriticals(type, equipmentNum); - int neededHits = Math.max(0, hits - existingHits); + int neededHits = max(0, hits - existingHits); int usedHits = 0; for (int loc = 0; loc < getEntity().locations(); loc++) { if (neededHits > usedHits) { @@ -1282,19 +1495,69 @@ public Money getSellValue() { return partsValue; } + /** + * Computes the total cargo capacity of the entity, accounting for transport bays + * and mounted equipment designated for cargo, only if the entity is fully crewed. + * + *

    The total cargo capacity is the sum of the following:

    + *
      + *
    • The usable capacities of transport bays that are instances of {@link CargoBay}, + * {@link RefrigeratedCargoBay}, or {@link InsulatedCargoBay}, adjusted for damage.
    • + *
    • The tonnage of mounted equipment marked as cargo (via the {@code F_CARGO} flag), + * provided the equipment is operable and located in valid entity sections.
    • + *
    + * + *

    Important Considerations:

    + *
      + *
    • The method returns a cargo capacity of zero if the entity is not fully crewed.
    • + *
    • Capabilities of transport bays or mounted equipment that are damaged beyond operability + * or located in destroyed sections are not included in the calculation.
    • + *
    • The computation assumes no external conditions affect the equipment or bays beyond the + * immediate considerations of damage and operability.
    • + *
    + * + * @return The total cargo capacity of the entity if it is fully crewed; otherwise, {@code 0.0}. + */ public double getCargoCapacity() { - double capacity = 0; + if (!isFullyCrewed()) { + return 0.0; + } + + double capacity = 0.0; + + // Add capacities from transport bays for (Bay bay : entity.getTransportBays()) { + double bayCapacity = bay.getCapacity(); + double bayDamage = bay.getBayDamage(); + + double actualCapacity = max(0, bayCapacity - bayDamage); + if (bay instanceof CargoBay) { - capacity += bay.getCapacity(); + capacity += actualCapacity; + continue; } - if (bay instanceof PillionSeatCargoBay) { - capacity += bay.getCapacity(); + + if (bay instanceof RefrigeratedCargoBay) { + capacity += actualCapacity; + continue; } - if (bay instanceof StandardSeatCargoBay) { - capacity += bay.getCapacity(); + + if (bay instanceof InsulatedCargoBay) { + capacity += actualCapacity; + } + } + + // Add capacities from mounted equipment + for (Mounted mounted : entity.getMisc()) { + if (mounted.getType().hasFlag(F_CARGO)) { + // isOperable doesn't check if the mounted location still exists, so we check for + // that first. + if ((entity.getInternal(mounted.getLocation()) > 0) && (mounted.isOperable())) { + capacity += mounted.getTonnage(); + } } } + return capacity; } @@ -1411,6 +1674,7 @@ public double getCorrectBayCapacity(int unitType, double unitWeight) { * @param bayNumber integer representing the bay number that has been assigned * to a cargo entity */ + @Deprecated public void updateBayCapacity(int unitType, double unitWeight, boolean addUnit, int bayNumber) { // Default. Consume 1 bay of the appropriate type int amount = -1; @@ -1497,14 +1761,33 @@ public int getDocks() { return getEntity().getDocks(); } - // Get only collars to which a DropShip has been assigned + /** + * Get only collars to which a DropShip has been assigned Capacity + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public int getCurrentDocks() { - return dockCapacity; - } - - // Used to assign a Dropship to a collar on a specific Jumpship in the TO&E + return (int) Math.floor(getShipTransportedUnitsSummary().getCurrentTransportCapacity(DOCKING_COLLAR)); + } + + /** Used to assign a Dropship to a collar on a specific Jumpship in the TOE + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param docks + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setDocks(int docks) { - dockCapacity = docks; + getShipTransportedUnitsSummary().setCurrentTransportCapacity(DOCKING_COLLAR, docks); } public double getLightVehicleCapacity() { @@ -1517,14 +1800,33 @@ public double getLightVehicleCapacity() { return bays; } - // Get only bays to which a light tank has been assigned + /** Get only bays to which a light tank has been assigned + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @return capacity + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public double getCurrentLightVehicleCapacity() { - return lVeeCapacity; - } - - // Used to assign a tank to a bay on a specific transport ship in the TO&E + return getShipTransportedUnitsSummary().getCurrentTransportCapacity(LIGHT_VEHICLE_BAY); + } + + /** Used to assign a tank to a bay on a specific transport ship in the TOE + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param bays + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setLightVehicleCapacity(double bays) { - lVeeCapacity = bays; + getShipTransportedUnitsSummary().setCurrentTransportCapacity(LIGHT_VEHICLE_BAY, bays); } public double getHeavyVehicleCapacity() { @@ -1537,14 +1839,33 @@ public double getHeavyVehicleCapacity() { return bays; } - // Get only bays to which a heavy tank has been assigned + /** Get only bays to which a heavy tank has been assigned + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @return capacity + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public double getCurrentHeavyVehicleCapacity() { - return hVeeCapacity; - } - - // Used to assign a tank to a bay on a specific transport ship in the TO&E + return getShipTransportedUnitsSummary().getCurrentTransportCapacity(HEAVY_VEHICLE_BAY); + } + + /** Used to assign a tank to a bay on a specific transport ship in the TOE + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param bays + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setHeavyVehicleCapacity(double bays) { - hVeeCapacity = bays; + getShipTransportedUnitsSummary().setCurrentTransportCapacity(HEAVY_VEHICLE_BAY, bays); } public double getSuperHeavyVehicleCapacity() { @@ -1557,14 +1878,32 @@ public double getSuperHeavyVehicleCapacity() { return bays; } - // Get only bays to which a super heavy tank has been assigned + /** Get only bays to which a super heavy tank has been assigned + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public double getCurrentSuperHeavyVehicleCapacity() { - return shVeeCapacity; - } - - // Used to assign a tank to a bay on a specific transport ship in the TO&E + return getShipTransportedUnitsSummary().getCurrentTransportCapacity(SUPER_HEAVY_VEHICLE_BAY); + } + + /** Used to assign a tank to a bay on a specific transport ship in the TOE + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param bays + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setSuperHeavyVehicleCapacity(double bays) { - shVeeCapacity = bays; + getShipTransportedUnitsSummary().setCurrentTransportCapacity(SUPER_HEAVY_VEHICLE_BAY, bays); } public double getBattleArmorCapacity() { @@ -1577,14 +1916,33 @@ public double getBattleArmorCapacity() { return bays; } - // Get only bays to which a ba squad has been assigned + /** Get only bays to which a ba squad has been assigned + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @return capacity + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public double getCurrentBattleArmorCapacity() { - return baCapacity; - } - - // Used to assign a ba squad to a bay on a specific transport ship in the TO&E + return getShipTransportedUnitsSummary().getCurrentTransportCapacity(BATTLE_ARMOR_BAY); + } + + /** Used to assign a ba squad to a bay on a specific transport ship in the TOE + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param bays + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setBattleArmorCapacity(double bays) { - baCapacity = bays; + getShipTransportedUnitsSummary().setCurrentTransportCapacity(BATTLE_ARMOR_BAY, bays); } public double getInfantryCapacity() { @@ -1597,16 +1955,34 @@ public double getInfantryCapacity() { return bays; } - // Return the unused tonnage of any conventional infantry bays + /** Return the unused tonnage of any conventional infantry bays + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @return capacity + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public double getCurrentInfantryCapacity() { - return infCapacity; - } - - // Used to assign an infantry unit to a bay on a specific transport ship in the - // TO&E - // Tonnage consumed depends on the platoon/squad weight + return getShipTransportedUnitsSummary().getCurrentTransportCapacity(INFANTRY_BAY); + } + + /** Used to assign an infantry unit to a bay on a specific transport ship in the + * TOE Tonnage consumed depends on the platoon/squad weight + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param tonnage + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setInfantryCapacity(double tonnage) { - infCapacity = tonnage; + getShipTransportedUnitsSummary().setCurrentTransportCapacity(INFANTRY_BAY, tonnage); } public double getASFCapacity() { @@ -1619,14 +1995,33 @@ public double getASFCapacity() { return bays; } - // Get only bays to which a fighter has been assigned + /** Get only bays to which a fighter has been assigned + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @return capacity + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public double getCurrentASFCapacity() { - return aeroCapacity; - } - - // Used to assign a fighter to a bay on a specific transport ship in the TO&E + return getCurrentShipTransportCapacity(ASF_BAY); + } + + /** Used to assign a fighter to a bay on a specific transport ship in the TOE + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param bays + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setASFCapacity(double bays) { - aeroCapacity = bays; + setCurrentShipTransportCapacity(ASF_BAY, bays); } public double getSmallCraftCapacity() { @@ -1639,15 +2034,34 @@ public double getSmallCraftCapacity() { return bays; } - // Get only bays to which a small craft has been assigned + /** Get only bays to which a small craft has been assigned + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @return capacity + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public double getCurrentSmallCraftCapacity() { - return scCapacity; - } - - // Used to assign a small craft to a bay on a specific transport ship in the - // TO&E + return getShipTransportedUnitsSummary().getCurrentTransportCapacity(SMALL_CRAFT_BAY); + } + + /** Used to assign a small craft to a bay on a specific transport ship in the + * TOE + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param bays + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setSmallCraftCapacity(double bays) { - scCapacity = bays; + getShipTransportedUnitsSummary().setCurrentTransportCapacity(SMALL_CRAFT_BAY, bays); } public double getMekCapacity() { @@ -1660,14 +2074,33 @@ public double getMekCapacity() { return bays; } - // Get only bays to which a mek has been assigned + /** Get only bays to which a mek has been assigned + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @return capacity + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public double getCurrentMekCapacity() { - return mekCapacity; - } - - // Used to assign a mek or LAM to a bay on a specific transport ship in the TO&E + return getShipTransportedUnitsSummary().getCurrentTransportCapacity(MEK_BAY); + } + + /** Used to assign a mek or LAM to a bay on a specific transport ship in the TOE + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param bays + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setMekCapacity(double bays) { - mekCapacity = bays; + getShipTransportedUnitsSummary().setCurrentTransportCapacity(MEK_BAY, bays); } public double getProtoMekCapacity() { @@ -1680,14 +2113,33 @@ public double getProtoMekCapacity() { return bays; } - // Get only bays to which a protomek has been assigned + /** Get only bays to which a protomek has been assigned + * @deprecated this only checks ship transport type, use + * getCurrentTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * to replicate this + * @return capacity + * @see Unit#getCurrentTransportCapacity(CampaignTransportType, TransporterType) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public double getCurrentProtoMekCapacity() { - return protoCapacity; - } - - // Used to assign a Protomek to a bay on a specific transport ship in the TO&E + return getShipTransportedUnitsSummary().getCurrentTransportCapacity(PROTO_MEK_BAY); + } + + /** Used to assign a Protomek to a bay on a specific transport ship in the TOE + * @deprecated this only sets for ship transport type. Transport Capacities + * should not be manually updated with this, it should happen inside of any + * loading flows. If you really need to replicate this use + * setCurrentShipTransportCapacity(SHIP_TRANSPORT, appropriate bay class) + * - but you probably don't want to do that + * @param bays + * @see Unit#initializeTransportSpace(CampaignTransportType) + * @see Unit#setCurrentShipTransportCapacity(TransporterType, double) + * @see CampaignTransportType#SHIP_TRANSPORT + */ + @Deprecated public void setProtoCapacity(double bays) { - protoCapacity = bays; + getShipTransportedUnitsSummary().setCurrentTransportCapacity(PROTO_MEK_BAY, bays); } /** @@ -1698,36 +2150,165 @@ public void setProtoCapacity(double bays) { * this data * will be used to actually load the unit into a bay on the transport. * + * @param transporterType type (Enum) of Transporter to transport the units in * @param units Vector of units that we wish to load into this transport */ - public void loadTransportShip(Vector units) { - for (Unit u : units) { - int unitType = u.getEntity().getUnitType(); - double unitWeight; - if (u.getEntity().getUnitType() == UnitType.INFANTRY) { - unitWeight = calcInfantryBayWeight(u.getEntity()); - } else { - unitWeight = u.getEntity().getWeight(); - } - int bayNumber = Utilities.selectBestBayFor(u.getEntity(), getEntity()); - addTransportedUnit(u, bayNumber); - updateBayCapacity(unitType, unitWeight, false, bayNumber); + public Set loadShipTransport(TransporterType transporterType, Set units) { + Vector unitsVector = new Vector<>(); + for (Unit unit : units) { + unitsVector.add(unit); } + + return getShipTransportedUnitsSummary().loadTransportShip(unitsVector, transporterType); } /** - * Calculates transport bay space required by an infantry platoon, - * which is not the same as the flat weight of that platoon + * Bay unloading utility used when removing units from bay-equipped transport + * units + * and/or moving them to a new transport * - * @param unit The Entity that we need the weight for + * @param unit The unit that we wish to unload from this transport */ - public double calcInfantryBayWeight(Entity unit) { - PlatoonType type = PlatoonType.getPlatoonType(unit); - if ((unit instanceof Infantry) && (type == PlatoonType.MECHANIZED)) { - return type.getWeight() * ((Infantry) unit).getSquadCount(); - } else { - return type.getWeight(); + public void unloadFromTransportShip(Unit unit) { + getShipTransportedUnitsSummary().unloadFromTransportShip(unit); + } + + /** + * Bay unloading utility used when removing a bay-equipped Transport unit + * This removes all units assigned to the transport from it + * + */ + public void unloadTransportShip() { + getShipTransportedUnitsSummary().clearTransportedUnits(campaign); + } + + // Transport Assignments + + /** + * Returns the current capacity + * + * @param transporterType class of Transporter + * @return capacity + */ + public double getCurrentShipTransportCapacity(TransporterType transporterType) { + return getShipTransportedUnitsSummary().getCurrentTransportCapacity(transporterType); + } + + /** + * Gets a value indicating whether or not this unit is assigned + * to a transport. + * @return true if this unit has a tacticalTransportAssignment that isn't null + */ + public boolean hasTacticalTransportAssignment() { + return (tacticalTransportAssignment != null); + } + + /** + * Gets the tactical transport assignment for this unit, + * or null if this unit is not being transported. + * @return transport assignment + */ + public @Nullable ITransportAssignment getTacticalTransportAssignment() { + return tacticalTransportAssignment; + } + + /** + * Sets the transport assignment for this unit. + * + * @param assignment The transport ship assignment, or null if this unit + * is not being transported. + */ + public void setTacticalTransportAssignment(@Nullable ITransportAssignment assignment) { + tacticalTransportAssignment = assignment; + } + + /** + * Returns the current capacity for the provided transporter type + * + * @param transporterType class of Transporter + * @return capacity + */ + public double getCurrentTacticalTransportCapacity(TransporterType transporterType) { + return getTacticalTransportedUnitsSummary().getCurrentTransportCapacity(transporterType); + } + + /** + * Returns the current capacity + * + * @param campaignTransportType type (enum) being checked + * @param transporterType class of Transporter + * @return remaining capacity + * @see CampaignTransportType + */ + public double getCurrentTransportCapacity(CampaignTransportType campaignTransportType, TransporterType transporterType) { + return getTransportedUnitsSummary(campaignTransportType).getCurrentTransportCapacity(transporterType); + } + + /** + * Set the transport capacity for the specified transporter type to a specific capacity + * @param transporterType type (Enum) of transporter we want to set the capacity + * @param capacity how much this transporter should be able to transport + */ + public void setCurrentShipTransportCapacity(TransporterType transporterType, double capacity) { + getShipTransportedUnitsSummary().setCurrentTransportCapacity(transporterType, capacity); + } + + /** + * Set the transport capacity for the specified transporter type to a specific capacity + * @param transporterType type (Enum) of transporter we want to set the capacity + * @param capacity how much this transporter should be able to transport + */ + public void setCurrentTacticalTransportCapacity(TransporterType transporterType, double capacity) { + getTacticalTransportedUnitsSummary().setCurrentTransportCapacity(transporterType, capacity); + } + + /** + * For the provided campaign transport type (enum), return the transporters + * this unit + * @param campaignTransportType type (enum) of campaign transport + * @return set of Transporter types (class) + */ + public Set getTransportCapabilities(CampaignTransportType campaignTransportType) { + return getTransportedUnitsSummary(campaignTransportType).getTransportCapabilities(); + } + + /** + * Does this unit have any assigned tactical transported units? + * @return true if the unit is assigned tactical transports + */ + public boolean hasTacticalTransportedUnits() { + if (hasTransportedUnitsType(TACTICAL_TRANSPORT)) { + return getTacticalTransportedUnitsSummary().hasTransportedUnits(); } + return false; + } + + /** + * @return the set of units being transported by this unit. + */ + public Set getTacticalTransportedUnits() { + return getTacticalTransportedUnitsSummary().getTransportedUnits(); + } + + /** + * Adds a unit to our set of transported units. + * + * @param transportedUnit The unit being transported by this instance. + + */ + private void addTacticalTransportedUnit(Unit transportedUnit) { + getTacticalTransportedUnitsSummary().addTransportedUnit(Objects.requireNonNull(transportedUnit)); + } + + + /** + * Removes a unit from our set of transported units. + * + * @param unit The unit to remove from our set of transported units. + * @return True if the unit was removed, otherwise false. + */ + private boolean removeTacticalTransportedUnit(Unit unit) { + return getTacticalTransportedUnitsSummary().removeTransportedUnit(unit); } /** @@ -1735,49 +2316,50 @@ public double calcInfantryBayWeight(Entity unit) { * units * and/or moving them to a new transport * - * @param u The unit that we wish to unload from this transport + * @param transportedUnit The unit that we wish to unload from this transport */ - public void unloadFromTransportShip(Unit u) { - Objects.requireNonNull(u); - - // Remove this unit from our collection of transported units. - removeTransportedUnit(u); - - // And if the unit is being transported by us, - // then update its transport ship assignment (provided the - // assignment is actually to us!). - if (u.hasTransportShipAssignment() - && u.getTransportShipAssignment().getTransportShip().equals(this)) { - double unitWeight; - if (u.getEntity().getUnitType() == UnitType.INFANTRY) { - unitWeight = calcInfantryBayWeight(u.getEntity()); - } else { - unitWeight = u.getEntity().getWeight(); - } + public void unloadTacticalTransport(Unit transportedUnit) { + getTacticalTransportedUnitsSummary().unloadFromTransport(transportedUnit); + } - updateBayCapacity(u.getEntity().getUnitType(), unitWeight, - true, u.getTransportShipAssignment().getBayNumber()); + /** + * Transporter loading utility used when assigning units to transport units + * For each passed-in unit, this will assign the unit to the specified bay, + * or the type of Transporter if one isn't provided. Once in the MM lobby, + * will be used to actually load the unit into a bay on the transport. + * + * @param transporterType type (Enum) of bay or Transporter + * @param units units being loaded + * @return the old transports of the units, or an empty set if none + */ + public Set loadTacticalTransport(TransporterType transporterType, Set units) { + return getTacticalTransportedUnitsSummary().loadTransport(units, null, transporterType); + } - u.setTransportShipAssignment(null); - } + /** + * Transporter loading utility used when assigning units to transport units + * For each passed-in unit, this will assign the unit to the specified bay, + * or the type of Transporter if one isn't provided. Once in the MM lobby, + * will be used to actually load the unit into a bay on the transport. + * + * @param transportedUnit Unit we wish to load + * @param transportedLocation specific bay (Transporter), or null + * @param transporterType type (Enum) of bay or Transporter + * @return the old transport of the unit, or an empty set if none + */ + public Unit loadTacticalTransport(Unit transportedUnit, @Nullable Transporter transportedLocation, TransporterType transporterType) { + return getTacticalTransportedUnitsSummary().loadTransport(transportedLocation, transporterType, transportedUnit); } + /** * Bay unloading utility used when removing a bay-equipped Transport unit * This removes all units assigned to the transport from it */ - public void unloadTransportShip() { - clearTransportedUnits(); - initializeBaySpace(); - - // And now reset the Transported values for all the units we just booted - campaign.getHangar().forEachUnit(u -> { - if (u.hasTransportShipAssignment() - && Objects.equals(this, u.getTransportShipAssignment().getTransportShip())) { - u.setTransportShipAssignment(null); - } - }); + public void unloadTransport(CampaignTransportType campaignTransportType) { + getTransportedUnitsSummary(campaignTransportType).clearTransportedUnits(campaign); } + // End Transport Assignments public double getUnitCostMultiplier() { double multiplier = 1.0; @@ -1876,59 +2458,35 @@ public void writeToXML(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "techId", tech.getId()); } - // If this entity is assigned to a transport, write that + // If this entity is assigned to a transport ship, write that if (hasTransportShipAssignment()) { pw.println(MHQXMLUtility.indentStr(indent) + ""); } - for (Unit unit : getTransportedUnits()) { + for (Unit unit : getShipTransportedUnits()) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "transportedUnitId", unit.getId()); } - - // Used transport bay space - if ((getEntity() != null) && !getEntity().getTransportBays().isEmpty()) { - if (aeroCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "asfCapacity", aeroCapacity); - } - - if (baCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "baCapacity", baCapacity); - } - - if (dockCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "dockCapacity", dockCapacity); - } - - if (hVeeCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "hVeeCapacity", hVeeCapacity); - } - - if (infCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "infCapacity", infCapacity); - } - - if (lVeeCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lVeeCapacity", lVeeCapacity); - } - - if (mekCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "mekCapacity", mekCapacity); - } - - if (protoCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "protoCapacity", protoCapacity); - } - - if (scCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "scCapacity", scCapacity); + // START new transports + // If this entity is assigned to a transport, write that + if (hasTacticalTransportAssignment()) { + String transportedLocation = ""; + if (getTacticalTransportAssignment().hasTransportedLocation()) { + transportedLocation += " transportedLocation=\"" + getTacticalTransportAssignment().getTransportedLocation() + "\""; + } else if (getTacticalTransportAssignment().hasTransporterType()) { + transportedLocation += " transporterType=\"" + getTacticalTransportAssignment().getTransporterType() + "\""; } + pw.println(MHQXMLUtility.indentStr(indent) + ""); + } - if (shVeeCapacity > 0) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shVeeCapacity", shVeeCapacity); - } + for (Unit unit : getTacticalTransportedUnits()) { + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "tacticalTransportedUnitId", unit.getId()); } + + // END new transports // Salvage status if (salvaged) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "salvaged", true); @@ -2054,7 +2612,7 @@ public static Unit generateInstanceFromXML(final Node wn, final Version version, int bay = Integer.parseInt(attributes.getNamedItem("baynumber").getTextContent()); retVal.setTransportShipAssignment(new TransportShipAssignment(new UnitRef(id), bay)); } else if (wn2.getNodeName().equalsIgnoreCase("transportedUnitId")) { - retVal.addTransportedUnit(new UnitRef(UUID.fromString(wn2.getTextContent()))); + retVal.addShipTransportedUnit(new UnitRef(UUID.fromString(wn2.getTextContent()))); } else if (wn2.getNodeName().equalsIgnoreCase("asfCapacity")) { retVal.setASFCapacity(Double.parseDouble(wn2.getTextContent())); needsBayInitialization = false; @@ -2085,6 +2643,27 @@ public static Unit generateInstanceFromXML(final Node wn, final Version version, } else if (wn2.getNodeName().equalsIgnoreCase("shVeeCapacity")) { retVal.setSuperHeavyVehicleCapacity(Double.parseDouble(wn2.getTextContent())); needsBayInitialization = false; + } else if (wn2.getNodeName().equalsIgnoreCase("transportAssignment")) { + NamedNodeMap attributes = wn2.getAttributes(); + UUID id = UUID.fromString(attributes.getNamedItem("id").getTextContent()); + Transporter transportedLocation = null; + if (attributes.getNamedItem("transportedLocation") != null) { + int transportedLocationHash = Integer.parseInt(attributes.getNamedItem("transportedLocation").getTextContent()); + retVal.setTacticalTransportAssignment(new TransportAssignment(new UnitRef(id), transportedLocationHash)); + } else if (attributes.getNamedItem("transporterType") != null) { + try { + TransporterType transporterType = TransporterType.valueOf((attributes.getNamedItem("transporterType").getTextContent())); + retVal.setTacticalTransportAssignment(new TransportAssignment(new UnitRef(id), transporterType)); + } + catch (IllegalArgumentException e) { + logger.error(e, "Could not find transporter type."); + retVal.setTacticalTransportAssignment(new TransportAssignment(new UnitRef(id))); + } + } else { + retVal.setTacticalTransportAssignment(new TransportAssignment(new UnitRef(id))); + } + } else if (wn2.getNodeName().equalsIgnoreCase("tacticalTransportedUnitId")) { + retVal.addTacticalTransportedUnit(new UnitRef(UUID.fromString(wn2.getTextContent()))); } else if (wn2.getNodeName().equalsIgnoreCase("forceId")) { retVal.forceId = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("scenarioId")) { @@ -2109,7 +2688,7 @@ public static Unit generateInstanceFromXML(final Node wn, final Version version, // Set up bay space values after we've loaded everything from the unit record // Used for older campaign if (retVal.entity != null && retVal.getEntity().isLargeCraft() && needsBayInitialization) { - retVal.initializeBaySpace(); + retVal.initializeShipTransportSpace(); } } } catch (Exception ex) { @@ -3735,7 +4314,9 @@ public void resetPilotAndEntity() { // Clear any stale game data that may somehow have gotten set incorrectly getCampaign().clearGameData(entity); // Set up SPAs, Implants, Edge, etc - if (getCampaign().getCampaignOptions().isUseAbilities()) { + if (getCampaign().getCampaignOptions().isUseAbilities() + || getCampaign().getCampaignOptions().isUseEdge() + || getCampaign().getCampaignOptions().isUseImplants()) { PilotOptions options = new PilotOptions(); // MegaMek-style as it is sent to MegaMek // This double enumeration is annoying to work with for crew-served units. // Get the option names while we enumerate so they can be used later @@ -3745,9 +4326,14 @@ public void resetPilotAndEntity() { IOptionGroup group = i.nextElement(); for (Enumeration j = group.getOptions(); j.hasMoreElements();) { IOption option = j.nextElement(); - if (group.getKey().equals(PersonnelOptions.MD_ADVANTAGES)) { + if (getCampaign().getCampaignOptions().isUseImplants() + && group.getKey().equals(PersonnelOptions.MD_ADVANTAGES)) { cyberOptionNames.add(option.getName()); - } else { + } else if (getCampaign().getCampaignOptions().isUseEdge() + && group.getKey().equals(PersonnelOptions.EDGE_ADVANTAGES)) { + optionNames.add(option.getName()); + } else if(getCampaign().getCampaignOptions().isUseAbilities() + && !group.getKey().equals(PersonnelOptions.EDGE_ADVANTAGES)) { optionNames.add(option.getName()); } } @@ -4067,9 +4653,9 @@ private void calcCompositeCrew() { // when they customize in MM but we should put an option in MM to ignore those // limits // and set it to true when we start up through MHQ - entity.getCrew().setPiloting(Math.min(Math.max(piloting, 0), 8), 0); - entity.getCrew().setGunnery(Math.min(Math.max(gunnery, 0), 7), 0); - entity.getCrew().setArtillery(Math.min(Math.max(artillery, 0), 8), 0); + entity.getCrew().setPiloting(Math.min(max(piloting, 0), 8), 0); + entity.getCrew().setGunnery(Math.min(max(gunnery, 0), 7), 0); + entity.getCrew().setArtillery(Math.min(max(artillery, 0), 8), 0); if (entity instanceof SmallCraft || entity instanceof Jumpship) { // Use tacops crew hits calculations and current size versus maximum size entity.getCrew().setCurrentSize(nCrew + nGunners + nDrivers); @@ -4127,11 +4713,11 @@ private void refreshLAMPilot() { artillery += pilot.getGunneryInjuryMod(); } LAMPilot crew = (LAMPilot) entity.getCrew(); - crew.setPiloting(Math.min(Math.max(pilotingMek, 0), 8)); - crew.setGunnery(Math.min(Math.max(gunneryMek, 0), 7)); - crew.setPilotingAero(Math.min(Math.max(pilotingAero, 0), 8)); - crew.setGunneryAero(Math.min(Math.max(gunneryAero, 0), 7)); - entity.getCrew().setArtillery(Math.min(Math.max(artillery, 0), 8), 0); + crew.setPiloting(Math.min(max(pilotingMek, 0), 8)); + crew.setGunnery(Math.min(max(gunneryMek, 0), 7)); + crew.setPilotingAero(Math.min(max(pilotingAero, 0), 8)); + crew.setGunneryAero(Math.min(max(gunneryAero, 0), 7)); + entity.getCrew().setArtillery(Math.min(max(artillery, 0), 8), 0); entity.getCrew().setSize(1); entity.getCrew().setMissing(false, 0); } @@ -4167,13 +4753,13 @@ private void assignToCrewSlot(Person p, int slot, String gunType, String driveTy && p.getSkill(SkillType.S_ARTILLERY).getFinalSkillValue() < artillery) { artillery = p.getSkill(SkillType.S_ARTILLERY).getFinalSkillValue(); } - entity.getCrew().setPiloting(Math.min(Math.max(piloting, 0), 8), slot); - entity.getCrew().setGunnery(Math.min(Math.max(gunnery, 0), 7), slot); + entity.getCrew().setPiloting(Math.min(max(piloting, 0), 8), slot); + entity.getCrew().setGunnery(Math.min(max(gunnery, 0), 7), slot); // also set RPG gunnery skills in case present in game options - entity.getCrew().setGunneryL(Math.min(Math.max(gunnery, 0), 7), slot); - entity.getCrew().setGunneryM(Math.min(Math.max(gunnery, 0), 7), slot); - entity.getCrew().setGunneryB(Math.min(Math.max(gunnery, 0), 7), slot); - entity.getCrew().setArtillery(Math.min(Math.max(artillery, 0), 7), slot); + entity.getCrew().setGunneryL(Math.min(max(gunnery, 0), 7), slot); + entity.getCrew().setGunneryM(Math.min(max(gunnery, 0), 7), slot); + entity.getCrew().setGunneryB(Math.min(max(gunnery, 0), 7), slot); + entity.getCrew().setArtillery(Math.min(max(artillery, 0), 7), slot); entity.getCrew().setToughness(p.getToughness(), slot); entity.getCrew().setExternalIdAsString(p.getId().toString(), slot); @@ -4433,76 +5019,76 @@ public void addDriver(Person p) { addDriver(p, false); } - public void addDriver(Person p, boolean useTransfers) { - Objects.requireNonNull(p); + public void addDriver(Person person, boolean useTransfers) { + Objects.requireNonNull(person); - ensurePersonIsRegistered(p); - drivers.add(p); - p.setUnit(this); + ensurePersonIsRegistered(person); + drivers.add(person); + person.setUnit(this); resetPilotAndEntity(); if (useTransfers) { - ServiceLogger.reassignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.reassignedTo(person, getCampaign().getLocalDate(), getName()); } else { - ServiceLogger.assignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.assignedTo(person, getCampaign().getLocalDate(), getName()); } - MekHQ.triggerEvent(new PersonCrewAssignmentEvent(p, this)); + MekHQ.triggerEvent(new PersonCrewAssignmentEvent(campaign, person, this)); } public void addGunner(Person p) { addGunner(p, false); } - public void addGunner(Person p, boolean useTransfers) { - Objects.requireNonNull(p); + public void addGunner(Person person, boolean useTransfers) { + Objects.requireNonNull(person); - ensurePersonIsRegistered(p); - gunners.add(p); - p.setUnit(this); + ensurePersonIsRegistered(person); + gunners.add(person); + person.setUnit(this); resetPilotAndEntity(); if (useTransfers) { - ServiceLogger.reassignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.reassignedTo(person, getCampaign().getLocalDate(), getName()); } else { - ServiceLogger.assignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.assignedTo(person, getCampaign().getLocalDate(), getName()); } - MekHQ.triggerEvent(new PersonCrewAssignmentEvent(p, this)); + MekHQ.triggerEvent(new PersonCrewAssignmentEvent(campaign, person, this)); } public void addVesselCrew(Person p) { addVesselCrew(p, false); } - public void addVesselCrew(Person p, boolean useTransfers) { - Objects.requireNonNull(p); + public void addVesselCrew(Person person, boolean useTransfers) { + Objects.requireNonNull(person); - ensurePersonIsRegistered(p); - vesselCrew.add(p); - p.setUnit(this); + ensurePersonIsRegistered(person); + vesselCrew.add(person); + person.setUnit(this); resetPilotAndEntity(); if (useTransfers) { - ServiceLogger.reassignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.reassignedTo(person, getCampaign().getLocalDate(), getName()); } else { - ServiceLogger.assignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.assignedTo(person, getCampaign().getLocalDate(), getName()); } - MekHQ.triggerEvent(new PersonCrewAssignmentEvent(p, this)); + MekHQ.triggerEvent(new PersonCrewAssignmentEvent(campaign, person, this)); } public void setNavigator(Person p) { setNavigator(p, false); } - public void setNavigator(Person p, boolean useTransfers) { - Objects.requireNonNull(p); + public void setNavigator(Person person, boolean useTransfers) { + Objects.requireNonNull(person); - ensurePersonIsRegistered(p); - navigator = p; - p.setUnit(this); + ensurePersonIsRegistered(person); + navigator = person; + person.setUnit(this); resetPilotAndEntity(); if (useTransfers) { - ServiceLogger.reassignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.reassignedTo(person, getCampaign().getLocalDate(), getName()); } else { - ServiceLogger.assignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.assignedTo(person, getCampaign().getLocalDate(), getName()); } - MekHQ.triggerEvent(new PersonCrewAssignmentEvent(p, this)); + MekHQ.triggerEvent(new PersonCrewAssignmentEvent(campaign, person, this)); } public boolean isTechOfficer(@Nullable Person p) { @@ -4513,19 +5099,19 @@ public void setTechOfficer(Person p) { setTechOfficer(p, false); } - public void setTechOfficer(Person p, boolean useTransfers) { - Objects.requireNonNull(p); + public void setTechOfficer(Person person, boolean useTransfers) { + Objects.requireNonNull(person); - ensurePersonIsRegistered(p); - techOfficer = p; - p.setUnit(this); + ensurePersonIsRegistered(person); + techOfficer = person; + person.setUnit(this); resetPilotAndEntity(); if (useTransfers) { - ServiceLogger.reassignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.reassignedTo(person, getCampaign().getLocalDate(), getName()); } else { - ServiceLogger.assignedTo(p, getCampaign().getLocalDate(), getName()); + ServiceLogger.assignedTo(person, getCampaign().getLocalDate(), getName()); } - MekHQ.triggerEvent(new PersonCrewAssignmentEvent(p, this)); + MekHQ.triggerEvent(new PersonCrewAssignmentEvent(campaign, person, this)); } public void setTech(Person p) { @@ -4555,7 +5141,7 @@ private void ensurePersonIsRegistered(final Person person) { Objects.requireNonNull(person); if (getCampaign().getPerson(person.getId()) == null) { getCampaign().recruitPerson(person, person.getPrisonerStatus(), true, false); - logger.warn(String.format("The person %s added this unit %s, was not in the campaign.", + logger.debug(String.format("The person %s added this unit %s, was not in the campaign.", person.getFullName(), getName())); } } @@ -4589,7 +5175,7 @@ public void addPilotOrSoldier(final Person person, final @Nullable Unit oldUnit, ServiceLogger.addedToTOEForce(getCampaign(), person, getCampaign().getLocalDate(), getCampaign().getForceFor(this)); } - MekHQ.triggerEvent(new PersonCrewAssignmentEvent(person, this)); + MekHQ.triggerEvent(new PersonCrewAssignmentEvent(campaign, person, this)); } /** @@ -4622,7 +5208,7 @@ public void remove(final @Nullable Person person, final boolean log) { engineer = null; } resetPilotAndEntity(); - MekHQ.triggerEvent(new PersonCrewAssignmentEvent(person, this)); + MekHQ.triggerEvent(new PersonCrewAssignmentEvent(campaign, person, this)); } if (log) { @@ -4688,8 +5274,8 @@ public List getDrivers() { return Collections.unmodifiableList(drivers); } - public List getGunners() { - return Collections.unmodifiableList(gunners); + public Set getGunners() { + return Collections.unmodifiableSet(gunners); } public List getVesselCrew() { @@ -4735,7 +5321,7 @@ public int getMothballTime() { * @param t The time (in minutes) remaining to mothball or activate the unit. */ public void setMothballTime(int t) { - mothballTime = Math.max(t, 0); + mothballTime = max(t, 0); } /** @@ -4854,6 +5440,18 @@ public void completeMothball() { setMothballTime(0); setMothballed(true); + + // We don't want to clear transport assignments, but we do want to remove the + // transport from the list of potential transports, if it's a transport. + if (campaign != null) { + if (!getTransportCapabilities(SHIP_TRANSPORT).isEmpty()) { + getCampaign().removeCampaignTransporter(SHIP_TRANSPORT, this); + } + + if (!getTransportCapabilities(TACTICAL_TRANSPORT).isEmpty()) { + getCampaign().removeCampaignTransporter(TACTICAL_TRANSPORT, this); + } + } } /** @@ -4927,6 +5525,18 @@ public void completeActivation() { mothballInfo.restorePreMothballInfo(this, getCampaign()); mothballInfo = null; } + + // If this unit is a transport, let's add it to the campaign's + // transporter map. + if (campaign != null) { + if (!getTransportCapabilities(SHIP_TRANSPORT).isEmpty()) { + getCampaign().addCampaignTransport(SHIP_TRANSPORT, this); + } + + if (!getTransportCapabilities(TACTICAL_TRANSPORT).isEmpty()) { + getCampaign().addCampaignTransport(TACTICAL_TRANSPORT, this); + } + } } /** @@ -5324,6 +5934,20 @@ public boolean requiresMaintenance() { return !(getEntity() instanceof Infantry) || getEntity() instanceof BattleArmor; } + /** + * Not always opposite to isUnmaintained() - both are false for units that do not require maintenance. + * @return true if unit requires maintenance and has a tech assigned, false otherwise. + * @see #isUnmaintained() + */ + public boolean isMaintained() { + return requiresMaintenance() && (getTech() != null); + } + + /** + * Not always opposite to isMaintained() - both are false for units that do not require maintenance. + * @return true if unit requires maintenance and does not have a tech assigned, false otherwise. + * @see #isMaintained() + */ public boolean isUnmaintained() { return requiresMaintenance() && (getTech() == null); } @@ -5843,18 +6467,27 @@ public void fixReferences(Campaign campaign) { } } } - for (int ii = gunners.size() - 1; ii >= 0; --ii) { - Person gunner = gunners.get(ii); + for (Person gunner : new HashSet<>(gunners)) { if (gunner instanceof UnitPersonRef) { - gunners.set(ii, campaign.getPerson(gunner.getId())); - if (gunners.get(ii) == null) { + Person updatedGunner = campaign.getPerson(gunner.getId()); + if (updatedGunner != null) { + if (!gunners.remove(gunner)) { //Remove gunner person ref & log if it fails + logger.warn(String.format("Unit %s ('%s') could not remove person ref %s", + getId(), getName(), gunner.getId())); + } + if (!gunners.add(updatedGunner)) { //Add gunner person & log if it fails + logger.warn(String.format("Unit %s ('%s') could not add person %s", + getId(), getName(), updatedGunner.getId())); + } + } + else { logger.error( - String.format("Unit %s ('%s') references missing gunner %s", - getId(), getName(), gunner.getId())); - gunners.remove(ii); + String.format("Unit %s ('%s') references missing gunner %s", + getId(), getName(), gunner.getId())); } } } + for (int ii = vesselCrew.size() - 1; ii >= 0; --ii) { Person crew = vesselCrew.get(ii); if (crew instanceof UnitPersonRef) { @@ -5902,38 +6535,22 @@ public void fixReferences(Campaign campaign) { mothballInfo.fixReferences(campaign); } - if ((transportShipAssignment != null) - && (transportShipAssignment.getTransportShip() instanceof UnitRef)) { - Unit transportShip = campaign.getHangar().getUnit(transportShipAssignment.getTransportShip().getId()); - if (transportShip != null) { - transportShipAssignment = new TransportShipAssignment(transportShip, - transportShipAssignment.getBayNumber()); - } else { - logger.error( - String.format("Unit %s ('%s') references missing transport ship %s", - getId(), getName(), transportShipAssignment.getTransportShip().getId())); + if (hasTransportShipAssignment()) { + getTransportShipAssignment().fixReferences(campaign, this); + } - transportShipAssignment = null; - } + if (hasTacticalTransportAssignment()) { + getTacticalTransportAssignment().fixReferences(campaign, this); } - if (!transportedUnits.isEmpty()) { - Set newTransportedUnits = new HashSet<>(); - for (Unit transportedUnit : transportedUnits) { - if (transportedUnit instanceof UnitRef) { - Unit realUnit = campaign.getHangar().getUnit(transportedUnit.getId()); - if (realUnit != null) { - newTransportedUnits.add(realUnit); - } else { - logger.error( - String.format("Unit %s ('%s') references missing transported unit %s", - getId(), getName(), transportedUnit.getId())); - } - } else { - newTransportedUnits.add(transportedUnit); + for (CampaignTransportType campaignTransportType : CampaignTransportType.values()) { + if (hasTransportedUnits(campaignTransportType)) { + getTransportedUnitsSummary(campaignTransportType).fixReferences(campaign, this); + initializeTransportSpace(campaignTransportType); + if (isMothballed() && campaign != null) { + } } - transportedUnits = newTransportedUnits; } } diff --git a/MekHQ/src/mekhq/campaign/unit/UnitOrder.java b/MekHQ/src/mekhq/campaign/unit/UnitOrder.java index 29d9e0b9e74..a2446a7242c 100644 --- a/MekHQ/src/mekhq/campaign/unit/UnitOrder.java +++ b/MekHQ/src/mekhq/campaign/unit/UnitOrder.java @@ -213,6 +213,7 @@ public TargetRoll getAllAcquisitionMods() { // TODO: Fix weight classes // TODO: aero large craft // TODO: support vehicles + // see EntityWeightClass.java in megamek for weight classes if (entity instanceof Mek) { if (!((Mek) entity).isIndustrial()) { target.addModifier(0, "BattleMek"); @@ -233,6 +234,18 @@ public TargetRoll getAllAcquisitionMods() { default: target.addModifier(3, "Assault"); } + } else if (entity instanceof SupportTank) { + switch (entity.getWeightClass()) { + case EntityWeightClass.WEIGHT_SMALL_SUPPORT: + target.addModifier(-1, "Small Support"); + break; + case EntityWeightClass.WEIGHT_MEDIUM_SUPPORT: + target.addModifier(0, "Medium Support"); + break; + case EntityWeightClass.WEIGHT_LARGE_SUPPORT: + target.addModifier(1, "Large Support"); + break; + } } else if (entity instanceof BattleArmor) { target.addModifier(0, "BattleArmor"); } else if (entity instanceof Infantry) { @@ -262,7 +275,7 @@ public TargetRoll getAllAcquisitionMods() { target.addModifier(3, "Assault"); } } else if (entity instanceof ConvFighter) { - target.addModifier(+0, "Conventional Fighter"); + target.addModifier(0, "Conventional Fighter"); } else if (entity instanceof Aero) { target.addModifier(0, "Aerospace Fighter"); switch (entity.getWeightClass()) { diff --git a/MekHQ/src/mekhq/campaign/unit/actions/HirePersonnelUnitAction.java b/MekHQ/src/mekhq/campaign/unit/actions/HirePersonnelUnitAction.java index 5515b0e2793..cf89ceca143 100644 --- a/MekHQ/src/mekhq/campaign/unit/actions/HirePersonnelUnitAction.java +++ b/MekHQ/src/mekhq/campaign/unit/actions/HirePersonnelUnitAction.java @@ -29,7 +29,7 @@ import mekhq.campaign.personnel.generator.DefaultSkillGenerator; import mekhq.campaign.unit.Unit; -import java.util.List; +import java.util.Set; /** * Hires a full complement of personnel for a unit. @@ -155,7 +155,7 @@ public void execute(Campaign campaign, Unit unit) { && unit.getEntity().getWeaponList().stream() .anyMatch(weapon -> (weapon.getType() instanceof WeaponType) && (((WeaponType) weapon.getType()).getDamage() == WeaponType.DAMAGE_ARTILLERY))) { - final List gunners = unit.getGunners(); + final Set gunners = unit.getGunners(); if (!gunners.isEmpty() && gunners.stream().noneMatch(person -> person.getSkills().hasSkill(SkillType.S_ARTILLERY))) { new DefaultSkillGenerator(campaign.getRandomSkillPreferences()).generateArtillerySkill(ObjectUtility.getRandomItem(gunners)); } diff --git a/MekHQ/src/mekhq/campaign/unit/actions/RestoreUnitAction.java b/MekHQ/src/mekhq/campaign/unit/actions/RestoreUnitAction.java index fd683e91334..0c639becb85 100644 --- a/MekHQ/src/mekhq/campaign/unit/actions/RestoreUnitAction.java +++ b/MekHQ/src/mekhq/campaign/unit/actions/RestoreUnitAction.java @@ -61,7 +61,7 @@ public RestoreUnitAction() { /** * Creates a new {@code RestoreUnitAction} instance using * the provided {@link IEntityCopyFactory}. - * + * * @param entityCopyFactory The factory to create entity copies with. */ public RestoreUnitAction(IEntityCopyFactory entityCopyFactory) { @@ -84,7 +84,7 @@ public void execute(Campaign campaign, Unit unit) { /** * Restore a unit by swapping out its entity and replacing its parts. - * + * * @param campaign The campaign which owns the unit. * @param unit The unit to restore. * @param newEntity The new entity to assign to the unit. @@ -107,7 +107,7 @@ private void restoreUnit(Campaign campaign, Unit unit, Entity newEntity) { unit.removeParts(); - unit.initializeBaySpace(); + unit.initializeAllTransportSpace(); unit.initializeParts(true); unit.runDiagnostic(false); @@ -117,7 +117,7 @@ private void restoreUnit(Campaign campaign, Unit unit, Entity newEntity) { /** * Copies the C3 network setup from the source to the target. - * + * * @param source The source {@link Entity}. * @param target The target {@link Entity}. */ @@ -150,7 +150,7 @@ private static void copyC3Networks(Entity source, Entity target) { /** * Restores a unit using the old per-part logic. - * + * * @param campaign The campaign which owns the unit. * @param unit The unit to restore. */ @@ -235,7 +235,7 @@ private void oldUnitRestoration(Campaign campaign, Unit unit) { public interface IEntityCopyFactory { /** * Gets a copy of the entity. - * + * * @param entity The entity to copy. * @return A copy of the entity, or {@code null} if a copy could not be made. */ @@ -250,7 +250,7 @@ public interface IEntityCopyFactory { private static class FileSystemEntityCopyFactory implements IEntityCopyFactory { /** * Get a copy of the entity from the {@link MekSummaryCache}. - * + * * @param entity The entity to copy. * @return A copy of the entity, or {@code null} if a copy could not be made. */ diff --git a/MekHQ/src/mekhq/campaign/unit/cleanup/EquipmentUnscrambler.java b/MekHQ/src/mekhq/campaign/unit/cleanup/EquipmentUnscrambler.java index c12a5183dad..56b1e56d3e4 100644 --- a/MekHQ/src/mekhq/campaign/unit/cleanup/EquipmentUnscrambler.java +++ b/MekHQ/src/mekhq/campaign/unit/cleanup/EquipmentUnscrambler.java @@ -85,7 +85,7 @@ protected EquipmentProposal createProposal() { protected abstract List createSteps(); - protected abstract @Nullable String createReport(final EquipmentProposal proposal); + protected abstract @Nullable String createReport(EquipmentProposal proposal); public static EquipmentUnscrambler create(final Unit unit) { Objects.requireNonNull(unit, "Unit must not be null"); diff --git a/MekHQ/src/mekhq/campaign/unit/cleanup/UnscrambleStep.java b/MekHQ/src/mekhq/campaign/unit/cleanup/UnscrambleStep.java index a2ded644414..d04c48e13a6 100644 --- a/MekHQ/src/mekhq/campaign/unit/cleanup/UnscrambleStep.java +++ b/MekHQ/src/mekhq/campaign/unit/cleanup/UnscrambleStep.java @@ -22,7 +22,7 @@ import mekhq.campaign.parts.equipment.MissingEquipmentPart; public abstract class UnscrambleStep { - public abstract void visit(final EquipmentProposal proposal, final EquipmentPart part); + public abstract void visit(EquipmentProposal proposal, EquipmentPart part); public void visit(final EquipmentProposal proposal, final MissingEquipmentPart part) { diff --git a/MekHQ/src/mekhq/campaign/unit/enums/TransporterType.java b/MekHQ/src/mekhq/campaign/unit/enums/TransporterType.java new file mode 100644 index 00000000000..b989ea85f0b --- /dev/null +++ b/MekHQ/src/mekhq/campaign/unit/enums/TransporterType.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2025-2025 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.unit.enums; + +import megamek.common.*; +import mekhq.campaign.enums.CampaignTransportType; + +/** + * Entities are equipped with different Transporters. + * TransporterTypes are the different kinds of + * transporters from MegaMek that are implemented + * to be used with CampaignTransportTypes, like + * Mek Bays, Docking Collars, Battle Armor Handles, + * or Infantry Compartments. + * + * @see Transporter + * @see CampaignTransportType + */ +public enum TransporterType { + + // region Enum declarations + TANK_TRAILER_HITCH(TankTrailerHitch.class), + HEAVY_VEHICLE_BAY(HeavyVehicleBay.class), + NAVAL_REPAIR_FACILITY(NavalRepairFacility.class), + REINFORCED_REPAIR_FACILITY(ReinforcedRepairFacility.class), + DROPSHUTTLE_BAY(DropshuttleBay.class), + LIGHT_VEHICLE_BAY(LightVehicleBay.class), + SUPER_HEAVY_VEHICLE_BAY(SuperHeavyVehicleBay.class), + MEK_BAY(MekBay.class), + PROTO_MEK_BAY(ProtoMekBay.class), + ASF_BAY(ASFBay.class), + SMALL_CRAFT_BAY(SmallCraftBay.class), + INFANTRY_BAY(InfantryBay.class), + BATTLE_ARMOR_BAY(BattleArmorBay.class), + INFANTRY_COMPARTMENT(InfantryCompartment.class), + DOCKING_COLLAR(DockingCollar.class), + BATTLE_ARMOR_HANDLES(BattleArmorHandles.class), + BATTLE_ARMOR_HANDLES_TANK(BattleArmorHandlesTank.class), + CLAMP_MOUNT_MEK(ClampMountMek.class), + CLAMP_MOUNT_TANK(ClampMountTank.class), + PROTO_MEK_CLAMP_MOUNT(ProtoMekClampMount.class); + + // endregion Enum declarations + + // region Variable declarations + private final Class transporterClass; + // end region Variable declarations + + // region Constructor + TransporterType(Class transporterClass) { + this.transporterClass = transporterClass; + } + // endregion Constructor + + /** + * An Entity's Transporters need to be mapped to their + * TransporterTypes. For the provided Transporter, + * this returns its corresponding TransporterType, + * or null if it's not found. + * + * @see Transporter + * @param transporter specific transporter to return the type of + * @return TransporterType (enum) of the provided transporter, or null + * @param extends Transporter + */ + public static TransporterType getTransporterType(T transporter) { + for (TransporterType transporterType : TransporterType.values()) { + if (transporterType.getTransporterClass() == transporter.getClass()) { + return transporterType; + } + } + return null; + } + + /** + * The specific Class of Transporter that corresponds to this TransporterType + * @return Class that extends Transporter + */ + public Class getTransporterClass() { return transporterClass; } +} diff --git a/MekHQ/src/mekhq/campaign/universe/IUnitGenerator.java b/MekHQ/src/mekhq/campaign/universe/IUnitGenerator.java index 533ddeb6f4f..18df92ecbf2 100644 --- a/MekHQ/src/mekhq/campaign/universe/IUnitGenerator.java +++ b/MekHQ/src/mekhq/campaign/universe/IUnitGenerator.java @@ -86,7 +86,7 @@ static boolean unitTypeSupportsWeightClass(final int unitType) { * @param unitType UnitType constant * @return true if the generator supports the unit type */ - boolean isSupportedUnitType(final int unitType); + boolean isSupportedUnitType(int unitType); /** * Generate a single unit. @@ -170,9 +170,9 @@ static boolean unitTypeSupportsWeightClass(final int unitType) { * @param filter All generated units return true when the filter function is applied. * @return A unit that matches the criteria */ - @Nullable MekSummary generate(final String faction, final int unitType, final int weightClass, final int year, - final int quality, final Collection movementModes, - final Collection missionRoles, @Nullable Predicate filter); + @Nullable MekSummary generate(String faction, int unitType, int weightClass, int year, + int quality, Collection movementModes, + Collection missionRoles, @Nullable Predicate filter); /** * Generates a list of units. @@ -261,10 +261,10 @@ default List generate(final int count, final String faction, final i * @param filter All generated units return true when the filter function is applied. * @return A list of units matching the criteria. */ - @Nullable List generate(final int count, final String faction, final int unitType, - final int weightClass, final int year, final int quality, - final Collection movementModes, - final Collection missionRoles, + @Nullable List generate(int count, String faction, int unitType, + int weightClass, int year, int quality, + Collection movementModes, + Collection missionRoles, @Nullable Predicate filter); /** @@ -274,7 +274,7 @@ default List generate(final int count, final String faction, final i * @param parameters data structure containing unit generation parameters * @return The generated unit, or null if none are generated. */ - @Nullable MekSummary generate(final UnitGeneratorParameters parameters); + @Nullable MekSummary generate(UnitGeneratorParameters parameters); /** * Generates the given count of units to be used in an OpFor using the given set of parameters. @@ -284,7 +284,7 @@ default List generate(final int count, final String faction, final i * @param parameters data structure containing unit generation parameters * @return List of generated units. Empty if none are generated. */ - List generate(final int count, final UnitGeneratorParameters parameters); + List generate(int count, UnitGeneratorParameters parameters); /** * Generates a list of turrets given a skill level, quality and year @@ -294,5 +294,5 @@ default List generate(final int count, final String faction, final i * @param currentYear The current year * @return List of turrets */ - List generateTurrets(final int num, final SkillLevel skill, final int quality, final int currentYear); + List generateTurrets(int num, SkillLevel skill, int quality, int currentYear); } diff --git a/MekHQ/src/mekhq/campaign/universe/NewsItem.java b/MekHQ/src/mekhq/campaign/universe/NewsItem.java index a2d91b2c4cc..62bdd4b3c37 100644 --- a/MekHQ/src/mekhq/campaign/universe/NewsItem.java +++ b/MekHQ/src/mekhq/campaign/universe/NewsItem.java @@ -24,7 +24,6 @@ import jakarta.xml.bind.annotation.*; import megamek.codeUtilities.ObjectUtility; import megamek.common.Compute; -import mekhq.MekHQ; import mekhq.utilities.MHQXMLUtility; import java.time.LocalDate; @@ -172,9 +171,7 @@ public String getHeadlineForReport() { } public String getFullDescription() { - return "

    " + getHeadline() + "

    (" - + MekHQ.getMHQOptions().getDisplayFormattedDate(date) - + ")

    " + getPrefix() + description + "

    "; + return "

    " + getHeadline() + "

    " + description; } // JAXB marshalling support diff --git a/MekHQ/src/mekhq/campaign/universe/SocioIndustrialData.java b/MekHQ/src/mekhq/campaign/universe/SocioIndustrialData.java index 8135d07324c..7385749b3da 100644 --- a/MekHQ/src/mekhq/campaign/universe/SocioIndustrialData.java +++ b/MekHQ/src/mekhq/campaign/universe/SocioIndustrialData.java @@ -67,7 +67,7 @@ public String toString() { /** @return the USILR rating as a HTML description */ public String getHTMLDescription() { - // TODO: Internationalization + // TODO: MHQInternationalization // TODO: Some way to encode "advanced" ultra-tech worlds (rating "AA" for technological sophistication) // TODO: Some way to encode "regressed" worlds // Note that rating "E" isn't used in official USILR codes, but we add them for completeness diff --git a/MekHQ/src/mekhq/campaign/universe/StarUtil.java b/MekHQ/src/mekhq/campaign/universe/StarUtil.java index 3178e170990..949f85ba1f0 100644 --- a/MekHQ/src/mekhq/campaign/universe/StarUtil.java +++ b/MekHQ/src/mekhq/campaign/universe/StarUtil.java @@ -18,23 +18,17 @@ */ package mekhq.campaign.universe; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.TreeSet; - import megamek.codeUtilities.MathUtility; import megamek.codeUtilities.ObjectUtility; import megamek.common.EquipmentType; import megamek.logging.MMLogger; import mekhq.campaign.universe.PlanetarySystem.SpectralDefinition; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.*; + /** Static method only helper class for stars */ public final class StarUtil { private static final MMLogger logger = MMLogger.create(StarUtil.class); @@ -67,6 +61,7 @@ public final class StarUtil { public static final double SOLAR_TEMP = 5778.0; /** Gravitational constant (in m^3 kg^-1 s^-2 */ public static final double GRAV_CONSTANT = 6.673848e-11; + private static final double STEFAN_BOLTZMANN_CONSTANT = 5.67e-8; // Temperature, mass, luminosity and size are all linked together. When // modifying those tables, @@ -802,7 +797,63 @@ public static String getIconImage(PlanetarySystem system) { return STAR_ICON_DATA.get(ObjectUtility.nonNull(system.getIcon(), "default")); } - private StarUtil() { + /** + * Estimates the radius of a star using its luminosity and temperature, based on the + * Stefan-Boltzmann law: + *
    +     * R = √(L / (4πσT⁴))
    +     * 
    + * + * Where: + *
      + *
    • R is the radius of the star (in meters).
    • + *
    • L is the luminosity of the star (in watts).
    • + *
    • T is the effective temperature of the star (in Kelvin).
    • + *
    • σ is the Stefan-Boltzmann constant (5.67 × 10⁻⁸ W·m⁻²·K⁻⁴).
    • + *
    + * + * @param luminosity The luminosity of the star in watts. Must be greater than 0. + * @param temperature The effective temperature of the star in Kelvin. Must be greater than 0. + * @return The radius of the star in meters. + * @throws IllegalArgumentException if luminosity or temperature is less than or equal to 0. + */ + public static double estimateStarRadius(double luminosity, double temperature) { + // Temperature raised to the 4th power + double temperaturePower4 = Math.pow(temperature, 4); + + // Denominator: 4 * π * σ * T^4 + double denominator = 4 * Math.PI * STEFAN_BOLTZMANN_CONSTANT * temperaturePower4; + + // Radius calculation + return Math.sqrt(luminosity / denominator); + } + + /** + * Calculates the spectral type number for a planetary system by combining its spectral class + * and spectral subclass. The spectral class is multiplied by 10, and the subclass is extracted + * as a numeric value from the provided spectral type string. + * + *

    The spectral type is a combination of letters and numbers (e.g., "F5V"). + * This method extracts the numeric part (e.g., "5") from the spectral type and adds it to the + * spectral class (multiplied by 10).

    + * + * @param spectralClass The spectral class of the planetary system as an integer. Multiplied by + * 10 in the calculation. + * @param spectralType The spectral type of the planetary system as a string (e.g., "F5V"), + * from which the subclass (numeric portion) will be extracted. + * @return The combined spectral type number, calculated as {@code spectralClass * 10 + spectralSubClass}, + * where {@code spectralSubClass} is the numeric portion of the spectral type string. + */ + public static int getSpectralTypeNumber(int spectralClass, String spectralType) { + spectralClass *= 10; + + int spectralSubClass = 5; + try { + spectralSubClass = Integer.parseInt(spectralType.replaceAll("\\D", "")); + } catch (NumberFormatException e) { + logger.info("Failed to fetch spectralSubClass from spectralType: " + spectralType); + } + return spectralClass + spectralSubClass; } } diff --git a/MekHQ/src/mekhq/campaign/universe/Systems.java b/MekHQ/src/mekhq/campaign/universe/Systems.java index c45154bff88..0f5291d6e60 100644 --- a/MekHQ/src/mekhq/campaign/universe/Systems.java +++ b/MekHQ/src/mekhq/campaign/universe/Systems.java @@ -18,20 +18,6 @@ */ package mekhq.campaign.universe; -import java.io.FileInputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Writer; -import java.time.LocalDate; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Consumer; - -import org.w3c.dom.DOMException; -import org.w3c.dom.Node; - import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.Marshaller; @@ -43,6 +29,15 @@ import megamek.logging.MMLogger; import mekhq.Utilities; import mekhq.utilities.MHQXMLUtility; +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; + +import java.io.*; +import java.time.LocalDate; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Consumer; /** * This will eventually replace the Planets object as our source of @@ -57,6 +52,9 @@ public class Systems { private static Systems systems; + private final int HPG_RADIUS_A_STATION = 50; + private final int HPG_RADIUS_B_STATION = 30; + // Marshaller / unmarshaller instances private static Marshaller marshaller; private static Unmarshaller unmarshaller; @@ -225,7 +223,7 @@ public void recalcHPGNetwork() { hpgNetworkCacheDate = null; } - public Collection getHPGNetwork(LocalDate when) { + public Collection getHPGNetwork(LocalDate when) { if ((null != when) && when.equals(hpgNetworkCacheDate)) { return hpgNetworkCache; } @@ -233,13 +231,25 @@ public Collection getHPGNetwork(LocalDate when) { Set result = new HashSet<>(); for (PlanetarySystem system : systemList.values()) { Integer hpg = system.getHPG(when); - if ((null != hpg) && (hpg == EquipmentType.RATING_A)) { - Collection neighbors = getNearbySystems(system, 50); - for (PlanetarySystem neighbor : neighbors) { - hpg = neighbor.getHPG(when); - if (null != hpg) { - HPGLink link = new HPGLink(system, neighbor, hpg); - result.add(link); + if (hpg != null) { + int distance = 0; + if (hpg == EquipmentType.RATING_A) { + distance = HPG_RADIUS_A_STATION; + } + + if (hpg == EquipmentType.RATING_B) { + distance = HPG_RADIUS_B_STATION; + } + + Collection neighbors = getNearbySystems(system, distance); + + if (distance > 0) { + for (PlanetarySystem neighbor : neighbors) { + hpg = neighbor.getHPG(when); + if (null != hpg) { + HPGLink link = new HPGLink(system, neighbor, hpg); + result.add(link); + } } } } diff --git a/MekHQ/src/mekhq/campaign/universe/generators/battleMekQualityGenerators/AbstractBattleMekQualityGenerator.java b/MekHQ/src/mekhq/campaign/universe/generators/battleMekQualityGenerators/AbstractBattleMekQualityGenerator.java index 6c82c2dcab0..ea2ae52d2be 100644 --- a/MekHQ/src/mekhq/campaign/universe/generators/battleMekQualityGenerators/AbstractBattleMekQualityGenerator.java +++ b/MekHQ/src/mekhq/campaign/universe/generators/battleMekQualityGenerators/AbstractBattleMekQualityGenerator.java @@ -46,5 +46,5 @@ public BattleMekQualityGenerationMethod getMethod() { * @param roll the modified roll to use * @return the generated IUnitRating magic int for Dragoon Quality */ - public abstract int generate(final int roll); + public abstract int generate(int roll); } diff --git a/MekHQ/src/mekhq/campaign/universe/generators/battleMekWeightClassGenerators/AbstractBattleMekWeightClassGenerator.java b/MekHQ/src/mekhq/campaign/universe/generators/battleMekWeightClassGenerators/AbstractBattleMekWeightClassGenerator.java index d4fccae37a7..e60e3e8b4fb 100644 --- a/MekHQ/src/mekhq/campaign/universe/generators/battleMekWeightClassGenerators/AbstractBattleMekWeightClassGenerator.java +++ b/MekHQ/src/mekhq/campaign/universe/generators/battleMekWeightClassGenerators/AbstractBattleMekWeightClassGenerator.java @@ -48,5 +48,5 @@ public BattleMekWeightClassGenerationMethod getMethod() { * signify no generation and EntityWeightClass.WEIGHT_SUPER_HEAVY to signify Star League-era * generation. */ - public abstract int generate(final int roll); + public abstract int generate(int roll); } diff --git a/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java b/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java index 421c2fe8cd7..d5abfb1fd92 100644 --- a/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java +++ b/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java @@ -46,7 +46,6 @@ 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; @@ -75,6 +74,8 @@ import java.util.stream.IntStream; import java.util.stream.Stream; +import static mekhq.campaign.personnel.education.EducationController.setInitialEducationLevel; + /** * Startup: * Second Panel: Presets, Date, Starting Faction, Starting Planet, AtB @@ -372,9 +373,9 @@ private void generateCommandingOfficer(final Campaign campaign, * @param numMekWarriors the number of MekWarriors in their force, used to * determine their rank */ - protected abstract void generateCommandingOfficerRank(final Faction faction, - final CompanyGenerationPersonTracker tracker, - final int numMekWarriors); + protected abstract void generateCommandingOfficerRank(Faction faction, + CompanyGenerationPersonTracker tracker, + int numMekWarriors); /** * This generates the initial officer list and assigns the type @@ -633,14 +634,10 @@ private void finalizePersonnel(final Campaign campaign, trackers.forEach(t -> campaign.recruitPerson(t.getPerson(), true)); // Now that they are recruited, we can simulate backwards a few years and - // generate marriages - // and children + // 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); - } + Person person = tracker.getPerson(); + setInitialEducationLevel(campaign, person); } if (getOptions().isRunStartingSimulation()) { @@ -650,7 +647,7 @@ private void finalizePersonnel(final Campaign campaign, for (final CompanyGenerationPersonTracker tracker : trackers) { if (getOptions().isSimulateRandomMarriages()) { - campaign.getMarriage().processNewWeek(campaign, date, tracker.getPerson()); + campaign.getMarriage().processNewWeek(campaign, date, tracker.getPerson(), true); } if (getOptions().isSimulateRandomProcreation()) { @@ -956,9 +953,9 @@ private void generateEntity(final Campaign campaign, * @return the MekSummary generated from the provided parameters, or null if * generation fails */ - protected abstract @Nullable MekSummary generateMekSummary(final Campaign campaign, - final AtBRandomMekParameters parameters, - final Faction faction); + protected abstract @Nullable MekSummary generateMekSummary(Campaign campaign, + AtBRandomMekParameters parameters, + Faction faction); /** * @param campaign the campaign to generate for diff --git a/MekHQ/src/mekhq/campaign/universe/generators/partGenerators/AbstractPartGenerator.java b/MekHQ/src/mekhq/campaign/universe/generators/partGenerators/AbstractPartGenerator.java index aec55d6fd56..eebd2f17c8b 100644 --- a/MekHQ/src/mekhq/campaign/universe/generators/partGenerators/AbstractPartGenerator.java +++ b/MekHQ/src/mekhq/campaign/universe/generators/partGenerators/AbstractPartGenerator.java @@ -87,7 +87,7 @@ public List generate(final List inputParts) { * unassigned. Implementors are required to clone the parts as required. * @return a warehouse containing the generated parts */ - public abstract Warehouse generateWarehouse(final List inputParts); + public abstract Warehouse generateWarehouse(List inputParts); /** * This creates a clone of the input part, with it not being omni-podded if it was originally. diff --git a/MekHQ/src/mekhq/campaign/universe/selectors/factionSelectors/AbstractFactionSelector.java b/MekHQ/src/mekhq/campaign/universe/selectors/factionSelectors/AbstractFactionSelector.java index 6281d16392e..13527685dff 100644 --- a/MekHQ/src/mekhq/campaign/universe/selectors/factionSelectors/AbstractFactionSelector.java +++ b/MekHQ/src/mekhq/campaign/universe/selectors/factionSelectors/AbstractFactionSelector.java @@ -57,7 +57,7 @@ public void setOptionsDirect(final RandomOriginOptions options) { * @param campaign The {@link Campaign} within which this {@link Faction} exists. * @return A {@link Faction} selected for {@code campaign}. */ - public abstract @Nullable Faction selectFaction(final Campaign campaign); + public abstract @Nullable Faction selectFaction(Campaign campaign); /** * Clears any cache associated with faction selection. diff --git a/MekHQ/src/mekhq/campaign/universe/selectors/planetSelectors/AbstractPlanetSelector.java b/MekHQ/src/mekhq/campaign/universe/selectors/planetSelectors/AbstractPlanetSelector.java index e39535414a4..60d60d4446d 100644 --- a/MekHQ/src/mekhq/campaign/universe/selectors/planetSelectors/AbstractPlanetSelector.java +++ b/MekHQ/src/mekhq/campaign/universe/selectors/planetSelectors/AbstractPlanetSelector.java @@ -59,7 +59,7 @@ public void setOptionsDirect(final RandomOriginOptions options) { * @param campaign The {@link Campaign} to use when selecting a planet. * @return A {@link Planet} or {@code null}. */ - public abstract @Nullable Planet selectPlanet(final Campaign campaign); + public abstract @Nullable Planet selectPlanet(Campaign campaign); /** * Select a {@link Planet} for a {@link Campaign} and optional {@link} Faction. @@ -67,7 +67,7 @@ public void setOptionsDirect(final RandomOriginOptions options) { * @param faction An optional {@link Faction} to use when selecting a planet. * @return A {@link Planet} or {@code null}. */ - public abstract @Nullable Planet selectPlanet(final Campaign campaign, final @Nullable Faction faction); + public abstract @Nullable Planet selectPlanet(Campaign campaign, @Nullable Faction faction); /** * Clears any cache associated with planet selection. diff --git a/MekHQ/src/mekhq/campaign/utilities/CampaignTransportUtilities.java b/MekHQ/src/mekhq/campaign/utilities/CampaignTransportUtilities.java new file mode 100644 index 00000000000..4eed44c1ac3 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/utilities/CampaignTransportUtilities.java @@ -0,0 +1,258 @@ +/** + * Copyright (c) 2025-2025 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.utilities; + +import megamek.common.*; +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.unit.enums.TransporterType; + +import java.util.*; + +import static mekhq.campaign.unit.enums.TransporterType.*; + +public class CampaignTransportUtilities { + // region Static Helpers + + interface Visitor { + boolean isInterestedIn(Entity entity); + + EnumSet getTransporterTypes(T entity, CampaignTransportType campaignTransportType); + } + + /** + * Helps the menus need to check less when generating Transports. Let's get a list of + * TransporterTypes that this Entity could potentially be transported in. This will make + * it much easier to determine what Transporters we should even look at. + * In addition, CampaignTransportTypes that can't use certain TransporterTypes is handled, + * like Ship Transports not being able to use InfantryCompartments or BattleArmorHandles. + * Use a Bay! Or DockingCollar. + * + * @see TransporterType + * @param campaignTransportType type (enum) of campaign transport - some transport types can't use certain transporters + * @param unit unit we want to get the Transporter types that could potentially hold it + * @return Transporter types that could potentially transport this entity + */ + public static EnumSet mapEntityToTransporters(CampaignTransportType campaignTransportType, Entity unit) { + return getTransportTypeClassifier(unit).map(v -> v.getTransporterTypes(unit, campaignTransportType)).orElse(EnumSet.noneOf(TransporterType.class)); + } + + + /** + * Most slots are 1:1, infantry use their tonnage in some cases + * + * @param transporterType type (Enum) of Transporter + * @param transportedUnit Entity we want the capacity usage of + * @return how much capacity this unit uses when being transported in this kind of transporter + */ + public static double transportCapacityUsage(TransporterType transporterType, Entity transportedUnit) { + if (transportedUnit instanceof Infantry) { + if (transporterType == INFANTRY_BAY) { + return calcInfantryBayWeight(transportedUnit); + } + else if (transporterType == INFANTRY_COMPARTMENT) { + return calcInfantryCompartmentWeight(transportedUnit); + } + } + return 1.0; + } + + /** + * Calculates transport bay space required by an infantry platoon, + * which is not the same as the flat weight of that platoon + * + * @param entity The Entity that we need the weight for + * @return Capacity in tons needed to transport this entity + */ + private static double calcInfantryBayWeight(Entity entity) { + InfantryBay.PlatoonType type = InfantryBay.PlatoonType.getPlatoonType(entity); + + if ((entity instanceof Infantry) && (type == InfantryBay.PlatoonType.MECHANIZED)) { + return type.getWeight() * ((Infantry) entity).getSquadCount(); + } else { + return type.getWeight(); + } + } + + /** + * Calculates transport space required for an infantry compartment + * + * @param entity The Entity that we need the weight for + * @return Capacity in tons needed to transport this entity + */ + private static double calcInfantryCompartmentWeight(Entity entity) { + return entity.getWeight(); + } + + + private static final List visitors = List.of( + + new Visitor() { + @Override + public boolean isInterestedIn(Entity entity) { + return entity instanceof ProtoMek; + } + + @Override + public EnumSet getTransporterTypes(ProtoMek entity, CampaignTransportType campaignTransportType) { + EnumSet transporters = EnumSet.noneOf(TransporterType.class); + transporters.add(PROTO_MEK_BAY); + + //Ship transports can't use some transport types + if (!(campaignTransportType.isShipTransport())) { + transporters.add(PROTO_MEK_CLAMP_MOUNT); + } + + return transporters; + } + }, + new Visitor() { + + @Override + public boolean isInterestedIn(Entity entity) { + return entity instanceof Aero; + } + + @Override + public EnumSet getTransporterTypes(Aero entity, CampaignTransportType campaignTransportType) { + EnumSet transporters = EnumSet.noneOf(TransporterType.class); + if (entity.isFighter()) { + transporters.add(ASF_BAY); + } + if ((entity.isFighter()) || entity.isSmallCraft()) { + transporters.add(SMALL_CRAFT_BAY); + } + if (entity.hasETypeFlag(Entity.ETYPE_DROPSHIP) && (entity.getWeight() <= 5000)) { + transporters.add(DROPSHUTTLE_BAY); + } + if (entity.hasETypeFlag(Entity.ETYPE_DROPSHIP) || entity.hasETypeFlag(Entity.ETYPE_JUMPSHIP)) { + transporters.add(NAVAL_REPAIR_FACILITY); + transporters.add(REINFORCED_REPAIR_FACILITY); + } + if (entity instanceof Dropship && !((Dropship) entity).isDockCollarDamaged()) { + transporters.add(DOCKING_COLLAR); + } + + return transporters; + } + }, + new Visitor() { + + @Override + public boolean isInterestedIn(Entity entity) { + return entity instanceof Tank; + } + + @Override + public EnumSet getTransporterTypes(Tank entity, CampaignTransportType campaignTransportType) { + + EnumSet transporters = EnumSet.noneOf(TransporterType.class); + + if (entity.getWeight() <= 50) { + transporters.add(LIGHT_VEHICLE_BAY); + } + + if (entity.getWeight() <= 100) { + transporters.add(HEAVY_VEHICLE_BAY); + } + + if (entity.getWeight() <= 150) { + transporters.add(SUPER_HEAVY_VEHICLE_BAY); + } + return transporters; + } + }, + new Visitor() { + + @Override + public boolean isInterestedIn(Entity entity) { + return entity instanceof Mek; + } + + @Override + public EnumSet getTransporterTypes(Mek entity, CampaignTransportType campaignTransportType) { + EnumSet transporters = EnumSet.noneOf(TransporterType.class); + boolean loadableQuadVee = (entity instanceof QuadVee) && (entity.getConversionMode() == QuadVee.CONV_MODE_MEK); + boolean loadableLAM = (entity instanceof LandAirMek) && (entity.getConversionMode() != LandAirMek.CONV_MODE_FIGHTER); + boolean loadableOtherMek = (entity != null) && !(entity instanceof QuadVee) && !(entity instanceof LandAirMek); + if (loadableQuadVee || loadableLAM || loadableOtherMek) { + transporters.add(MEK_BAY); + + } else { + if ((entity instanceof QuadVee) && (entity.getConversionMode() == QuadVee.CONV_MODE_VEHICLE)) { + if (entity.getWeight() <= 50) { + transporters.add(LIGHT_VEHICLE_BAY); + } + + if (entity.getWeight() <= 100) { + transporters.add(HEAVY_VEHICLE_BAY); + } + + if (entity.getWeight() <= 100) { + transporters.add(SUPER_HEAVY_VEHICLE_BAY); + } + } + } + return transporters; + } + }, + new Visitor() { + + @Override + public boolean isInterestedIn(Entity entity) { + return entity instanceof Infantry; + } + + @Override + public EnumSet getTransporterTypes(Infantry entity, CampaignTransportType campaignTransportType) { + EnumSet transporters = EnumSet.noneOf(TransporterType.class); + + //Ship transports can't use some transport types + if (!(campaignTransportType.isShipTransport())) { + transporters.add(INFANTRY_COMPARTMENT); + } + + if (entity instanceof BattleArmor baEntity) { + transporters.add(BATTLE_ARMOR_BAY); + + //Ship transports can't use some transport types + if (baEntity.canDoMechanizedBA() && !campaignTransportType.isShipTransport()) { + transporters.add(BATTLE_ARMOR_HANDLES); + transporters.add(BATTLE_ARMOR_HANDLES_TANK); + + if (baEntity.hasMagneticClamps()) { + transporters.add(CLAMP_MOUNT_MEK); + transporters.add(CLAMP_MOUNT_TANK); + } + + } + + } else { + transporters.add(INFANTRY_BAY); + } + + return transporters; + } + }); + + private static Optional getTransportTypeClassifier(Entity entity) { + return visitors.stream().filter(v -> v.isInterestedIn(entity)).findFirst(); + } + // endregion Static Helpers +} diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index aed006bb84c..13255533cc9 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -33,21 +33,21 @@ import megamek.logging.MMLogger; import megameklab.util.UnitPrintManager; import mekhq.MekHQ; -import mekhq.campaign.Kill; -import mekhq.campaign.ResolveScenarioTracker; -import mekhq.campaign.ResolveScenarioTracker.PersonStatus; +import mekhq.campaign.autoresolve.AutoResolveMethod; import mekhq.campaign.event.*; import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; -import mekhq.campaign.force.StrategicFormation; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.mission.*; import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.enums.MissionStatus; +import mekhq.campaign.mission.enums.ScenarioStatus; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.autoAwards.AutoAwardsController; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.PersonnelStatus; +import mekhq.campaign.stratcon.StratconScenario; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Faction; import mekhq.campaign.universe.Factions; @@ -61,12 +61,14 @@ import mekhq.gui.view.LanceAssignmentView; import mekhq.gui.view.MissionViewPanel; import mekhq.gui.view.ScenarioViewPanel; +import mekhq.utilities.MHQInternationalization; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; import java.awt.*; import java.io.File; +import java.time.LocalDate; import java.util.List; import java.util.*; import java.util.stream.Collectors; @@ -385,7 +387,16 @@ private void completeMission() { int xpAward = getMissionXpAward(cmd.getStatus(), mission); if (xpAward > 0) { + LocalDate today = getCampaign().getLocalDate(); for (Person person : getCampaign().getActivePersonnel()) { + if (person.isChild(today)) { + continue; + } + + if (person.isDependent()) { + continue; + } + person.awardXP(getCampaign(), xpAward); } } @@ -477,10 +488,8 @@ && getCampaign().getFinances().getBalance().isGreaterOrEqualThan(rdd.totalPayout } } - // resolve bonus parts exchange if (getCampaign().getCampaignOptions().isUseAtB() && (mission instanceof AtBContract)) { getCampaign().getContractMarket().checkForFollowup(getCampaign(), (AtBContract) mission); - bonusPartExchange((AtBContract) mission); } // prompt autoAwards ceremony @@ -493,7 +502,7 @@ && getCampaign().getFinances().getBalance().isGreaterOrEqualThan(rdd.totalPayout Objects.equals(String.valueOf(cmd.getStatus()), "Success")); } - // prompt enemy prisoner ransom & freeing + // prompt enemy prisoner ransom and freeing // this should always be placed after autoAwards, so that prisoners are not // factored into autoAwards if (getCampaign().getCampaignOptions().isUseAtBPrisonerRansom()) { @@ -620,37 +629,6 @@ private int getMissionXpAward(MissionStatus missionStatus, Mission mission) { }; } - /** - * Credits the campaign finances with additional funds based on campaign - * settings and remaining Bonus Parts. - * - * @param mission the mission just concluded - */ - private void bonusPartExchange(AtBContract mission) { - final ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.CampaignGUI", - MekHQ.getMHQOptions().getLocale()); - - double bonusPartExchangeValue = getCampaign().getCampaignOptions().getBonusPartExchangeValue(); - - if (bonusPartExchangeValue != 0.0) { - int bonusPartMaxExchangeCount = getCampaign().getCampaignOptions().getBonusPartMaxExchangeCount(); - - int spareBonusParts = mission.getNumBonusParts(); - - if (bonusPartMaxExchangeCount != 0) { - spareBonusParts = Math.min(bonusPartMaxExchangeCount, spareBonusParts); - } - - bonusPartExchangeValue *= spareBonusParts; - - getCampaign().getFinances().credit( - TransactionType.BONUS_EXCHANGE, - getCampaign().getLocalDate(), - Money.of(bonusPartExchangeValue), - resourceMap.getString("spareBonusPartExchange.text")); - } - } - private void deleteMission() { final Mission mission = comboMission.getSelectedItem(); if (mission == null) { @@ -700,83 +678,29 @@ private void addScenario() { } private void clearAssignedUnits() { - if (0 == JOptionPane.showConfirmDialog(null, "Do you really want to remove all units from this scenario?", + if (0 == JOptionPane.showConfirmDialog(null, + "Do you really want to remove all units from this scenario?", "Clear Units?", JOptionPane.YES_NO_OPTION)) { int row = scenarioTable.getSelectedRow(); Scenario scenario = scenarioModel.getScenario(scenarioTable.convertRowIndexToModel(row)); - if (null == scenario) { - return; - } - scenario.clearAllForcesAndPersonnel(getCampaign()); - } - } - private void resolveScenario() { - int row = scenarioTable.getSelectedRow(); - Scenario scenario = scenarioModel.getScenario(scenarioTable.convertRowIndexToModel(row)); - if (null == scenario) { - return; - } - boolean control = JOptionPane.showConfirmDialog(getFrame(), - "Did your side control the battlefield at the end of the scenario?", "Control of Battlefield?", - JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION; - ResolveScenarioTracker tracker = new ResolveScenarioTracker(scenario, getCampaign(), control); - ChooseMulFilesDialog chooseFilesDialog = new ChooseMulFilesDialog(getFrame(), true, tracker); - chooseFilesDialog.setVisible(true); - if (chooseFilesDialog.wasCancelled()) { - return; - } - // tracker.postProcessEntities(control); - ResolveScenarioWizardDialog resolveDialog = new ResolveScenarioWizardDialog(getFrame(), true, tracker); - resolveDialog.setVisible(true); - - if (!getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()) { - RetirementDefectionDialog dialog = new RetirementDefectionDialog(getCampaignGui(), - getCampaign().getMission(scenario.getMissionId()), false); - - if (!dialog.wasAborted()) { - getCampaign().applyRetirement(dialog.totalPayout(), dialog.getUnitAssignments()); + if (scenario == null) { + return; } - } - - if (getCampaign().getCampaignOptions().isEnableAutoAwards()) { - HashMap personnel = new HashMap<>(); - HashMap> scenarioKills = new HashMap<>(); - for (UUID personId : tracker.getPeopleStatus().keySet()) { - Person person = getCampaign().getPerson(personId); - PersonStatus status = tracker.getPeopleStatus().get(personId); - int injuryCount = 0; + // This handles StratCon undeployment + if (scenario instanceof AtBScenario) { + StratconScenario stratConScenario = ((AtBScenario) scenario).getStratconScenario(getCampaign()); - if (!person.getStatus().isDead() || getCampaign().getCampaignOptions().isIssuePosthumousAwards()) { - if (status.getHits() > person.getHitsPrior()) { - injuryCount = status.getHits() - person.getHitsPrior(); - } + if (stratConScenario != null) { + stratConScenario.resetScenario(getCampaign()); + return; } - - personnel.put(personId, injuryCount); - scenarioKills.put(personId, tracker.getPeopleStatus().get(personId).getKills()); } - boolean isCivilianHelp = false; - - if (tracker.getScenario() instanceof AtBScenario) { - isCivilianHelp = ((AtBScenario) tracker.getScenario()).getScenarioType() == AtBScenario.CIVILIANHELP; - } - - AutoAwardsController autoAwardsController = new AutoAwardsController(); - autoAwardsController.PostScenarioController(getCampaign(), personnel, scenarioKills, isCivilianHelp); - } - - for (UUID personId : tracker.getPeopleStatus().keySet()) { - Person person = getCampaign().getPerson(personId); - - if (person.getStatus() == PersonnelStatus.MIA && !control) { - person.changeStatus(getCampaign(), getCampaign().getLocalDate(), PersonnelStatus.POW); - } + // This handles Legacy AtB undeployment + scenario.clearAllForcesAndPersonnel(getCampaign()); } - - MekHQ.triggerEvent(new ScenarioResolvedEvent(scenario)); } private void printRecordSheets() { @@ -852,10 +776,102 @@ private void startScenario() { startScenario(null); } + + /** + * Resolve the selected scenario by proving a MUL file + */ + private void resolveScenario() { + Scenario scenario = getSelectedScenario(); + if (null == scenario) { + return; + } + getCampaign().getApp().resolveScenario(scenario); + } + + /** + * Auto-resolve the selected scenario. + * Can run both the auto resolve using princess or using the ACS engine + */ private void autoResolveScenario() { + Scenario scenario = getSelectedScenario(); + if (null == scenario) { + return; + } + promptAutoResolve(scenario); + } + + private void runAbstractCombatAutoResolve(Scenario scenario) { + List chosen = playerUnits(scenario, new StringBuilder()); + if (chosen.isEmpty()) { + return; + } + getCampaign().getApp().startAutoResolve((AtBScenario) scenario, chosen); + } + + private void runPrincessAutoResolve() { startScenario(getCampaign().getAutoResolveBehaviorSettings()); } + private void promptAutoResolve(Scenario scenario) { + // the options for the auto resolve method follow a predefined order, which is the same as the order in the enum + // and it uses that to preselect the option that is currently set in the campaign options + Object[] options = new Object[]{ + MHQInternationalization.getText("AutoResolveMethod.PRINCESS.text"), + MHQInternationalization.getText("AutoResolveMethod.ABSTRACT_COMBAT.text"), + }; + + var preSelectedOptionIndex = getCampaignOptions().getAutoResolveMethod().ordinal(); + + var selectedOption = JOptionPane.showOptionDialog(getFrame(), + MHQInternationalization.getText("AutoResolveMethod.promptForAutoResolveMethod.text"), + MHQInternationalization.getText("AutoResolveMethod.promptForAutoResolveMethod.title"), + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, options[preSelectedOptionIndex]); + + if (selectedOption == JOptionPane.CLOSED_OPTION) { + return; + } + + var autoResolveMethod = AutoResolveMethod.values()[selectedOption]; + + if (autoResolveMethod == AutoResolveMethod.PRINCESS) { + runPrincessAutoResolve(); + } else if (autoResolveMethod == AutoResolveMethod.ABSTRACT_COMBAT) { + runAbstractCombatAutoResolve(scenario); + } + } + + + private List playerUnits(Scenario scenario, StringBuilder undeployed) { + Vector uids = scenario.getForces(getCampaign()).getAllUnits(true); + if (uids.isEmpty()) { + return Collections.emptyList(); + } + List chosen = new ArrayList<>(); + for (UUID uid : uids) { + Unit u = getCampaign().getUnit(uid); + + if ((null != u) && (null != u.getEntity())) { + if (null == u.checkDeployment()) { + // Make sure the unit's entity and pilot are fully up to date! + u.resetPilotAndEntity(); + chosen.add(u); + } else { + undeployed.append('\n').append(u.getName()).append(" (").append(u.checkDeployment()).append(')'); + } + } + } + return chosen; + } + + private Scenario getSelectedScenario() { + int row = scenarioTable.getSelectedRow(); + if (row < 0) { + return null; + } + return scenarioModel.getScenario(scenarioTable.convertRowIndexToModel(row)); + } + private void startScenario(BehaviorSettings autoResolveBehaviorSettings) { int row = scenarioTable.getSelectedRow(); if (row < 0) { @@ -908,11 +924,12 @@ private void startScenario(BehaviorSettings autoResolveBehaviorSettings) { // code to support deployment of reinforcements for legacy ATB scenarios. if ((scenario instanceof AtBScenario) && !(scenario instanceof AtBDynamicScenario)) { - StrategicFormation assignedLance = ((AtBScenario) scenario).getStrategicFormation(getCampaign()); - if (assignedLance != null) { - int assignedForceId = assignedLance.getForceId(); + + CombatTeam combatTeam = ((AtBScenario) scenario).getCombatTeamById(getCampaign()); + if (combatTeam != null) { + int assignedForceId = combatTeam.getForceId(); int cmdrStrategy = 0; - Person commander = getCampaign().getPerson(StrategicFormation.findCommander(assignedForceId, getCampaign())); + Person commander = getCampaign().getPerson(CombatTeam.findCommander(assignedForceId, getCampaign())); if ((null != commander) && (null != commander.getSkill(SkillType.S_STRATEGY))) { cmdrStrategy = commander.getSkill(SkillType.S_STRATEGY).getLevel(); } @@ -1281,14 +1298,26 @@ public void refreshScenarioView() { SwingUtilities.invokeLater(() -> scrollScenarioView.getVerticalScrollBar().setValue(0)); final boolean canStartGame = scenario.canStartScenario(getCampaign()); + btnStartGame.setEnabled(canStartGame); btnJoinGame.setEnabled(canStartGame); btnLoadGame.setEnabled(canStartGame); btnGetMul.setEnabled(canStartGame); - btnClearAssignedUnits.setEnabled(canStartGame); + + final boolean hasTrack = scenario.getHasTrack(); + if (hasTrack) { + btnClearAssignedUnits.setEnabled(canStartGame && getCampaign().isGM()); + } else { + btnClearAssignedUnits.setEnabled(canStartGame); + } + btnResolveScenario.setEnabled(canStartGame); - btnAutoResolveScenario.setEnabled(canStartGame); + if (scenario instanceof AtBScenario) { + btnAutoResolveScenario.setEnabled(canStartGame); + } btnPrintRS.setEnabled(canStartGame); + + } public void refreshLanceAssignments() { diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index e27d869d2bd..e685c6bf53c 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -2,7 +2,7 @@ * CampaignGUI.java * * Copyright (c) 2009 Jay Lawson (jaylawson39 at yahoo.com). All rights reserved. - * Copyright (c) 2020-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -40,7 +40,6 @@ import mekhq.campaign.Campaign; import mekhq.campaign.CampaignController; import mekhq.campaign.CampaignOptions; -import mekhq.campaign.CampaignPreset; import mekhq.campaign.event.*; import mekhq.campaign.finances.Money; import mekhq.campaign.finances.financialInstitutions.FinancialInstitutions; @@ -71,13 +70,12 @@ import mekhq.campaign.report.TransportReport; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.NewsItem; +import mekhq.gui.campaignOptions.CampaignOptionsDialog; import mekhq.gui.dialog.*; import mekhq.gui.dialog.CampaignExportWizard.CampaignExportWizardState; -import mekhq.gui.dialog.nagDialogs.*; import mekhq.gui.dialog.reportDialogs.*; import mekhq.gui.enums.MHQTabType; import mekhq.gui.model.PartsTableModel; -import mekhq.gui.panes.campaignOptions.SelectPresetDialog; import mekhq.io.FileType; import mekhq.utilities.MHQXMLUtility; import org.w3c.dom.Document; @@ -93,13 +91,12 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.time.format.DateTimeFormatter; -import java.time.temporal.TemporalAdjusters; import java.util.List; import java.util.*; import java.util.stream.IntStream; import java.util.zip.GZIPOutputStream; -import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CANCELLED; +import static mekhq.gui.dialog.nagDialogs.NagController.triggerDailyNags; /** * The application's main frame. @@ -278,7 +275,6 @@ private void initComponents() { // Move the window frame.setLocation(x, y); - initMenu(); frame.setJMenuBar(menuBar); frame.getContentPane().setLayout(new BorderLayout()); @@ -289,7 +285,7 @@ private void initComponents() { frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent evt) { - getApplication().exit(); + getApplication().exit(true); } }); @@ -362,12 +358,7 @@ private void initMenu() { JMenuItem menuNew = new JMenuItem(resourceMap.getString("menuNew.text")); menuNew.setMnemonic(KeyEvent.VK_N); menuNew.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.ALT_DOWN_MASK)); - menuNew.addActionListener(evt -> { - int decision = new NewCampaignConfirmationDialog().YesNoOption(); - if (decision == JOptionPane.YES_OPTION) { - new DataLoadingDialog(frame, app, null).setVisible(true); - } - }); + menuNew.addActionListener(evt -> handleInAppNewCampaign()); menuFile.add(menuNew); // region menuImport @@ -376,25 +367,6 @@ private void initMenu() { JMenu menuImport = new JMenu(resourceMap.getString("menuImport.text")); menuImport.setMnemonic(KeyEvent.VK_I); - final JMenuItem miImportCampaignPreset = new JMenuItem(resourceMap.getString("miImportCampaignPreset.text")); - miImportCampaignPreset.setToolTipText(resourceMap.getString("miImportCampaignPreset.toolTipText")); - miImportCampaignPreset.setName("miImportCampaignPreset"); - miImportCampaignPreset.setMnemonic(KeyEvent.VK_C); - miImportCampaignPreset.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_DOWN_MASK)); - miImportCampaignPreset.addActionListener(evt -> { - final SelectPresetDialog campaignPresetSelectionDialog = - new SelectPresetDialog(getFrame(), true, false); - if (campaignPresetSelectionDialog.getReturnState() == PRESET_SELECTION_CANCELLED) { - return; - } - final CampaignPreset preset = campaignPresetSelectionDialog.getSelectedPreset(); - if (preset == null) { - return; - } - preset.applyContinuousToCampaign(getCampaign()); - }); - menuImport.add(miImportCampaignPreset); - JMenuItem miImportPerson = new JMenuItem(resourceMap.getString("miImportPerson.text")); miImportPerson.setMnemonic(KeyEvent.VK_P); miImportPerson.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.ALT_DOWN_MASK)); @@ -495,25 +467,6 @@ private void initMenu() { JMenu miExportXMLFile = new JMenu(resourceMap.getString("menuExportXML.text")); miExportXMLFile.setMnemonic(KeyEvent.VK_X); - final JMenuItem miExportCampaignPreset = new JMenuItem(resourceMap.getString("miExportCampaignPreset.text")); - miExportCampaignPreset.setName("miExportCampaignPreset"); - miExportCampaignPreset.setMnemonic(KeyEvent.VK_C); - miExportCampaignPreset.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_DOWN_MASK)); - miExportCampaignPreset.addActionListener(evt -> { - final CreateCampaignPresetDialog createCampaignPresetDialog = new CreateCampaignPresetDialog(getFrame(), - getCampaign(), null); - if (!createCampaignPresetDialog.showDialog().isConfirmed()) { - return; - } - final CampaignPreset preset = createCampaignPresetDialog.getPreset(); - if (preset == null) { - return; - } - preset.writeToFile(getFrame(), - FileDialogs.saveCampaignPreset(getFrame(), preset).orElse(null)); - }); - miExportXMLFile.add(miExportCampaignPreset); - JMenuItem miExportRankSystems = new JMenuItem(resourceMap.getString("miExportRankSystems.text")); miExportRankSystems.setName("miExportRankSystems"); miExportRankSystems.setMnemonic(KeyEvent.VK_R); @@ -696,7 +649,7 @@ private void initMenu() { JMenuItem menuExitItem = new JMenuItem(resourceMap.getString("menuExit.text")); menuExitItem.setMnemonic(KeyEvent.VK_E); menuExitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.ALT_DOWN_MASK)); - menuExitItem.addActionListener(evt -> getApplication().exit()); + menuExitItem.addActionListener(evt -> getApplication().exit(true)); menuFile.add(menuExitItem); menuBar.add(menuFile); @@ -1051,6 +1004,58 @@ private void initMenu() { // endregion Help Menu } + /** + * Handles a new campaign event triggered from within an existing campaign. + *

    + * This method performs the following actions in sequence: + *

      + *
    • Prompts the user to save any current progress through a confirmation dialog.
    • + *
    • If the user chooses to cancel or closes the dialog, the operation is aborted.
    • + *
    • If the user agrees to save and the save operation fails, the operation is aborted.
    • + *
    • Unregisters all event handlers associated with the current campaign, including those + * in the CampaignGUI and tabs.
    • + *
    • Starts a new campaign by displaying a data loading dialog.
    • + *
    + *

    + */ + private void handleInAppNewCampaign() { + int decision = new NewCampaignConfirmationDialog().YesNoOption(); + if (decision != JOptionPane.YES_OPTION) { + return; + } + + // Prompt the user to save + int savePrompt = JOptionPane.showConfirmDialog(null, + resourceMap.getString("savePrompt.text"), + resourceMap.getString("savePrompt.title"), + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + + // Abort if the user cancels, closes the dialog, or fails to save + if (savePrompt == JOptionPane.CANCEL_OPTION || savePrompt == JOptionPane.CLOSED_OPTION || + (savePrompt == JOptionPane.YES_OPTION && !app.getCampaigngui().saveCampaign(null))) { + return; + } + + // Unregister handlers for campaign tabs + for (int i = 0; i < tabMain.getTabCount(); i++) { + Component tab = tabMain.getComponentAt(i); + if (tab instanceof CampaignGuiTab) { + ((CampaignGuiTab) tab).disposeTab(); + } + } + + // Unregister other handlers + MekHQ.unregisterHandler(this); + + if (getCampaign().getStoryArc() != null) { + MekHQ.unregisterHandler(getCampaign().getStoryArc()); + } + + // Start a new campaign + new DataLoadingDialog(frame, app, null, null, true).setVisible(true); + } + private void initStatusBar() { statusPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 20, 4)); statusPanel.getAccessibleContext().setAccessibleName("Status Bar"); @@ -1112,7 +1117,19 @@ private void initTopButtons() { String padding = " "; JButton btnAdvanceDay = new JButton(padding + resourceMap.getString("btnAdvanceDay.text") + padding); btnAdvanceDay.setToolTipText(resourceMap.getString("btnAdvanceDay.toolTipText")); - btnAdvanceDay.addActionListener(evt -> getCampaignController().advanceDay()); + btnAdvanceDay.addActionListener(evt -> { + // We disable the button here, as we don't want the user to be able to advance day + // again, until after Advance Day has completed. + btnAdvanceDay.setEnabled(false); + + SwingUtilities.invokeLater(() -> { + try { + getCampaignController().advanceDay(); + } finally { + btnAdvanceDay.setEnabled(true); + } + }); + }); btnAdvanceDay.setMnemonic(KeyEvent.VK_A); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 2; @@ -1315,8 +1332,7 @@ public void focusOnPerson(UUID id) { public void showNews(int id) { NewsItem news = getCampaign().getNews().getNewsItem(id); if (null != news) { - NewsReportDialog nrd = new NewsReportDialog(frame, news); - nrd.setVisible(true); + new NewsDialog(getCampaign(), news); } } @@ -1446,8 +1462,9 @@ private void menuOptionsActionPerformed(final ActionEvent evt) { final RandomDivorceMethod randomDivorceMethod = oldOptions.getRandomDivorceMethod(); final RandomMarriageMethod randomMarriageMethod = oldOptions.getRandomMarriageMethod(); final RandomProcreationMethod randomProcreationMethod = oldOptions.getRandomProcreationMethod(); - final CampaignOptionsDialog cod = new CampaignOptionsDialog(getFrame(), getCampaign(), false); - cod.setVisible(true); + + CampaignOptionsDialog optionsDialog = new CampaignOptionsDialog(getFrame(), getCampaign()); + optionsDialog.setVisible(true); final CampaignOptions newOptions = getCampaign().getCampaignOptions(); @@ -1623,21 +1640,8 @@ public void refitUnit(Refit r, boolean selectModelName) { if (getCampaign().isWorkingOnRefit(tech) || tech.isEngineer()) { continue; } - StringBuilder nameBuilder = new StringBuilder(128); - nameBuilder.append("") - .append(tech.getFullName()) - .append(", ") - .append(SkillType.getColoredExperienceLevelName(tech.getSkillLevel(getCampaign(), false))) - .append(" ") - .append(tech.getPrimaryRoleDesc()) - .append(" (") - .append(getCampaign().getTargetFor(r, tech).getValueAsString()) - .append("+), ") - .append(tech.getMinutesLeft()) - .append('/') - .append(tech.getDailyAvailableTechTime()) - .append(" minutes"); - name = nameBuilder.toString(); + String nameBuilder = "" + tech.getFullName() + ", " + SkillType.getColoredExperienceLevelName(tech.getSkillLevel(getCampaign(), false)) + " " + tech.getPrimaryRoleDesc() + " (" + getCampaign().getTargetFor(r, tech).getValueAsString() + "+), " + tech.getMinutesLeft() + '/' + tech.getDailyAvailableTechTime() + " minutes"; + name = nameBuilder; techHash.put(name, tech); if (tech.isRightTechTypeFor(r)) { techList.add(lastRightTech++, name); @@ -2443,99 +2447,21 @@ public void undeployForce(Force f, boolean killSubs) { // region Subscriptions @Subscribe public void handleDayEnding(DayEndingEvent dayEndingEvent) { - if (checkForOverdueLoans(dayEndingEvent)) { - return; - } - - if (checkForDueScenarios(dayEndingEvent)) { - return; - } - - if (new UnmaintainedUnitsNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new PregnantCombatantNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { + if (triggerDailyNags(getCampaign())) { dayEndingEvent.cancel(); return; } - if (new PrisonersNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new UntreatedPersonnelNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new EndContractNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new NoCommanderNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new InvalidFactionNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new UnableToAffordJumpNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new InsufficientAstechsNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new InsufficientAstechTimeNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new InsufficientMedicsNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); + // Compulsory New Day Blockers + if (checkForOverdueLoans(dayEndingEvent)) { return; } - if (getCampaign().getLocalDate() - .equals(getCampaign().getLocalDate().with(TemporalAdjusters.lastDayOfMonth()))) { - if (new UnableToAffordExpensesNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - } - - if (new UnableToAffordLoanPaymentNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); + if (checkForDueScenarios(dayEndingEvent)) { return; } - if (getCampaign().getCampaignOptions().isUseAtB()) { - if (new ShortDeploymentNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new UnresolvedStratConContactsNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - - if (new OutstandingScenariosNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) { - dayEndingEvent.cancel(); - return; - } - } - + // Optional New Day Blocker if (getCampaign().getCampaignOptions().isUseRandomRetirement()) { int turnoverPrompt = getCampaign().checkTurnoverPrompt(); @@ -2558,7 +2484,8 @@ public void handleDayEnding(DayEndingEvent dayEndingEvent) { return; default: throw new IllegalStateException( - "Unexpected value in mekhq/gui/CampaignGUI.java/handleDayEnding: " + turnoverPrompt); + "Unexpected value in mekhq/gui/CampaignGUI.java/handleDayEnding: " + + turnoverPrompt); } } } diff --git a/MekHQ/src/mekhq/gui/FileDialogs.java b/MekHQ/src/mekhq/gui/FileDialogs.java index 873f9f325eb..6ebedceaaa3 100644 --- a/MekHQ/src/mekhq/gui/FileDialogs.java +++ b/MekHQ/src/mekhq/gui/FileDialogs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 The MegaMek Team. All rights reserved. + * Copyright (c) 2018-2025 The MegaMek Team. All rights reserved. * * This file is part of MekHQ. * @@ -24,10 +24,10 @@ import javax.swing.JFrame; +import mekhq.CampaignPreset; import mekhq.MekHQ; import mekhq.MHQConstants; import mekhq.campaign.Campaign; -import mekhq.campaign.CampaignPreset; import mekhq.campaign.mission.Scenario; import mekhq.campaign.mission.ScenarioTemplate; import mekhq.io.FileType; diff --git a/MekHQ/src/mekhq/gui/FinancesTab.java b/MekHQ/src/mekhq/gui/FinancesTab.java index 69b644c7698..014a9fb80c4 100644 --- a/MekHQ/src/mekhq/gui/FinancesTab.java +++ b/MekHQ/src/mekhq/gui/FinancesTab.java @@ -35,6 +35,7 @@ import mekhq.gui.enums.MHQTabType; import mekhq.gui.model.FinanceTableModel; import mekhq.gui.model.LoanTableModel; +import mekhq.gui.sorter.*; import mekhq.gui.utilities.JScrollPaneWithSpeed; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; @@ -54,6 +55,9 @@ import javax.swing.*; import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; + import java.awt.*; import java.text.SimpleDateFormat; import java.time.LocalDate; @@ -102,6 +106,12 @@ public void initTab() { financeModel = new FinanceTableModel(); financeTable = new JTable(financeModel); + // make column headers in the table clickable and sortable + TableRowSorter sorter = new TableRowSorter<>(financeTable.getModel()); + sorter.setComparator(FinanceTableModel.COL_DEBIT, new FormattedNumberSorter()); + sorter.setComparator(FinanceTableModel.COL_CREDIT, new FormattedNumberSorter()); + sorter.setComparator(FinanceTableModel.COL_BALANCE, new FormattedNumberSorter()); + financeTable.setRowSorter(sorter); financeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); FinanceTableMouseAdapter.connect(getCampaignGui(), financeTable, financeModel); financeTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); diff --git a/MekHQ/src/mekhq/gui/ForceRenderer.java b/MekHQ/src/mekhq/gui/ForceRenderer.java index acecca9f71f..b24e298ab48 100644 --- a/MekHQ/src/mekhq/gui/ForceRenderer.java +++ b/MekHQ/src/mekhq/gui/ForceRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2013-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -32,7 +32,7 @@ import javax.swing.tree.DefaultTreeCellRenderer; import java.awt.*; -import static mekhq.campaign.force.Force.STRATEGIC_FORMATION_OVERRIDE_NONE; +import static mekhq.campaign.force.Force.COMBAT_TEAM_OVERRIDE_NONE; public class ForceRenderer extends DefaultTreeCellRenderer { private static final MMLogger logger = MMLogger.create(ForceRenderer.class); @@ -53,7 +53,7 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean if (value instanceof Unit unit) { String name = ReportingUtilities.messageSurroundedBySpanWithColor( MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "No Crew"); - if (((Unit) value).getEntity() instanceof GunEmplacement) { + if (unit.getEntity() instanceof GunEmplacement) { name = "AutoTurret"; } String c3network = ""; @@ -127,10 +127,16 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean transport.append("
    Transported by: ") .append(unit.getTransportShipAssignment().getTransportShip().getName()); } - String text = name + ", " + unitName + c3network + transport; + String tacticalTransport = ""; + if (unit.hasTacticalTransportAssignment()) { + transport.append("
    Transported by: ") + .append(unit.getTacticalTransportAssignment().getTransport().getName()); + } + + String text = name + ", " + unitName + c3network + transport + tacticalTransport; Force force = unit.getCampaign().getForce(unit.getForceId()); - if((null != person) && (null != force) && (person.getId() == force.getForceCommanderID())) { + if((null != person) && (null != force) && (person.getId().equals(force.getForceCommanderID()))) { text = "" + text + ""; } setText("" + text + ""); @@ -149,16 +155,15 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean setOpaque(true); } - String format; - if (force.isStrategicFormation()) { - format = (force.getOverrideStrategicFormation() != STRATEGIC_FORMATION_OVERRIDE_NONE) ? - "%s" : "%s"; - } else { - format = (force.getOverrideStrategicFormation() != STRATEGIC_FORMATION_OVERRIDE_NONE) ? - "%s" : "%s"; - } + String formattedForceName = String.format("%s%s%s%s%s%s", + force.isCombatTeam() ? "" : "", + force.getOverrideCombatTeam() != COMBAT_TEAM_OVERRIDE_NONE ? "" : "", + force.getName(), + force.isCombatTeam() ? "" : "", + force.getOverrideCombatTeam() != COMBAT_TEAM_OVERRIDE_NONE ? "" : "", + force.isConvoyForce() ? " Ξ" : force.isCombatForce() ? "" : " ∅"); - setText(String.format(format, force.getName())); + setText(formattedForceName); } else { logger.error("Attempted to render node with unknown node class of " + ((value != null) ? value.getClass() : "null")); diff --git a/MekHQ/src/mekhq/gui/InterstellarMapPanel.java b/MekHQ/src/mekhq/gui/InterstellarMapPanel.java index 7f1d9089a4d..ee2dacda9cc 100644 --- a/MekHQ/src/mekhq/gui/InterstellarMapPanel.java +++ b/MekHQ/src/mekhq/gui/InterstellarMapPanel.java @@ -46,6 +46,8 @@ import java.util.List; import java.util.*; +import static megamek.common.ITechnology.RATING_A; + /** * This is not functional yet. Just testing things out. * A lot of this code is borrowed from InterstellarMap.java in MekWars @@ -599,26 +601,26 @@ protected void paintComponent(Graphics g) { double x = map2scrX(system.getX()); double y = map2scrY(system.getY()); int hpgRating = ObjectUtility.nonNull(system.getHPG(now), EquipmentType.RATING_X); - if (hpgRating == EquipmentType.RATING_A) { + if (hpgRating == RATING_A) { g2.setPaint(Color.CYAN); arc.setArcByCenter(x, y, size * 1.6, 0, 360, Arc2D.OPEN); g2.setStroke(thick); g2.draw(arc); } - if (hpgRating == EquipmentType.RATING_A || hpgRating == EquipmentType.RATING_B) { - g2.setPaint(Color.CYAN); + if (hpgRating == RATING_A || hpgRating == EquipmentType.RATING_B) { + g2.setPaint(Color.BLUE); arc.setArcByCenter(x, y, size * 1.3, 0, 360, Arc2D.OPEN); g2.setStroke(thin); g2.draw(arc); } if (hpgRating == EquipmentType.RATING_C) { - g2.setPaint(Color.CYAN); + g2.setPaint(Color.ORANGE); arc.setArcByCenter(x, y, size * 1.3, 0, 360, Arc2D.OPEN); g2.setStroke(dashed); g2.draw(arc); } if (hpgRating == EquipmentType.RATING_D) { - g2.setPaint(darkCyan); + g2.setPaint(Color.RED); arc.setArcByCenter(x, y, size * 1.3, 0, 360, Arc2D.OPEN); g2.setStroke(dotted); g2.draw(arc); @@ -629,14 +631,14 @@ protected void paintComponent(Graphics g) { PlanetarySystem p1 = link.primary; PlanetarySystem p2 = link.secondary; if (isSystemVisible(p1, false) || isSystemVisible(p2, false)) { - if (link.rating == EquipmentType.RATING_A) { + if (link.rating == RATING_A) { g2.setPaint(Color.CYAN); g2.setStroke(thick); g2.draw(new Line2D.Double(map2scrX(p1.getX()), map2scrY(p1.getY()), map2scrX(p2.getX()), map2scrY(p2.getY()))); } if (link.rating == EquipmentType.RATING_B) { - g2.setPaint(Color.CYAN); + g2.setPaint(Color.BLUE); g2.setStroke(dashed); g2.draw(new Line2D.Double(map2scrX(p1.getX()), map2scrY(p1.getY()), map2scrX(p2.getX()), map2scrY(p2.getY()))); @@ -752,10 +754,12 @@ protected void paintComponent(Graphics g) { ++i; } } else { - // Just a dark grey circle then - g2.setPaint(Color.DARK_GRAY); - arc.setArcByCenter(x, y, size, 0, 360.0, Arc2D.PIE); - g2.fill(arc); + if (optEmptySystems.isSelected()) { + // Just a dark grey circle then + g2.setPaint(Color.DARK_GRAY); + arc.setArcByCenter(x, y, size, 0, 360.0, Arc2D.PIE); + g2.fill(arc); + } } } else { g2.setPaint(getSystemColor(system)); @@ -843,7 +847,7 @@ protected void paintComponent(Graphics g) { optionPanel.add(Box.createRigidArea(new Dimension(0, 10))); optionPanel.add(createLabel("Options:")); optEmptySystems = createOptionCheckBox("Empty systems", checkboxIcon, checkboxSelectedIcon); - optEmptySystems.setSelected(true); + optEmptySystems.setSelected(false); optionPanel.add(optEmptySystems); optTerritory = createOptionCheckBox("Territory", checkboxIcon, checkboxSelectedIcon); optTerritory.setSelected(true); @@ -1097,7 +1101,7 @@ public Color getSystemColor(PlanetarySystem p) { case EquipmentType.RATING_D -> new Color(59, 82, 139); case EquipmentType.RATING_C -> new Color(33, 144, 140); case EquipmentType.RATING_B -> new Color(93, 200, 99); - case EquipmentType.RATING_A -> new Color(253, 231, 37); + case RATING_A -> new Color(253, 231, 37); default -> Color.BLACK; }; } @@ -1107,7 +1111,7 @@ public Color getSystemColor(PlanetarySystem p) { case EquipmentType.RATING_D -> new Color(81, 18, 124); case EquipmentType.RATING_C -> new Color(182, 54, 121); case EquipmentType.RATING_B -> new Color(251, 136, 97); - case EquipmentType.RATING_A -> new Color(252, 253, 191); + case RATING_A -> new Color(252, 253, 191); default -> Color.BLACK; }; } @@ -1117,7 +1121,7 @@ public Color getSystemColor(PlanetarySystem p) { case EquipmentType.RATING_D -> new Color(126, 3, 168); case EquipmentType.RATING_C -> new Color(204, 70, 120); case EquipmentType.RATING_B -> new Color(248, 148, 65); - case EquipmentType.RATING_A -> new Color(240, 249, 33); + case RATING_A -> new Color(240, 249, 33); default -> Color.BLACK; }; } @@ -1127,7 +1131,7 @@ public Color getSystemColor(PlanetarySystem p) { case EquipmentType.RATING_D -> new Color(86, 15, 110); case EquipmentType.RATING_C -> new Color(187, 55, 84); case EquipmentType.RATING_B -> new Color(249, 140, 10); - case EquipmentType.RATING_A -> new Color(252, 255, 164); + case RATING_A -> new Color(252, 255, 164); default -> Color.BLACK; }; } @@ -1137,7 +1141,7 @@ public Color getSystemColor(PlanetarySystem p) { case EquipmentType.RATING_D -> new Color(66, 77, 107); case EquipmentType.RATING_C -> new Color(124, 123, 120); case EquipmentType.RATING_B -> new Color(188, 175, 111); - case EquipmentType.RATING_A -> new Color(255, 234, 70); + case RATING_A -> new Color(255, 234, 70); default -> Color.BLACK; }; } @@ -1179,7 +1183,7 @@ public Color getSystemColor(PlanetarySystem p) { case EquipmentType.RATING_D -> new Color(84, 84, 84); case EquipmentType.RATING_C -> new Color(168, 168, 168); case EquipmentType.RATING_B -> new Color(222, 73, 104); - case EquipmentType.RATING_A -> new Color(252, 253, 191); + case RATING_A -> new Color(252, 253, 191); default -> Color.BLACK; }; } diff --git a/MekHQ/src/mekhq/gui/MekLabTab.java b/MekHQ/src/mekhq/gui/MekLabTab.java index dd16188a3d2..d850ea8fa38 100644 --- a/MekHQ/src/mekhq/gui/MekLabTab.java +++ b/MekHQ/src/mekhq/gui/MekLabTab.java @@ -26,6 +26,8 @@ import java.awt.GridBagLayout; import java.awt.Insets; import java.io.File; +import java.util.Properties; +import java.util.ResourceBundle; import javax.swing.BorderFactory; import javax.swing.BoxLayout; @@ -43,6 +45,7 @@ import megamek.logging.MMLogger; import megameklab.MMLConstants; import megameklab.ui.EntitySource; +import megameklab.ui.FileNameManager; import megameklab.ui.battleArmor.BABuildTab; import megameklab.ui.battleArmor.BAEquipmentTab; import megameklab.ui.battleArmor.BAStructureTab; @@ -70,6 +73,7 @@ import megameklab.ui.supportVehicle.SVBuildTab; import megameklab.ui.supportVehicle.SVEquipmentTab; import megameklab.ui.supportVehicle.SVStructureTab; +import megameklab.ui.util.MegaMekLabFileSaver; import megameklab.ui.util.RefreshListener; import megameklab.util.CConfig; import megameklab.util.UnitUtil; @@ -81,6 +85,7 @@ public class MekLabTab extends CampaignGuiTab { private static final MMLogger logger = MMLogger.create(MekLabTab.class); + protected final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.MekLabTab"); CampaignGUI campaignGUI; @@ -100,6 +105,7 @@ public class MekLabTab extends CampaignGuiTab { private JLabel lblCost; private JButton btnRefit; + private JButton btnSaveForLater; private JButton btnClear; private JButton btnRemove; @@ -110,11 +116,13 @@ public class MekLabTab extends CampaignGuiTab { private JLabel lblTons; private JPanel shoppingPanel; + private MegaMekLabFileSaver fileSaver; // region Constructors public MekLabTab(CampaignGUI gui, String name) { super(gui, name); this.campaignGUI = gui; + this.fileSaver = new MegaMekLabFileSaver(logger, resources.getString("dialog.saveAs.title")); this.repaint(); } // endregion Constructors @@ -139,6 +147,16 @@ public void initTab() { } campaignGUI.refitUnit(refit, true); }); + btnSaveForLater = new JButton("Save For Later"); + btnSaveForLater.addActionListener(evt -> { + Entity entity = labPanel.getEntity(); + UnitUtil.compactCriticals(entity); + labPanel.refreshAll(); // The crits may have moved + fileSaver.saveUnitAs(this.getFrame(), entity); + // Refresh unit cache so newly-saved file is available for refits. + MekSummaryCache.refreshUnitData(false); + }); + btnClear = new JButton("Clear Changes"); btnClear.addActionListener(evt -> resetUnit()); btnRemove = new JButton("Remove from Lab"); @@ -189,6 +207,8 @@ public void initTab() { c.insets = new Insets(0, 5, 2, 5); summaryPane.add(btnRefit, c); c.gridy++; + summaryPane.add(btnSaveForLater, c); + c.gridy++; summaryPane.add(btnClear, c); c.gridy++; summaryPane.add(btnRemove, c); @@ -333,6 +353,7 @@ public void refreshRefitSummary() { if (tonnage < testEntity.calculateWeight()) { btnRefit.setEnabled(false); btnRefit.setToolTipText("Unit is overweight."); + btnSaveForLater.setEnabled(true); // } else if (entity.getWeight() > testEntity.calculateWeight()) { // Taharqa: We are now going to allow users to build underweight // units, we will just give @@ -342,15 +363,19 @@ public void refreshRefitSummary() { } else if (sb.length() > 0) { btnRefit.setEnabled(false); btnRefit.setToolTipText(sb.toString()); + btnSaveForLater.setEnabled(true); } else if (null != refit.checkFixable()) { btnRefit.setEnabled(false); btnRefit.setToolTipText(refit.checkFixable()); + btnSaveForLater.setEnabled(true); } else if (refit.getRefitClass() == Refit.NO_CHANGE && entity.getWeight() == testEntity.calculateWeight()) { btnRefit.setEnabled(false); btnRefit.setToolTipText("Nothing to change."); + btnSaveForLater.setEnabled(false); } else { btnRefit.setEnabled(true); btnRefit.setToolTipText(null); + btnSaveForLater.setEnabled(true); } lblName.setText("" + unit.getName() + ""); @@ -482,9 +507,11 @@ private EntityPanel getCorrectLab(Entity en) { } } - private abstract static class EntityPanel extends JTabbedPane implements RefreshListener, EntitySource { + private abstract static class EntityPanel extends JTabbedPane implements RefreshListener, EntitySource, FileNameManager { private boolean refreshRequired = false; + private String fileName = ""; + private String originalName = ""; @Override public abstract Entity getEntity(); @@ -497,6 +524,23 @@ public void scheduleRefresh() { SwingUtilities.invokeLater(this::performRefresh); } + @Override + public String getFileName() { + return fileName; + } + + @Override + public boolean hasEntityNameChanged() { + return !MegaMekLabFileSaver.createUnitFilename(getEntity()).equals(originalName); + } + + @Override + public void setFileName(String fileName) { + this.fileName = fileName; + // If the filename is reloaded, restart tracking of the unit name changing. + this.originalName = MegaMekLabFileSaver.createUnitFilename(getEntity()); + } + private void performRefresh() { if (refreshRequired) { refreshRequired = false; diff --git a/MekHQ/src/mekhq/gui/ReportHyperlinkListener.java b/MekHQ/src/mekhq/gui/ReportHyperlinkListener.java index 5e7de10555a..0852a4a2e7d 100644 --- a/MekHQ/src/mekhq/gui/ReportHyperlinkListener.java +++ b/MekHQ/src/mekhq/gui/ReportHyperlinkListener.java @@ -2,7 +2,7 @@ * ReportHyperlinkListener.java * * Copyright (c) 2009 - Jay Lawson (jaylawson39 at yahoo.com). All Rights Reserved. - * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,15 +21,16 @@ */ package mekhq.gui; -import java.util.UUID; - -import javax.swing.event.HyperlinkEvent; -import javax.swing.event.HyperlinkListener; - import megamek.logging.MMLogger; import mekhq.campaign.unit.Unit; +import mekhq.gui.dialog.VocationalExperienceAwardDialog; import mekhq.gui.dialog.reportDialogs.MaintenanceReportDialog; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkEvent.EventType; +import javax.swing.event.HyperlinkListener; +import java.util.UUID; + public class ReportHyperlinkListener implements HyperlinkListener { private static final MMLogger logger = MMLogger.create(ReportHyperlinkListener.class); @@ -44,6 +45,7 @@ public class ReportHyperlinkListener implements HyperlinkListener { public static final String REPAIR = "REPAIR"; public static final String CONTRACT_MARKET = "CONTRACT_MARKET"; public static final String UNIT_MARKET = "UNIT_MARKET"; + public static final String PERSONNEL_ADVANCEMENT = "PERSONNEL_ADVANCEMENT"; // endregion Variable Declarations // region Constructors @@ -54,7 +56,7 @@ public ReportHyperlinkListener(final CampaignGUI campaignGUI) { @Override public void hyperlinkUpdate(final HyperlinkEvent evt) { - if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + if (evt.getEventType() == EventType.ACTIVATED) { if (evt.getDescription().startsWith(UNIT_MARKET)) { // Must come before UNIT since it starts with UNIT as // well campaignGUI.showUnitMarket(); @@ -68,13 +70,6 @@ public void hyperlinkUpdate(final HyperlinkEvent evt) { } else if (evt.getDescription().startsWith(PERSONNEL_MARKET)) { // Must come before PERSON since it starts // with PERSON as well campaignGUI.hirePersonMarket(); - } else if (evt.getDescription().startsWith(PERSON)) { - try { - final UUID id = UUID.fromString(evt.getDescription().split(":")[1]); - campaignGUI.focusOnPerson(id); - } catch (Exception e) { - logger.error("", e); - } } else if (evt.getDescription().startsWith(NEWS)) { try { final int id = Integer.parseInt(evt.getDescription().split("\\|")[1]); @@ -103,6 +98,20 @@ public void hyperlinkUpdate(final HyperlinkEvent evt) { } } else if (evt.getDescription().startsWith(CONTRACT_MARKET)) { campaignGUI.showContractMarket(); + } else if (evt.getDescription().startsWith(PERSONNEL_ADVANCEMENT)) { // Must come before PERSON since it starts + // with PERSON as well + try { + new VocationalExperienceAwardDialog(campaignGUI.getCampaign()); + } catch (Exception e) { + logger.error("", e); + } + } else if (evt.getDescription().startsWith(PERSON)) { + try { + final UUID id = UUID.fromString(evt.getDescription().split(":")[1]); + campaignGUI.focusOnPerson(id); + } catch (Exception e) { + logger.error("", e); + } } } } diff --git a/MekHQ/src/mekhq/gui/SpecialAbilityPanel.java b/MekHQ/src/mekhq/gui/SpecialAbilityPanel.java deleted file mode 100644 index 8df7b45b31a..00000000000 --- a/MekHQ/src/mekhq/gui/SpecialAbilityPanel.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MekHQ. - * - * MegaMek 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. - * - * MegaMek 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 MegaMek. If not, see . - */ -package mekhq.gui; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.GridLayout; -import java.util.ResourceBundle; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextArea; - -import mekhq.MekHQ; -import mekhq.campaign.personnel.SpecialAbility; -import mekhq.gui.dialog.EditSpecialAbilityDialog; -import mekhq.gui.panes.CampaignOptionsPane; - -/** - * An extension of JPanel that displays information about special abilities - * - * @author Jay Lawson - */ -public class SpecialAbilityPanel extends JPanel { - private SpecialAbility abil; - private JButton btnRemove; - private JButton btnEdit; - private final CampaignOptionsPane cop; - - public SpecialAbilityPanel(SpecialAbility a, CampaignOptionsPane cop) { - this.abil = a; - this.cop = cop; - - btnEdit = new JButton("Edit"); - btnRemove = new JButton("Remove"); - - btnEdit.addActionListener(evt -> editSPA()); - - btnRemove.addActionListener(evt -> remove()); - - setLayout(new GridBagLayout()); - - initComponents(); - } - - private void initComponents() { - final ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.SpecialAbilityPanel", - MekHQ.getMHQOptions().getLocale()); - - GridBagConstraints c = new GridBagConstraints(); - - JTextArea txtDesc = new JTextArea(abil.getDescription()); - txtDesc.setEditable(false); - txtDesc.setBackground(this.getBackground()); - - JPanel pnlButton = new JPanel(new GridLayout(0, 2)); - pnlButton.add(btnEdit); - pnlButton.add(btnRemove); - - c.gridx = 0; - c.gridy = 0; - c.weighty = 0.0; - c.gridwidth = 4; - c.weightx = 0.0; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.NORTHWEST; - add(pnlButton, c); - - c.gridx = 0; - c.gridy = 1; - c.weighty = 0.0; - c.gridwidth = 4; - c.weightx = 1.0; - c.fill = GridBagConstraints.HORIZONTAL; - c.anchor = GridBagConstraints.NORTHWEST; - add(txtDesc, c); - - c.gridx = 0; - c.gridy = 2; - c.weighty = 0.0; - c.gridwidth = 1; - c.fill = GridBagConstraints.HORIZONTAL; - c.anchor = GridBagConstraints.NORTHWEST; - add(new JLabel("XP Cost"), c); - c.gridx = 0; - c.gridy = 3; - c.weighty = 1.0; - add(new JLabel(Integer.toString(abil.getCost())), c); - - c.gridx = 1; - c.gridy = 2; - c.weighty = 0.0; - JLabel lblPrereq = new JLabel("Prerequisites"); - lblPrereq.setToolTipText(resourceMap.getString("lblPrereq.toolTipText")); - add(lblPrereq, c); - c.gridx = 1; - c.gridy = 3; - c.weighty = 1.0; - add(new JLabel("" + abil.getAllPrereqDesc() + ""), c); - - c.gridx = 2; - c.gridy = 2; - c.weighty = 0.0; - JLabel lblIncompatible = new JLabel("Incompatible"); - lblIncompatible.setToolTipText(resourceMap.getString("lblIncompatible.toolTipText")); - add(lblIncompatible, c); - c.gridx = 2; - c.gridy = 3; - c.weighty = 1.0; - add(new JLabel("" + abil.getInvalidDesc() + ""), c); - - c.gridx = 3; - c.gridy = 2; - c.weighty = 0.0; - JLabel lblRemove = new JLabel("Removes"); - lblRemove.setToolTipText(resourceMap.getString("lblRemove.toolTipText")); - add(lblRemove, c); - c.gridx = 3; - c.gridy = 3; - c.weighty = 1.0; - add(new JLabel("" + abil.getRemovedDesc() + ""), c); - - this.setBorder(BorderFactory.createTitledBorder(abil.getDisplayName())); - } - - private void remove() { - cop.btnRemoveSPA(abil.getName()); - } - - private void editSPA() { - EditSpecialAbilityDialog esad = new EditSpecialAbilityDialog(null, abil, cop.getCurrentSPA()); - esad.setVisible(true); - this.removeAll(); - initComponents(); - this.revalidate(); - this.repaint(); - } -} diff --git a/MekHQ/src/mekhq/gui/StratconPanel.java b/MekHQ/src/mekhq/gui/StratconPanel.java index fc6326052cc..df0bf5c16b5 100644 --- a/MekHQ/src/mekhq/gui/StratconPanel.java +++ b/MekHQ/src/mekhq/gui/StratconPanel.java @@ -18,8 +18,10 @@ import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBDynamicScenario; import mekhq.campaign.stratcon.*; import mekhq.campaign.stratcon.StratconBiomeManifest.ImageType; +import mekhq.campaign.stratcon.StratconScenario.ScenarioState; import mekhq.gui.stratcon.StratconScenarioWizard; import mekhq.gui.stratcon.TrackForceAssignmentUI; @@ -40,6 +42,8 @@ import java.util.Map; import static mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment.Allied; +import static mekhq.campaign.stratcon.StratconScenario.ScenarioState.PRIMARY_FORCES_COMMITTED; +import static mekhq.campaign.stratcon.StratconScenario.ScenarioState.UNRESOLVED; /** * This panel handles AtB-Stratcon GUI interactions with a specific scenario @@ -62,6 +66,7 @@ public class StratconPanel extends JPanel implements ActionListener { private static final String RCLICK_COMMAND_CAPTURE_FACILITY = "CaptureFacility"; private static final String RCLICK_COMMAND_ADD_FACILITY = "AddFacility"; private static final String RCLICK_COMMAND_REMOVE_SCENARIO = "RemoveScenario"; + private static final String RCLICK_COMMAND_RESET_DEPLOYMENT = "ResetDeployment"; /** * What to do when drawing a hex @@ -158,24 +163,39 @@ private void buildRightClickMenu(StratconCoords coords) { StratconScenario scenario = getSelectedScenario(); + if (campaignState.getContract().getCommandRights().isIntegrated()) { + menuItemManageForceAssignments = new JMenuItem(); + menuItemManageForceAssignments.setText("Unable to Deploy: Integrated Command"); + rightClickMenu.add(menuItemManageForceAssignments); + } + // display "Manage Force Assignment" if there is not a force already on the hex // except if there is already a non-cloaked scenario here. if (StratconRulesManager.canManuallyDeployAnyForce(coords, currentTrack, campaignState.getContract())) { menuItemManageForceAssignments = new JMenuItem(); - menuItemManageForceAssignments.setText("Manage Force Assignment"); + menuItemManageForceAssignments.setText("Manage Deployment"); menuItemManageForceAssignments.setActionCommand(RCLICK_COMMAND_MANAGE_FORCES); menuItemManageForceAssignments.addActionListener(this); rightClickMenu.add(menuItemManageForceAssignments); } - // display "Manage Scenario" if - // there is already a visible scenario on the hex - if ((scenario != null) && !scenario.getBackingScenario().isCloaked()) { - menuItemManageScenario = new JMenuItem(); - menuItemManageScenario.setText("Manage Scenario"); - menuItemManageScenario.setActionCommand(RCLICK_COMMAND_MANAGE_SCENARIO); - menuItemManageScenario.addActionListener(this); - rightClickMenu.add(menuItemManageScenario); + // display "Manage Scenario" if there is already a visible scenario on the hex + if (scenario != null) { + AtBDynamicScenario backingScenario = scenario.getBackingScenario(); + + if (backingScenario != null && !backingScenario.isCloaked()) { + menuItemManageScenario = new JMenuItem(); + + if (scenario.getCurrentState().equals(UNRESOLVED)) { + menuItemManageScenario.setText("Manage Deployment"); + } else { + menuItemManageScenario.setText("Manage Reinforcements"); + } + + menuItemManageScenario.setActionCommand(RCLICK_COMMAND_MANAGE_FORCES); + menuItemManageScenario.addActionListener(this); + rightClickMenu.add(menuItemManageScenario); + } } if ((currentTrack != null) && currentTrack.getAssignedCoordForces().containsKey(coords)) { @@ -196,26 +216,26 @@ private void buildRightClickMenu(StratconCoords coords) { rightClickMenu.addSeparator(); menuItemGMReveal = new JMenuItem(); - menuItemGMReveal.setText(currentTrack.isGmRevealed() ? "Hide Sector" : "Reveal Sector"); + menuItemGMReveal.setText(currentTrack.isGmRevealed() ? "Hide Sector (GM)" : "Reveal Sector (GM)"); menuItemGMReveal.setActionCommand(RCLICK_COMMAND_REVEAL_TRACK); menuItemGMReveal.addActionListener(this); rightClickMenu.add(menuItemGMReveal); if (currentTrack.getFacility(coords) != null) { menuItemRemoveFacility = new JMenuItem(); - menuItemRemoveFacility.setText("Remove Facility"); + menuItemRemoveFacility.setText("Remove Facility (GM)"); menuItemRemoveFacility.setActionCommand(RCLICK_COMMAND_REMOVE_FACILITY); menuItemRemoveFacility.addActionListener(this); rightClickMenu.add(menuItemRemoveFacility); menuItemSwitchOwner = new JMenuItem(); - menuItemSwitchOwner.setText("Switch Owner"); + menuItemSwitchOwner.setText("Switch Owner (GM)"); menuItemSwitchOwner.setActionCommand(RCLICK_COMMAND_CAPTURE_FACILITY); menuItemSwitchOwner.addActionListener(this); rightClickMenu.add(menuItemSwitchOwner); } else { menuItemAddFacility = new JMenu(); - menuItemAddFacility.setText("Add Facility"); + menuItemAddFacility.setText("Add Facility (GM)"); JMenu menuItemAddAlliedFacility = new JMenu(); menuItemAddAlliedFacility.setText("Allied"); @@ -248,10 +268,16 @@ private void buildRightClickMenu(StratconCoords coords) { if (scenario != null) { JMenuItem removeScenarioItem = new JMenuItem(); - removeScenarioItem.setText("Remove Scenario"); + removeScenarioItem.setText("Remove Scenario (GM)"); removeScenarioItem.setActionCommand(RCLICK_COMMAND_REMOVE_SCENARIO); removeScenarioItem.addActionListener(this); rightClickMenu.add(removeScenarioItem); + + JMenuItem resetDeploymentItem = new JMenuItem(); + resetDeploymentItem.setText("Reset Deployment (GM)"); + resetDeploymentItem.setActionCommand(RCLICK_COMMAND_RESET_DEPLOYMENT); + resetDeploymentItem.addActionListener(this); + rightClickMenu.add(resetDeploymentItem); } } } @@ -855,10 +881,10 @@ private String buildSelectedHexInfo(Campaign campaign) { StringBuilder infoBuilder = new StringBuilder(); infoBuilder.append("
    "); - infoBuilder.append("Average Temperature: "); + infoBuilder.append("Average Temperature: "); infoBuilder.append(currentTrack.getTemperature()); infoBuilder.append("°C
    "); - infoBuilder.append("Terrain Type: "); + infoBuilder.append("Terrain Type: "); infoBuilder.append(currentTrack.getTerrainTile(boardState.getSelectedCoords())); infoBuilder.append("
    "); @@ -866,7 +892,7 @@ private String buildSelectedHexInfo(Campaign campaign) { || currentTrack.getRevealedCoords().contains(boardState.getSelectedCoords()); if (coordsRevealed) { infoBuilder.append("Recon Complete
    "); + .append("'>Recon Complete
    "); } if (currentTrack.getAssignedCoordForces().containsKey(boardState.getSelectedCoords())) { @@ -875,13 +901,13 @@ private String buildSelectedHexInfo(Campaign campaign) { infoBuilder.append(force.getName()).append(" assigned"); if (currentTrack.getStickyForces().contains(forceID)) { - infoBuilder.append(" - remain deployed"); + infoBuilder.append(" - remain deployed"); } infoBuilder.append("
    ") - .append("Returns on ") + .append("Returns on ") .append(currentTrack.getAssignedForceReturnDates().get(forceID)) - .append("
    "); + .append("

    "); } } @@ -913,14 +939,19 @@ private String buildSelectedHexInfo(Campaign campaign) { } else { infoBuilder.append("Recon Incomplete"); + .append("'>Recon Incomplete"); } infoBuilder.append("
    "); StratconScenario selectedScenario = getSelectedScenario(); - if ((selectedScenario != null) && - ((selectedScenario.getDeploymentDate() != null) || currentTrack.isGmRevealed())) { - infoBuilder.append(selectedScenario.getInfo(campaign, true)); + if (selectedScenario != null) { + AtBDynamicScenario backingScenario = selectedScenario.getBackingScenario(); + + if (coordsRevealed + || !backingScenario.isCloaked() + || currentTrack.isGmRevealed()) { + infoBuilder.append(selectedScenario.getInfo(campaign)); + } } infoBuilder.append(""); @@ -945,32 +976,99 @@ public StratconCoords getSelectedCoords() { } /** - * Event handler for various button and menu item presses. + * Handles action events triggered by various StratCon-related commands from the right-click context menu. + * This method processes user interactions to update the game state, scenarios, facilities, and UI elements + * based on the selected command and inputs from the context menu. + * + *

    The supported commands and their effects are as follows:

    + *
      + *
    • {@code RCLICK_COMMAND_MANAGE_FORCES}: Displays the force management UI for the selected coordinates. + *
        + *
      • If no scenario exists at the selected coordinates, the force management UI is directly displayed.
      • + *
      • If a scenario exists, it only displays the UI if the scenario is unresolved.
      • + *
      + *
    • + *
    • {@code RCLICK_COMMAND_MANAGE_SCENARIO}: Displays the scenario wizard with the current scenario at the + * selected coordinates if the scenario's state is {@code PRIMARY_FORCES_COMMITTED}.
    • + *
    • {@code RCLICK_COMMAND_REVEAL_TRACK}: Toggles the "GM revealed" state for the current track and updates + * the menu text to reflect the state ("Hide Track" or "Reveal Track").
    • + *
    • {@code RCLICK_COMMAND_STICKY_FORCE}: Toggles the sticky force assignment for a given force ID at the + * selected track. When toggled:
    • + *
    • -- If selected, the force is added to the track as sticky.
    • + *
    • -- If deselected, the force is removed from the track's sticky forces.
    • + *
    • {@code RCLICK_COMMAND_REMOVE_FACILITY}: Deletes the facility present at the selected coordinates.
    • + *
    • {@code RCLICK_COMMAND_CAPTURE_FACILITY}: Changes the ownership of the facility at the selected coordinates + * to a different faction or player, as per the rules defined in {@link StratconRulesManager}.
    • + *
    • {@code RCLICK_COMMAND_ADD_FACILITY}: Adds a new facility to the selected coordinates. The facility's + * properties (visibility, type, etc.) are copied from the provided source facility.
    • + *
    • {@code RCLICK_COMMAND_REMOVE_SCENARIO}: Deletes the currently selected scenario from the campaign.
    • + *
    + * + * @param evt the {@link ActionEvent} representing the user's action. Contains information about + * the triggering source and command (e.g., which menu item was selected). + * + *

    Behavior:

    + *
      + *
    • The method retrieves the {@link StratconCoords} currently selected by the user, and performs actions based on the + * provided command string in the event.
    • + *
    • The scenarios, forces, and facilities of the {@link #currentTrack} are modified based on the command type, and + * updates are visually reflected in the UI.
    • + *
    • If a UI-related command is processed (e.g., displaying the scenario wizard or force assignment UI), the appropriate + * UI components are updated and made visible to the user.
    • + *
    + * + *

    General Information: If no valid {@link StratconCoords} are selected at the time of the event, + * the method will terminate with no further action. Certain commands (e.g., {@code RCLICK_COMMAND_REVEAL_TRACK}, + * {@code RCLICK_COMMAND_ADD_FACILITY}) require valid coordinates or source properties to execute successfully.

    + * + *

    If no specific actions from the above list are matched (no corresponding `case`), the method performs no effect.

    */ @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(ActionEvent evt) { StratconCoords selectedCoords = boardState.getSelectedCoords(); if (selectedCoords == null) { return; } - switch (e.getActionCommand()) { + boolean isPrimaryForce = false; + StratconScenario selectedScenario = currentTrack.getScenario(selectedCoords); + switch (evt.getActionCommand()) { case RCLICK_COMMAND_MANAGE_FORCES: - assignmentUI.display(campaign, campaignState, selectedCoords); - assignmentUI.setVisible(true); - break; + if (selectedScenario == null) { + assignmentUI.display(campaign, campaignState, selectedCoords); + assignmentUI.setVisible(true); + isPrimaryForce = true; + } + + if (selectedScenario != null) { + ScenarioState currentState = selectedScenario.getCurrentState(); + + if (currentState.equals(UNRESOLVED)) { + assignmentUI.display(campaign, campaignState, selectedCoords); + assignmentUI.setVisible(true); + isPrimaryForce = true; + } + } + // Deliberate fall-through case RCLICK_COMMAND_MANAGE_SCENARIO: - scenarioWizard.setCurrentScenario(currentTrack.getScenario(selectedCoords), - currentTrack, campaignState); - scenarioWizard.toFront(); - scenarioWizard.setVisible(true); + // It's possible a scenario may have been placed when deploying the force, so we + // need to recheck + selectedScenario = currentTrack.getScenario(selectedCoords); + if (selectedScenario != null + && selectedScenario.getCurrentState() == PRIMARY_FORCES_COMMITTED) { + scenarioWizard.setCurrentScenario(currentTrack.getScenario(selectedCoords), + currentTrack, campaignState, isPrimaryForce); + + scenarioWizard.toFront(); + scenarioWizard.setVisible(true); + } break; case RCLICK_COMMAND_REVEAL_TRACK: currentTrack.setGmRevealed(!currentTrack.isGmRevealed()); menuItemGMReveal.setText(currentTrack.isGmRevealed() ? "Hide Track" : "Reveal Track"); break; case RCLICK_COMMAND_STICKY_FORCE: - JCheckBoxMenuItem source = (JCheckBoxMenuItem) e.getSource(); + JCheckBoxMenuItem source = (JCheckBoxMenuItem) evt.getSource(); int forceID = (int) source.getClientProperty(RCLICK_COMMAND_STICKY_FORCE_ID); if (source.isSelected()) { @@ -987,7 +1085,7 @@ public void actionPerformed(ActionEvent e) { StratconRulesManager.switchFacilityOwner(currentTrack.getFacility(selectedCoords)); break; case RCLICK_COMMAND_ADD_FACILITY: - JMenuItem eventSource = (JMenuItem) e.getSource(); + JMenuItem eventSource = (JMenuItem) evt.getSource(); StratconFacility facility = (StratconFacility) eventSource .getClientProperty(RCLICK_COMMAND_ADD_FACILITY); StratconFacility newFacility = facility.clone(); @@ -1001,6 +1099,13 @@ public void actionPerformed(ActionEvent e) { campaign.removeScenario(scenario.getBackingScenario()); } break; + case RCLICK_COMMAND_RESET_DEPLOYMENT: + StratconScenario scenarioToReset = getSelectedScenario(); + + if (scenarioToReset != null) { + scenarioToReset.resetScenario(campaign); + } + break; } repaint(); diff --git a/MekHQ/src/mekhq/gui/StratconTab.java b/MekHQ/src/mekhq/gui/StratconTab.java index 896caa2fba8..63ba740b9ac 100644 --- a/MekHQ/src/mekhq/gui/StratconTab.java +++ b/MekHQ/src/mekhq/gui/StratconTab.java @@ -30,8 +30,8 @@ import mekhq.gui.utilities.JScrollPaneWithSpeed; import javax.swing.*; -import java.awt.Dialog.ModalityType; import java.awt.*; +import java.awt.Dialog.ModalityType; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -55,7 +55,7 @@ public class StratconTab extends CampaignGuiTab { private JLabel campaignStatusText; private JLabel objectiveStatusText; private JScrollPane expandedObjectivePanel; - private boolean objectivesCollapsed = true; + private boolean objectivesCollapsed = false; CampaignManagementDialog cmd; @@ -96,12 +96,12 @@ public void mousePressed(MouseEvent me) { } }); - setLayout(new GridLayout()); + setLayout(new BorderLayout()); stratconPanel = new StratconPanel(getCampaignGui(), infoPanelText); JScrollPane scrollPane = new JScrollPane(stratconPanel); scrollPane.getHorizontalScrollBar().setUnitIncrement(StratconPanel.HEX_X_RADIUS); scrollPane.getVerticalScrollBar().setUnitIncrement(StratconPanel.HEX_Y_RADIUS); - this.add(scrollPane); + this.add(scrollPane, BorderLayout.CENTER); // TODO: lance role assignment UI here? @@ -109,8 +109,8 @@ public void mousePressed(MouseEvent me) { cmd = new CampaignManagementDialog(this); JScrollPane infoScrollPane = new JScrollPaneWithSpeed(infoPanel); - this.add(infoScrollPane); - + infoScrollPane.setMaximumSize(new Dimension(UIUtil.scaleForGUI(UIUtil.scaleForGUI(600), infoScrollPane.getHeight()))); + this.add(infoScrollPane, BorderLayout.EAST); MekHQ.registerHandler(this); } @@ -121,28 +121,43 @@ private void initializeInfoPanel() { int gridY = 0; infoPanel = new JPanel(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); - constraints.anchor = GridBagConstraints.NORTHWEST; - constraints.gridx = gridY++; - infoPanel.add(new JLabel("Current Campaign Status:"), constraints); + // Default settings for left-aligned components + constraints.anchor = GridBagConstraints.WEST; + constraints.fill = GridBagConstraints.NONE; + constraints.weightx = 0.0; + constraints.weighty = 0.0; + constraints.insets = new Insets(5, 5, 5, 5); + constraints.gridx = 0; + // Add campaign status text constraints.gridy = gridY++; infoPanel.add(campaignStatusText, constraints); + // Add "Manage Campaign State" button JButton btnManageCampaignState = new JButton("Manage SP/CVP"); btnManageCampaignState.addActionListener(this::showCampaignStateManagement); constraints.gridy = gridY++; infoPanel.add(btnManageCampaignState, constraints); + // Add an expanded objective panel (scrollable) expandedObjectivePanel = new JScrollPaneWithSpeed(objectiveStatusText); - expandedObjectivePanel.setPreferredSize(new Dimension(400, 300)); + expandedObjectivePanel.setPreferredSize(new Dimension(UIUtil.scaleForGUI(550, 300))); constraints.gridy = gridY++; + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.weightx = 1.0; infoPanel.add(expandedObjectivePanel, constraints); + // Reset horizontal fill for subsequent components + constraints.fill = GridBagConstraints.NONE; + constraints.weightx = 0.0; + + // Add "Assigned Sectors" label JLabel lblCurrentTrack = new JLabel("Assigned Sectors:"); constraints.gridy = gridY++; infoPanel.add(lblCurrentTrack, constraints); + // Add track list wrapped in a scroll pane listModel = new DefaultListModel<>(); listCurrentTrack = new JList<>(listModel); listCurrentTrack.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -151,14 +166,27 @@ private void initializeInfoPanel() { listCurrentTrack.addListSelectionListener(evt -> trackSelectionHandler()); JScrollPane scrollPane = new JScrollPane(listCurrentTrack); - scrollPane.setPreferredSize(new Dimension(UIUtil.scaleForGUI(400), - listCurrentTrack.getFixedCellHeight() * 10)); constraints.gridy = gridY++; - + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.weightx = 1.0; infoPanel.add(scrollPane, constraints); - constraints.gridx = 2; - constraints.gridheight = 2; + + // Reset horizontal fill again + constraints.fill = GridBagConstraints.NONE; + constraints.weightx = 0.0; + + // Add additional info panel text or components + constraints.gridx = 0; + constraints.gridy = gridY++; + constraints.gridheight = 3; infoPanel.add(infoPanelText, constraints); + + // Add a spacer to push all components upward (top alignment) + constraints.gridx = 0; + constraints.gridy = gridY++; + constraints.weighty = 1.0; + constraints.fill = GridBagConstraints.VERTICAL; + infoPanel.add(new JPanel(), constraints); // Invisible filler component } /** @@ -222,18 +250,17 @@ public void updateCampaignState() { expandedObjectivePanel.setVisible(true); StringBuilder sb = new StringBuilder(); - sb.append("") - .append(currentContract.getContractType()).append(": ").append(currentContract.getName()) - .append("
    ") - .append(campaignState.getBriefingText()); + sb.append("").append(currentContract.getContractType()).append(": ") + .append(currentContract.getName()).append("
    ") + .append("").append(campaignState.getBriefingText()).append(""); if (currentContract.getEndingDate().isBefore(currentDate)) { sb.append("
    Contract term has expired!"); } - sb.append("
    Campaign Victory Points: ").append(campaignState.getVictoryPoints()) - .append("
    Support Points: ").append(campaignState.getSupportPoints()) - .append("
    Deployment Period: ").append(currentTDI.track.getDeploymentTime()) + sb.append("
    Campaign Victory Points: ").append(campaignState.getVictoryPoints()) + .append("
    Support Points: ").append(campaignState.getSupportPoints()) + .append("
    Deployment Period: ").append(currentTDI.track.getDeploymentTime()) .append(" days") .append(""); @@ -373,7 +400,7 @@ private String buildStrategicObjectiveText(StratconCampaignState campaignState) } } - // special case text reminding player to complete required scenarios + // special case text reminding player to complete Turning Point scenarios if (!campaignState.getContract().getCommandRights().isIndependent()) { boolean contractIsActive = campaignState.getContract().isActiveOn(getCampaignGui().getCampaign().getLocalDate()); @@ -385,7 +412,7 @@ private String buildStrategicObjectiveText(StratconCampaignState campaignState) sb.append("").append(OBJECTIVE_FAILED); } - sb.append(" Maintain Campaign Victory Point count above 0 by completing required scenarios") + sb.append(" Maintain Campaign Victory Point count above 0 by completing Turning Point scenarios") .append("
    "); } @@ -431,7 +458,8 @@ private void showCampaignStateManagement(ActionEvent e) { if (selectedTrack == null) { return; } - cmd.display(selectedTrack.contract.getStratconCampaignState(), selectedTrack.track, getCampaign().isGM()); + cmd.display(getCampaign(), selectedTrack.contract.getStratconCampaignState(), + selectedTrack.track, getCampaign().isGM()); cmd.setModalityType(ModalityType.APPLICATION_MODAL); cmd.setVisible(true); } diff --git a/MekHQ/src/mekhq/gui/TOETab.java b/MekHQ/src/mekhq/gui/TOETab.java index 6d2332aa1c3..87f92ee8e95 100644 --- a/MekHQ/src/mekhq/gui/TOETab.java +++ b/MekHQ/src/mekhq/gui/TOETab.java @@ -175,8 +175,12 @@ public Dimension getPreferredScrollableViewportSize() { tabUnit.add("Unit", scrollUnit); panForceView.add(tabUnit, BorderLayout.CENTER); SwingUtilities.invokeLater(() -> scrollUnit.getVerticalScrollBar().setValue(0)); - tabUnit.setSelectedIndex(tabUnitLastSelectedIndex); - tabUnit.addChangeListener(evt -> forceViewTabChange()); // added late so it won't overwrite + try { + tabUnit.setSelectedIndex(tabUnitLastSelectedIndex); + tabUnit.addChangeListener(evt -> forceViewTabChange()); // added late so it won't overwrite + } catch (ArrayIndexOutOfBoundsException ignored) {} + // We can ignore here because if the selected index is out of bounds, we're just going + // to not select the unit in the TO&E. } else if (node instanceof Force) { final JScrollPane scrollForce = new JScrollPaneWithSpeed(new ForceViewPanel((Force) node, getCampaign())); panForceView.add(scrollForce, BorderLayout.CENTER); diff --git a/MekHQ/src/mekhq/gui/adapter/LoanTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/LoanTableMouseAdapter.java index c0e5835cb54..12f6dc900a9 100644 --- a/MekHQ/src/mekhq/gui/adapter/LoanTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/LoanTableMouseAdapter.java @@ -18,16 +18,6 @@ */ package mekhq.gui.adapter; -import java.awt.event.ActionEvent; -import java.util.Optional; -import java.util.UUID; - -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPopupMenu; -import javax.swing.JTable; - import mekhq.MekHQ; import mekhq.campaign.event.LoanRemovedEvent; import mekhq.campaign.event.PartRemovedEvent; @@ -37,6 +27,11 @@ import mekhq.gui.dialog.PayCollateralDialog; import mekhq.gui.model.LoanTableModel; +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.util.Optional; +import java.util.UUID; + public class LoanTableMouseAdapter extends JPopupMenuAdapter { private CampaignGUI gui; private JTable loanTable; @@ -69,7 +64,7 @@ public void actionPerformed(ActionEvent action) { if (0 == JOptionPane .showConfirmDialog( null, - "Defaulting on this loan will affect your unit rating the same as a contract breach.\nDo you wish to proceed?", + "Defaulting on this loan will affect your unit rating.\nDo you wish to proceed?", "Default on " + selectedLoan + "?", JOptionPane.YES_NO_OPTION)) { PayCollateralDialog pcd = new PayCollateralDialog( gui.getFrame(), true, gui.getCampaign(), selectedLoan); diff --git a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java index aaa398abb43..cbe371ec561 100644 --- a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java @@ -49,6 +49,7 @@ 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; import mekhq.campaign.personnel.randomEvents.PersonalityController; import mekhq.campaign.personnel.ranks.Rank; import mekhq.campaign.personnel.ranks.RankSystem; @@ -78,6 +79,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.lang.Math.round; import static megamek.client.ui.WrapLayout.wordWrap; import static mekhq.campaign.personnel.education.Academy.skillParser; import static mekhq.campaign.personnel.education.EducationController.getAcademy; @@ -143,6 +145,7 @@ public class PersonnelTableMouseAdapter extends JPopupMenuAdapter { private static final String CMD_REMOVE_PREGNANCY = "PREGNANCY_SPOUSE"; private static final String CMD_LOYALTY = "LOYALTY"; private static final String CMD_PERSONALITY = "PERSONALITY"; + private static final String CMD_ADD_RANDOM_ABILITY = "ADD_RANDOM_ABILITY"; private static final String CMD_IMPRISON = "IMPRISON"; private static final String CMD_FREE = "FREE"; @@ -911,8 +914,12 @@ public void actionPerformed(ActionEvent action) { break; } case CMD_BUY_EDGE: { - final int cost = gui.getCampaign().getCampaignOptions().getEdgeCost(); + int baseCost = gui.getCampaign().getCampaignOptions().getEdgeCost(); + double costMultiplier = gui.getCampaign().getCampaignOptions().getXpCostMultiplier(); for (Person person : people) { + double intelligenceCostMultiplier = person.getIntelligenceXpCostMultiplier(gui.getCampaign().getCampaignOptions()); + int cost = (int) round(baseCost * intelligenceCostMultiplier * costMultiplier); + selectedPerson.spendXP(cost); person.changeEdge(1); // Make the new edge point available to support personnel, but don't reset until @@ -1103,16 +1110,15 @@ public void actionPerformed(ActionEvent action) { return; } - // pay person - for (Person person : people) { - person.payPerson(Money.of(payment)); - MekHQ.triggerEvent(new PersonChangedEvent(person)); - } + // pay person & add expense + Map personMoneyMap = new HashMap<>(); + personMoneyMap.put(selectedPerson, Money.of(payment)); + gui.getCampaign().payPersonnel(TransactionType.MISCELLANEOUS, + Money.of(payment), + String.format(resources.getString("givePayment.format"),selectedPerson.getFullName()), + personMoneyMap); + MekHQ.triggerEvent(new PersonChangedEvent(selectedPerson)); - // add expense - gui.getCampaign().removeFunds(TransactionType.MISCELLANEOUS, Money.of(payment), - String.format(resources.getString("givePayment.format"), - selectedPerson.getFullName())); break; } @@ -1130,6 +1136,14 @@ public void actionPerformed(ActionEvent action) { } break; } + case CMD_ADD_RANDOM_ABILITY: { + SingleSpecialAbilityGenerator singleSpecialAbilityGenerator = new SingleSpecialAbilityGenerator(); + for (Person person : people) { + singleSpecialAbilityGenerator.rollSPA(gui.getCampaign(), person); + MekHQ.triggerEvent(new PersonChangedEvent(person)); + } + break; + } // region Randomization Menu case CMD_RANDOM_NAME: { @@ -1489,14 +1503,25 @@ protected Optional createPopupMenu() { } final PersonnelRole[] roles = PersonnelRole.values(); - menu = new JMenu(resources.getString("changePrimaryRole.text")); + menu = new JMenu(resources.getString("changePrimaryRole.text")); for (final PersonnelRole role : roles) { - if (person.canPerformRole(gui.getCampaign().getLocalDate(), person, role, true)) { - cbMenuItem = new JCheckBoxMenuItem(role.getName(person.isClanPersonnel())); + boolean allCanPerform = true; + + for (Person selectedPerson : getSelectedPeople()) { + if (!selectedPerson.canPerformRole(gui.getCampaign().getLocalDate(), role, true)) { + allCanPerform = false; + break; + } + } + + if (allCanPerform) { + cbMenuItem = new JCheckBoxMenuItem(role.toString()); cbMenuItem.setActionCommand(makeCommand(CMD_PRIMARY_ROLE, role.name())); - cbMenuItem.setSelected(person.getPrimaryRole() == role); cbMenuItem.addActionListener(this); + if (oneSelected && role == person.getPrimaryRole()) { + cbMenuItem.setSelected(true); + } menu.add(cbMenuItem); } } @@ -1504,11 +1529,22 @@ protected Optional createPopupMenu() { menu = new JMenu(resources.getString("changeSecondaryRole.text")); for (final PersonnelRole role : roles) { - if (person.canPerformRole(gui.getCampaign().getLocalDate(), person, role, false)) { - cbMenuItem = new JCheckBoxMenuItem(role.getName(person.isClanPersonnel())); + boolean allCanPerform = true; + + for (Person selectedPerson : getSelectedPeople()) { + if (!selectedPerson.canPerformRole(gui.getCampaign().getLocalDate(), role, false)) { + allCanPerform = false; + break; + } + } + + if (allCanPerform) { + cbMenuItem = new JCheckBoxMenuItem(role.toString()); cbMenuItem.setActionCommand(makeCommand(CMD_SECONDARY_ROLE, role.name())); - cbMenuItem.setSelected(person.getSecondaryRole() == role); cbMenuItem.addActionListener(this); + if (oneSelected && role == person.getSecondaryRole()) { + cbMenuItem.setSelected(true); + } menu.add(cbMenuItem); } } @@ -1912,7 +1948,7 @@ protected Optional createPopupMenu() { if (!spa.isEligible(person)) { continue; } - cost = (int) Math.round((spa.getCost() + cost = (int) round((spa.getCost() * person.getIntelligenceXpCostMultiplier(gui.getCampaign().getCampaignOptions()) * gui.getCampaign().getCampaignOptions().getXpCostMultiplier())); String costDesc; @@ -2208,8 +2244,7 @@ protected Optional createPopupMenu() { String type = SkillType.getSkillList()[i]; int cost = person.hasSkill(type) ? person.getSkill(type).getCostToImprove() : SkillType.getType(type).getCost(0); - cost = (int) Math - .round(cost * person.getIntelligenceXpCostMultiplier(gui.getCampaign().getCampaignOptions()) + cost = (int) round(cost * person.getIntelligenceXpCostMultiplier(gui.getCampaign().getCampaignOptions()) * gui.getCampaign().getCampaignOptions().getXpCostMultiplier()); if (cost >= 0) { @@ -2231,7 +2266,7 @@ protected Optional createPopupMenu() { // Edge Purchasing if (gui.getCampaign().getCampaignOptions().isUseEdge()) { JMenu edgeMenu = new JMenu(resources.getString("edge.text")); - int cost = (int) Math.round(gui.getCampaign().getCampaignOptions().getEdgeCost() + int cost = (int) round(gui.getCampaign().getCampaignOptions().getEdgeCost() * person.getIntelligenceXpCostMultiplier(gui.getCampaign().getCampaignOptions()) * gui.getCampaign().getCampaignOptions().getXpCostMultiplier()); @@ -3000,6 +3035,13 @@ protected Optional createPopupMenu() { menu.add(menuItem); } + if (gui.getCampaign().getCampaignOptions().isUseAbilities()) { + menuItem = new JMenuItem(resources.getString("addRandomSPA.text")); + menuItem.setActionCommand(CMD_ADD_RANDOM_ABILITY); + menuItem.addActionListener(this); + menu.add(menuItem); + } + JMenuHelpers.addMenuIfNonEmpty(popup, menu); } // endregion GM Menu diff --git a/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java index f15a1ec29a6..2e609db69f5 100644 --- a/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2018-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -26,6 +26,7 @@ import megamek.common.enums.SkillLevel; import megamek.logging.MMLogger; import mekhq.MekHQ; +import mekhq.campaign.enums.CampaignTransportType; import mekhq.campaign.event.DeploymentChangedEvent; import mekhq.campaign.event.NetworkChangedEvent; import mekhq.campaign.event.OrganizationChangedEvent; @@ -46,19 +47,24 @@ import mekhq.gui.dialog.ForceTemplateAssignmentDialog; import mekhq.gui.dialog.MarkdownEditorDialog; import mekhq.gui.dialog.iconDialogs.LayeredForceIconDialog; +import mekhq.gui.menus.AssignForceToShipTransportMenu; +import mekhq.gui.menus.AssignForceToTacticalTransportMenu; import mekhq.gui.menus.ExportUnitSpriteMenu; import mekhq.gui.utilities.JMenuHelpers; import mekhq.gui.utilities.StaticChecks; +import mekhq.utilities.MHQInternationalization; import javax.swing.*; import javax.swing.tree.TreePath; import java.awt.event.ActionEvent; import java.util.*; -import static mekhq.campaign.force.Force.STRATEGIC_FORMATION_OVERRIDE_FALSE; -import static mekhq.campaign.force.Force.STRATEGIC_FORMATION_OVERRIDE_NONE; -import static mekhq.campaign.force.Force.STRATEGIC_FORMATION_OVERRIDE_TRUE; -import static mekhq.campaign.force.StrategicFormation.recalculateStrategicFormations; +import static mekhq.campaign.enums.CampaignTransportType.SHIP_TRANSPORT; +import static mekhq.campaign.enums.CampaignTransportType.TACTICAL_TRANSPORT; +import static mekhq.campaign.force.CombatTeam.recalculateCombatTeams; +import static mekhq.campaign.force.Force.COMBAT_TEAM_OVERRIDE_FALSE; +import static mekhq.campaign.force.Force.COMBAT_TEAM_OVERRIDE_NONE; +import static mekhq.campaign.force.Force.COMBAT_TEAM_OVERRIDE_TRUE; public class TOEMouseAdapter extends JPopupMenuAdapter { private static final MMLogger logger = MMLogger.create(TOEMouseAdapter.class); @@ -96,14 +102,12 @@ public static void connect(CampaignGUI gui, JTree tree) { private static final String DEPLOY_UNIT = "DEPLOY_UNIT"; private static final String GOTO_UNIT = "GOTO_UNIT"; private static final String REMOVE_UNIT = "REMOVE_UNIT"; - private static final String UNASSIGN_FROM_SHIP = "UNASSIGN_FROM_SHIP"; private static final String UNDEPLOY_UNIT = "UNDEPLOY_UNIT"; private static final String COMMAND_ADD_UNIT = "ADD_UNIT|FORCE|"; private static final String COMMAND_ASSIGN_TO_SHIP = "ASSIGN_TO_SHIP|UNIT|"; private static final String COMMAND_REMOVE_UNIT = "REMOVE_UNIT|UNIT|empty|"; private static final String COMMAND_DEPLOY_UNIT = "DEPLOY_UNIT|UNIT|"; - private static final String COMMAND_UNASSIGN_FROM_SHIP = "UNASSIGN_FROM_SHIP|UNIT|empty|"; private static final String COMMAND_UNDEPLOY_UNIT = "UNDEPLOY_UNIT|UNIT|empty|"; private static final String COMMAND_GOTO_UNIT = "GOTO_UNIT|UNIT|empty|"; @@ -128,6 +132,7 @@ public static void connect(CampaignGUI gui, JTree tree) { private static final String CHANGE_NAME = "CHANGE_NAME"; private static final String CHANGE_COMBAT_STATUS = "CHANGE_COMBAT_STATUS"; private static final String CHANGE_COMBAT_STATUSES = "CHANGE_COMBAT_STATUSES"; + private static final String CHANGE_CONVOY_STATUS = "CHANGE_CONVOY_STATUS"; private static final String CHANGE_STRATEGIC_FORCE_OVERRIDE = "CHANGE_STRATEGIC_FORCE_OVERRIDE"; private static final String REMOVE_STRATEGIC_FORCE_OVERRIDE = "REMOVE_STRATEGIC_FORCE_OVERRIDE"; @@ -140,6 +145,7 @@ public static void connect(CampaignGUI gui, JTree tree) { private static final String COMMAND_CHANGE_FORCE_NAME = "CHANGE_NAME|FORCE|empty|"; private static final String COMMAND_CHANGE_FORCE_COMBAT_STATUS = "CHANGE_COMBAT_STATUS|FORCE|empty|"; private static final String COMMAND_CHANGE_FORCE_COMBAT_STATUSES = "CHANGE_COMBAT_STATUSES|FORCE|empty|"; + private static final String COMMAND_CHANGE_FORCE_CONVOY_STATUS = "CHANGE_CONVOY_STATUS|FORCE|empty|"; private static final String COMMAND_CHANGE_STRATEGIC_FORCE_OVERRIDE = "CHANGE_STRATEGIC_FORCE_OVERRIDE|FORCE|empty|"; private static final String COMMAND_REMOVE_STRATEGIC_FORCE_OVERRIDE = "REMOVE_STRATEGIC_FORCE_OVERRIDE|FORCE|empty|"; @@ -176,7 +182,6 @@ public static void connect(CampaignGUI gui, JTree tree) { private static final String LOAD_UNITS_DIALOG_TITLE = "Also deploy transported units?"; private static final String ASSIGN_FORCE_TRN_TITLE = "Assign Force to Transport Ship"; - private static final String UNASSIGN_FORCE_TRN_TITLE = "Unassign Force from Transport Ship"; private static final String MEK_CARRIERS = "Mek Transports"; private static final String PROTOMEK_CARRIERS = "ProtoMek Transports"; private static final String LVEE_CARRIERS = "Light Vehicle Transports"; @@ -319,20 +324,11 @@ public void actionPerformed(ActionEvent action) { } } // now load the units - ship.loadTransportShip(units); + //ship.loadTransportShip(units); MekHQ.triggerEvent(new UnitChangedEvent(ship)); gui.getTOETab().refreshForceView(); } } - } else if (command.contains(UNASSIGN_FROM_SHIP)) { - for (Unit u : units) { - if (u.hasTransportShipAssignment()) { - Unit oldShip = u.getTransportShipAssignment().getTransportShip(); - oldShip.unloadFromTransportShip(u); - MekHQ.triggerEvent(new UnitChangedEvent(oldShip)); - } - } - gui.getTOETab().refreshForceView(); } else if (command.contains(TOEMouseAdapter.ADD_UNIT)) { if (null != singleForce) { Unit u = gui.getCampaign().getUnit(UUID.fromString(target)); @@ -433,41 +429,68 @@ public void actionPerformed(ActionEvent action) { } final boolean combatForce = !singleForce.isCombatForce(); + final boolean subforces = command.contains(TOEMouseAdapter.CHANGE_COMBAT_STATUSES); for (final Force force : forces) { force.setCombatForce(combatForce, subforces); + force.setConvoyForce(false); + + + } + + for (Force formation : gui.getCampaign().getAllForces()) { + MekHQ.triggerEvent(new OrganizationChangedEvent(formation)); } - gui.undeployForces(forces); + gui.getTOETab().refreshForceView(); + } else if (command.contains(TOEMouseAdapter.CHANGE_CONVOY_STATUS)) { + if (singleForce == null) { + return; + } + + final boolean convoyForce = !singleForce.isConvoyForce(); + for (final Force force : forces) { + force.setConvoyForce(convoyForce); + } + + for (Force parentForce : singleForce.getAllParents()) { + parentForce.setConvoyForce(false); + } + + for (Force childForce : singleForce.getAllSubForces()) { + childForce.setConvoyForce(false); + } for (Force formation : gui.getCampaign().getAllForces()) { MekHQ.triggerEvent(new OrganizationChangedEvent(formation)); } + + gui.getTOETab().refreshForceView(); } else if (command.contains(CHANGE_STRATEGIC_FORCE_OVERRIDE)) { if (singleForce == null) { return; } - boolean formationState = singleForce.isStrategicFormation(); - singleForce.setStrategicFormation(!formationState); - singleForce.setOverrideStrategicFormation(formationState ? STRATEGIC_FORMATION_OVERRIDE_FALSE : STRATEGIC_FORMATION_OVERRIDE_TRUE); + boolean formationState = singleForce.isCombatTeam(); + singleForce.setCombatTeamStatus(!formationState); + singleForce.setOverrideCombatTeam(formationState ? COMBAT_TEAM_OVERRIDE_FALSE : COMBAT_TEAM_OVERRIDE_TRUE); for (Force childForce : singleForce.getAllSubForces()) { - childForce.setOverrideStrategicFormation(STRATEGIC_FORMATION_OVERRIDE_NONE); + childForce.setOverrideCombatTeam(COMBAT_TEAM_OVERRIDE_NONE); } for (Force parentForce : singleForce.getAllParents()) { - parentForce.setOverrideStrategicFormation(STRATEGIC_FORMATION_OVERRIDE_NONE); + parentForce.setOverrideCombatTeam(COMBAT_TEAM_OVERRIDE_NONE); } - recalculateStrategicFormations(gui.getCampaign()); + recalculateCombatTeams(gui.getCampaign()); } else if (command.contains(REMOVE_STRATEGIC_FORCE_OVERRIDE)) { if (singleForce == null) { return; } - singleForce.setOverrideStrategicFormation(STRATEGIC_FORMATION_OVERRIDE_NONE); - recalculateStrategicFormations(gui.getCampaign()); + singleForce.setOverrideCombatTeam(COMBAT_TEAM_OVERRIDE_NONE); + recalculateCombatTeams(gui.getCampaign()); } else if (command.contains(TOEMouseAdapter.REMOVE_FORCE)) { for (Force force : forces) { if (null != force && null != force.getParentForce()) { @@ -481,9 +504,19 @@ public void actionPerformed(ActionEvent action) { // Clear any transport assignments of units in the deleted force clearTransportAssignment(force.getAllUnits(false)); + for (Force childForce : force.getAllSubForces()) { + gui.getCampaign().removeForce(childForce); + } + gui.getCampaign().removeForce(force); } } + + // We cycle through all forces because we need to assess how the removal affected them, + // Even for truly huge campaigns this is still very cheap. + for (Force force : forces) { + MekHQ.triggerEvent(new OrganizationChangedEvent(gui.getCampaign(), force)); + } } else if (command.contains(TOEMouseAdapter.REMOVE_LANCE_TECH)) { if (null != singleForce && singleForce.getTechID() != null) { Person oldTech = gui.getCampaign().getPerson(singleForce.getTechID()); @@ -540,13 +573,13 @@ public void actionPerformed(ActionEvent action) { HashSet extraUnits = new HashSet<>(); for (Unit unit : units) { if (null != unit && null != scenario) { - if (unit.hasTransportedUnits()) { + if (unit.hasShipTransportedUnits()) { //I don't think units are deployed through this anymore // Prompt the player to also deploy any units transported by this one int optionChoice = JOptionPane.showConfirmDialog(null, TOEMouseAdapter.LOAD_UNITS_DIALOG_TEXT, TOEMouseAdapter.LOAD_UNITS_DIALOG_TITLE, JOptionPane.YES_NO_OPTION); if (optionChoice == JOptionPane.YES_OPTION) { - extraUnits.addAll(unit.getTransportedUnits()); + extraUnits.addAll(unit.getShipTransportedUnits()); } } scenario.addUnit(unit.getId()); @@ -1046,27 +1079,36 @@ protected Optional createPopupMenu() { } if (gui.getCampaign().getCampaignOptions().isUseAtB()) { - menuItem = new JMenuItem(force.isCombatForce() ? "Make Non-Combat Force" : "Make Combat Force"); + menuItem = new JMenuItem(force.isCombatForce() ? "Make Support Force" : "Remove Support Designation"); menuItem.setActionCommand(COMMAND_CHANGE_FORCE_COMBAT_STATUS + forceIds); menuItem.addActionListener(this); popup.add(menuItem); menuItem = new JMenuItem(force.isCombatForce() ? - "Make Force and Subforces Non-Combat Forces" : "Make Force and Subforces Combat Forces"); + "Mark All Forces as Support Forces" : "Remove Support Designation from All Forces"); menuItem.setActionCommand(COMMAND_CHANGE_FORCE_COMBAT_STATUSES + forceIds); menuItem.addActionListener(this); + menuItem.setEnabled(!force.isConvoyForce()); popup.add(menuItem); - JMenuItem optionStrategicForceOverride = new JMenuItem((force.isStrategicFormation() ? - "Never" : "Always") + " Consider Force a Strategic Formation"); + if (gui.getCampaign().getCampaignOptions().isUseStratCon()) { + menuItem = new JMenuItem(!force.isConvoyForce() ? + "Mark force as a Resupply Convoy" : "Remove Resupply Convoy Designation"); + menuItem.setActionCommand(COMMAND_CHANGE_FORCE_CONVOY_STATUS + forceIds); + menuItem.addActionListener(this); + popup.add(menuItem); + } + + JMenuItem optionStrategicForceOverride = new JMenuItem((force.isCombatTeam() ? + "Never" : "Always") + " Consider Force a Combat Team"); optionStrategicForceOverride.setActionCommand(COMMAND_CHANGE_STRATEGIC_FORCE_OVERRIDE + forceIds); optionStrategicForceOverride.addActionListener(this); popup.add(optionStrategicForceOverride); - JMenuItem optionRemoveStrategicForceOverride = new JMenuItem("Remove Strategic Force Override"); + JMenuItem optionRemoveStrategicForceOverride = new JMenuItem("Remove Combat Team Override"); optionRemoveStrategicForceOverride.setActionCommand(COMMAND_REMOVE_STRATEGIC_FORCE_OVERRIDE + forceIds); optionRemoveStrategicForceOverride.addActionListener(this); - optionRemoveStrategicForceOverride.setVisible(force.getOverrideStrategicFormation() != STRATEGIC_FORMATION_OVERRIDE_NONE); + optionRemoveStrategicForceOverride.setVisible(force.getOverrideCombatTeam() != COMBAT_TEAM_OVERRIDE_NONE); popup.add(optionRemoveStrategicForceOverride); } @@ -1102,12 +1144,22 @@ protected Optional createPopupMenu() { menuItem = new JMenuItem("Undeploy Force"); menuItem.setActionCommand(TOEMouseAdapter.COMMAND_UNDEPLOY_FORCE + forceIds); menuItem.addActionListener(this); - menuItem.setEnabled(!gui.getCampaign().getCampaignOptions().isUseStratCon()); + + boolean enable = true; + for (Force individualForce : forces) { + int scenarioId = individualForce.getScenarioId(); + Scenario scenario = gui.getCampaign().getScenario(scenarioId); + + if (scenario != null && scenario.getHasTrack()) { + enable = false; + break; + } + } + menuItem.setEnabled(enable); popup.add(menuItem); } menuItem = new JMenuItem("Remove Force"); - menuItem.setActionCommand(TOEMouseAdapter.COMMAND_REMOVE_FORCE + forceIds); menuItem.addActionListener(this); menuItem.setEnabled( @@ -1117,149 +1169,14 @@ protected Optional createPopupMenu() { // Attempt to Assign all units in the selected force(s) to a transport ship. // This checks to see if the ship is in a basic state that can accept units. // Capacity gets checked once the action is submitted. - menu = new JMenu(TOEMouseAdapter.ASSIGN_FORCE_TRN_TITLE); - // Add submenus for different types of transports - JMenu m_trn = new JMenu(TOEMouseAdapter.MEK_CARRIERS); - JMenu pm_trn = new JMenu(TOEMouseAdapter.PROTOMEK_CARRIERS); - JMenu lv_trn = new JMenu(TOEMouseAdapter.LVEE_CARRIERS); - JMenu hv_trn = new JMenu(TOEMouseAdapter.HVEE_CARRIERS); - JMenu shv_trn = new JMenu(TOEMouseAdapter.SHVEE_CARRIERS); - JMenu ba_trn = new JMenu(TOEMouseAdapter.BA_CARRIERS); - JMenu i_trn = new JMenu(TOEMouseAdapter.INFANTRY_CARRIERS); - JMenu a_trn = new JMenu(TOEMouseAdapter.ASF_CARRIERS); - JMenu sc_trn = new JMenu(TOEMouseAdapter.SC_CARRIERS); - JMenu ds_trn = new JMenu(TOEMouseAdapter.DS_CARRIERS); - JMenu singleUnitMenu = new JMenu(); - if (!unitsInForces.isEmpty()) { - Unit unit = unitsInForces.get(0); - StringBuilder unitIds = new StringBuilder(unit.getId().toString()); - boolean allUnitsSameType = false; - double unitWeight = 0; - int singleUnitType = -1; - for (int i = 1; i < unitsInForces.size(); i++) { - unitIds.append('|').append(unitsInForces.get(i).getId().toString()); - } - - // Check to see if all selected units are of the same type - for (int i = 0; i < UnitType.SIZE; i++) { - if (StaticChecks.areAllUnitsSameType(unitsInForces, i)) { - singleUnitType = i; - allUnitsSameType = true; - singleUnitMenu.setText(String.format(TOEMouseAdapter.VARIABLE_TRANSPORT, - UnitType.getTypeName(singleUnitType))); - break; - } - } - - // Only display the Assign to Ship command if your command has at least 1 valid - // transport - // and if your selection does not include a transport - if (!gui.getCampaign().getTransportShips().isEmpty()) { - for (Unit ship : gui.getCampaign().getTransportShips()) { - if (ship.isSalvage() || (ship.getCommander() == null)) { - continue; - } - - UUID id = ship.getId(); - if (allUnitsSameType) { - double capacity = ship.getCorrectBayCapacity(singleUnitType, unitWeight); - if (capacity > 0) { - JMenuItem shipMenuItem = new JMenuItem( - ship.getName() + " , Space available: " + capacity); - shipMenuItem - .setActionCommand(TOEMouseAdapter.COMMAND_ASSIGN_TO_SHIP + id + '|' + unitIds); - shipMenuItem.addActionListener(this); - shipMenuItem.setEnabled(true); - singleUnitMenu.add(shipMenuItem); - singleUnitMenu.setEnabled(true); - } - } else { - // Add this ship to the appropriate submenu(s). Most transports will fit into - // multiple - // categories - if (ship.getASFCapacity() > 0) { - a_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentASFCapacity())); - a_trn.setEnabled(true); - } - - if (ship.getBattleArmorCapacity() > 0) { - ba_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentBattleArmorCapacity())); - ba_trn.setEnabled(true); - } - - if (ship.getInfantryCapacity() > 0) { - i_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentInfantryCapacity())); - i_trn.setEnabled(true); - } + JMenuHelpers.addMenuIfNonEmpty(popup, new AssignForceToShipTransportMenu(gui.getCampaign(), new HashSet<>(unitsInForces))); + unassignShipTransportMenuClass(unitsInForces, popup); + unassignFromShipTransportMenuClass(unitsInForces, popup); - if (ship.getMekCapacity() > 0) { - m_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentMekCapacity())); - m_trn.setEnabled(true); - } - - if (ship.getProtoMekCapacity() > 0) { - pm_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentProtoMekCapacity())); - pm_trn.setEnabled(true); - } - - if (ship.getSmallCraftCapacity() > 0) { - sc_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentSmallCraftCapacity())); - sc_trn.setEnabled(true); - } - - if (ship.getDocks() > 0) { - ds_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentDocks())); - ds_trn.setEnabled(true); - } - - if (ship.getLightVehicleCapacity() > 0) { - lv_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentLightVehicleCapacity())); - lv_trn.setEnabled(true); - } - - if (ship.getHeavyVehicleCapacity() > 0) { - hv_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentHeavyVehicleCapacity())); - hv_trn.setEnabled(true); - } - - if (ship.getSuperHeavyVehicleCapacity() > 0) { - shv_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentSuperHeavyVehicleCapacity())); - shv_trn.setEnabled(true); - } - } - } - } - JMenuHelpers.addMenuIfNonEmpty(menu, a_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, ba_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, i_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, m_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, pm_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, sc_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, ds_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, lv_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, hv_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, shv_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, singleUnitMenu); - JMenuHelpers.addMenuIfNonEmpty(popup, menu); - - if (StaticChecks.areAllUnitsTransported(unitsInForces)) { - menuItem = new JMenuItem(TOEMouseAdapter.UNASSIGN_FORCE_TRN_TITLE); - menuItem.setActionCommand(TOEMouseAdapter.COMMAND_UNASSIGN_FROM_SHIP + unitIds); - menuItem.addActionListener(this); - menuItem.setEnabled(true); - popup.add(menuItem); - } + JMenuHelpers.addMenuIfNonEmpty(popup, new AssignForceToTacticalTransportMenu(gui.getCampaign(), new HashSet<>(unitsInForces))); + unassignTacticalTransportMenuClass(unitsInForces, popup); + unassignFromTacticalTransportMenuClass(unitsInForces, popup); } } else if (unitsSelected) { Unit unit = units.get(0); @@ -1464,158 +1381,36 @@ protected Optional createPopupMenu() { JMenuHelpers.addMenuIfNonEmpty(menu, missionMenu); } JMenuHelpers.addMenuIfNonEmpty(popup, menu); - - // First, only display the Assign to Ship command if your command has at least 1 - // valid transport - boolean allUnitsSameType = false; - double unitWeight = 0; - int singleUnitType = -1; - - // Add submenus for different types of transports - JMenu m_trn = new JMenu(TOEMouseAdapter.MEK_CARRIERS); - JMenu pm_trn = new JMenu(TOEMouseAdapter.PROTOMEK_CARRIERS); - JMenu lv_trn = new JMenu(TOEMouseAdapter.LVEE_CARRIERS); - JMenu hv_trn = new JMenu(TOEMouseAdapter.HVEE_CARRIERS); - JMenu shv_trn = new JMenu(TOEMouseAdapter.SHVEE_CARRIERS); - JMenu ba_trn = new JMenu(TOEMouseAdapter.BA_CARRIERS); - JMenu i_trn = new JMenu(TOEMouseAdapter.INFANTRY_CARRIERS); - JMenu a_trn = new JMenu(TOEMouseAdapter.ASF_CARRIERS); - JMenu sc_trn = new JMenu(TOEMouseAdapter.SC_CARRIERS); - JMenu ds_trn = new JMenu(TOEMouseAdapter.DS_CARRIERS); - JMenu singleUnitMenu = new JMenu(); - - // Check to see if all selected units are of the same type - for (int i = 0; i < UnitType.SIZE; i++) { - if (StaticChecks.areAllUnitsSameType(units, i)) { - singleUnitType = i; - allUnitsSameType = true; - singleUnitMenu.setText(String.format(TOEMouseAdapter.VARIABLE_TRANSPORT, - UnitType.getTypeName(singleUnitType))); - break; - } - } - - if (!gui.getCampaign().getTransportShips().isEmpty()) { - // Attempt to Assign unit to a transport ship. This checks to see if the ship - // is in a basic state that can accept units. Capacity gets checked once the - // action - // is submitted. - menu = new JMenu("Assign Unit to Transport Ship"); - for (Unit ship : gui.getCampaign().getTransportShips()) { - if (ship.isSalvage() || (ship.getCommander() == null)) { - continue; - } - - UUID id = ship.getId(); - if (allUnitsSameType) { - double capacity = ship.getCorrectBayCapacity(singleUnitType, unitWeight); - if (capacity > 0) { - JMenuItem shipMenuItem = new JMenuItem( - ship.getName() + " , Space available: " + capacity); - shipMenuItem - .setActionCommand(TOEMouseAdapter.COMMAND_ASSIGN_TO_SHIP + id + '|' + unitIds); - shipMenuItem.addActionListener(this); - shipMenuItem.setEnabled(true); - singleUnitMenu.add(shipMenuItem); - singleUnitMenu.setEnabled(true); - } - } else { - // Add this ship to the appropriate submenu(s). Most transports will fit into - // multiple - // categories - if (ship.getASFCapacity() > 0) { - a_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentASFCapacity())); - a_trn.setEnabled(true); - } - - if (ship.getBattleArmorCapacity() > 0) { - ba_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentBattleArmorCapacity())); - ba_trn.setEnabled(true); - } - - if (ship.getInfantryCapacity() > 0) { - i_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentInfantryCapacity())); - i_trn.setEnabled(true); - } - - if (ship.getMekCapacity() > 0) { - m_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentMekCapacity())); - m_trn.setEnabled(true); - } - - if (ship.getProtoMekCapacity() > 0) { - pm_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentProtoMekCapacity())); - pm_trn.setEnabled(true); - } - - if (ship.getSmallCraftCapacity() > 0) { - sc_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentSmallCraftCapacity())); - sc_trn.setEnabled(true); - } - - if (ship.getDocks() > 0) { - ds_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentDocks())); - ds_trn.setEnabled(true); - } - - if (ship.getLightVehicleCapacity() > 0) { - lv_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentLightVehicleCapacity())); - lv_trn.setEnabled(true); - } - - if (ship.getHeavyVehicleCapacity() > 0) { - hv_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentHeavyVehicleCapacity())); - hv_trn.setEnabled(true); - } - - if (ship.getSuperHeavyVehicleCapacity() > 0) { - shv_trn.add(transportMenuItem(ship.getName(), id, unitIds.toString(), - ship.getCurrentSuperHeavyVehicleCapacity())); - shv_trn.setEnabled(true); - } - } - } - } - - JMenuHelpers.addMenuIfNonEmpty(menu, a_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, ba_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, i_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, m_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, pm_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, sc_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, ds_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, lv_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, hv_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, shv_trn); - JMenuHelpers.addMenuIfNonEmpty(menu, singleUnitMenu); - JMenuHelpers.addMenuIfNonEmpty(popup, menu); } if (StaticChecks.areAllUnitsDeployed(units)) { menuItem = new JMenuItem("Undeploy Unit"); menuItem.setActionCommand(TOEMouseAdapter.COMMAND_UNDEPLOY_UNIT + unitIds); menuItem.addActionListener(this); - menuItem.setEnabled(!gui.getCampaign().getCampaignOptions().isUseStratCon()); - popup.add(menuItem); - } - if (StaticChecks.areAllUnitsTransported(units) && !StaticChecks.areAnyUnitsDeployed(units)) { - menuItem = new JMenuItem("Unassign Unit from Transport Ship"); - menuItem.setActionCommand(TOEMouseAdapter.COMMAND_UNASSIGN_FROM_SHIP + unitIds); - menuItem.addActionListener(this); - menuItem.setEnabled(true); + boolean enable = true; + for (Unit individualUnit : units) { + int scenarioId = individualUnit.getScenarioId(); + Scenario scenario = gui.getCampaign().getScenario(scenarioId); + + if (scenario != null && scenario.getHasTrack()) { + enable = false; + break; + } + } + + menuItem.setEnabled(enable); popup.add(menuItem); } + JMenuHelpers.addMenuIfNonEmpty(popup, new AssignForceToShipTransportMenu(gui.getCampaign(), new HashSet<>(units))); + unassignShipTransportMenuClass(units, popup); + unassignFromShipTransportMenuClass(units, popup); + + JMenuHelpers.addMenuIfNonEmpty(popup, new AssignForceToTacticalTransportMenu(gui.getCampaign(), new HashSet<>(units))); + unassignTacticalTransportMenuClass(units, popup); + unassignFromTacticalTransportMenuClass(units, popup); + if (!multipleSelection) { popup.add(new ExportUnitSpriteMenu(gui.getFrame(), gui.getCampaign(), unit)); @@ -1685,38 +1480,82 @@ private void clearTransportAssignment(Vector unitsToUpdate) { */ private void clearTransportAssignment(@Nullable Unit currentUnit) { if (currentUnit != null) { - if (currentUnit.hasTransportShipAssignment()) { - currentUnit.getTransportShipAssignment() - .getTransportShip() - .unloadFromTransportShip(currentUnit); - } - // If the unit IS a transport, unassign all units from it - if (currentUnit.hasTransportedUnits()) { - currentUnit.unloadTransportShip(); + for (CampaignTransportType campaignTransportType : CampaignTransportType.values()) { + if (currentUnit.hasTransportAssignment(campaignTransportType)) { + Unit oldTransport = currentUnit.unloadFromTransport(campaignTransportType); + oldTransport.initializeTransportSpace(campaignTransportType); + gui.getCampaign().updateTransportInTransports(campaignTransportType, oldTransport); + } + + if (currentUnit.hasTransportedUnits(campaignTransportType)) { + currentUnit.unloadTransport(campaignTransportType); + } } } } - /** - * Worker function that creates a new instance of a JMenuItem for a set of - * transport ship characteristics - * Used to have a single ship appear on multiple menu entries defined by type of - * unit transported - * Displays the remaining capacity in bays of the specified type - * - * @param shipName String name of this ship. - * @param shipId Unique id of this ship. Used to fill out - * actionPerformed(ActionEvent) - * @param unitIds String of units delimited by | used to fill out - * actionPerformed(ActionEvent) - * @param capacity Double representing the capacity of the designated bay type - */ + private void unassignShipTransportMenuClass(Vector units, JPopupMenu popup) { + if (units.stream().allMatch(Unit::hasTransportShipAssignment) && !StaticChecks.areAnyUnitsDeployed(units)) { + JMenuItem menuItem = new JMenuItem(MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", "TOEMouseAdapter.unassign.SHIP_TRANSPORT.text")); + menuItem.addActionListener(evt -> { + unassignTransportAction(SHIP_TRANSPORT, units.toArray(new Unit[0]));}); + menuItem.setEnabled(true); + popup.add(menuItem); + } + } + + private void unassignTacticalTransportMenuClass(Vector units, JPopupMenu popup) { + if (units.stream().allMatch(Unit::hasTacticalTransportAssignment) && !StaticChecks.areAnyUnitsDeployed(units)) { + JMenuItem menuItem = new JMenuItem(MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", "TOEMouseAdapter.unassign.TACTICAL_TRANSPORT.text")); + menuItem.addActionListener(evt -> { + unassignTransportAction(TACTICAL_TRANSPORT, units.toArray(new Unit[0])); + }); + menuItem.setEnabled(true); + popup.add(menuItem); + } + } - private JMenuItem transportMenuItem(String shipName, UUID shipId, String unitIds, double capacity) { - JMenuItem menuItem = new JMenuItem(shipName + " , Space available: " + capacity); - menuItem.setActionCommand(TOEMouseAdapter.COMMAND_ASSIGN_TO_SHIP + shipId + '|' + unitIds); - menuItem.addActionListener(this); + private void unassignTransportAction(CampaignTransportType campaignTransportType, Unit... units) { + Set transportsToUpdate = new HashSet<>(); + for (Unit transportedUnit : units) { + transportsToUpdate.add(transportedUnit.unloadFromTransport(campaignTransportType)); + MekHQ.triggerEvent(new UnitChangedEvent(transportedUnit)); + } - return menuItem; + for (Unit transportToUpdate : transportsToUpdate) { + transportToUpdate.initializeTransportSpace(campaignTransportType); + gui.getCampaign().updateTransportInTransports(campaignTransportType, transportToUpdate); + MekHQ.triggerEvent(new UnitChangedEvent(transportToUpdate)); + } + } + + private void unassignFromTransportAction(CampaignTransportType campaignTransportType, Unit... units) { + for (Unit transport : units) { + if (transport.hasTransportedUnits(campaignTransportType)) { + unassignTransportAction(campaignTransportType, + transport.getTransportedUnits(campaignTransportType).toArray(new Unit[0])); + } + } + } + + private void unassignFromShipTransportMenuClass(Vector units, JPopupMenu popup) { + if (units.stream().allMatch(Unit::hasShipTransportedUnits) && !StaticChecks.areAnyUnitsDeployed(units)) { + JMenuItem menuItem = new JMenuItem(MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", "TOEMouseAdapter.unassignFrom.SHIP_TRANSPORT.text")); + menuItem.addActionListener(evt -> { + unassignFromTransportAction(SHIP_TRANSPORT, units.toArray(new Unit[0]));}); + menuItem.setEnabled(true); + popup.add(menuItem); + } + } + + private void unassignFromTacticalTransportMenuClass(Vector units, JPopupMenu popup) { + if (units.stream().allMatch(Unit::hasTacticalTransportedUnits) && !StaticChecks.areAnyUnitsDeployed(units)) { + JMenuItem menuItem = new JMenuItem(MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", "TOEMouseAdapter.unassignFrom.TACTICAL_TRANSPORT.text")); + menuItem.addActionListener(evt -> { + unassignFromTransportAction(TACTICAL_TRANSPORT, units.toArray(new Unit[0])); + }); + menuItem.setEnabled(true); + popup.add(menuItem); + } } } diff --git a/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java index 63c0a4e028a..65410288234 100644 --- a/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java @@ -37,10 +37,11 @@ import mekhq.campaign.event.UnitChangedEvent; import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; +import mekhq.campaign.mission.Scenario; import mekhq.campaign.parts.Part; import mekhq.campaign.parts.Refit; -import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.parts.enums.PartQuality; +import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.personnel.Person; import mekhq.campaign.unit.Unit; import mekhq.campaign.unit.actions.*; @@ -477,8 +478,11 @@ public void actionPerformed(ActionEvent action) { gui.getFrame(), "Name for this unit?", "Unit Name", JOptionPane.QUESTION_MESSAGE, null, null, selectedUnit.getFluffName()); - selectedUnit.setFluffName((fluffName != null) ? fluffName : ""); - MekHQ.triggerEvent(new UnitChangedEvent(selectedUnit)); + String oldFluffName = selectedUnit.getFluffName(); + selectedUnit.setFluffName((fluffName != null) ? fluffName : oldFluffName); + if (fluffName != null) { + MekHQ.triggerEvent(new UnitChangedEvent(selectedUnit)); + } } else if (command.equals(COMMAND_RESTORE_UNIT)) { IUnitAction restoreUnitAction = new RestoreUnitAction(); for (Unit u : units) { @@ -1009,6 +1013,16 @@ protected Optional createPopupMenu() { menuItem = new JMenuItem("Undeploy Unit"); menuItem.setActionCommand(COMMAND_UNDEPLOY); menuItem.addActionListener(this); + + boolean enable = true; + int scenarioId = unit.getScenarioId(); + Scenario scenario = gui.getCampaign().getScenario(scenarioId); + + if (scenario != null && scenario.getHasTrack()) { + enable = false; + } + + menuItem.setEnabled(enable); menu.add(menuItem); } diff --git a/MekHQ/src/mekhq/gui/baseComponents/AbstractMHQDialog.java b/MekHQ/src/mekhq/gui/baseComponents/AbstractMHQDialogBasic.java similarity index 79% rename from MekHQ/src/mekhq/gui/baseComponents/AbstractMHQDialog.java rename to MekHQ/src/mekhq/gui/baseComponents/AbstractMHQDialogBasic.java index 28dcda8f66c..566731ed7d9 100644 --- a/MekHQ/src/mekhq/gui/baseComponents/AbstractMHQDialog.java +++ b/MekHQ/src/mekhq/gui/baseComponents/AbstractMHQDialogBasic.java @@ -1,71 +1,71 @@ -/* - * 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.gui.baseComponents; - -import megamek.client.ui.baseComponents.AbstractDialog; -import mekhq.MekHQ; - -import javax.swing.*; -import java.util.ResourceBundle; - -/** - * This is the base class for dialogs in MegaMek. This class handles setting the UI, managing the X - * button, managing the escape key, and saving the dialog preferences. - * - * Inheriting classes must call initialize() in their constructors and override createCenterPane() - */ -public abstract class AbstractMHQDialog extends AbstractDialog { - //region Constructors - /** - * This creates a non-modal AbstractMHQDialog using the default MHQ resource bundle. This is - * the normal constructor to use for an AbstractMHQDialog. - */ - protected AbstractMHQDialog(final JFrame frame, final String name, final String title) { - this(frame, false, name, title); - } - - /** - * This creates an AbstractMHQDialog using the default MHQ resource bundle. It allows one - * to create modal dialogs. - */ - protected AbstractMHQDialog(final JFrame frame, final boolean modal, final String name, final String title) { - this(frame, modal, ResourceBundle.getBundle("mekhq.resources.GUI", - MekHQ.getMHQOptions().getLocale()), name, title); - } - - /** - * This creates an AbstractMHQDialog using the specified resource bundle. This is not recommended - * by default. - */ - protected AbstractMHQDialog(final JFrame frame, final boolean modal, final ResourceBundle resources, - final String name, final String title) { - super(frame, modal, resources, name, title); - } - //endregion Constructors - - /** - * This override forces the preferences for this class to be tracked in MekHQ instead of MegaMek. - * @throws Exception if there's an issue initializing the preferences. Normally this means - * a component has not had its name value set. - */ - @Override - protected void setPreferences() throws Exception { - setPreferences(MekHQ.getMHQPreferences().forClass(getClass())); - } -} +/* + * Copyright (c) 2021-2025 - 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.gui.baseComponents; + +import megamek.client.ui.baseComponents.AbstractDialog; +import mekhq.MekHQ; + +import javax.swing.*; +import java.util.ResourceBundle; + +/** + * This is the base class for dialogs in MegaMek. This class handles setting the UI, managing the X + * button, managing the escape key, and saving the dialog preferences. + *

    + * Inheriting classes must call initialize() in their constructors and override createCenterPane() + */ +public abstract class AbstractMHQDialogBasic extends AbstractDialog { + //region Constructors + /** + * This creates a non-modal AbstractMHQDialog using the default MHQ resource bundle. This is + * the normal constructor to use for an AbstractMHQDialog. + */ + protected AbstractMHQDialogBasic(final JFrame frame, final String name, final String title) { + this(frame, false, name, title); + } + + /** + * This creates an AbstractMHQDialog using the default MHQ resource bundle. It allows one + * to create modal dialogs. + */ + protected AbstractMHQDialogBasic(final JFrame frame, final boolean modal, final String name, final String title) { + this(frame, modal, ResourceBundle.getBundle("mekhq.resources.GUI", + MekHQ.getMHQOptions().getLocale()), name, title); + } + + /** + * This creates an AbstractMHQDialog using the specified resource bundle. This is not recommended + * by default. + */ + protected AbstractMHQDialogBasic(final JFrame frame, final boolean modal, final ResourceBundle resources, + final String name, final String title) { + super(frame, modal, resources, name, title); + } + //endregion Constructors + + /** + * This override forces the preferences for this class to be tracked in MekHQ instead of MegaMek. + * @throws Exception if there's an issue initializing the preferences. Normally this means + * a component has not had its name value set. + */ + @Override + protected void setPreferences() throws Exception { + setPreferences(MekHQ.getMHQPreferences().forClass(getClass())); + } +} diff --git a/MekHQ/src/mekhq/gui/baseComponents/AbstractMHQNagDialog.java b/MekHQ/src/mekhq/gui/baseComponents/AbstractMHQNagDialog.java index 8a5a4b9e8bc..9f03c8a3a4c 100644 --- a/MekHQ/src/mekhq/gui/baseComponents/AbstractMHQNagDialog.java +++ b/MekHQ/src/mekhq/gui/baseComponents/AbstractMHQNagDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2024-2025 The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,51 +18,220 @@ */ package mekhq.gui.baseComponents; -import megamek.client.ui.baseComponents.AbstractNagDialog; -import megamek.client.ui.enums.DialogResult; +import megamek.client.ui.swing.util.UIUtil; import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.campaign.Campaign.AdministratorSpecialization; +import mekhq.campaign.personnel.Person; import javax.swing.*; +import java.awt.*; import java.util.ResourceBundle; -public abstract class AbstractMHQNagDialog extends AbstractNagDialog { - //region Variable Declarations - private final Campaign campaign; - //endregion Variable Declarations - - //region Constructors - protected AbstractMHQNagDialog(final JFrame frame, final String name, final String title, - final String description, final Campaign campaign, - final String key) { - super(frame, ResourceBundle.getBundle("mekhq.resources.GUI", - MekHQ.getMHQOptions().getLocale()), name, title, key); - this.campaign = campaign; - setDescription(description.isBlank() ? description : resources.getString(description)); - setShow(checkNag()); - if (isShow()) { - initialize(); - } else { - setResult(DialogResult.CONFIRMED); +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerDescription; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +/** + * An abstract base class for displaying a nag dialog within MekHQ. + *

    + * This dialog is used to show certain informational or warning messages during a campaign, + * with the option for users to ignore future messages of the same type. + * It includes visual elements for presenting a speaker image, description, and some configurable + * details, along with buttons and a checkbox for user input. + *

    + * + *

    + * Extending this class allows customization of the dialog’s behavior and content, + * while maintaining a consistent design across the application. + *

    + * + * Features: + *
      + *
    • Displays a two-panel layout with speaker details and a message.
    • + *
    • Allows users to advance to the next day or cancel, optionally ignoring future warnings.
    • + *
    • Localized using resource bundles.
    • + *
    + */ +public abstract class AbstractMHQNagDialog extends JDialog { + /** + * Unique key for this nag dialog, used to track if the dialog has been ignored + * by the user in the campaign settings. + */ + private final String nagKey; + /** + * The right-side description label which displays the message. + * Dynamically updated when {@link #setRightDescriptionMessage(String)} is called. + */ + private JLabel rightDescription; + private String rightDescriptionMessage; + + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + + /** + * Indicates whether the user selected the "Advance Day" option. + */ + private boolean advanceDaySelected = true; + + protected final transient ResourceBundle resources = ResourceBundle.getBundle( + "mekhq.resources.GUI", MekHQ.getMHQOptions().getLocale()); + + /** + * Constructs an AbstractMHQNagDialog with the provided campaign and nag key. + * + * @param campaign The current campaign, used to determine speaker details and other contextual data. + * @param nagKey A unique key to identify this nag dialog for tracking ignore preferences. + */ + public AbstractMHQNagDialog(final Campaign campaign, final String nagKey) { + setTitle(resources.getString("incomingTransmission.title")); + + this.nagKey = nagKey; + this.rightDescriptionMessage = ""; + + setLayout(new BorderLayout()); + + // Main Panel to hold both boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(10, 10, 10, 10); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + // Left box for speaker details + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get speaker details + Person speaker = campaign.getSeniorAdminPerson(AdministratorSpecialization.COMMAND); + String speakerName = (speaker != null) ? speaker.getFullTitle() : campaign.getName(); + + // Add speaker image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, speaker, speakerName); + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, 10))); // Add spacing + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + mainPanel.add(leftBox, constraints); + + // Right box: Just a message + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, rightDescriptionMessage)); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + constraints.gridx = 1; + constraints.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, constraints); + + add(mainPanel, BorderLayout.CENTER); + + // Checkbox Panel + JPanel checkboxPanel = new JPanel(); + JCheckBox ignoreFutureNags = new JCheckBox(resources.getString("ignoreFutureNags.checkbox")); + checkboxPanel.add(ignoreFutureNags); + + // Buttons panel + JPanel buttonPanel = new JPanel(); + + JButton advanceDayButton = new JButton(resources.getString("button.advanceDay")); + advanceDayButton.addActionListener(e -> { + advanceDaySelected = true; + MekHQ.getMHQOptions().setNagDialogIgnore(this.nagKey, ignoreFutureNags.isSelected()); + dispose(); + }); + + JButton cancelButton = new JButton(resources.getString("button.cancel")); + cancelButton.addActionListener(e -> { + advanceDaySelected = false; + MekHQ.getMHQOptions().setNagDialogIgnore(this.nagKey, ignoreFutureNags.isSelected()); + dispose(); + }); + + buttonPanel.add(advanceDayButton); + buttonPanel.add(cancelButton); + + // Combine Checkbox and Buttons into a single panel + JPanel southPanel = new JPanel(new BorderLayout()); + southPanel.add(checkboxPanel, BorderLayout.NORTH); + southPanel.add(buttonPanel, BorderLayout.SOUTH); + add(southPanel, BorderLayout.SOUTH); + + // Dialog settings + pack(); + setModal(true); + setLocationRelativeTo(null); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); } - //endregion Constructors - //region Getters - public Campaign getCampaign() { - return campaign; + /** + * Displays the dialog to the user and waits for a response. + * + *

    + * This method makes the dialog visible and halts further execution until the user + * either dismisses or interacts with the dialog (e.g., clicks a button). + *

    + */ + public void showDialog() { + setVisible(true); } - //endregion Getters - //region Button Actions - @Override - protected void okAction() { - MekHQ.getMHQOptions().setNagDialogIgnore(getKey(), getChkIgnore().isSelected()); + /** + * Updates the message displayed in the right-hand panel of the dialog. + * + *

    + * This method updates the content dynamically and repaints the dialog to reflect changes immediately. + *

    + * + * @param rightDescriptionMessage The message to display, expected to be a valid HTML string. + */ + public void setRightDescriptionMessage(String rightDescriptionMessage) { + this.rightDescriptionMessage = rightDescriptionMessage; + + // Update the right description JLabel + rightDescription.setText( + String.format("
    %s
    ", + RIGHT_WIDTH, rightDescriptionMessage)); + + repaint(); + pack(); } - @Override - protected void cancelAction() { - MekHQ.getMHQOptions().setNagDialogIgnore(getKey(), getChkIgnore().isSelected()); + /** + * Checks if the user selected the "Advance Day" action. + * + *

    + * This result is set when the user interacts with the dialog’s buttons, either + * advancing to the next day or cancelling the dialog. This value will be {@code true} + * if the "Advance Day" button was selected, otherwise {@code false}. + *

    + * + * @return {@code true} if "Advance Day" was canceled, otherwise {@code false}. + */ + public boolean wasAdvanceDayCanceled() { + return !advanceDaySelected; } - //endregion Button Actions } diff --git a/MekHQ/src/mekhq/gui/baseComponents/MHQDialogImmersive.java b/MekHQ/src/mekhq/gui/baseComponents/MHQDialogImmersive.java new file mode 100644 index 00000000000..64f593044ed --- /dev/null +++ b/MekHQ/src/mekhq/gui/baseComponents/MHQDialogImmersive.java @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2025 - 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.gui.baseComponents; + +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.enums.PersonnelRole; +import mekhq.campaign.unit.Unit; + +import javax.swing.*; +import javax.swing.event.HyperlinkEvent.EventType; +import java.awt.*; +import java.util.List; + +import static java.lang.Math.max; +import static megamek.client.ui.WrapLayout.wordWrap; +import static megamek.client.ui.swing.util.FlatLafStyleBuilder.setFontScaling; +import static mekhq.campaign.force.Force.FORCE_NONE; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; +import static mekhq.utilities.MHQInternationalization.getFormattedTextAt; + +/** + * An immersive dialog used in MekHQ to display interactions between speakers, + * messages, and actions. The dialog supports entities such as speakers, campaign, + * buttons, and optional details for enhanced storytelling. + * + *

    It allows displaying one or more speakers in a dialog alongside a central message, + * optional out-of-character notes, and UI buttons for user interaction.

    + * + *

    The dialog is flexible in terms of panel layout and width adjustments, + * allowing for dynamic configurations based on the input parameters.

    + */ +public class MHQDialogImmersive extends JDialog { + private final String RESOURCE_BUNDLE = "mekhq.resources.GUI"; + + private Campaign campaign; + + private int CENTER_WIDTH = UIUtil.scaleForGUI(400); + + private final int INSERT_SIZE = UIUtil.scaleForGUI(5); + protected final int IMAGE_WIDTH = 125; // This is scaled to GUI by 'scaleImageIconToWidth' + + private JPanel northPanel; + private JPanel southPanel; + private JPanel buttonPanel; + private Person leftSpeaker; + private Person rightSpeaker; + + private int dialogChoice; + + private static final MMLogger logger = MMLogger.create(MHQDialogImmersive.class); + + /** + * Retrieves the user's selected dialog choice. + *

    + * Usage: This allows us to keep function code out of the GUI element, + * making it far easier to test what's happening for any given option selection. Create + * the dialog, as normal, then fetch whatever decision the user made and perform any code + * actions required. + *

    + * + * @return An integer representing the index of the button selected by the user. + * If the dialog is closed without selection, this will return the {@code defaultChoiceIndex} + * defined during construction. + */ + public int getDialogChoice() { + return dialogChoice; + } + + /** + * Full constructor for initializing the immersive dialog with detailed layouts. + * + * @param campaign The {@link Campaign} tied to the dialog. + * @param leftSpeaker Optional left-side {@link Person}; {@code null} if none. + * @param rightSpeaker Optional right-side {@link Person}; {@code null} if none. + * @param centerMessage The main message displayed in the dialog's center. + * @param buttons The list of {@link ButtonLabelTooltipPair} actions for the dialog. + * @param outOfCharacterMessage Optional out-of-character message below the buttons. + * @param centerWidth Optional width for the center panel; defaults if null. + */ + public MHQDialogImmersive(Campaign campaign, @Nullable Person leftSpeaker, + @Nullable Person rightSpeaker, String centerMessage, + List buttons, @Nullable String outOfCharacterMessage, + @Nullable Integer centerWidth) { + // Initialize + this.campaign = campaign; + this.leftSpeaker = leftSpeaker; + this.rightSpeaker = rightSpeaker; + + CENTER_WIDTH = (centerWidth != null) ? centerWidth : CENTER_WIDTH; + + // Title + setTitle(); + + // Main Panel to hold all boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + int gridx = 0; + + // Left box for speaker details + if (leftSpeaker != null) { + JPanel pnlLeftSpeaker = buildSpeakerPanel(leftSpeaker, campaign); + + // Add pnlLeftSpeaker to mainPanel + constraints.gridx = gridx; + constraints.gridy = 0; + constraints.weightx = 1; + constraints.weighty = 1; + mainPanel.add(pnlLeftSpeaker, constraints); + gridx++; + } + + // Center box for the message + JPanel pnlCenter = createCenterBox(centerMessage, buttons); + constraints.gridx = gridx; + constraints.gridy = 0; + constraints.weightx = 2; + constraints.weighty = 2; + mainPanel.add(pnlCenter, constraints); + gridx++; + + // Right box for speaker details + if (rightSpeaker != null) { + JPanel pnlRightSpeaker = buildSpeakerPanel(rightSpeaker, campaign); + + // Add pnlRightSpeaker to mainPanel + constraints.gridx = gridx; + constraints.gridy = 0; + constraints.weightx = 1; + constraints.weighty = 1; + mainPanel.add(pnlRightSpeaker, constraints); + } + + // Add mainPanel to dialog + add(mainPanel, BorderLayout.CENTER); + + // Bottom panel, for OOC information + southPanel = new JPanel(new BorderLayout()); + if (outOfCharacterMessage != null) { + populateOutOfCharacterPanel(outOfCharacterMessage); + } + + // Add southPanel to the dialog + add(southPanel, BorderLayout.SOUTH); + + // Dialog settings + setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + setModal(true); + pack(); + setLocationRelativeTo(null); // Needs to be after pack + setVisible(true); + } + + /** + * Sets the title of the dialog window using localized text. + */ + protected void setTitle() { + setTitle(getFormattedTextAt(RESOURCE_BUNDLE, "incomingTransmission.title")); + } + + /** + * Creates and returns a central panel containing the main dialog message and a button panel. + * This panel is designed to display a central message, typically in HTML format, + * using a {@link JEditorPane}, along with an optional list of buttons displayed below the message. + *
      + *
    • The message is placed in the {@link JEditorPane}, styled for a consistent width.
    • + *
    • The panel includes a scrollable viewport if the message content overflows.
    • + *
    • An additional button panel is added at the bottom of the central panel.
    • + *
    + * + * @param centerMessage The main dialog message as a string, typically in HTML format. + * This can include basic HTML for formatting purposes. + * @param buttons A list of {@link ButtonLabelTooltipPair} objects defining the buttons to + * be displayed at the bottom of the panel. These buttons can have labels, + * tooltips, and custom actions. + * @return A {@link JPanel} with the message displayed in the center and buttons at the bottom. + */ + + private JPanel createCenterBox(String centerMessage, List buttons) { + northPanel = new JPanel(new BorderLayout()); + + // Buttons panel + buttonPanel = new JPanel(); + populateButtonPanel(buttons); + + // Create a JEditorPane for the center message + JEditorPane editorPane = new JEditorPane(); + editorPane.setContentType("text/html"); + editorPane.setEditable(false); + editorPane.setFocusable(false); + editorPane.setBorder(BorderFactory.createEmptyBorder()); + + // Use inline CSS to set font family, size, and other style properties + String fontStyle = "font-family: Noto Sans;"; + editorPane.setText(String.format("
    %s
    ", + max(buttonPanel.getPreferredSize().width, CENTER_WIDTH), fontStyle, centerMessage)); + setFontScaling(editorPane, false, 1.1); + // Add a HyperlinkListener to capture hyperlink clicks + editorPane.addHyperlinkListener(evt -> { + if (evt.getEventType() == EventType.ACTIVATED) { + handleHyperlinkClick(campaign, evt.getDescription()); + } + }); + + // Wrap the JEditorPane in a JScrollPane + JScrollPane scrollPane = new JScrollPane(editorPane); + scrollPane.setMinimumSize(new Dimension(CENTER_WIDTH, scrollPane.getHeight())); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + + // Create a container with a border for the padding + JPanel scrollPaneContainer = new JPanel(new BorderLayout()); + scrollPaneContainer.setBorder(BorderFactory.createEmptyBorder(INSERT_SIZE, 0, INSERT_SIZE, 0)); + scrollPaneContainer.add(scrollPane, BorderLayout.CENTER); + + // Add the scrollPane with padding to the northPanel + northPanel.add(scrollPaneContainer, BorderLayout.CENTER); + + // Ensure the scrollbars default to the top-left position + SwingUtilities.invokeLater(() -> scrollPane.getViewport().setViewPosition(new Point(0, 0))); + + northPanel.add(buttonPanel, BorderLayout.SOUTH); + + return northPanel; + } + + /** + * Handles hyperlink clicks from HTML content. + *

    + * Usage
    + * This method provides a default implementation that does nothing. Subclasses should + * override this to provide specific behavior when hyperlinks are clicked. + *

    + * + * @param campaign The {@link Campaign} instance that contains relevant data. + * @param href The hyperlink reference (e.g., a URL or a specific identifier). + */ + protected void handleHyperlinkClick(Campaign campaign, String href) { + logger.error("handleHyperlinkClick() was not overridden in the subclass."); + } + + /** + * Populates the Out-of-Character (OOC) panel with a specific message, resizing as needed. + *

    + * This method appends a formatted OOC message to the bottom of the dialog, ensuring proper width + * alignment with any visible speaker panels. + * + * @param outOfCharacterMessage The OOC message to display. + */ + private void populateOutOfCharacterPanel(String outOfCharacterMessage) { + JPanel pnlOutOfCharacter = new JPanel(new GridBagLayout()); + pnlOutOfCharacter.setBorder(BorderFactory.createEtchedBorder()); + + JLabel lblOutOfCharacter = new JLabel( + String.format("

    %s
    ", + CENTER_WIDTH, outOfCharacterMessage)); + lblOutOfCharacter.setBorder(BorderFactory.createEmptyBorder(INSERT_SIZE, INSERT_SIZE, + INSERT_SIZE, INSERT_SIZE)); + + pnlOutOfCharacter.add(lblOutOfCharacter); + + southPanel.add(pnlOutOfCharacter, BorderLayout.SOUTH); + } + + /** + * Populates the button panel with the provided buttons. + *

    + * Each button in the panel represents a {@link ButtonLabelTooltipPair}, defining its displayed label + * and optional tooltip. Clicking a button will set the {@code dialogChoice} to the corresponding index + * of the button in the list and close the dialog window. + * + * @param buttons A list of button label-tooltip pairs defining the content of the buttons. + */ + private void populateButtonPanel(List buttons) { + buttonPanel.setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.NONE; + + for (ButtonLabelTooltipPair buttonStrings : buttons) { + JButton button = new JButton(buttonStrings.btnLabel()); + + String tooltip = buttonStrings.btnTooltip(); + if (tooltip != null) { + button.setToolTipText(wordWrap(tooltip)); + } + + button.setMinimumSize(button.getPreferredSize()); + + button.addActionListener(evt -> { + dialogChoice = buttons.indexOf(buttonStrings); + dispose(); + }); + + buttonPanel.add(button, gbc); + + gbc.gridx++; + if (gbc.gridx % 3 == 0) { // Move to a new row after every third button + gbc.gridx = 0; + gbc.gridy++; + } + } + } + + /** + * Builds a panel for displaying a speaker's image, name, and role. + *

    + * This method creates a vertically stacked panel that includes the person's icon, title, + * and any additional descriptive information (e.g., roles, forces, or campaign affiliations). + * + * @param speaker The character shown in the dialog, can be {@code null} for no speaker + * @param campaign The current campaign. + * @return A {@link JPanel} forming the speaker's dialog box. + */ + protected JPanel buildSpeakerPanel(@Nullable Person speaker, Campaign campaign) { + JPanel speakerBox = new JPanel(); + speakerBox.setLayout(new BoxLayout(speakerBox, BoxLayout.Y_AXIS)); + speakerBox.setAlignmentX(Component.CENTER_ALIGNMENT); + speakerBox.setMaximumSize(new Dimension(IMAGE_WIDTH, Integer.MAX_VALUE)); + + // Get speaker details + String speakerName = campaign.getName(); + if (speaker != null) { + speakerName = speaker.getFullTitle(); + } + + // Add speaker image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, IMAGE_WIDTH); + } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, speaker, speakerName); + JLabel leftDescription = new JLabel( + String.format("

    %s
    ", + IMAGE_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the speakerBox + speakerBox.add(imageLabel); + speakerBox.add(leftDescription); + + return speakerBox; + } + + /** + * Assembles the speaker description based on the provided speaker and campaign details. + * + *

    + * The description includes: + *

      + *
    • The speaker's title and roles (both primary and secondary, if applicable).
    • + *
    • The force associated with the speaker.
    • + *
    • A fallback to the campaign name if the speaker is not available.
    • + *
    + * + * @param campaign The current campaign. + * @param speaker The {@link Person} representing the speaker, or {@code null} to use fallback data. + * @param speakerName The name/title to use for the speaker if one exists. + * @return A {@link StringBuilder} containing the formatted HTML description of the speaker. + */ + public static StringBuilder getSpeakerDescription(Campaign campaign, Person speaker, String speakerName) { + StringBuilder speakerDescription = new StringBuilder(); + + if (speaker != null) { + speakerDescription.append("").append(speakerName).append(""); + + boolean isClan = campaign.getFaction().isClan(); + + PersonnelRole primaryRole = speaker.getPrimaryRole(); + if (!primaryRole.isNone()) { + speakerDescription.append("
    ").append(primaryRole.getName(isClan)); + } + + PersonnelRole secondaryRole = speaker.getSecondaryRole(); + if (!secondaryRole.isNone()) { + speakerDescription.append("
    ").append(secondaryRole.getName(isClan)); + } + + Unit assignedUnit = speaker.getUnit(); + if (assignedUnit != null) { + int forceId = assignedUnit.getForceId(); + + if (forceId != FORCE_NONE) { + Force force = campaign.getForce(forceId); + + if (force != null) { + speakerDescription.append("
    ").append(force.getName()); + } + } + } + } else { + speakerDescription.append("").append(campaign.getName()).append(""); + } + return speakerDescription; + } + + /** + * Retrieves the speaker's icon for dialogs. If no speaker is supplied, the faction icon + * for the campaign is returned instead. + * + * @param campaign the {@link Campaign} instance containing the faction icon; can be + * {@code null} to use a default image. + * @param speaker the {@link Person} serving as the speaker for the dialog; can be {@code null}. + * @return an {@link ImageIcon} for the speaker's portrait, or the faction icon if the speaker is {@code null}. + */ + public static @Nullable ImageIcon getSpeakerIcon(@Nullable Campaign campaign, @Nullable Person speaker) { + if (campaign == null) { + return new ImageIcon("data/images/universe/factions/logo_mercenaries.png"); + } + + if (speaker == null) { + return campaign.getCampaignFactionIcon(); + } + + return speaker.getPortrait().getImageIcon(); + } + + /** + * Represents a label-tooltip pair for constructing UI buttons. + * Each button displays a label and optionally provides a tooltip when hovered. + */ + public record ButtonLabelTooltipPair(String btnLabel, String btnTooltip) { + /** + * Constructs a ButtonLabelTooltipPair with the given label and tooltip. + * + * @param btnLabel The label for the button. Must not be {@code null}. + * @param btnTooltip The tooltip for the button. Can be {@code null} if no tooltip is given. + * @throws IllegalArgumentException if {@code btnLabel} is {@code null}. + */ + public ButtonLabelTooltipPair(String btnLabel, @Nullable String btnTooltip) { + if (btnLabel == null) { + throw new IllegalArgumentException("btnLabel cannot be null."); + } + this.btnLabel = btnLabel; + this.btnTooltip = btnTooltip; + } + + /** + * Retrieves the button label. + * + * @return The button label as a {@link String}. + */ + @Override + public String btnLabel() { + return btnLabel; + } + + /** + * Retrieves the button tooltip. + * + * @return The button tooltip as a {@link String}, or {@code null} if no tooltip is set. + */ + @Override + public @Nullable String btnTooltip() { + return btnTooltip; + } + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsAbilityInfo.java b/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsAbilityInfo.java new file mode 100644 index 00000000000..f4266afefa5 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsAbilityInfo.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions; + +import mekhq.campaign.personnel.SpecialAbility; + +/** + * The {@code AbilityInfo} class represents information about a specific ability, + * encapsulating its name, the associated {@link SpecialAbility}, its active status, + * and its category. + */ +public class CampaignOptionsAbilityInfo { + private String name; + private SpecialAbility ability; + private boolean isEnabled; + private AbilityCategory category; + + /** + * Enum {@code AbilityCategory} represents the categories abilities can belong to. + * Categories available: + *
      + *
    • {@code COMBAT_ABILITIES}: Abilities related to combat actions
    • + *
    • {@code MANEUVERING_ABILITIES}: Abilities related to movement and maneuvering
    • + *
    • {@code UTILITY_ABILITIES}: Abilities providing utility or non-combat benefits
    • + *
    + */ + public enum AbilityCategory { + COMBAT_ABILITY, MANEUVERING_ABILITY, UTILITY_ABILITY + } + + /** + * Constructs an {@code AbilityInfo} object with all fields initialized. + * + * @param name the name of the ability + * @param ability the {@link SpecialAbility} associated with this ability + * @param isEnabled {@code true} if the ability is enabled, otherwise {@code false} + * @param category the category of the ability, represented as an {@link AbilityCategory} + */ + public CampaignOptionsAbilityInfo(String name, SpecialAbility ability, boolean isEnabled, AbilityCategory category) { + this.name = name; + this.ability = ability; + this.isEnabled = isEnabled; + this.category = category; + } + + /** + * Returns the name of the ability. + * + * @return the name of the ability + */ + public String getName() { + return name; + } + + /** + * Sets the name of the ability. + * + * @param name the new name of the ability + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the {@link SpecialAbility} object associated with this ability. + * + * @return the associated {@link SpecialAbility} + */ + public SpecialAbility getAbility() { + return ability; + } + + /** + * Sets the {@link SpecialAbility} object associated with this ability. + * + * @param ability the new {@link SpecialAbility} object to associate + */ + public void setAbility(SpecialAbility ability) { + this.ability = ability; + } + + /** + * Returns whether the ability is enabled or active. + * + * @return {@code true} if the ability is enabled, otherwise {@code false} + */ + public boolean isEnabled() { + return isEnabled; + } + + /** + * Sets the enabled/active status of the ability. + * + * @param enabled {@code true} to enable the ability, {@code false} to disable it + */ + public void setEnabled(boolean enabled) { + this.isEnabled = enabled; + } + + /** + * Returns the category of the ability. + * + * @return the {@link AbilityCategory} of the ability + */ + public AbilityCategory getCategory() { + return category; + } + + /** + * Sets the category of the ability. + * + * @param category the new {@link AbilityCategory} for the ability + */ + public void setCategory(AbilityCategory category) { + this.category = category; + } + + /** + * Returns a string representation of the ability, displaying only its name. + * + * @return the name of the ability + */ + @Override + public String toString() { + return name; + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsDialog.java b/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsDialog.java new file mode 100644 index 00000000000..a37832057a5 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsDialog.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions; + +import megamek.common.annotations.Nullable; +import mekhq.CampaignPreset; +import mekhq.campaign.Campaign; +import mekhq.gui.FileDialogs; +import mekhq.gui.baseComponents.AbstractMHQButtonDialog; +import mekhq.gui.campaignOptions.components.CampaignOptionsButton; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; + +import static mekhq.gui.campaignOptions.CampaignOptionsDialog.CampaignOptionsDialogMode.ABRIDGED; +import static mekhq.gui.campaignOptions.CampaignOptionsDialog.CampaignOptionsDialogMode.NORMAL; +import static mekhq.gui.campaignOptions.CampaignOptionsDialog.CampaignOptionsDialogMode.STARTUP; +import static mekhq.gui.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CANCELLED; + +/** + * The {@code CampaignOptionsDialog} class represents a dialog window for presenting + * and modifying the campaign options in MekHQ. It provides a user interface + * for accessing, editing, and applying various gameplay-related settings for a campaign. + * The dialog also supports applying presets and saving settings for future use. + *

    + * This dialog is an extension of {@link AbstractMHQButtonDialog} and integrates closely + * with a {@link Campaign} instance, representing the current or a predefined campaign setup. + * It facilitates user interaction for fine-tuning the game campaign experience. + *

    + * + * Key Features: + *
      + *
    • Initialization in multiple modes, such as NORMAL, STARTUP, and ABRIDGED.
    • + *
    • Ability to load and save presets for recurring configurations.
    • + *
    • Application of campaign settings directly to the active {@link Campaign} instance.
    • + *
    • Visual notifications, such as a notice about StratCon activation during the campaign.
    • + *
    + */ +public class CampaignOptionsDialog extends AbstractMHQButtonDialog { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + private final Campaign campaign; + private final CampaignOptionsPane campaignOptionsPane; + private final CampaignOptionsDialogMode mode; + + private boolean wasCanceled = true; + + public enum CampaignOptionsDialogMode { + NORMAL, + STARTUP, + ABRIDGED + } + + /** + * Constructs a {@code CampaignOptionsDialog} with the specified parent frame and + * campaign instance. Initializes the dialog using the default {@code NORMAL} mode. + * + * @param frame the parent {@link JFrame} for this dialog + * @param campaign the {@link Campaign} instance representing the current campaign + */ + public CampaignOptionsDialog(final JFrame frame, final Campaign campaign) { + super(frame, true, resources, "CampaignOptionsDialog", "campaignOptions.title"); + this.campaign = campaign; + this.campaignOptionsPane = new CampaignOptionsPane(frame, campaign, NORMAL); + this.mode = NORMAL; + initialize(); + + setLocationRelativeTo(frame); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + } + + /** + * Constructs a {@code CampaignOptionsDialog} with the specified parent frame, + * campaign instance, campaign preset, and mode. If a preset is provided, it is + * automatically applied to the campaign. + * + * @param frame the parent {@link JFrame} for this dialog + * @param campaign the {@link Campaign} instance for the current or preconfigured campaign + * @param preset an optional {@link CampaignPreset} to initialize the campaign (can be null) + * @param mode the {@link CampaignOptionsDialogMode} defining the behavior of the dialog + */ + public CampaignOptionsDialog(final JFrame frame, final Campaign campaign, @Nullable CampaignPreset preset, + CampaignOptionsDialogMode mode) { + super(frame, true, resources, "CampaignOptionsDialog", "campaignOptions.title"); + this.campaign = campaign; + this.campaignOptionsPane = new CampaignOptionsPane(frame, campaign, mode); + this.mode = mode; + initialize(); + + if (preset != null) { + applyPreset(preset); + } + + setLocationRelativeTo(frame); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + } + + /** + * Indicates whether the dialog was canceled by the user. + * + * @return {@code true} if the user canceled the dialog, {@code false} otherwise + */ + public boolean wasCanceled() { + return wasCanceled; + } + + /** + * Creates the central pane of the dialog, which contains the campaign options UI. + * + * @return a {@link Container} representing the center pane of the dialog + */ + @Override + protected Container createCenterPane() { + return campaignOptionsPane; + } + + /** + * Creates the button panel for the dialog, allowing the user to apply settings, + * load and save presets, or cancel the dialog. + * + * @return a {@link JPanel} representing the button panel + */ + @Override + protected JPanel createButtonPanel() { + final JPanel pnlButtons = new JPanel(new GridLayout(1, 0)); + + // Apply Settings + JButton btnApplySettings = new CampaignOptionsButton("ApplySettings"); + btnApplySettings.addActionListener(evt -> { + wasCanceled = false; + campaignOptionsPane.applyCampaignOptionsToCampaign(null, mode == STARTUP, + false); + dispose(); + showStratConNotice(); + }); + pnlButtons.add(btnApplySettings); + + // Save Preset + if (mode != ABRIDGED) { + JButton btnSavePreset = new CampaignOptionsButton("SavePreset"); + btnSavePreset.addActionListener(evt -> btnSaveActionPerformed()); + pnlButtons.add(btnSavePreset); + } + + // Load Preset + JButton btnLoadPreset = new CampaignOptionsButton("LoadPreset"); + btnLoadPreset.addActionListener(evt -> btnLoadActionPerformed()); + pnlButtons.add(btnLoadPreset); + + // Cancel + JButton btnCancel = new CampaignOptionsButton("Cancel"); + btnCancel.addActionListener(evt -> dispose()); + pnlButtons.add(btnCancel); + + return pnlButtons; + } + + /** + * Handles the "Save Preset" button action. Opens a dialog to create a new preset and + * save the campaign configuration to a file if confirmed. + */ + private void btnSaveActionPerformed() { + final CreateCampaignPreset createCampaignPresetDialog + = new CreateCampaignPreset(null, campaign, null); + + if (!createCampaignPresetDialog.showDialog().isConfirmed()) { + return; + } + + final CampaignPreset preset = createCampaignPresetDialog.getPreset(); + if (preset == null) { + return; + } + + campaignOptionsPane.applyCampaignOptionsToCampaign(preset, mode == STARTUP, true); + + preset.writeToFile(null, + FileDialogs.saveCampaignPreset(null, preset).orElse(null)); + } + + /** + * Handles the "Load Preset" button action. Opens a preset selection dialog, + * and applies the selected preset to the current campaign options if a preset + * is selected and not canceled. + */ + private void btnLoadActionPerformed() { + final SelectPresetDialog presetSelectionDialog = + new SelectPresetDialog(null, true, false); + if (presetSelectionDialog.getReturnState() != PRESET_SELECTION_CANCELLED) { + campaignOptionsPane.applyPreset(presetSelectionDialog.getSelectedPreset()); + } + } + + /** + * Applies a preset to the campaign options pane. This allows the user to quickly + * configure the campaign settings based on predefined presets. + * + * @param preset the {@link CampaignPreset} instance to apply + */ + public void applyPreset(CampaignPreset preset) { + campaignOptionsPane.applyPreset(preset); + } + + /** + * Displays a promo notice for StratCon functionality when the campaign options dialog + * is closed if the campaign is starting on the first day, and StratCon is enabled. + */ + private void showStratConNotice() { + // we don't store whether this dialog has previously appeared, + // instead we just have it appear only when Campaign Options is closed, + // the current day is the first day of the campaign, and StratCon is enabled + if (!campaign.getCampaignOptions().isUseStratCon() + || !campaign.getLocalDate().equals(campaign.getCampaignStartDate())) { + return; + } + + ImageIcon imageIcon = new ImageIcon("data/images/stratcon/stratConPromo.png"); + JLabel imageLabel = new JLabel(imageIcon); + JPanel imagePanel = new JPanel(new GridBagLayout()); + imagePanel.add(imageLabel); + + String title = resources.getString("stratConPromo.title"); + + String message = resources.getString("stratConPromo.message"); + JLabel messageLabel = new JLabel(message); + JPanel messagePanel = new JPanel(new GridBagLayout()); + messagePanel.add(messageLabel); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); + panel.add(imagePanel); + panel.add(messagePanel); + + Object[] options = { + resources.getString("stratConPromo.button") + }; + + JOptionPane.showOptionDialog(null, panel, title, JOptionPane.DEFAULT_OPTION, + JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsPane.java new file mode 100644 index 00000000000..474dcde8c5b --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsPane.java @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions; + +import megamek.common.annotations.Nullable; +import mekhq.CampaignPreset; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.RandomSkillPreferences; +import mekhq.campaign.event.OptionsChangedEvent; +import mekhq.campaign.personnel.SkillType; +import mekhq.campaign.universe.Faction; +import mekhq.gui.baseComponents.AbstractMHQTabbedPane; +import mekhq.gui.campaignOptions.CampaignOptionsAbilityInfo.AbilityCategory; +import mekhq.gui.campaignOptions.CampaignOptionsDialog.CampaignOptionsDialogMode; +import mekhq.gui.campaignOptions.contents.*; + +import javax.swing.*; +import java.time.LocalDate; +import java.util.Map; +import java.util.ResourceBundle; + +import static java.lang.Math.round; +import static mekhq.campaign.force.CombatTeam.recalculateCombatTeams; +import static mekhq.gui.campaignOptions.CampaignOptionsDialog.CampaignOptionsDialogMode.ABRIDGED; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createSubTabs; + +/** + * The {@code CampaignOptionsPane} class represents a tabbed pane used for displaying + * and managing various campaign options in MekHQ. It organizes these options + * into tabs and sub-tabs, enabling users to configure different aspects of a campaign. + * This component serves as the central UI for campaign settings management. + * + *

    + * The pane is initialized with a {@link Campaign} instance, which provides the campaign's + * data and allows options to be applied directly to the active campaign. + * The dialog supports multiple modes, such as {@code NORMAL}, {@code ABRIDGED}, + * and {@code STARTUP}, to determine the level of detail and features shown. + *

    + * + * Key Features: + *
      + *
    • Organizes options into logical groups, such as General, Human Resources, + * Advancement, Logistics, and Operations.
    • + *
    • Supports loading and applying campaign presets for streamlined configuration.
    • + *
    • Dynamically handles UI scaling and scrolling speed based on environment properties.
    • + *
    • Allows scalability for future addition of new campaign settings.
    • + *
    + */ +public class CampaignOptionsPane extends AbstractMHQTabbedPane { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + private static final int SCROLL_SPEED = 16; + private static final int HEADER_FONT_SIZE = 5; + + private final Campaign campaign; + private final CampaignOptions campaignOptions; + private final CampaignOptionsDialogMode mode; + + private GeneralTab generalTab; + private PersonnelTab personnelTab; + private BiographyTab biographyTab; + private RelationshipsTab relationshipsTab; + private TurnoverAndRetentionTab turnoverAndRetentionTab; + private AdvancementTab advancementTab; + private SkillsTab skillsTab; + private AbilitiesTab abilitiesTab; + private RepairAndMaintenanceTab repairAndMaintenanceTab; + private EquipmentAndSuppliesTab equipmentAndSuppliesTab; + private FinancesTab financesTab; + private MarketsTab marketsTab; + private RulesetsTab rulesetsTab; + + /** + * Constructs a {@code CampaignOptionsPane} for managing campaign settings. + * This initializes the tabbed pane and populates it with categories and sub-tabs + * based on the provided {@link Campaign} instance and dialog mode. + * + * @param frame the parent {@link JFrame} for this pane + * @param campaign the {@link Campaign} object representing the current campaign + * @param mode the {@link CampaignOptionsDialogMode} for configuring the pane's behavior + */ + public CampaignOptionsPane(final JFrame frame, final Campaign campaign, CampaignOptionsDialogMode mode) { + super(frame, resources, "campaignOptionsDialog"); + this.campaign = campaign; + this.campaignOptions = campaign.getCampaignOptions(); + this.mode = mode; + + initialize(); + } + + /** + * Initializes the campaign options pane by creating all parent tabs and adding + * sub-tabs for various campaign settings categories. Dynamically adjusts tab fonts + * and layout based on UI scaling settings. + */ + @Override + protected void initialize() { + double uiScale = 1; + try { + uiScale = Double.parseDouble(System.getProperty("flatlaf.uiScale")); + } catch (Exception ignored) {} + + addTab(String.format("%s", round(HEADER_FONT_SIZE * uiScale), + resources.getString("generalPanel.title")), createGeneralTab()); + + JTabbedPane humanResourcesParentTab = createHumanResourcesParentTab(); + createTab("humanResourcesParentTab", humanResourcesParentTab); + + JTabbedPane advancementParentTab = createAdvancementParentTab(); + createTab("advancementParentTab", advancementParentTab); + + JTabbedPane equipmentAndSuppliesParentTab = createEquipmentAndSuppliesParentTab(); + createTab("logisticsAndMaintenanceParentTab", equipmentAndSuppliesParentTab); + + JTabbedPane strategicOperationsParentTab = createStrategicOperationsParentTab(); + createTab("strategicOperationsParentTab", strategicOperationsParentTab); + } + + /** + * Adds a new tab to the pane. Wrapper method for adding a resource-labeled tab + * containing a {@link JScrollPane} to the campaign options pane. Dynamically adjusts font size + * for consistent scaling across all UI elements. + * + * @param resourceName the resource string key to locate the tab title + * @param tab the {@link JTabbedPane} to add as content for the tab + */ + private void createTab(String resourceName, JTabbedPane tab) { + JScrollPane tabScrollPane = new JScrollPane(tab); + + // Increase scroll speed + tabScrollPane.getVerticalScrollBar().setUnitIncrement(SCROLL_SPEED); + tabScrollPane.getHorizontalScrollBar().setUnitIncrement(SCROLL_SPEED); + + // Dynamically adjust font size based on the GUI scale + double uiScale = 1; + try { + uiScale = Double.parseDouble(System.getProperty("flatlaf.uiScale")); + } catch (Exception ignored) {} + + if (mode != ABRIDGED) { + addTab(String.format("%s", + round(HEADER_FONT_SIZE * uiScale), + resources.getString(resourceName + ".title")), + tabScrollPane); + } + } + + /** + * Creates the panel for general campaign options. Loads settings for general preferences + * and initializes it with current campaign options. + * + * @return a {@link JScrollPane} containing the general tab panel + */ + private JScrollPane createGeneralTab() { + generalTab = new GeneralTab(campaign, getFrame()); + JPanel createdGeneralTab = generalTab.createGeneralTab(); + generalTab.loadValuesFromCampaignOptions(); + + return new JScrollPane(createdGeneralTab); + } + + /** + * Creates the "Human Resources" parent tab. This tab organizes related sub-tabs concerning + * personnel management, relationships, turnover, and biography options. + * + * @return a {@link JTabbedPane} containing sub-tabs for the human resources category + */ + private JTabbedPane createHumanResourcesParentTab() { + // Parent Tab + JTabbedPane humanResourcesParentTab = new JTabbedPane(); + + // Personnel + personnelTab = new PersonnelTab(campaignOptions); + + JTabbedPane personnelContentTabs = createSubTabs(Map.of( + "personnelGeneralTab", personnelTab.createGeneralTab(), + "personnelInformationTab", personnelTab.createPersonnelInformationTab(), + "awardsTab", personnelTab.createAwardsTab(), + "prisonersAndDependentsTab", personnelTab.createPrisonersAndDependentsTab(), + "medicalTab", personnelTab.createMedicalTab(), + "salariesTab", personnelTab.createSalariesTab())); + personnelTab.loadValuesFromCampaignOptions(); + + // Biography + biographyTab = new BiographyTab(campaign); + + JTabbedPane biographyContentTabs = createSubTabs(Map.of( + "biographyGeneralTab", biographyTab.createGeneralTab(), + "backgroundsTab", biographyTab.createBackgroundsTab(), + "deathTab", biographyTab.createDeathTab(), + "educationTab", biographyTab.createEducationTab(), + "nameAndPortraitGenerationTab", biographyTab.createNameAndPortraitGenerationTab(), + "rankTab", biographyTab.createRankTab())); + biographyTab.loadValuesFromCampaignOptions(); + + // Relationships + relationshipsTab = new RelationshipsTab(campaignOptions); + + JTabbedPane relationshipsContentTabs = createSubTabs(Map.of( + "marriageTab", relationshipsTab.createMarriageTab(), + "divorceTab", relationshipsTab.createDivorceTab(), + "procreationTab", relationshipsTab.createProcreationTab())); + relationshipsTab.loadValuesFromCampaignOptions(); + + // Turnover and Retention + turnoverAndRetentionTab = new TurnoverAndRetentionTab(campaignOptions); + + JTabbedPane turnoverAndRetentionContentTabs = createSubTabs(Map.of( + "turnoverTab", turnoverAndRetentionTab.createTurnoverTab(), + "fatigueTab", turnoverAndRetentionTab.createFatigueTab())); + turnoverAndRetentionTab.loadValuesFromCampaignOptions(); + + // Add Tabs + humanResourcesParentTab.addTab(String.format("%s", 4, + resources.getString("personnelContentTabs.title")), personnelContentTabs); + humanResourcesParentTab.addTab(String.format("%s", 4, + resources.getString("biographyContentTabs.title")), biographyContentTabs); + humanResourcesParentTab.addTab(String.format("%s", 4, + resources.getString("relationshipsContentTabs.title")), relationshipsContentTabs); + humanResourcesParentTab.addTab(String.format("%s", 4, + resources.getString("turnoverAndRetentionContentTabs.title")), turnoverAndRetentionContentTabs); + + addTab(String.format("%s", 4, + resources.getString("humanResourcesParentTab.title")), humanResourcesParentTab); + + return humanResourcesParentTab; + } + + /** + * Creates the "Advancement" parent tab. This tab organizes related sub-tabs for awards, + * skill randomization, general skill management, and special pilot abilities (SPAs). + * + * @return a {@link JTabbedPane} containing sub-tabs for the advancement category + */ + private JTabbedPane createAdvancementParentTab() { + // Parent Tab + JTabbedPane advancementParentTab = new JTabbedPane(); + + // Advancement + advancementTab = new AdvancementTab(campaign); + + JTabbedPane awardsAndRandomizationContentTabs = createSubTabs(Map.of( + "xpAwardsTab", advancementTab.xpAwardsTab(), + "randomizationTab", advancementTab.skillRandomizationTab())); + advancementTab.loadValuesFromCampaignOptions(); + + // Skills + skillsTab = new SkillsTab(campaignOptions); + + JTabbedPane skillsContentTabs = createSubTabs(Map.of( + "combatSkillsTab", skillsTab.createSkillsTab(true), + "supportSkillsTab", skillsTab.createSkillsTab(false))); + skillsTab.loadValuesFromCampaignOptions(); + + // SPAs + abilitiesTab = new AbilitiesTab(); + + JTabbedPane abilityContentTabs = createSubTabs(Map.of( + "combatAbilitiesTab", abilitiesTab.createAbilitiesTab(AbilityCategory.COMBAT_ABILITY), + "maneuveringAbilitiesTab", abilitiesTab.createAbilitiesTab(AbilityCategory.MANEUVERING_ABILITY), + "utilityAbilitiesTab", abilitiesTab.createAbilitiesTab(AbilityCategory.UTILITY_ABILITY))); + // the loading of values from the campaign is built into the AbilitiesTab class so not called here. + + // Add Tabs + advancementParentTab.addTab(String.format("%s", 4, + resources.getString("awardsAndRandomizationContentTabs.title")), awardsAndRandomizationContentTabs); + advancementParentTab.addTab(String.format("%s", 4, + resources.getString("skillsContentTabs.title")), skillsContentTabs); + advancementParentTab.addTab(String.format("%s", 4, + resources.getString("abilityContentTabs.title")), abilityContentTabs); + + addTab(String.format("%s", 4, + resources.getString("advancementParentTab.title")), advancementParentTab); + + return advancementParentTab; + } + + /** + * Creates the "Logistics and Maintenance" parent tab. This tab organizes related sub-tabs + * for equipment acquisition, repair, maintenance, and supply management options. + * + * @return a {@link JTabbedPane} containing sub-tabs for the logistics and maintenance category + */ + private JTabbedPane createEquipmentAndSuppliesParentTab() { + // Parent Tab + JTabbedPane equipmentAndSuppliesParentTab = new JTabbedPane(); + + // Repair and Maintenance + repairAndMaintenanceTab = new RepairAndMaintenanceTab(campaignOptions); + + JTabbedPane repairsAndMaintenanceContentTabs = createSubTabs(Map.of( + "repairTab", repairAndMaintenanceTab.createRepairTab(), + "maintenanceTab", repairAndMaintenanceTab.createMaintenanceTab())); + repairAndMaintenanceTab.loadValuesFromCampaignOptions(); + + // Supplies and Acquisition + equipmentAndSuppliesTab = new EquipmentAndSuppliesTab(campaignOptions); + + JTabbedPane suppliesAndAcquisitionContentTabs = createSubTabs(Map.of( + "acquisitionTab", equipmentAndSuppliesTab.createAcquisitionTab(), + "planetaryAcquisitionTab", equipmentAndSuppliesTab.createPlanetaryAcquisitionTab(), + "techLimitsTab", equipmentAndSuppliesTab.createTechLimitsTab())); + equipmentAndSuppliesTab.loadValuesFromCampaignOptions(); + + // Add tabs + equipmentAndSuppliesParentTab.addTab(String.format("%s", 4, + resources.getString("suppliesAndAcquisitionContentTabs.title")), suppliesAndAcquisitionContentTabs); + equipmentAndSuppliesParentTab.addTab(String.format("%s", 4, + resources.getString("repairsAndMaintenanceContentTabs.title")), repairsAndMaintenanceContentTabs); + + addTab(String.format("%s", 4, + resources.getString("logisticsAndMaintenanceParentTab.title")), equipmentAndSuppliesParentTab); + + return equipmentAndSuppliesParentTab; + } + + /** + * Creates the "Strategic Operations" parent tab. This tab organizes related sub-tabs for + * finances, market management (personnel, units, and contracts), and ruleset configuration. + * + * @return a {@link JTabbedPane} containing sub-tabs for the strategic operations category + */ + private JTabbedPane createStrategicOperationsParentTab() { + // Parent Tab + JTabbedPane strategicOperationsParentTab = new JTabbedPane(); + + // Finances + financesTab = new FinancesTab(campaign); + + JTabbedPane financesContentTabs = createSubTabs(Map.of( + "financesGeneralTab", financesTab.createFinancesGeneralOptionsTab(), + "priceMultipliersTab", financesTab.createPriceMultipliersTab())); + financesTab.loadValuesFromCampaignOptions(); + + // Markets + marketsTab = new MarketsTab(campaign); + + JTabbedPane marketsContentTabs = createSubTabs(Map.of( + "personnelMarketTab", marketsTab.createPersonnelMarketTab(), + "unitMarketTab", marketsTab.createUnitMarketTab(), + "contractMarketTab", marketsTab.createContractMarketTab())); + marketsTab.loadValuesFromCampaignOptions(); + + // Rulesets + rulesetsTab = new RulesetsTab(campaignOptions); + + JTabbedPane rulesetsContentTabs = createSubTabs(Map.of( + "stratConGeneralTab", rulesetsTab.createStratConTab(), + "legacyTab", rulesetsTab.createLegacyTab())); + rulesetsTab.loadValuesFromCampaignOptions(); + + // Add tabs + strategicOperationsParentTab.addTab(String.format("%s", 4, + resources.getString("financesContentTabs.title")), financesContentTabs); + strategicOperationsParentTab.addTab(String.format("%s", 4, + resources.getString("marketsContentTabs.title")), marketsContentTabs); + strategicOperationsParentTab.addTab(String.format("%s", 4, + resources.getString("rulesetsContentTabs.title")), rulesetsContentTabs); + + addTab(String.format("%s", 4, + resources.getString("strategicOperationsParentTab.title")), strategicOperationsParentTab); + + return strategicOperationsParentTab; + } + + /** + * Applies the currently configured campaign options to the active {@link Campaign}. + * This method processes all tabs in the dialog, applying the options to the campaign + * in logical order (e.g., "General" first, followed by other categories). + * + * @param preset an optional {@link CampaignPreset} used to override campaign options + * @param isStartUp specifies whether this is run as part of a startup initialization + * @param isSaveAction determines if this action is saving options to a preset + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignPreset preset, boolean isStartUp, + boolean isSaveAction) { + CampaignOptions options = this.campaignOptions; + RandomSkillPreferences presetRandomSkillPreferences = null; + Map presetSkills = null; + + if (preset != null) { + options = preset.getCampaignOptions(); + presetRandomSkillPreferences = preset.getRandomSkillPreferences(); + presetSkills = preset.getSkills(); + } + + // Everything assumes general tab will be the first applied. + // While this shouldn't break anything, it's not worth moving around. + // For all other tabs, it makes sense to apply them in the order they + // appear in the dialog; however, this shouldn't make any major difference. + generalTab.applyCampaignOptionsToCampaign(options, isStartUp, isSaveAction); + + // Human Resources + personnelTab.applyCampaignOptionsToCampaign(options); + biographyTab.applyCampaignOptionsToCampaign(options); + relationshipsTab.applyCampaignOptionsToCampaign(options); + turnoverAndRetentionTab.applyCampaignOptionsToCampaign(options); + + // Advancement + advancementTab.applyCampaignOptionsToCampaign(options, presetRandomSkillPreferences); + skillsTab.applyCampaignOptionsToCampaign(options, presetSkills); + abilitiesTab.applyCampaignOptionsToCampaign(preset); + + // Logistics + equipmentAndSuppliesTab.applyCampaignOptionsToCampaign(options); + repairAndMaintenanceTab.applyCampaignOptionsToCampaign(options); + + // Operations + financesTab.applyCampaignOptionsToCampaign(options); + marketsTab.applyCampaignOptionsToCampaign(options); + rulesetsTab.applyCampaignOptionsToCampaign(options); + + // Tidy up + if (preset == null) { + recalculateCombatTeams(campaign); + MekHQ.triggerEvent(new OptionsChangedEvent(campaign, options)); + } + } + + /** + * Applies the values from a {@link CampaignPreset} to all tabs in the dialog. This propagates + * preset-specific configuration to all associated components and sub-tabs, including + * campaign-related properties such as dates, factions, and skills. + * + * @param campaignPreset the {@link CampaignPreset} containing the preset options to apply + */ + public void applyPreset(@Nullable CampaignPreset campaignPreset) { + if (campaignPreset == null) { + return; + } + + CampaignOptions presetCampaignOptions = campaignPreset.getCampaignOptions(); + LocalDate presetDate = campaignPreset.getDate(); + Faction presetFaction = campaignPreset.getFaction(); + + generalTab.loadValuesFromCampaignOptions(presetCampaignOptions, presetDate, presetFaction); + + // Human Resources + personnelTab.loadValuesFromCampaignOptions(presetCampaignOptions); + biographyTab.loadValuesFromCampaignOptions(presetCampaignOptions, + presetCampaignOptions.getRandomOriginOptions(), campaignPreset.getRankSystem()); + relationshipsTab.loadValuesFromCampaignOptions(presetCampaignOptions); + turnoverAndRetentionTab.loadValuesFromCampaignOptions(presetCampaignOptions); + + // Advancement + advancementTab.loadValuesFromCampaignOptions(presetCampaignOptions, + campaignPreset.getRandomSkillPreferences()); + skillsTab.loadValuesFromCampaignOptions(presetCampaignOptions, campaignPreset.getSkills()); + // The ability tab is a special case, so handled differently to other tabs + abilitiesTab.buildAllAbilityInfo(campaignPreset.getSpecialAbilities()); + + // Logistics + equipmentAndSuppliesTab.loadValuesFromCampaignOptions(presetCampaignOptions); + repairAndMaintenanceTab.loadValuesFromCampaignOptions(presetCampaignOptions); + + // Operations + financesTab.loadValuesFromCampaignOptions(presetCampaignOptions); + marketsTab.loadValuesFromCampaignOptions(presetCampaignOptions); + rulesetsTab.loadValuesFromCampaignOptions(presetCampaignOptions); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsUtilities.java b/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsUtilities.java new file mode 100644 index 00000000000..395f4134efa --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/CampaignOptionsUtilities.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions; + +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; +import mekhq.gui.campaignOptions.components.CampaignOptionsStandardPanel; + +import javax.swing.*; +import javax.swing.GroupLayout.Alignment; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * The {@code CampaignOptionsUtilities} class provides utility methods and constants + * for managing, creating, and organizing user interface components related to the + * campaign options dialog in the MekHQ application. This class focuses on assisting + * in the creation and layout of panels, tabs, and other related components. + * + *

    + * This class is designed to be stateless and does not rely on any specific instance variables, + * making its methods accessible in a static fashion. + *

    + * + * Key Features: + *
      + *
    • Provides reusable methods to create and configure {@link JPanel} objects.
    • + *
    • Handles creation of organized, alphabetized tab groups with specialized handling for + * "general options" tabs.
    • + *
    • Offers UI utility methods for processing resource names, image directories, + * and dynamic content scaling.
    • + *
    • Supports internationalization through the {@link ResourceBundle} for localized strings.
    • + *
    + */ +public class CampaignOptionsUtilities { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + final static String IMAGE_DIRECTORY = "data/images/universe/factions/"; + + /** + * Retrieves the directory path for storing faction-related image resources. + * + * @return a {@link String} representing the path to the image directory. + */ + public static String getImageDirectory() { + return IMAGE_DIRECTORY; + } + + /** + * Creates a {@link GroupLayout} with default gap settings for the specified panel. + * + *

    + * The created {@link GroupLayout} automatically enables gaps between components + * and containers for improved layout consistency and readability. + *

    + * + * @param panel the {@link JPanel} to which the {@link GroupLayout} will be applied. + * @return the {@link GroupLayout} instance configured for the given panel. + */ + public static GroupLayout createGroupLayout(JPanel panel) { + final GroupLayout layout = new GroupLayout(panel); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + return layout; + } + + /** + * Creates a parent panel for the specified child panel and configures its layout. + * The child panel is encapsulated in the parent {@link JPanel}, ensuring consistent spacing + * and margins. + * + * @param panel the child {@link JPanel} that will be added to the parent panel. + * @param name the identifier name for the parent panel, used for UI tracking and debugging purposes. + * @return a fully initialized {@link JPanel} configured as a parent container. + */ + public static JPanel createParentPanel(JPanel panel, String name) { + // Create Panel + final JPanel parentPanel = new CampaignOptionsStandardPanel(name); + final GroupLayout parentLayout = createGroupLayout(parentPanel); + + // Layout + parentPanel.setLayout(parentLayout); + + parentLayout.setVerticalGroup( + parentLayout.createSequentialGroup() + .addComponent(panel)); + + parentLayout.setHorizontalGroup( + parentLayout.createParallelGroup(Alignment.CENTER) + .addComponent(panel)); + + return parentPanel; + } + + /** + * Dynamically creates a {@link JTabbedPane} from a map of tab names and panels. + * + *

    + * This method organizes the tabs in alphabetic order except for tabs whose + * names contain "GeneralTab", which are moved to the front as a prioritized tab. + * Each panel is wrapped in a custom layout that includes additional components, + * such as quotes or additional spacing elements, for better visual formatting. + *

    + * + *

    + * Tabs are localized using the {@link ResourceBundle}, which maps tab names to + * their corresponding displayed titles. + *

    + * + * @param panels a map where the key is the resource name of the tab, and the value + * is the {@link JPanel} displayed as the content of the tab. + * @return a {@link JTabbedPane} containing the organized and formatted tabs. + */ + static JTabbedPane createSubTabs(Map panels) { + // We use a list here to ensure that the tabs always display in the same order, + // and that order might as well be alphabetic. + List tabNames = new ArrayList<>(panels.keySet()); + tabNames.sort(String.CASE_INSENSITIVE_ORDER); + + // This is a special case handler to ensure 'general options' tabs always appear first + int indexToMoveToFront = -1; + for (int i=0; i
    %s
    ", + UIUtil.scaleForGUI(mainPanel.getPreferredSize().width), + resources.getString(tabName + ".border"))); + + GridBagConstraints quoteConstraints = new GridBagConstraints(); + quoteConstraints.gridx = GridBagConstraints.RELATIVE; + quoteConstraints.gridy = GridBagConstraints.RELATIVE; + quotePanel.add(quote, quoteConstraints); + + // Create a BorderLayout panel for mainPanel + JPanel mainPanelHolder = new JPanel(new GridBagLayout()); + GridBagConstraints mainConstraints = new GridBagConstraints(); + mainConstraints.gridx = GridBagConstraints.RELATIVE; + mainConstraints.gridy = GridBagConstraints.RELATIVE; + mainPanelHolder.add(mainPanel, mainConstraints); + + // Reorganize mainPanel to include quotePanel at bottom + JPanel contentPanel = new JPanel(new BorderLayout()); + contentPanel.setName(tabName); + contentPanel.add(mainPanelHolder, BorderLayout.CENTER); + + contentPanel.add(quotePanel, BorderLayout.SOUTH); + + // Create a wrapper panel for its easy alignment controls + JPanel wrapperPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.NORTH; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + + wrapperPanel.add(contentPanel, gbc); + + tabbedPane.addTab(resources.getString(tabName + ".title"), wrapperPanel); + } + + return tabbedPane; + } + + /** + * Determines an appropriate wrap size for text rendering, using a default when + * no value is specified. + * + * @param customWrapSize an optional {@link Integer} specifying the desired wrap size. + * If this parameter is {@code null}, a default value of 100 is used. + * @return the processed wrap size value; defaults to 100 if {@code customWrapSize} is null. + */ + public static int processWrapSize(@Nullable Integer customWrapSize) { + return customWrapSize == null ? 100 : customWrapSize; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/CreateCampaignPresetDialog.java b/MekHQ/src/mekhq/gui/campaignOptions/CreateCampaignPreset.java similarity index 95% rename from MekHQ/src/mekhq/gui/dialog/CreateCampaignPresetDialog.java rename to MekHQ/src/mekhq/gui/campaignOptions/CreateCampaignPreset.java index 0697cd47ed1..46bd4974f22 100644 --- a/MekHQ/src/mekhq/gui/dialog/CreateCampaignPresetDialog.java +++ b/MekHQ/src/mekhq/gui/campaignOptions/CreateCampaignPreset.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog; +package mekhq.gui.campaignOptions; import megamek.client.ui.baseComponents.MMButton; import megamek.client.ui.baseComponents.MMComboBox; @@ -28,10 +28,10 @@ import megamek.common.annotations.Nullable; import megamek.common.options.GameOptions; import megamek.common.util.sorter.NaturalOrderComparator; +import mekhq.CampaignPreset; import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.CampaignOptions; -import mekhq.campaign.CampaignPreset; import mekhq.campaign.RandomSkillPreferences; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.SpecialAbility; @@ -44,9 +44,12 @@ import mekhq.campaign.universe.companyGeneration.CompanyGenerationOptions; import mekhq.gui.baseComponents.AbstractMHQValidationButtonDialog; import mekhq.gui.baseComponents.SortedComboBoxModel; +import mekhq.gui.dialog.CompanyGenerationOptionsDialog; +import mekhq.gui.dialog.DateChooser; import mekhq.gui.displayWrappers.FactionDisplay; import javax.swing.*; +import javax.swing.GroupLayout.Alignment; import javax.swing.border.TitledBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; @@ -59,7 +62,7 @@ /** * @author Justin "Windchild" Bowen */ -public class CreateCampaignPresetDialog extends AbstractMHQValidationButtonDialog { +public class CreateCampaignPreset extends AbstractMHQValidationButtonDialog { //region Variable Declarations private final Campaign campaign; private CampaignPreset preset; @@ -96,8 +99,8 @@ public class CreateCampaignPresetDialog extends AbstractMHQValidationButtonDialo //endregion Variable Declarations //region Constructors - public CreateCampaignPresetDialog(final JFrame frame, final Campaign campaign, - final @Nullable CampaignPreset preset) { + public CreateCampaignPreset(final JFrame frame, final Campaign campaign, + final @Nullable CampaignPreset preset) { super(frame, "CreateCampaignPresetDialog", "CreateCampaignPresetDialog.title"); this.campaign = campaign; setPreset(preset); @@ -538,32 +541,32 @@ public Component getListCellRendererComponent(final JList list, final Object layout.setVerticalGroup( layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(getChkSpecifyDate()) - .addComponent(btnDate, GroupLayout.Alignment.LEADING)) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(btnDate, Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(getChkSpecifyFaction()) - .addComponent(getComboFaction(), GroupLayout.Alignment.LEADING)) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(getComboFaction(), Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(getChkSpecifyPlanet()) - .addComponent(getChkStartingSystemFactionSpecific(), GroupLayout.Alignment.LEADING)) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(getChkStartingSystemFactionSpecific(), Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(getComboStartingSystem()) - .addComponent(getComboStartingPlanet(), GroupLayout.Alignment.LEADING)) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(getComboStartingPlanet(), Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(getChkSpecifyRankSystem()) - .addComponent(getComboRankSystem(), GroupLayout.Alignment.LEADING)) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(getComboRankSystem(), Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(lblContractCount) - .addComponent(getSpnContractCount(), GroupLayout.Alignment.LEADING)) + .addComponent(getSpnContractCount(), Alignment.LEADING)) .addComponent(getChkGM()) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(getChkSpecifyCompanyGenerationOptions()) - .addComponent(btnCompanyGenerationOptions, GroupLayout.Alignment.LEADING)) + .addComponent(btnCompanyGenerationOptions, Alignment.LEADING)) ); layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) + layout.createParallelGroup(Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(getChkSpecifyDate()) .addComponent(btnDate)) @@ -623,14 +626,14 @@ private JPanel createContinuousPanel() { layout.setVerticalGroup( layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(getChkSpecifyGameOptions()) - .addComponent(btnGameOptions, GroupLayout.Alignment.LEADING)) + .addComponent(btnGameOptions, Alignment.LEADING)) .addComponent(getChkSpecifyCampaignOptions()) ); layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) + layout.createParallelGroup(Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(getChkSpecifyGameOptions()) .addComponent(btnGameOptions)) diff --git a/MekHQ/src/mekhq/gui/panes/campaignOptions/SelectPresetDialog.java b/MekHQ/src/mekhq/gui/campaignOptions/SelectPresetDialog.java similarity index 61% rename from MekHQ/src/mekhq/gui/panes/campaignOptions/SelectPresetDialog.java rename to MekHQ/src/mekhq/gui/campaignOptions/SelectPresetDialog.java index 342da2a0337..a2e87fb4d6c 100644 --- a/MekHQ/src/mekhq/gui/panes/campaignOptions/SelectPresetDialog.java +++ b/MekHQ/src/mekhq/gui/campaignOptions/SelectPresetDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2024-2025 The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -16,18 +16,21 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.panes.campaignOptions; +package mekhq.gui.campaignOptions; +import megamek.client.ui.swing.util.UIUtil; import megamek.logging.MMLogger; +import mekhq.CampaignPreset; import mekhq.MekHQ; -import mekhq.campaign.CampaignPreset; +import mekhq.gui.campaignOptions.components.CampaignOptionsButton; import javax.swing.*; import javax.swing.GroupLayout.Alignment; +import javax.swing.LayoutStyle.ComponentPlacement; import java.awt.*; import java.util.ResourceBundle; -import static megamek.client.ui.WrapLayout.wordWrap; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createGroupLayout; /** * A dialog for selecting campaign presets. Extends {@link JDialog}. @@ -35,14 +38,10 @@ * Provides options to select a preset, customize a preset, or cancel the operation. */ public class SelectPresetDialog extends JDialog { - private static String RESOURCE_PACKAGE = "mekhq/resources/NEWCampaignOptionsDialog"; + private static String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE, MekHQ.getMHQOptions().getLocale()); - final static String OPTION_SELECT_PRESET = resources.getString("presetDialogSelect.name"); - final static String OPTION_CUSTOMIZE_PRESET = resources.getString("presetDialogCustomize.name"); - final static String OPTION_CANCEL = resources.getString("presetDialogCancel.name"); - private static final MMLogger logger = MMLogger.create(SelectPresetDialog.class); private int returnState; @@ -79,23 +78,31 @@ public CampaignPreset getSelectedPreset() { */ public SelectPresetDialog(JFrame frame, boolean includePresetSelectOption, boolean includeCustomizePresetOption) { super(frame, resources.getString("presetDialog.title"), true); + final int DIALOG_WIDTH = UIUtil.scaleForGUI(400); + final int INSERT_SIZE = UIUtil.scaleForGUI(10); returnState = PRESET_SELECTION_CANCELLED; setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); - ImageIcon image = new ImageIcon("data/images/misc/megamek-splash.png"); - JLabel imageLabel = new JLabel(image); + ImageIcon imageIcon = new ImageIcon("data/images/misc/megamek-splash.png"); + + int width = UIUtil.scaleForGUI(imageIcon.getIconWidth()); + int height = UIUtil.scaleForGUI(imageIcon.getIconHeight()); + + Image image = imageIcon.getImage(); + Image scaledImage = image.getScaledInstance(width, height, Image.SCALE_SMOOTH); + + imageIcon = new ImageIcon(scaledImage); + JLabel imageLabel = new JLabel(imageIcon); add(imageLabel, BorderLayout.NORTH); JPanel centerPanel = new JPanel(); - final GroupLayout layout = new GroupLayout(centerPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); + final GroupLayout layout = createGroupLayout(centerPanel); centerPanel.setLayout(layout); JLabel descriptionLabel = new JLabel(String.format( - "
    %s
    ", - resources.getString("presetDialog.description"))); + "
    %s
    ", + DIALOG_WIDTH, resources.getString("presetDialog.description"))); final DefaultListModel campaignPresets = new DefaultListModel<>(); campaignPresets.addAll(CampaignPreset.getCampaignPresets()); @@ -114,7 +121,6 @@ public Component getListCellRendererComponent(JList list, Object value, boolean cellHasFocus) { if (value instanceof CampaignPreset preset) { setText(preset.getTitle()); - setToolTipText(wordWrap(preset.getDescription())); } setHorizontalAlignment(JLabel.CENTER); @@ -128,20 +134,71 @@ public Component getListCellRendererComponent(JList list, Object value, layout.setVerticalGroup( layout.createSequentialGroup() .addComponent(descriptionLabel) + .addPreferredGap(ComponentPlacement.UNRELATED, INSERT_SIZE, INSERT_SIZE) .addComponent(comboBox) ); layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) + layout.createParallelGroup(Alignment.CENTER) .addComponent(descriptionLabel) .addComponent(comboBox) ); - add(centerPanel, BorderLayout.CENTER); + JPanel outerPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.CENTER; + + // Add padding/margin to outerPanel layout using an empty border + outerPanel.setBorder(BorderFactory.createEmptyBorder(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE)); + outerPanel.add(centerPanel, gbc); + + JPanel bottomPanel = new JPanel(); + bottomPanel.setBorder(BorderFactory.createEtchedBorder()); + JLabel newLabel = new JLabel(); + newLabel.setHorizontalAlignment(JLabel.CENTER); + bottomPanel.add(newLabel); + + // Add 10px gap between outerPanel and bottomPanel + gbc.gridy = 1; + gbc.insets = new Insets(10, 0, 0, 0); + outerPanel.add(bottomPanel, gbc); + + comboBox.addActionListener(e -> { + Object selectedItem = comboBox.getSelectedItem(); + if (selectedItem instanceof CampaignPreset preset) { + newLabel.setText(String.format( + "
    %s
    ", + DIALOG_WIDTH * 0.9, + preset.getDescription())); + } else { + newLabel.setText(String.format( + "
    %s
    ", + DIALOG_WIDTH * 0.9, + "No description available.")); + } + revalidate(); + repaint(); + }); + + // Set the initial text in newLabel based on the currently selected item in comboBox + Object initialItem = comboBox.getSelectedItem(); + if (initialItem instanceof CampaignPreset preset) { + newLabel.setText(String.format( + "
    %s
    ", + DIALOG_WIDTH * 0.9, + preset.getDescription())); + } else { + newLabel.setText(String.format( + "
    %s
    ", + DIALOG_WIDTH * 0.9, + "No description available.")); + } + + add(outerPanel, BorderLayout.CENTER); + JPanel buttonPanel = new JPanel(); - JButton buttonSelect = new JButton(OPTION_SELECT_PRESET); - buttonSelect.setToolTipText(resources.getString("presetDialogSelect.tooltip")); + JButton buttonSelect = new CampaignOptionsButton("PresetDialogSelect"); buttonSelect.addActionListener(e -> { selectedPreset = (CampaignPreset) comboBox.getSelectedItem(); returnState = PRESET_SELECTION_SELECT; @@ -150,8 +207,7 @@ public Component getListCellRendererComponent(JList list, Object value, buttonSelect.setEnabled(includePresetSelectOption); buttonPanel.add(buttonSelect); - JButton buttonCustomize = new JButton(OPTION_CUSTOMIZE_PRESET); - buttonCustomize.setToolTipText(resources.getString("presetDialogCustomize.tooltip")); + JButton buttonCustomize = new CampaignOptionsButton("PresetDialogCustomize"); buttonCustomize.addActionListener(e -> { selectedPreset = (CampaignPreset) comboBox.getSelectedItem(); returnState = PRESET_SELECTION_CUSTOMIZE; @@ -160,8 +216,7 @@ public Component getListCellRendererComponent(JList list, Object value, buttonCustomize.setEnabled(includeCustomizePresetOption); buttonPanel.add(buttonCustomize); - JButton buttonCancel = new JButton(OPTION_CANCEL); - buttonCancel.setToolTipText(resources.getString("presetDialogCancel.tooltip")); + JButton buttonCancel = new CampaignOptionsButton("PresetDialogCancel"); buttonCancel.addActionListener(e -> { returnState = PRESET_SELECTION_CANCELLED; dispose(); diff --git a/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsButton.java b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsButton.java new file mode 100644 index 00000000000..f41c1e4d86e --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsButton.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions.components; + +import megamek.common.annotations.Nullable; + +import javax.swing.*; +import java.util.ResourceBundle; + +import static megamek.client.ui.WrapLayout.wordWrap; +import static megamek.client.ui.swing.util.FlatLafStyleBuilder.setFontScaling; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.processWrapSize; + +/** + * A specialized {@link JButton} used in the campaign options dialog. + *

    + * This button's text and tooltip are dynamically loaded from a resource bundle + * based on a given name. The tooltip can optionally include word wrapping + * with a configurable wrap size. + *

    + * The button also supports font scaling adjustments. + */ +public class CampaignOptionsButton extends JButton { + /** + * The path to the resource bundle containing text and tooltip information + * for this component. + */ + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + + /** + * The {@link ResourceBundle} used to load localized strings for button text and tooltips. + */ + static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + /** + * Constructs a new instance of {@link CampaignOptionsButton} with the specified name. + *

    + * The name is used to determine the button's visible text and tooltip, as well + * as to generate its unique internal name. + *

    + * The text is located in the resource bundle key {@code "lbl" + name + ".text"}. + * The tooltip is located in the resource bundle key {@code "lbl" + name + ".tooltip"}. + * + * @param name the name used to fetch the button's text and tooltip, and to set its name + */ + public CampaignOptionsButton(String name) { + this(name, null); + } + + /** + * Constructs a new instance of {@link CampaignOptionsButton} with the specified + * name and a custom tooltip wrap size. + *

    + * The name is used to determine the button's visible text and tooltip, as well + * as to generate its unique internal name. The text and tooltip are fetched + * from the resource bundle, located at keys {@code "lbl" + name + ".text"} + * and {@code "lbl" + name + ".tooltip"} respectively. + *

    + * If a custom wrap size is provided, the tooltip text will be word-wrapped + * accordingly. If {@code customWrapSize} is {@code null}, a default wrap size is used. + * + * @param name the name used to fetch the button's text and tooltip, and to set its name + * @param customWrapSize the maximum number of characters per tooltip line, + * or {@code null} for the default wrap size + */ + public CampaignOptionsButton(String name, @Nullable Integer customWrapSize) { + // Sets the button's text from the resource bundle + super(resources.getString("lbl" + name + ".text")); + + // Sets the button's tooltip, applying word wrapping based on customWrapSize + setToolTipText(wordWrap(resources.getString("lbl" + name + ".tooltip"), + processWrapSize(customWrapSize))); + + // Sets the button's internal name + setName("btn" + name); + + // Applies font scaling with default scaling disabled + setFontScaling(this, false, 1); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsCheckBox.java b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsCheckBox.java new file mode 100644 index 00000000000..f56126cab1a --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsCheckBox.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions.components; + +import megamek.common.annotations.Nullable; +import mekhq.gui.campaignOptions.CampaignOptionsUtilities; + +import javax.swing.*; +import java.util.ResourceBundle; + +import static megamek.client.ui.WrapLayout.wordWrap; +import static megamek.client.ui.swing.util.FlatLafStyleBuilder.setFontScaling; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.processWrapSize; + +/** + * A specialized {@link JCheckBox} used in the campaign options dialog. + *

    + * This check box's text and tooltip are dynamically loaded from a resource bundle + * based on a given name. The tooltip can optionally include word wrapping + * with a configurable wrap size. + *

    + * The checkbox also supports font scaling adjustments. + */ +public class CampaignOptionsCheckBox extends JCheckBox { + /** + * The path to the resource bundle containing text and tooltip information + * for this component. + */ + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + + /** + * The {@link ResourceBundle} used to load localized strings for checkbox text and tooltips. + */ + static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + /** + * Constructs a new instance of {@link CampaignOptionsCheckBox} with the specified name. + *

    + * The name is used to determine the checkbox's visible text and tooltip, as well + * as to generate its unique internal name. + *

    + * The text is located in the resource bundle key {@code "lbl" + name + ".text"}. + * The tooltip is located in the resource bundle key {@code "lbl" + name + ".tooltip"}. + *

    + * A default wrap size is used for the tooltip text if {@link CampaignOptionsUtilities#processWrapSize} + * is not overridden. + * + * @param name the name used to fetch the checkbox's text and tooltip, and to set its name + */ + public CampaignOptionsCheckBox(String name) { + this(name, null); + } + + /** + * Constructs a new instance of {@link CampaignOptionsCheckBox} with the specified + * name and a custom tooltip wrap size. + *

    + * The name is used to determine the checkbox's visible text and tooltip, as well + * as to generate its unique internal name. The text and tooltip are fetched + * from the resource bundle, located at keys {@code "lbl" + name + ".text"} + * and {@code "lbl" + name + ".tooltip"} respectively. + *

    + * If a custom wrap size is provided, the tooltip text will be word-wrapped + * accordingly. If {@code customWrapSize} is {@code null}, a default wrap size is used. + * + * @param name the name used to fetch the checkbox's text and tooltip, and to set its name + * @param customWrapSize the maximum number of characters per tooltip line, + * or {@code null} for the default wrap size + */ + public CampaignOptionsCheckBox(String name, @Nullable Integer customWrapSize) { + // Sets the checkbox's text from the resource bundle, wrapped in HTML tags + super(String.format("%s", resources.getString("lbl" + name + ".text"))); + + // Sets the checkbox's internal name + setName("chk" + name); + + // Sets the checkbox's tooltip, applying word wrapping based on customWrapSize + setToolTipText(wordWrap(resources.getString("lbl" + name + ".tooltip"), + processWrapSize(customWrapSize))); + + // Applies font scaling with default scaling disabled + setFontScaling(this, false, 1); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsGridBagConstraints.java b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsGridBagConstraints.java new file mode 100644 index 00000000000..3764b03e005 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsGridBagConstraints.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions.components; + +import megamek.common.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.Objects; + +/** + * A custom implementation of {@link GridBagConstraints} designed for use with + * panels in the campaign options dialog. + *

    + * This class allows for simplified initialization of {@link GridBagConstraints} + * with configurable anchor and fill properties, as well as preset insets. + *

    + * It is intended to be paired with {@code CampaignOptionsStandardPanel} or similar components + * using the {@link GridBagLayout}. + */ +public class CampaignOptionsGridBagConstraints extends GridBagConstraints { + + /** + * Constructs an instance of {@link GridBagConstraints} with default settings + * for the specified {@link JPanel}. + *

    + * The {@code JPanel} will automatically be set to use a {@link GridBagLayout}. + * Default constraints include: + *

    + *
      + *
    • {@code anchor} set to {@link GridBagConstraints#NORTHWEST}
    • + *
    • {@code fill} set to {@link GridBagConstraints#BOTH}
    • + *
    • {@code insets} set to {@code new Insets(5, 5, 5, 5)}
    • + *
    + * + * @param panel the {@link JPanel} for which the {@link GridBagConstraints} is created + */ + public CampaignOptionsGridBagConstraints(JPanel panel) { + this(panel, null, null); + } + + /** + * Constructs an instance of {@link GridBagConstraints} with configurable + * anchor and fill properties for the specified {@link JPanel}. + *

    + * The {@code JPanel} will automatically be set to use a {@link GridBagLayout}. + * If {@code anchor} or {@code fill} values are not provided, the following default + *

    + * values are used: + *
      + *
    • Default {@code anchor}: {@link GridBagConstraints#NORTHWEST}
    • + *
    • Default {@code fill}: {@link GridBagConstraints#BOTH}
    • + *
    + * Default {@code insets} are set to {@code new Insets(5, 5, 5, 5)}. + * + * @param panel the {@link JPanel} for which the {@link GridBagConstraints} is created + * @param anchor the anchor setting for the {@link GridBagConstraints}, or {@code null} to use + * the default value {@link GridBagConstraints#NORTHWEST} + * @param fill the fill setting for the {@link GridBagConstraints}, or {@code null} to use the + * default value {@link GridBagConstraints#BOTH} + */ + public CampaignOptionsGridBagConstraints(JPanel panel, @Nullable Integer anchor, @Nullable Integer fill) { + super(); + + // Set up GridBagLayout on the panel + panel.setLayout(new GridBagLayout()); + + // Assign anchor and fill, using defaults if not provided + this.anchor = Objects.requireNonNullElse(anchor, GridBagConstraints.NORTHWEST); + this.fill = Objects.requireNonNullElse(fill, GridBagConstraints.BOTH); + + // Set default insets + this.insets = new Insets(5, 5, 5, 5); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsHeaderPanel.java b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsHeaderPanel.java new file mode 100644 index 00000000000..0b283ffc522 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsHeaderPanel.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions.components; + +import megamek.client.ui.swing.util.UIUtil; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; + +import static java.lang.Math.round; +import static megamek.client.ui.swing.util.FlatLafStyleBuilder.setFontScaling; + +/** + * A specialized {@link JPanel} designed for headers in the campaign options dialog. + *

    + * This panel includes a header label, an image, and optionally a body label for additional text. + * The text for the labels is dynamically loaded from a resource bundle based on the provided name. + * The image is scaled to fit the layout, and font scaling is applied to the labels to ensure + * consistent appearance. + */ +public class CampaignOptionsHeaderPanel extends JPanel { + /** + * The path to the resource bundle containing label text for the header panel. + */ + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + + /** + * The {@link ResourceBundle} used to load localized strings for the header and body labels. + */ + static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + /** + * Constructs a new instance of {@link CampaignOptionsHeaderPanel} with a header label + * and an image. + *

    + * The panel is named {@code "pnl" + name + "HeaderPanel"}. The header label's text is + * fetched from a resource bundle using {@code "lbl" + name + ".text"}. + * The image is loaded from the specified file path and scaled appropriately. + * + * @param name the name of the header panel, used to construct the resource bundle keys + * @param imageAddress the file path of the image to be displayed in the panel + */ + public CampaignOptionsHeaderPanel(String name, String imageAddress) { + this(name, imageAddress, false); + } + + /** + * Constructs a new instance of {@link CampaignOptionsHeaderPanel} with a header label, + * an image, and optionally a body label for descriptive text. + *

    + * The panel is named {@code "pnl" + name + "HeaderPanel"}. The header label's text is + * fetched from a resource bundle using {@code "lbl" + name + ".text"}. If {@code includeBodyText} + * is {@code true}, an additional body label is created using the resource key + * {@code "lbl" + name + "Body.text"}. The image is loaded from the specified file path + * and scaled appropriately. + * + * @param name the name of the header panel, used to construct the resource bundle keys + * @param imageAddress the file path of the image to be displayed in the panel + * @param includeBodyText if {@code true}, includes a body label below the image + */ + public CampaignOptionsHeaderPanel(String name, String imageAddress, boolean includeBodyText) { + // Load and scale the image using the provided file path + ImageIcon imageIcon = new ImageIcon(imageAddress); + int width = (int) UIUtil.scaleForGUI(round(imageIcon.getIconWidth() * 0.75)); + int height = (int) UIUtil.scaleForGUI(round(imageIcon.getIconHeight() * 0.75)); + Image image = imageIcon.getImage(); + Image scaledImage = image.getScaledInstance(width, height, Image.SCALE_SMOOTH); + imageIcon = new ImageIcon(scaledImage); + + // Create a JLabel to display the image in the panel + JLabel lblImage = new JLabel(imageIcon); + + // Create the header label with text from the resource bundle + final JLabel lblHeader = new JLabel(resources.getString("lbl" + name + ".text"), SwingConstants.CENTER); + lblHeader.setName("lbl" + name); + setFontScaling(lblHeader, true, 2); + + // Optionally create a body label with additional text, if includeBodyText is true + JLabel lblBody = new JLabel(); + if (includeBodyText) { + lblBody = new JLabel(String.format( + "

    %s
    ", + UIUtil.scaleForGUI(750), + resources.getString("lbl" + name + "Body.text")), SwingConstants.CENTER); + lblBody.setName("lbl" + name + "Body"); + setFontScaling(lblBody, false, 1); + } + + // Initialize the panel's layout using a GridBagLayout + new CampaignOptionsStandardPanel("pnl" + name + "HeaderPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(this); + + // Configure and add components to the panel + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + this.add(lblHeader, layout); + + layout.gridy++; + this.add(lblImage, layout); + + if (includeBodyText) { + layout.gridy++; + layout.gridwidth = 1; + this.add(lblBody, layout); + } + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsLabel.java b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsLabel.java new file mode 100644 index 00000000000..2c83d97b174 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsLabel.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions.components; + +import megamek.common.annotations.Nullable; + +import javax.swing.*; +import java.util.ResourceBundle; + +import static megamek.client.ui.WrapLayout.wordWrap; +import static megamek.client.ui.swing.util.FlatLafStyleBuilder.setFontScaling; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.processWrapSize; + +/** + * A specialized {@link JLabel} component designed for use in campaign options dialogs. + *

    + * The label's text and tooltip are dynamically loaded from a resource bundle + * based on the provided name. Tooltip text can also be configured for word wrapping, + * and an option exists to create the label without a tooltip. + */ +public class CampaignOptionsLabel extends JLabel { + + /** + * The path to the resource bundle containing text and tooltip data for the label. + */ + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + + /** + * The {@link ResourceBundle} used to retrieve text and tooltips for the label. + */ + static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + /** + * Constructs a {@link CampaignOptionsLabel} with the specified name. + *

    + * The label's text is retrieved using the resource key {@code "lbl" + name + ".text"}. + * The tooltip is retrieved using the resource key {@code "lbl" + name + ".tooltip"}. + * If the keys do not exist in the resource bundle, an exception will be thrown. + *

    + * A tooltip is included by default, and a default line wrapping size is used for the tooltip text. + * + * @param name the base name of the label, used to construct resource bundle keys + */ + public CampaignOptionsLabel(String name) { + this(name, null, false); + } + + /** + * Constructs a {@link CampaignOptionsLabel} with the specified name, + * optional custom tooltip wrap size, and optional tooltip exclusion. + *

    + * The label's text is retrieved using the resource key {@code "lbl" + name + ".text"}. + * If {@code noTooltip} is {@code false}, the tooltip is retrieved using the resource key + * {@code "lbl" + name + ".tooltip"} and word-wrapped based on the provided {@code customWrapSize}, + * defaulting to 100 characters if {@code customWrapSize} is {@code null}. + * If {@code noTooltip} is {@code true}, no tooltip is set for the label. + *

    + * If the resource keys do not exist in the resource bundle, an exception will be thrown. + * + * @param name the base name of the label, used to construct resource bundle keys + * @param customWrapSize the maximum number of characters per line in the tooltip, or {@code null} + * to use the default value of 100 + * @param noTooltip if {@code true}, the label is created without a tooltip + */ + public CampaignOptionsLabel(String name, @Nullable Integer customWrapSize, boolean noTooltip) { + // Set the label's text using the resource bundle with HTML formatting for better rendering + super(String.format("%s", + resources.getString("lbl" + name + ".text"))); + + // Configure the tooltip if not excluded + if (!noTooltip) { + setToolTipText(wordWrap(resources.getString("lbl" + name + ".tooltip"), + processWrapSize(customWrapSize))); + } + + // Set the internal name of the label + setName("lbl" + name); + + // Apply font scaling + setFontScaling(this, false, 1); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsSpinner.java b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsSpinner.java new file mode 100644 index 00000000000..c2b952749b7 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsSpinner.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions.components; + +import megamek.common.annotations.Nullable; + +import javax.swing.*; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import static megamek.client.ui.WrapLayout.wordWrap; +import static megamek.client.ui.swing.util.FlatLafStyleBuilder.setFontScaling; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.processWrapSize; + +/** + * A specialized {@link JSpinner} component for use in campaign options dialogs. + *

    + * This spinner is highly configurable, supporting both integer and double values, + * customizable tooltips (with word wrapping), and dynamic resource loading for names + * and tooltips. The spinner also applies consistent styling for consistent + * appearance in the UI. + */ +public class CampaignOptionsSpinner extends JSpinner { + + /** + * The path to the resource bundle containing text and tooltip information for the spinner. + */ + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + + /** + * The {@link ResourceBundle} used to load localized tooltip text for the spinner. + */ + static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + /** + * Creates a {@link CampaignOptionsSpinner} with fully configurable numeric values and tooltip settings. + *

    + * The spinner uses a {@link SpinnerNumberModel} that supports both integer and double values. + * The tooltip is fetched from a resource bundle using the key {@code "lbl" + name + ".tooltip"} and can be + * wrapped to a specified line length using {@code customWrapSize}. If {@code noTooltip} is set to {@code true}, + * the spinner will not have a tooltip. + * + * @param name the name of the spinner, used to construct its resource bundle keys and internal name + * @param customWrapSize the maximum number of characters per line for the tooltip (or 100 by default if {@code null}) + * @param defaultValue the default value of the spinner (integer or double) + * @param minimum the minimum value for the spinner (integer or double) + * @param maximum the maximum value for the spinner (integer or double) + * @param stepSize the step value for incrementing or decrementing the spinner (integer or double) + * @param noTooltip if {@code true}, the spinner will not have a tooltip + */ + public CampaignOptionsSpinner(String name, @Nullable Integer customWrapSize, + Number defaultValue, Number minimum, + Number maximum, Number stepSize, boolean noTooltip) { + super(createSpinnerModel(defaultValue, minimum, maximum, stepSize)); + + if (!noTooltip) { + setToolTipText(wordWrap(getTooltipText(name), processWrapSize(customWrapSize))); + } + + configureSpinner(name); + } + + /** + * Creates a {@link CampaignOptionsSpinner} for integer values with default tooltip settings. + *

    + * The spinner will be initialized with an integer-based {@link SpinnerNumberModel} using the + * provided minimum, maximum, step size, and default value. + * + * @param name the name of the spinner, used to construct its resource bundle keys and internal name + * @param defaultValue the default value of the spinner (integer) + * @param minimum the minimum value for the spinner (integer) + * @param maximum the maximum value for the spinner (integer) + * @param stepSize the step value for incrementing or decrementing the spinner (integer) + */ + public CampaignOptionsSpinner(String name, int defaultValue, int minimum, + int maximum, int stepSize) { + this(name, null, defaultValue, minimum, maximum, stepSize, false); + } + + /** + * Creates a {@link CampaignOptionsSpinner} for double values with default tooltip settings. + *

    + * The spinner will be initialized with a double-based {@link SpinnerNumberModel} using the + * provided minimum, maximum, step size, and default value. + * + * @param name the name of the spinner, used to construct its resource bundle keys and internal name + * @param defaultValue the default value of the spinner (double) + * @param minimum the minimum value for the spinner (double) + * @param maximum the maximum value for the spinner (double) + * @param stepSize the step value for incrementing or decrementing the spinner (double) + */ + public CampaignOptionsSpinner(String name, double defaultValue, double minimum, + double maximum, double stepSize) { + this(name, null, defaultValue, minimum, maximum, stepSize, false); + } + + /** + * Creates a {@link SpinnerNumberModel} for the spinner by detecting whether + * integer or double-based values should be used. + * + * @param defaultValue the default value (integer or double) + * @param minimum the minimum value (integer or double) + * @param maximum the maximum value (integer or double) + * @param stepSize the step size (integer or double) + * @return a configured {@link SpinnerNumberModel} for the spinner + */ + private static SpinnerNumberModel createSpinnerModel(Number defaultValue, Number minimum, + Number maximum, Number stepSize) { + if (defaultValue instanceof Double || minimum instanceof Double || + maximum instanceof Double || stepSize instanceof Double) { + // Use a double-based model for floating-point precision + return new SpinnerNumberModel( + defaultValue.doubleValue(), minimum.doubleValue(), maximum.doubleValue(), stepSize.doubleValue() + ); + } else { + // Use an integer-based model for whole numbers + return new SpinnerNumberModel( + defaultValue.intValue(), minimum.intValue(), maximum.intValue(), stepSize.intValue() + ); + } + } + + /** + * Configures the spinner's common settings, including name, font scaling, and text alignment. + * + * @param name the base name of the spinner, used to set its internal name + */ + private void configureSpinner(String name) { + setName("spn" + name); + setFontScaling(this, false, 1); + + // Align text in the spinner editor to the left + DefaultEditor editor = (DefaultEditor) this.getEditor(); + editor.getTextField().setHorizontalAlignment(JTextField.LEFT); + } + + /** + * Fetches the tooltip text for the spinner from the resource bundle. + * If the resource key is not found, an empty string is returned as the tooltip. + * + * @param name the name of the spinner, used to construct the resource bundle key for the tooltip + * @return the tooltip text for the spinner, or an empty string if the key does not exist + */ + private String getTooltipText(String name) { + try { + return resources.getString("lbl" + name + ".tooltip"); + } catch (MissingResourceException e) { + // Return an empty string if the resource is missing + return ""; + } + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsStandardPanel.java b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsStandardPanel.java new file mode 100644 index 00000000000..7948a87d0dc --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsStandardPanel.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions.components; + +import megamek.client.ui.swing.util.UIUtil; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; + +/** + * A specialized {@link JPanel} tailored for use in campaign options dialogs. + *

    + * This panel offers configurable borders (titled or untitled) and applies a standardized + * layout and styling for consistency across the UI. The panel's name can also be dynamically + * assigned based on the provided {@code name} parameter. + */ +public class CampaignOptionsStandardPanel extends JPanel { + + /** + * The path to the resource bundle containing localized strings for border titles. + */ + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + + /** + * The {@link ResourceBundle} used to fetch localized strings for titles and labels. + */ + static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + /** + * Constructs a {@link CampaignOptionsStandardPanel} without a border. + *

    + * The name of the panel is set to {@code "pnl" + name}. The layout should + * be configured using {@code createGroupLayout} and assigned to the panel using {@code setLayout}. + * + * @param name the name of the panel, used to set its internal name + */ + public CampaignOptionsStandardPanel(String name) { + this(name, false, ""); + } + + /** + * Constructs a {@link CampaignOptionsStandardPanel} with an optional untitled border. + *

    + * If {@code includeBorder} is {@code true}, an etched border is applied to the panel. The name of + * the panel is set to {@code "pnl" + name}. The layout should be configured using + * {@code createGroupLayout} and assigned to the panel using {@code setLayout}. + * + * @param name the name of the panel, used to set its internal name + * @param includeBorder {@code true} if the panel should include an untitled border + */ + public CampaignOptionsStandardPanel(String name, boolean includeBorder) { + this(name, includeBorder, ""); + } + + /** + * Constructs a {@link CampaignOptionsStandardPanel} with an optional titled border. + *

    + * If {@code includeBorder} is {@code true}, a border is applied to the panel. If {@code borderTitle} + * is provided, it is used to fetch the localized string for the border's title from the resource bundle. + * The name of the panel is set to {@code "pnl" + name}. The layout should be configured using + * {@code createGroupLayout} and assigned to the panel using {@code setLayout}. + * + * @param name the name of the panel, used to set its internal name + * @param includeBorder {@code true} if the panel should include a border + * @param borderTitle the resource bundle key for the border's title; an empty string indicates no title + */ + public CampaignOptionsStandardPanel(String name, boolean includeBorder, String borderTitle) { + borderTitle = borderTitle.isBlank() ? "" : resources.getString("lbl" + borderTitle + ".text"); + + // Set a standardized panel behavior and preferred size scaling + new JPanel() { + @Override + public Dimension getPreferredSize() { + Dimension standardSize = super.getPreferredSize(); + return UIUtil.scaleForGUI(Math.max(standardSize.width, 500), standardSize.height); + } + }; + + if (includeBorder) { + if (borderTitle.isBlank()) { + // Add an untitled etched border + setBorder(BorderFactory.createEtchedBorder()); + } else { + // Add a titled border with localized title + setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), + String.format("%s", borderTitle))); + } + } + + // Set the name of the panel dynamically + setName("pnl" + name); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsTextField.java b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsTextField.java new file mode 100644 index 00000000000..ca4683b9e0d --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/components/CampaignOptionsTextField.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 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.gui.campaignOptions.components; + +import megamek.common.annotations.Nullable; + +import javax.swing.*; +import java.util.ResourceBundle; + +import static megamek.client.ui.WrapLayout.wordWrap; +import static megamek.client.ui.swing.util.FlatLafStyleBuilder.setFontScaling; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.processWrapSize; + +/** + * A specialized {@link JTextField} component designed for use in campaign options dialogs. + *

    + * This text field fetches its tooltip text dynamically from a resource bundle + * based on the provided name. It also supports a customizable tooltip wrap size while + * maintaining consistent UI scaling. + */ +public class CampaignOptionsTextField extends JTextField { + + /** + * The path to the resource bundle that contains tooltip text for the text field. + */ + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + + /** + * The {@link ResourceBundle} used to fetch localized tooltip and other properties. + */ + static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + /** + * Constructs a {@link CampaignOptionsTextField} with a default tooltip wrap size. + *

    + * The name of the text field is set to {@code "lbl" + name}, and its tooltip text is fetched + * using the key {@code "lbl" + name + ".tooltip"} from the resource bundle. Tooltips are + * word-wrapped to a default width of 100 characters. + * + * @param name the base name used to generate the text field's name and tooltip text. + */ + public CampaignOptionsTextField(String name) { + this(name, null); + } + + /** + * Constructs a {@link CampaignOptionsTextField} with a customizable tooltip wrap size. + *

    + * The name of the text field is set to {@code "lbl" + name}, and its tooltip text is fetched + * using the key {@code "lbl" + name + ".tooltip"} from the resource bundle. Tooltips are + * word-wrapped to the specified width in {@code customWrapSize}. + * + * @param name the base name used to generate the text field's name and tooltip text. + * @param customWrapSize the maximum number of characters (including spaces) per line in the tooltip text. + * If {@code null}, a default wrap size of 100 characters is used. + */ + public CampaignOptionsTextField(String name, @Nullable Integer customWrapSize) { + super(); + + // Set the tooltip text with word wrapping + setToolTipText(wordWrap( + resources.getString("lbl" + name + ".tooltip"), + processWrapSize(customWrapSize) + )); + + // Set the component name + setName("lbl" + name); + + // Apply UI font scaling + setFontScaling(this, false, 1); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/AbilitiesTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/AbilitiesTab.java new file mode 100644 index 00000000000..d1fa0173ecd --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/AbilitiesTab.java @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; +import megamek.common.options.IOption; +import megamek.common.options.IOptionGroup; +import mekhq.CampaignPreset; +import mekhq.campaign.personnel.PersonnelOptions; +import mekhq.campaign.personnel.SkillPerquisite; +import mekhq.campaign.personnel.SpecialAbility; +import mekhq.gui.campaignOptions.CampaignOptionsAbilityInfo; +import mekhq.gui.campaignOptions.CampaignOptionsAbilityInfo.AbilityCategory; +import mekhq.gui.campaignOptions.components.CampaignOptionsButton; +import mekhq.gui.campaignOptions.components.CampaignOptionsGridBagConstraints; +import mekhq.gui.campaignOptions.components.CampaignOptionsHeaderPanel; +import mekhq.gui.campaignOptions.components.CampaignOptionsStandardPanel; +import mekhq.gui.dialog.EditSpecialAbilityDialog; + +import javax.swing.*; +import java.awt.*; +import java.util.*; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static mekhq.gui.campaignOptions.CampaignOptionsAbilityInfo.AbilityCategory.COMBAT_ABILITY; +import static mekhq.gui.campaignOptions.CampaignOptionsAbilityInfo.AbilityCategory.MANEUVERING_ABILITY; +import static mekhq.gui.campaignOptions.CampaignOptionsAbilityInfo.AbilityCategory.UTILITY_ABILITY; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * The {@code AbilitiesTab} class represents a GUI tab for configuring and managing + * special abilities in a campaign. This class handles the initialization, categorization, + * and display of abilities, providing functionality for enabling, disabling, and customizing + * abilities within the combat, maneuvering, and utility categories. + *

    + * This tab is used as part of the MekHQ campaign options UI for managing personnel-related abilities. + */ +public class AbilitiesTab { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + private ArrayList level3Abilities; + private Map allAbilityInfo; + private JPanel combatTab; + private JPanel maneuveringTab; + private JPanel utilityTab; + + /** + * Constructor for the {@code AbilitiesTab} class. + * Initializes the tab by creating containers for ability categories and populating + * them with related ability information. + */ + public AbilitiesTab() { + initialize(); + } + + /** + * Initializes the internal data structures and UI components for managing abilities. + * Prepares the containers for each ability category (combat, maneuvering, utility) + * and populates the {@code allAbilityInfo} map by processing an initial set of abilities. + */ + private void initialize() { + allAbilityInfo = new HashMap<>(); + level3Abilities = new ArrayList<>(); + combatTab = new JPanel(); + maneuveringTab = new JPanel(); + utilityTab = new JPanel(); + buildAllAbilityInfo(SpecialAbility.getSpecialAbilities()); + } + + /** + * Builds and initializes the {@code allAbilityInfo} map, which holds information about + * abilities categorized into combat, maneuvering, and utility abilities. Ensures missing + * abilities are also included and updates the display. + * + * @param abilities A map of active special abilities keyed by name. + */ + public void buildAllAbilityInfo(Map abilities) { + // Remove old data + allAbilityInfo.clear(); + level3Abilities.clear(); + + // Build list of Level 3 abilities + PersonnelOptions personnelOptions = new PersonnelOptions(); + for (final Enumeration i = personnelOptions.getGroups(); i.hasMoreElements();) { + IOptionGroup group = i.nextElement(); + + if (!group.getKey().equalsIgnoreCase(PersonnelOptions.LVL3_ADVANTAGES)) { + continue; + } + + for (final Enumeration j = group.getOptions(); j.hasMoreElements();) { + IOption option = j.nextElement(); + level3Abilities.add(option.getName()); + } + } + + // Build abilities + buildAbilityInfo(abilities, true); + + Map allSpecialAbilities = SpecialAbility.getDefaultSpecialAbilities(); + Map missingAbilities = new HashMap<>(); + + for (SpecialAbility ability : allSpecialAbilities.values()) { + if (!allAbilityInfo.containsKey(ability.getName())) { + missingAbilities.put(ability.getName(), ability); + } + } + + if (!missingAbilities.isEmpty()) { + buildAbilityInfo(missingAbilities, false); + } + + // Clear and update tabs in-place + refreshAll(); + } + + /** + * Refreshes and updates all tabs related to abilities by clearing their contents + * and reloading the data. + */ + private void refreshAll() { + refreshTabContents(combatTab, AbilityCategory.COMBAT_ABILITY); + refreshTabContents(maneuveringTab, AbilityCategory.MANEUVERING_ABILITY); + refreshTabContents(utilityTab, AbilityCategory.UTILITY_ABILITY); + } + + /** + * Updates the contents of a specific ability category tab by rebuilding its layout + * and content based on the category. + * + * @param tab The {@code JPanel} representing the tab to be refreshed. + * @param category The ability category associated with the tab. + */ + private void refreshTabContents(JPanel tab, AbilityCategory category) { + tab.removeAll(); + JPanel newContents = createAbilitiesTab(category); + + // Add new content to the same panel + tab.setLayout(new BorderLayout()); + tab.add(newContents, BorderLayout.CENTER); + + tab.revalidate(); + tab.repaint(); + } + + /** + * Populates the {@code allAbilityInfo} map with special ability information and + * categorizes abilities based on their type (combat, maneuvering, utility). + * + * @param abilities A map of abilities to be processed. + * @param isEnabled {@code true} if these abilities should start as enabled; + * otherwise, {@code false}. + */ + private void buildAbilityInfo(Map abilities, boolean isEnabled) { + for (Entry entry : abilities.entrySet()) { + SpecialAbility clonedAbility = entry.getValue().clone(); + String abilityName = clonedAbility.getName(); + AbilityCategory category = getCategory(clonedAbility); + + if (!level3Abilities.contains(abilityName)) { + continue; + } + + // Mark the ability as active + allAbilityInfo.put(abilityName, new CampaignOptionsAbilityInfo(abilityName, + clonedAbility, isEnabled, category)); + } + } + + /** + * Determines the {@code AbilityCategory} for the given special ability based on + * skill prerequisites such as "Gunnery", "Piloting", etc. + * + * @param ability The {@code SpecialAbility} for which the category will be identified. + * @return The {@code AbilityCategory} for the specified ability. + */ + private AbilityCategory getCategory(SpecialAbility ability) { + for (SkillPerquisite skillPerquisite : ability.getPrereqSkills()) { + // Is the ability classified as a Combat Ability? + boolean isCombatAbility = Stream.of("Gunnery", "Artillery", "Small Arms") + .anyMatch(word -> Pattern.compile("\\b" + word) + .matcher(skillPerquisite.toString()) + .find()); + + if (isCombatAbility) { + return COMBAT_ABILITY; + } + + // Is the ability classified as a Maneuvering Ability? + boolean isManeuveringAbility = Stream.of("Piloting", "Anti-Mek") + .anyMatch(word -> Pattern.compile("\\b" + word) + .matcher(skillPerquisite.toString()) + .find()); + + if (isManeuveringAbility) { + return MANEUVERING_ABILITY; + } + } + + // If it isn't a Combat or Maneuvering ability, it's a utility ability + return UTILITY_ABILITY; + } + + /** + * Creates a new abilities configuration tab for a specific category. + * This includes adding controls, headers, and listed abilities. + * + * @param abilityCategory The {@code AbilityCategory} to generate a tab for. + * @return A {@code JPanel} representing the generated abilities tab. + */ + public JPanel createAbilitiesTab(AbilityCategory abilityCategory) { + // Header + JPanel headerPanel = switch (abilityCategory) { + case COMBAT_ABILITY -> + new CampaignOptionsHeaderPanel("CombatAbilitiesTab", + getImageDirectory() + "logo_aurigan_coalition.png"); + case MANEUVERING_ABILITY -> + new CampaignOptionsHeaderPanel("ManeuveringAbilitiesTab", + getImageDirectory() + "logo_clan_hells_horses.png"); + case UTILITY_ABILITY -> + new CampaignOptionsHeaderPanel("UtilityAbilitiesTab", + getImageDirectory() + "logo_circinus_federation.png"); + }; + + // Contents + JButton btnEnableAll = new CampaignOptionsButton("AddAll"); + btnEnableAll.addActionListener(e -> { + for (CampaignOptionsAbilityInfo abilityInfo : allAbilityInfo.values()) { + abilityInfo.setEnabled(true); + } + + refreshAll(); + }); + + JButton btnRemoveAll = new CampaignOptionsButton("RemoveAll"); + btnRemoveAll.addActionListener(e -> { + for (CampaignOptionsAbilityInfo abilityInfo : allAbilityInfo.values()) { + abilityInfo.setEnabled(false); + } + + refreshAll(); + }); + + // Layout the Panels + final JPanel panel = new CampaignOptionsStandardPanel("AbilitiesGeneralTab", true); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy++; + panel.add(btnEnableAll, layout); + layout.gridx++; + panel.add(btnRemoveAll, layout); + + int abilityCounter = 0; + + // Retrieve keySet and sort alphabetically + ArrayList sortedAbilityNames = new ArrayList<>(allAbilityInfo.keySet()); + Collections.sort(sortedAbilityNames); + + for (String abilityName : sortedAbilityNames) { + CampaignOptionsAbilityInfo abilityInfo = allAbilityInfo.get(abilityName); + + if (abilityInfo.getCategory() == abilityCategory) { + JPanel abilityPanel = createSPAPanel(abilityInfo); + + layout.gridx = abilityCounter % 3; + layout.gridy = 2 + (abilityCounter / 3); + abilityCounter++; + + layout.gridwidth = 1; + panel.add(abilityPanel, layout); + } + } + + // Create Parent Panel and return + JPanel parentPanel = createParentPanel(panel, "AbilitiesTab" + COMBAT_ABILITY.name()); + + return switch (abilityCategory) { + case COMBAT_ABILITY -> { + combatTab = parentPanel; + yield combatTab; + } + case MANEUVERING_ABILITY -> { + maneuveringTab = parentPanel; + yield maneuveringTab; + } + case UTILITY_ABILITY -> { + utilityTab = parentPanel; + yield utilityTab; + } + }; + } + + /** + * Creates a panel for rendering a single ability within the tab, enabling users + * to customize or enable/disable the ability. + * + * @param abilityInfo The {@code CampaignOptionsAbilityInfo} containing details + * about a specific ability. + * @return A {@code JPanel} containing the UI elements related to the ability. + */ + private JPanel createSPAPanel(CampaignOptionsAbilityInfo abilityInfo) { + SpecialAbility ability = abilityInfo.getAbility(); + + // Initialization + final JPanel panel = new AbilitiesTabStandardPanel(ability); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel, + GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL); + + // Contents + JCheckBox chkAbility = new JCheckBox(resources.getString("abilityEnable.text")); + chkAbility.setSelected(abilityInfo.isEnabled()); + chkAbility.addActionListener(e -> abilityInfo.setEnabled(chkAbility.isSelected())); + + JLabel lblCost = new JLabel(String.format(resources.getString("abilityCost.text"), + ability.getCost())); + + JLabel lblDescription = new JLabel(); + lblDescription.setText(String.format("

    %s
    ", + UIUtil.scaleForGUI(400), ability.getDescription())); + + JLabel lblPrerequisites = createAbilityLabel("prerequisites.text", ability.getAllPrereqDesc()); + JLabel lblIncompatible = createAbilityLabel("incompatible.text", ability.getInvalidDesc()); + JLabel lblRemoves = createAbilityLabel("removes.text", ability.getRemovedDesc()); + + JButton btnCustomizeAbility = new CampaignOptionsButton("CustomizeAbility", null); + btnCustomizeAbility.addActionListener(e -> { + if (editSPA(ability)) { + // This will run on the SWT thread + SwingUtilities.invokeLater(() -> { + // Update components with new values + lblCost.setText(String.format(resources.getString("abilityCost.text"), + ability.getCost())); + + String prerequisitesDescription = resources.getString("prerequisites.text") + + ability.getAllPrereqDesc(); + lblPrerequisites.setText(buildAbilityDescription(prerequisitesDescription)); + + String incompatibleDescription = resources.getString("incompatible.text") + + ability.getInvalidDesc(); + lblIncompatible.setText(buildAbilityDescription(incompatibleDescription)); + + String removesDescription = resources.getString("removes.text") + + ability.getRemovedDesc(); + lblRemoves.setText(buildAbilityDescription(removesDescription)); + }); + } + }); + + // Layout + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(chkAbility, layout); + layout.gridx++; + panel.add(lblCost, layout); + + layout.gridwidth = 3; + layout.gridx = 0; + layout.gridy++; + panel.add(lblDescription, layout); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy++; + panel.add(lblPrerequisites, layout); + layout.gridx++; + panel.add(lblIncompatible, layout); + layout.gridx++; + panel.add(lblRemoves, layout); + + layout.gridwidth = 3; + layout.gridx = 0; + layout.gridy++; + panel.add(btnCustomizeAbility, layout); + + return panel; + } + + /** + * Opens a dialog to edit the details of the specified special ability. + * + * @param ability The {@code SpecialAbility} instance to be edited. + * @return {@code true} if the user confirmed the changes; {@code false} if the operation + * was canceled. + */ + private boolean editSPA(SpecialAbility ability) { + EditSpecialAbilityDialog dialog = new EditSpecialAbilityDialog(null, ability, + Map.of(ability.getName(), ability.clone())); + dialog.setVisible(true); + + return !dialog.wasCancelled(); + } + + /** + * A custom {@code JPanel} implementation for displaying abilities configured in the tab. + * Displays the ability's name in a bordered, titled panel and scales the panel size appropriately + * for the UI. + */ + static class AbilitiesTabStandardPanel extends JPanel { + /** + * Constructs a panel representing an individual ability with a styled header based + * on the name of the given {@code SpecialAbility}. + * + * @param ability The {@code SpecialAbility} used to configure the layout and title of this panel. + */ + public AbilitiesTabStandardPanel(SpecialAbility ability) { + String name = ability.getDisplayName(); + + new JPanel() { + @Override + public Dimension getPreferredSize() { + Dimension standardSize = super.getPreferredSize(); + return UIUtil.scaleForGUI((Math.max(standardSize.width, 500)), standardSize.height); + } + }; + + setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), + String.format("%s", name))); + setName("pnl" + name); + } + } + + /** + * Creates a label with a description for a specific attribute of the ability. For example, + * prerequisites, incompatible abilities, or removed abilities. + * + * @param resourceKey The key used to retrieve the label text from resources. + * @param descriptionFromAbility The description related to the ability attribute. + * @return A {@code JLabel} with the generated description. + */ + private JLabel createAbilityLabel(String resourceKey, String descriptionFromAbility) { + String description = resources.getString(resourceKey) + descriptionFromAbility; + return new JLabel(buildAbilityDescription(description)); + } + + /** + * Builds the HTML-formatted description for a specific ability or attribute for display in the UI. + * + * @param description The plain text description to be formatted. + * @return A string containing the HTML-formatted description. + */ + private static String buildAbilityDescription(String description) { + return ("" + description + "") + .replaceAll("\\{", "") + .replaceAll("}", ""); + } + + /** + * Applies the current campaign options to a specified {@code CampaignPreset}. + * Enabled abilities are added to the preset or globally updated within the campaign. + * + * @param preset The {@code CampaignPreset} to apply abilities to, or {@code null} + * for directly setting them in the campaign. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignPreset preset) { + Map enabledAbilities = new HashMap<>(); + + for (CampaignOptionsAbilityInfo abilityInfo : allAbilityInfo.values()) { + if (abilityInfo.isEnabled()) { + enabledAbilities.put(abilityInfo.getAbility().getName(), abilityInfo.getAbility()); + } + } + + if (preset != null) { + preset.getSpecialAbilities().clear(); + preset.getSpecialAbilities().putAll(enabledAbilities); + } else { + SpecialAbility.replaceSpecialAbilities(enabledAbilities); + } + } +} + diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java new file mode 100644 index 00000000000..ecdefac3ba1 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java @@ -0,0 +1,1027 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.common.annotations.Nullable; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.RandomSkillPreferences; +import mekhq.campaign.personnel.SkillType; +import mekhq.campaign.personnel.enums.Phenotype; +import mekhq.gui.campaignOptions.components.*; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * The {@code AdvancementTab} class is responsible for rendering and managing two primary tabs + * in the campaign options interface: the Experience Awards (XP Awards) tab and the Skill + * Randomization tab. These tabs allow for customization of experience point distribution, + * randomization preferences, and skill probabilities in the campaign. + * + *

    This class provides methods to initialize the UI components, load current settings + * from the campaign options, and update the options based on user input.

    + */ +public class AdvancementTab { + private final Campaign campaign; + private final CampaignOptions campaignOptions; + private final RandomSkillPreferences randomSkillPreferences; + + //start XP Awards Tab + private JLabel lblXpCostMultiplier; + private JSpinner spnXpCostMultiplier; + + private JPanel pnlTasks; + private JLabel lblTaskXP; + private JSpinner spnTaskXP; + private JLabel lblNTasksXP; + private JSpinner spnNTasksXP; + private JLabel lblSuccessXP; + private JSpinner spnSuccessXP; + private JLabel lblMistakeXP; + private JSpinner spnMistakeXP; + + private JPanel pnlScenarios; + private JLabel lblScenarioXP; + private JSpinner spnScenarioXP; + private JLabel lblKillXP; + private JSpinner spnKillXP; + private JLabel lblKills; + private JSpinner spnKills; + + private JPanel pnlMissions; + private JLabel lblVocationalXP; + private JSpinner spnVocationalXP; + private JLabel lblVocationalXPFrequency; + private JSpinner spnVocationalXPFrequency; + private JLabel lblVocationalXPTargetNumber; + private JSpinner spnVocationalXPTargetNumber; + private JLabel lblMissionXpFail; + private JSpinner spnMissionXpFail; + private JLabel lblMissionXpSuccess; + private JSpinner spnMissionXpSuccess; + private JLabel lblMissionXpOutstandingSuccess; + private JSpinner spnMissionXpOutstandingSuccess; + + private JPanel pnlAdministrators; + private JLabel lblContractNegotiationXP; + private JSpinner spnContractNegotiationXP; + private JLabel lblAdminWeeklyXP; + private JSpinner spnAdminWeeklyXP; + private JLabel lblAdminWeeklyXPPeriod; + private JSpinner spnAdminWeeklyXPPeriod; + //end XP Awards Tab + + //start Skill Randomization Tab + private JCheckBox chkExtraRandomness; + + private JPanel pnlPhenotype; + private JLabel[] phenotypeLabels; + private JSpinner[] phenotypeSpinners; + + private JPanel pnlRandomAbilities; + private JLabel lblAbilityGreen; + private JSpinner spnAbilityGreen; + private JLabel lblAbilityReg; + private JSpinner spnAbilityReg; + private JLabel lblAbilityVet; + private JSpinner spnAbilityVet; + private JLabel lblAbilityElite; + private JSpinner spnAbilityElite; + + private JPanel pnlSkillGroups; + + private JPanel pnlTactics; + private JLabel lblTacticsGreen; + private JSpinner spnTacticsGreen; + private JLabel lblTacticsReg; + private JSpinner spnTacticsReg; + private JLabel lblTacticsVet; + private JSpinner spnTacticsVet; + private JLabel lblTacticsElite; + private JSpinner spnTacticsElite; + + private JPanel pnlSmallArms; + private JLabel lblCombatSA; + private JSpinner spnCombatSA; + private JLabel lblSupportSA; + private JSpinner spnSupportSA; + + private JPanel pnlArtillery; + private JLabel lblArtyProb; + private JSpinner spnArtyProb; + private JLabel lblArtyBonus; + private JSpinner spnArtyBonus; + + private JPanel pnlSecondarySkills; + private JLabel lblAntiMekSkill; + private JSpinner spnAntiMekSkill; + private JLabel lblSecondProb; + private JSpinner spnSecondProb; + private JLabel lblSecondBonus; + private JSpinner spnSecondBonus; + //end Skill Randomization Tab + + /** + * Constructs an {@code AdvancementTab} object for rendering and managing campaign advancement + * configurations. + * + * @param campaign The {@code Campaign} instance from which the campaign options and random + * skill preferences are retrieved. + */ + public AdvancementTab(Campaign campaign) { + this.campaign = campaign; + this.randomSkillPreferences = campaign.getRandomSkillPreferences(); + this.campaignOptions = campaign.getCampaignOptions(); + + initialize(); + loadValuesFromCampaignOptions(); + } + + /** + * Initializes the UI components for the XP Awards and Skill Randomization tabs. This includes + * setting up the labels, panels, and spinners for each of the categories within the respective tabs. + */ + private void initialize() { + initializeXPAwardsTab(); + initializeSkillRandomizationTab(); + } + + /** + * Initializes the XP Awards tab by setting up the UI components, such as labels, + * panels, and spinners, for various experience-related options within the campaign. + */ + private void initializeXPAwardsTab() { + lblXpCostMultiplier = new JLabel(); + spnXpCostMultiplier = new JSpinner(); + + pnlTasks = new JPanel(); + lblTaskXP = new JLabel(); + spnTaskXP = new JSpinner(); + lblNTasksXP = new JLabel(); + spnNTasksXP = new JSpinner(); + lblSuccessXP = new JLabel(); + spnSuccessXP = new JSpinner(); + lblMistakeXP = new JLabel(); + spnMistakeXP = new JSpinner(); + + pnlScenarios = new JPanel(); + lblScenarioXP = new JLabel(); + spnScenarioXP = new JSpinner(); + lblKillXP = new JLabel(); + spnKillXP = new JSpinner(); + lblKills = new JLabel(); + spnKills = new JSpinner(); + + pnlMissions = new JPanel(); + lblVocationalXP = new JLabel(); + spnVocationalXP = new JSpinner(); + lblVocationalXPFrequency = new JLabel(); + spnVocationalXPFrequency = new JSpinner(); + lblVocationalXPTargetNumber = new JLabel(); + spnVocationalXPTargetNumber = new JSpinner(); + lblMissionXpFail = new JLabel(); + spnMissionXpFail = new JSpinner(); + lblMissionXpSuccess = new JLabel(); + spnMissionXpSuccess = new JSpinner(); + lblMissionXpOutstandingSuccess = new JLabel(); + spnMissionXpOutstandingSuccess = new JSpinner(); + + pnlAdministrators = new JPanel(); + lblContractNegotiationXP = new JLabel(); + spnContractNegotiationXP = new JSpinner(); + lblAdminWeeklyXP = new JLabel(); + spnAdminWeeklyXP = new JSpinner(); + lblAdminWeeklyXPPeriod = new JLabel(); + spnAdminWeeklyXPPeriod = new JSpinner(); + } + + /** + * Creates and returns the Experience Awards (XP Awards) tab panel. This tab allows users to + * configure experience awards for tasks, scenarios, missions, and administrators, as well + * as set the overall XP cost multiplier. + * + * @return A {@code JPanel} containing the configuration options for XP Awards in the campaign. + */ + public JPanel xpAwardsTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("XpAwardsTab", + getImageDirectory() + "logo_clan_steel_viper.png"); + + // Contents + lblXpCostMultiplier = new CampaignOptionsLabel("XpCostMultiplier"); + spnXpCostMultiplier = new CampaignOptionsSpinner("XpCostMultiplier", + 1, 0, 5, 0.05); + + pnlTasks = createTasksPanel(); + pnlScenarios = createScenariosPanel(); + pnlAdministrators = createAdministratorsPanel(); + pnlMissions = createMissionsPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("XpAwardsTab", true); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblXpCostMultiplier, layout); + layout.gridx++; + panel.add(spnXpCostMultiplier, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 3; + panel.add(pnlTasks, layout); + layout.gridx += 3; + layout.gridwidth = 1; + panel.add(pnlMissions, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 3; + panel.add(pnlScenarios, layout); + layout.gridx += 3; + layout.gridwidth = 1; + panel.add(pnlAdministrators, layout); + + // Create Parent Panel and return + return createParentPanel(panel, "XpAwardsTab"); + } + + /** + * Creates and returns the Tasks panel, which allows users to configure settings + * for task-related experience awards, such as successful task completions or mistakes. + * + * @return A {@code JPanel} containing configuration options for task-related experience awards. + */ + private JPanel createTasksPanel() { + // Contents + lblTaskXP = new CampaignOptionsLabel("TaskXP"); + spnTaskXP = new CampaignOptionsSpinner("TaskXP", + 0, 0, 20, 1); + + lblNTasksXP = new CampaignOptionsLabel("NTasksXP"); + spnNTasksXP = new CampaignOptionsSpinner("NTasksXP", + 0, 0, 100, 1); + + lblSuccessXP = new CampaignOptionsLabel("SuccessXP"); + spnSuccessXP = new CampaignOptionsSpinner("SuccessXP", + 0, 0, 20, 1); + + lblMistakeXP = new CampaignOptionsLabel("MistakeXP"); + spnMistakeXP = new CampaignOptionsSpinner("MistakeXP", + 0, 0, 20, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("TasksPanel", true, + "TasksPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(spnTaskXP, layout); + layout.gridx++; + panel.add(lblTaskXP, layout); + layout.gridx++; + panel.add(spnNTasksXP, layout); + layout.gridx++; + panel.add(lblNTasksXP, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(spnSuccessXP, layout); + layout.gridx++; + layout.gridwidth = 2; + panel.add(lblSuccessXP, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(spnMistakeXP, layout); + layout.gridx++; + layout.gridwidth = 2; + panel.add(lblMistakeXP, layout); + + return panel; + } + + /** + * Creates and returns the Scenarios panel, which allows users to configure settings + * for experience awards related to scenarios, kills, and kill thresholds. + * + * @return A {@code JPanel} containing configuration options for scenario-related experience awards. + */ + private JPanel createScenariosPanel() { + // Contents + lblScenarioXP = new CampaignOptionsLabel("ScenarioXP"); + spnScenarioXP = new CampaignOptionsSpinner("ScenarioXP", + 0, 0, 20, 1); + lblKillXP = new CampaignOptionsLabel("KillXP"); + spnKillXP = new CampaignOptionsSpinner("KillXP", + 0, 0, 20, 1); + + lblKills = new CampaignOptionsLabel("Kills"); + spnKills = new CampaignOptionsSpinner("Kills", + 0, 0, 100, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("ScenariosPanel", true, + "ScenariosPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(spnScenarioXP, layout); + layout.gridx++; + layout.gridwidth = 2; + panel.add(lblScenarioXP, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(spnKillXP, layout); + layout.gridx++; + panel.add(lblKillXP, layout); + layout.gridx++; + panel.add(spnKills, layout); + layout.gridx++; + panel.add(lblKills, layout); + + return panel; + } + + /** + * Creates and returns the Missions panel, which allows users to configure settings + * related to mission performance and idle time experience bonuses in the campaign. + * + * @return A {@code JPanel} containing configuration options for mission-related experience settings. + */ + private JPanel createMissionsPanel() { + // Contents + lblVocationalXP = new CampaignOptionsLabel("VocationalXP"); + spnVocationalXP = new CampaignOptionsSpinner("VocationalXP", + 0, 0, 20, 1); + lblVocationalXPFrequency = new CampaignOptionsLabel("VocationalXPFrequency"); + spnVocationalXPFrequency = new CampaignOptionsSpinner("VocationalXPFrequency", + 0, 0, 12, 1); + lblVocationalXPTargetNumber = new CampaignOptionsLabel("VocationalXPTargetNumber"); + spnVocationalXPTargetNumber = new CampaignOptionsSpinner("VocationalXPTargetNumber", + 2, 0, 12, 1); + + lblMissionXpFail = new CampaignOptionsLabel("MissionXpFail"); + spnMissionXpFail = new CampaignOptionsSpinner("MissionXpFail", + 1, 0, 20, 1); + + lblMissionXpSuccess = new CampaignOptionsLabel("MissionXpSuccess"); + spnMissionXpSuccess = new CampaignOptionsSpinner("MissionXpSuccess", + 1, 0, 20, 1); + + lblMissionXpOutstandingSuccess = new CampaignOptionsLabel("MissionXpOutstandingSuccess"); + spnMissionXpOutstandingSuccess = new CampaignOptionsSpinner("MissionXpOutstandingSuccess", + 1, 0, 20, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("MissionsPanel", true, + "MissionsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(spnVocationalXP, layout); + layout.gridx++; + panel.add(lblVocationalXP, layout); + layout.gridx++; + panel.add(spnVocationalXPFrequency, layout); + layout.gridx++; + panel.add(lblVocationalXPFrequency, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(lblVocationalXPTargetNumber, layout); + layout.gridx += 2; + layout.gridwidth = 1; + panel.add(spnVocationalXPTargetNumber, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(spnMissionXpFail, layout); + layout.gridx++; + panel.add(lblMissionXpFail, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(spnMissionXpSuccess, layout); + layout.gridx++; + panel.add(lblMissionXpSuccess, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(spnMissionXpOutstandingSuccess, layout); + layout.gridx++; + layout.gridwidth = 2; + panel.add(lblMissionXpOutstandingSuccess, layout); + + return panel; + } + + /** + * Creates and returns the Administrators panel, which allows users to configure settings + * for contract negotiation experience points and weekly experience points for administrators. + * + * @return A {@code JPanel} containing configuration options for administrator experience settings. + */ + private JPanel createAdministratorsPanel() { + // Contents + lblAdminWeeklyXP = new CampaignOptionsLabel("AdminWeeklyXP"); + spnAdminWeeklyXP = new CampaignOptionsSpinner("AdminWeeklyXP", + 0, 0, 20, 1); + lblAdminWeeklyXPPeriod = new CampaignOptionsLabel("AdminWeeklyXPPeriod"); + spnAdminWeeklyXPPeriod = new CampaignOptionsSpinner("AdminWeeklyXPPeriod", + 1, 1, 52, 1); + + lblContractNegotiationXP = new CampaignOptionsLabel("ContractNegotiationXP"); + spnContractNegotiationXP = new CampaignOptionsSpinner("ContractNegotiationXP", + 0, 0, 20, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AdministratorsXpPanel", true, + "AdministratorsXpPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(spnAdminWeeklyXP, layout); + layout.gridx++; + panel.add(lblAdminWeeklyXP, layout); + layout.gridx++; + panel.add(spnAdminWeeklyXPPeriod, layout); + layout.gridx++; + panel.add(lblAdminWeeklyXPPeriod, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(spnContractNegotiationXP, layout); + layout.gridx++; + layout.gridwidth = 2; + panel.add(lblContractNegotiationXP, layout); + + return panel; + } + + /** + * Initializes the Skill Randomization tab by setting up the UI components, + * including phenotype configurations, random abilities, skill groups, and other + * randomization settings. + */ + private void initializeSkillRandomizationTab() { + chkExtraRandomness = new JCheckBox(); + + pnlPhenotype = new JPanel(); + phenotypeLabels = new JLabel[] {}; // This will be initialized properly later + phenotypeSpinners = new JSpinner[] {}; // This will be initialized properly later + + pnlSkillGroups = new JPanel(); + + pnlTactics = new JPanel(); + lblTacticsGreen = new JLabel(); + spnTacticsGreen = new JSpinner(); + lblTacticsReg = new JLabel(); + spnTacticsReg = new JSpinner(); + lblTacticsVet = new JLabel(); + spnTacticsVet = new JSpinner(); + lblTacticsElite = new JLabel(); + spnTacticsElite = new JSpinner(); + + pnlSmallArms = new JPanel(); + lblCombatSA = new JLabel(); + spnCombatSA = new JSpinner(); + lblSupportSA = new JLabel(); + spnSupportSA = new JSpinner(); + + pnlArtillery = new JPanel(); + lblArtyProb = new JLabel(); + spnArtyProb = new JSpinner(); + lblArtyBonus = new JLabel(); + spnArtyBonus = new JSpinner(); + + pnlSecondarySkills = new JPanel(); + lblAntiMekSkill = new JLabel(); + spnAntiMekSkill = new JSpinner(); + lblSecondProb = new JLabel(); + spnSecondProb = new JSpinner(); + lblSecondBonus = new JLabel(); + spnSecondBonus = new JSpinner(); + + pnlRandomAbilities = new JPanel(); + lblAbilityGreen = new JLabel(); + spnAbilityGreen = new JSpinner(); + lblAbilityReg = new JLabel(); + spnAbilityReg = new JSpinner(); + lblAbilityVet = new JLabel(); + spnAbilityVet = new JSpinner(); + lblAbilityElite = new JLabel(); + spnAbilityElite = new JSpinner(); + } + + /** + * Creates and returns the Skill Randomization tab panel. This tab allows users to configure + * settings related to skill randomization, including phenotype probabilities and skill bonuses + * for different experience levels and skill groups. + * + * @return A {@code JPanel} containing the configuration options for skill randomization. + */ + public JPanel skillRandomizationTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("SkillRandomizationTab", + getImageDirectory() + "logo_republic_of_the_sphere.png"); + + // Contents + chkExtraRandomness = new CampaignOptionsCheckBox("ExtraRandomness"); + + pnlPhenotype = createPhenotypePanel(); + pnlRandomAbilities = createAbilityPanel(); + pnlSkillGroups = createSkillGroupPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("SkillRandomizationTab", true); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(chkExtraRandomness, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(pnlPhenotype, layout); + layout.gridx++; + panel.add(pnlRandomAbilities, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(pnlSkillGroups, layout); + + // Create Parent Panel and return + return createParentPanel(panel, "XpAwardsTab"); + } + + /** + * Creates and returns the Phenotype panel, which allows users to configure settings + * for phenotype probabilities in the campaign. Each phenotype is assigned a spinner + * to adjust its probability. + * + * @return A {@code JPanel} containing configuration options for phenotype probabilities. + */ + private JPanel createPhenotypePanel() { + // Contents + List phenotypes = Phenotype.getExternalPhenotypes(); + phenotypeLabels = new JLabel[phenotypes.size()]; + phenotypeSpinners = new JSpinner[phenotypes.size()]; + + final JPanel panel = new CampaignOptionsStandardPanel("PhenotypesPanel", true, + "PhenotypesPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = -1; + layout.gridy = 0; + + for (int i = 0; i < phenotypes.size(); i++) { + phenotypeLabels[i] = new CampaignOptionsLabel(phenotypes.get(i).getName()); + phenotypeSpinners[i] = new CampaignOptionsSpinner(phenotypes.get(i).getName(), + 0, 0, 100, 1); + + layout.gridx++; + panel.add(phenotypeLabels[i], layout); + layout.gridx++; + panel.add(phenotypeSpinners[i], layout); + + if (i == (phenotypes.size() / 3)) { + layout.gridx = -1; + layout.gridy++; + } + } + + return panel; + } + + /** + * Creates and returns the Ability panel, which allows users to configure settings + * for bonuses to special abilities at different experience levels, such as green, + * regular, veteran, and elite. + * + * @return A {@code JPanel} containing configuration options for special ability bonuses. + */ + private JPanel createAbilityPanel() { + // Contents + lblAbilityGreen = new CampaignOptionsLabel("AbilityGreen"); + spnAbilityGreen = new CampaignOptionsSpinner("AbilityGreen", + 0, -10, 10, 1); + lblAbilityReg = new CampaignOptionsLabel("AbilityRegular"); + spnAbilityReg = new CampaignOptionsSpinner("AbilityRegular", + 0, -10, 10, 1); + + lblAbilityVet = new CampaignOptionsLabel("AbilityVeteran"); + spnAbilityVet = new CampaignOptionsSpinner("AbilityVeteran", + 0, -10, 10, 1); + + lblAbilityElite = new CampaignOptionsLabel("AbilityElite"); + spnAbilityElite = new CampaignOptionsSpinner("AbilityElite", + 0, -10, 10, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AbilityPanel", true, + "AbilityPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(lblAbilityGreen, layout); + layout.gridx++; + panel.add(spnAbilityGreen, layout); + layout.gridx++; + panel.add(lblAbilityReg, layout); + layout.gridx++; + panel.add(spnAbilityReg, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAbilityVet, layout); + layout.gridx++; + panel.add(spnAbilityVet, layout); + layout.gridx++; + panel.add(lblAbilityElite, layout); + layout.gridx++; + panel.add(spnAbilityElite, layout); + + return panel; + } + + /** + * Creates and returns the Skill Groups panel, which groups together related panels + * for configuring various skill-related options, including tactics, small arms, + * secondary skills, and artillery. + * + * @return A {@code JPanel} containing grouped configuration options for skills. + */ + private JPanel createSkillGroupPanel() { + // Contents + pnlArtillery = createArtilleryPanel(); + pnlSmallArms = createSmallArmsPanel(); + pnlSecondarySkills = createSecondarySkillPanel(); + + pnlTactics = createTacticsPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("SkillGroupsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(pnlTactics, layout); + layout.gridx++; + panel.add(pnlSmallArms, layout); + layout.gridx++; + panel.add(pnlSecondarySkills, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(pnlSecondarySkills, layout); + layout.gridx++; + panel.add(pnlArtillery, layout); + + return panel; + } + + /** + * Creates and returns the Tactics panel, which allows users to configure settings + * for Tactics modifiers for different skill levels, such as green, regular, veteran, and elite. + * + * @return A {@code JPanel} containing configuration options for Tactics modifiers. + */ + private JPanel createTacticsPanel() { + // Contents + lblTacticsGreen = new CampaignOptionsLabel("TacticsGreen"); + spnTacticsGreen = new CampaignOptionsSpinner("TacticsGreen", + 0, -10, 10, 1); + + lblTacticsReg = new CampaignOptionsLabel("TacticsRegular"); + spnTacticsReg = new CampaignOptionsSpinner("TacticsRegular", + 0, -10, 10, 1); + + lblTacticsVet = new CampaignOptionsLabel("TacticsVeteran"); + spnTacticsVet = new CampaignOptionsSpinner("TacticsVeteran", + 0, -10, 10, 1); + + lblTacticsElite = new CampaignOptionsLabel("TacticsElite"); + spnTacticsElite = new CampaignOptionsSpinner("TacticsElite", + 0, -10, 10, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("TacticsPanel", true, + "TacticsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(lblTacticsGreen, layout); + layout.gridx++; + panel.add(spnTacticsGreen, layout); + layout.gridx++; + panel.add(lblTacticsReg, layout); + layout.gridx++; + panel.add(spnTacticsReg, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblTacticsVet, layout); + layout.gridx++; + panel.add(spnTacticsVet, layout); + layout.gridx++; + panel.add(lblTacticsElite, layout); + layout.gridx++; + panel.add(spnTacticsElite, layout); + + return panel; + } + + /** + * Creates and returns the Small Arms panel, which allows users to configure settings + * for combat and non-combat small arms bonuses. + * + * @return A {@code JPanel} containing configuration options for small arms skill bonuses. + */ + private JPanel createSmallArmsPanel() { + // Contents + lblCombatSA = new CampaignOptionsLabel("CombatSmallArms"); + spnCombatSA = new CampaignOptionsSpinner("CombatSmallArms", + 0, -10, 10, 1); + + lblSupportSA = new CampaignOptionsLabel("NonCombatSmallArms"); + spnSupportSA = new CampaignOptionsSpinner("NonCombatSmallArms", + 0, -10, 10, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("SmallArmsPanel", + true, "SmallArmsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(lblCombatSA, layout); + layout.gridx++; + panel.add(spnCombatSA, layout); + layout.gridx++; + panel.add(lblSupportSA, layout); + layout.gridx++; + panel.add(spnSupportSA, layout); + + return panel; + } + + /** + * Creates and returns the Artillery panel, which allows users to configure settings + * for artillery-specific skills, including the chance for an artillery skill and + * the bonus associated with it. + * + * @return A {@code JPanel} containing configuration options for artillery-related skills. + */ + private JPanel createArtilleryPanel() { + // Contents + lblArtyProb = new CampaignOptionsLabel("ArtilleryChance"); + spnArtyProb = new CampaignOptionsSpinner("ArtilleryChance", + 0, -10, 10, 1); + + lblArtyBonus = new CampaignOptionsLabel("ArtilleryBonus"); + spnArtyBonus = new CampaignOptionsSpinner("ArtilleryBonus", + 0, -10, 10, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("ArtilleryPanel", + true, "ArtilleryPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(lblArtyProb, layout); + layout.gridx++; + panel.add(spnArtyProb, layout); + layout.gridx++; + panel.add(lblArtyBonus, layout); + layout.gridx++; + panel.add(spnArtyBonus, layout); + + return panel; + } + + /** + * Creates and returns the Secondary Skill panel, which allows users to configure settings + * related to special secondary skills such as Anti-Mek, secondary skill probability, + * and secondary skill bonuses. + * + * @return A {@code JPanel} containing configuration options for secondary skills. + */ + private JPanel createSecondarySkillPanel() { + // Contents + lblAntiMekSkill = new CampaignOptionsLabel("AntiMekChance"); + spnAntiMekSkill = new CampaignOptionsSpinner("AntiMekChance", + 0, -10, 10, 1); + + lblSecondProb = new CampaignOptionsLabel("SecondarySkillChance"); + spnSecondProb = new CampaignOptionsSpinner("SecondarySkillChance", + 0, -10, 10, 1); + + lblSecondBonus = new CampaignOptionsLabel("SecondarySkillBonus"); + spnSecondBonus = new CampaignOptionsSpinner("SecondarySkillBonus", + 0, -10, 10, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("SecondarySkillPanel", + true, "SecondarySkillPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(lblAntiMekSkill, layout); + layout.gridx++; + panel.add(spnAntiMekSkill, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblSecondProb, layout); + layout.gridx++; + panel.add(spnSecondProb, layout); + layout.gridx++; + panel.add(lblSecondBonus, layout); + layout.gridx++; + panel.add(spnSecondBonus, layout); + + return panel; + } + + /** + * Loads the current values for XP Awards and Skill Randomization settings into the UI components + * from the campaign options and random skill preferences. + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null, null); + } + + /** + * Loads the current values for XP Awards and Skill Randomization settings into the UI components + * from the given {@code CampaignOptions} and {@code RandomSkillPreferences} objects. + * + * @param presetCampaignOptions Optional {@code CampaignOptions} object to load values from; + * if {@code null}, values are loaded from the current campaign options. + * @param presetRandomSkillPreferences Optional {@code RandomSkillPreferences} object to load values + * from; if {@code null}, values are loaded from the current skill preferences. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions, + @Nullable RandomSkillPreferences presetRandomSkillPreferences) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + RandomSkillPreferences skillPreferences = presetRandomSkillPreferences; + if (skillPreferences == null) { + skillPreferences = this.randomSkillPreferences; + } + + //start XP Awards Tab + spnXpCostMultiplier.setValue(options.getXpCostMultiplier()); + spnTaskXP.setValue(options.getTaskXP()); + spnNTasksXP.setValue(options.getNTasksXP()); + spnSuccessXP.setValue(options.getSuccessXP()); + spnMistakeXP.setValue(options.getMistakeXP()); + spnScenarioXP.setValue(options.getScenarioXP()); + spnKillXP.setValue(options.getKillXPAward()); + spnKills.setValue(options.getKillsForXP()); + spnVocationalXP.setValue(options.getVocationalXP()); + spnVocationalXPFrequency.setValue(options.getVocationalXPCheckFrequency()); + spnVocationalXPTargetNumber.setValue(options.getVocationalXPTargetNumber()); + spnMissionXpFail.setValue(options.getMissionXpFail()); + spnMissionXpSuccess.setValue(options.getMissionXpSuccess()); + spnMissionXpOutstandingSuccess.setValue(options.getMissionXpOutstandingSuccess()); + spnContractNegotiationXP.setValue(options.getContractNegotiationXP()); + spnAdminWeeklyXP.setValue(options.getAdminXP()); + spnAdminWeeklyXPPeriod.setValue(options.getAdminXPPeriod()); + + //start Skill Randomization Tab + chkExtraRandomness.setSelected(skillPreferences.randomizeSkill()); + final int[] phenotypeProbabilities = options.getPhenotypeProbabilities(); + for (int i = 0; i < phenotypeSpinners.length; i++) { + phenotypeSpinners[i].setValue(phenotypeProbabilities[i]); + } + spnAbilityGreen.setValue(skillPreferences.getSpecialAbilityBonus(SkillType.EXP_GREEN)); + spnAbilityReg.setValue(skillPreferences.getSpecialAbilityBonus(SkillType.EXP_REGULAR)); + spnAbilityVet.setValue(skillPreferences.getSpecialAbilityBonus(SkillType.EXP_VETERAN)); + spnAbilityElite.setValue(skillPreferences.getSpecialAbilityBonus(SkillType.EXP_ELITE)); + spnTacticsGreen.setValue(skillPreferences.getTacticsMod(SkillType.EXP_GREEN)); + spnTacticsReg.setValue(skillPreferences.getTacticsMod(SkillType.EXP_REGULAR)); + spnTacticsVet.setValue(skillPreferences.getTacticsMod(SkillType.EXP_VETERAN)); + spnTacticsElite.setValue(skillPreferences.getTacticsMod(SkillType.EXP_ELITE)); + spnCombatSA.setValue(skillPreferences.getCombatSmallArmsBonus()); + spnSupportSA.setValue(skillPreferences.getSupportSmallArmsBonus()); + spnArtyProb.setValue(skillPreferences.getArtilleryProb()); + spnArtyBonus.setValue(skillPreferences.getArtilleryBonus()); + spnAntiMekSkill.setValue(skillPreferences.getAntiMekProb()); + spnSecondProb.setValue(skillPreferences.getSecondSkillProb()); + spnSecondBonus.setValue(skillPreferences.getSecondSkillBonus()); + } + + /** + * Applies the current values from the XP Awards and Skill Randomization tabs + * to the specified {@code CampaignOptions} and {@code RandomSkillPreferences}. + * + * @param presetCampaignOptions Optional {@code CampaignOptions} object to set values to; + * if {@code null}, values are applied to the current campaign options. + * @param presetRandomSkillPreferences Optional {@code RandomSkillPreferences} object to set values + * to; if {@code null}, values are applied to the current skill preferences. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions, + @Nullable RandomSkillPreferences presetRandomSkillPreferences) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + RandomSkillPreferences skillPreferences = presetRandomSkillPreferences; + if (skillPreferences == null) { + skillPreferences = this.randomSkillPreferences; + } + + //start XP Awards Tab + options.setXpCostMultiplier((double) spnXpCostMultiplier.getValue()); + options.setTaskXP((int) spnTaskXP.getValue()); + options.setNTasksXP((int) spnNTasksXP.getValue()); + options.setSuccessXP((int) spnSuccessXP.getValue()); + options.setMistakeXP((int) spnMistakeXP.getValue()); + options.setScenarioXP((int) spnScenarioXP.getValue()); + options.setKillXPAward((int) spnKillXP.getValue()); + options.setKillsForXP((int) spnKills.getValue()); + options.setVocationalXP((int) spnVocationalXP.getValue()); + options.setVocationalXPCheckFrequency((int) spnVocationalXPFrequency.getValue()); + options.setVocationalXPTargetNumber((int) spnVocationalXPTargetNumber.getValue()); + options.setMissionXpFail((int) spnMissionXpFail.getValue()); + options.setMissionXpSuccess((int) spnMissionXpSuccess.getValue()); + options.setMissionXpOutstandingSuccess((int) spnMissionXpOutstandingSuccess.getValue()); + options.setContractNegotiationXP((int) spnContractNegotiationXP.getValue()); + options.setAdminXP((int) spnAdminWeeklyXP.getValue()); + options.setAdminXPPeriod((int) spnAdminWeeklyXPPeriod.getValue()); + + //start Skill Randomization Tab + skillPreferences.setRandomizeSkill(chkExtraRandomness.isSelected()); + for (int i = 0; i < phenotypeSpinners.length; i++) { + options.setPhenotypeProbability(i, (int) phenotypeSpinners[i].getValue()); + } + + skillPreferences.setAntiMekProb((int) spnAntiMekSkill.getValue()); + skillPreferences.setArtilleryProb((int) spnArtyProb.getValue()); + skillPreferences.setArtilleryBonus((int) spnArtyBonus.getValue()); + skillPreferences.setSecondSkillProb((int) spnSecondProb.getValue()); + skillPreferences.setSecondSkillBonus((int) spnSecondBonus.getValue()); + skillPreferences.setTacticsMod(SkillType.EXP_GREEN, (int) spnTacticsGreen.getValue()); + skillPreferences.setTacticsMod(SkillType.EXP_REGULAR, (int) spnTacticsReg.getValue()); + skillPreferences.setTacticsMod(SkillType.EXP_VETERAN, (int) spnTacticsVet.getValue()); + skillPreferences.setTacticsMod(SkillType.EXP_ELITE, (int) spnTacticsElite.getValue()); + skillPreferences.setCombatSmallArmsBonus((int) spnCombatSA.getValue()); + skillPreferences.setSupportSmallArmsBonus((int) spnSupportSA.getValue()); + skillPreferences.setSpecialAbilityBonus(SkillType.EXP_GREEN, (int) spnAbilityGreen.getValue()); + skillPreferences.setSpecialAbilityBonus(SkillType.EXP_REGULAR, (int) spnAbilityReg.getValue()); + skillPreferences.setSpecialAbilityBonus(SkillType.EXP_VETERAN, (int) spnAbilityVet.getValue()); + skillPreferences.setSpecialAbilityBonus(SkillType.EXP_ELITE, (int) spnAbilityElite.getValue()); + + if (presetRandomSkillPreferences == null) { + campaign.setRandomSkillPreferences(randomSkillPreferences); + } + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/BiographyTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/BiographyTab.java new file mode 100644 index 00000000000..7721d74041f --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/BiographyTab.java @@ -0,0 +1,1478 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.generator.RandomGenderGenerator; +import megamek.client.generator.RandomNameGenerator; +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.RandomOriginOptions; +import mekhq.campaign.personnel.enums.AgeGroup; +import mekhq.campaign.personnel.enums.FamilialRelationshipDisplayLevel; +import mekhq.campaign.personnel.enums.PersonnelRole; +import mekhq.campaign.personnel.enums.RandomDeathMethod; +import mekhq.campaign.personnel.ranks.RankSystem; +import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Planet; +import mekhq.campaign.universe.PlanetarySystem; +import mekhq.gui.campaignOptions.components.*; +import mekhq.gui.panes.RankSystemsPane; + +import javax.swing.*; +import javax.swing.JSpinner.NumberEditor; +import java.awt.*; +import java.util.*; + +import static megamek.client.generator.RandomGenderGenerator.getPercentFemale; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * The `BiographyTab` class is responsible for managing the biography-related settings in the campaign options tab + * within the MekHQ application. It provides an interface for configuring various campaign settings, such as: + *
      + *
    • General campaign settings like gender distribution and familial relationships.
    • + *
    • Background options, including randomized personality traits and origin determination.
    • + *
    • Death-related settings such as probability of random deaths, age-group-specific deaths, etc.
    • + *
    • Education module settings for academic progression, dropout chances, and related configurations.
    • + *
    • Random name generation and portrait assignment based on roles and factions.
    • + *
    • Rank and hierarchy management for campaign personnel.
    • + *
    + * + * The class includes methods to initialize, load, and configure settings while providing GUI tools to enable user + * interaction. It also integrates with the current `Campaign` and `CampaignOptions` objects to synchronize settings. + * This class serves as the backbone for displaying and managing the "Biography" tab in the campaign options dialog. + */ +public class BiographyTab { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + private Campaign campaign; + private CampaignOptions campaignOptions; + private RandomOriginOptions randomOriginOptions; + + //start General Tab + private JCheckBox chkUseDylansRandomXP; + private JLabel lblGender; + private JSlider sldGender; + private JLabel lblNonBinaryDiceSize; + private JSpinner spnNonBinaryDiceSize; + private JLabel lblFamilyDisplayLevel; + private MMComboBox comboFamilyDisplayLevel; + private JPanel pnlAnniversariesPanel; + private JCheckBox chkAnnounceOfficersOnly; + private JCheckBox chkAnnounceBirthdays; + private JCheckBox chkAnnounceChildBirthdays; + private JCheckBox chkAnnounceRecruitmentAnniversaries; + //end General Tab + + //start Backgrounds Tab + private JPanel pnlRandomBackgrounds; + private JCheckBox chkUseRandomPersonalities; + private JCheckBox chkUseRandomPersonalityReputation; + private JCheckBox chkUseIntelligenceXpMultiplier; + private JCheckBox chkUseSimulatedRelationships; + private JPanel pnlRandomOriginOptions; + private JCheckBox chkRandomizeOrigin; + private JCheckBox chkRandomizeDependentsOrigin; + private JCheckBox chkRandomizeAroundSpecifiedPlanet; + private JCheckBox chkSpecifiedSystemFactionSpecific; + private JLabel lblSpecifiedSystem; + private MMComboBox comboSpecifiedSystem; + private JLabel lblSpecifiedPlanet; + private MMComboBox comboSpecifiedPlanet; + private JLabel lblOriginSearchRadius; + private JSpinner spnOriginSearchRadius; + private JLabel lblOriginDistanceScale; + private JSpinner spnOriginDistanceScale; + private JCheckBox chkAllowClanOrigins; + private JCheckBox chkExtraRandomOrigin; + //end Backgrounds Tab + + //start Death Tab + private JCheckBox chkKeepMarriedNameUponSpouseDeath; + private JLabel lblRandomDeathMethod; + private MMComboBox comboRandomDeathMethod; + private JCheckBox chkUseRandomClanPersonnelDeath; + private JCheckBox chkUseRandomPrisonerDeath; + private JCheckBox chkUseRandomDeathSuicideCause; + private JLabel lblPercentageRandomDeathChance; + private JSpinner spnPercentageRandomDeathChance; + + private JPanel pnlDeathAgeGroup; + private Map chkEnabledRandomDeathAgeGroups; + //end Death Tab + + //start Education Tab + private JCheckBox chkUseEducationModule; + private JLabel lblCurriculumXpRate; + private JSpinner spnCurriculumXpRate; + private JLabel lblMaximumJumpCount; + private JSpinner spnMaximumJumpCount; + private JCheckBox chkUseReeducationCamps; + private JCheckBox chkEnableOverrideRequirements; + private JCheckBox chkShowIneligibleAcademies; + private JLabel lblEntranceExamBaseTargetNumber; + private JSpinner spnEntranceExamBaseTargetNumber; + + private JPanel pnlEnableStandardSets; + private JCheckBox chkEnableLocalAcademies; + private JCheckBox chkEnablePrestigiousAcademies; + private JCheckBox chkEnableUnitEducation; + + private JPanel pnlXpAndSkillBonuses; + private JCheckBox chkEnableBonuses; + private JLabel lblFacultyXpMultiplier; + private JSpinner spnFacultyXpMultiplier; + + private JPanel pnlDropoutChance; + private JLabel lblAdultDropoutChance; + private JSpinner spnAdultDropoutChance; + private JLabel lblChildrenDropoutChance; + private JSpinner spnChildrenDropoutChance; + + private JPanel pnlAccidentsAndEvents; + private JCheckBox chkAllAges; + private JLabel lblMilitaryAcademyAccidents; + private JSpinner spnMilitaryAcademyAccidents; + //end Education Tab + + //start Name and Portrait Tab + private JCheckBox chkUseOriginFactionForNames; + private JLabel lblFactionNames; + private MMComboBox comboFactionNames; + + private JPanel pnlRandomPortrait; + private JCheckBox[] chkUsePortrait; + private JButton btnEnableAllPortraits; + private JButton btnDisableAllPortraits; + private JCheckBox chkAssignPortraitOnRoleChange; + //end Name and Portrait Tab + + //start Rank Tab + private RankSystemsPane rankSystemsPane; + //end Rank Tab + + /** + * Constructs the `BiographyTab` and initializes the campaign and its dependent options. + * + * @param campaign The current `Campaign` object to which the BiographyTab is linked. + * The campaign options and origin options are derived from this object. + */ + public BiographyTab(Campaign campaign) { + this.campaign = campaign; + this.campaignOptions = campaign.getCampaignOptions(); + this.randomOriginOptions = campaignOptions.getRandomOriginOptions(); + + initialize(); + } + + /** + * Initializes the various sections and settings tabs within the BiographyTab. + * This method organizes the following tabs: + *

    + *

  1. General Tab: Handles general campaign settings such as gender distribution and + * relationship displays.
  2. + *
  3. Background Tab: Configures randomized backgrounds for campaign characters.
  4. + *
  5. Death Tab: Sets up random death rules and options.
  6. + *
  7. Education Tab: Defines education-related gameplay settings.
  8. + *
  9. Name and Portrait Tab: Configures rules for name and portrait generation.
  10. + *
  11. Rank Tab: Manages the rank systems for campaign personnel.
  12. + *

    + */ + private void initialize() { + initializeGeneralTab(); + initializeBackgroundsTab(); + initializeDeathTab(); + initializeEducationTab(); + initializeNameAndPortraitTab(); + + rankSystemsPane = new RankSystemsPane(null, campaign); + } + + /** + * Initializes the Name and Portrait tab. The tab allows users to: + *
      + *
    • Enable or disable the use of origin factions for name generation.
    • + *
    • Assign portraits to personnel upon role changes.
    • + *
    • Customize which portraits should be used randomly based on roles.
    • + *
    + */ + private void initializeNameAndPortraitTab() { + chkUseOriginFactionForNames = new JCheckBox(); + lblFactionNames = new JLabel(); + comboFactionNames = new MMComboBox<>("comboFactionNames", getFactionNamesModel()); + chkAssignPortraitOnRoleChange = new JCheckBox(); + + pnlRandomPortrait = new JPanel(); + chkUsePortrait = new JCheckBox[1]; // We're going to properly initialize this later + btnEnableAllPortraits = new JButton(); + btnDisableAllPortraits = new JButton(); + } + + /** + * Initializes the Education tab, providing settings such as: + *
      + *
    • Setting curriculum XP rates.
    • + *
    • Enabling re-education camps or specific academy requirements.
    • + *
    • Managing academy dropout chances for adults and children.
    • + *
    • Supporting the configuration of education-related accidents and events.
    • + *
    + */ + private void initializeEducationTab() { + chkUseEducationModule = new JCheckBox(); + lblCurriculumXpRate = new JLabel(); + spnCurriculumXpRate = new JSpinner(); + lblMaximumJumpCount = new JLabel(); + spnMaximumJumpCount = new JSpinner(); + chkUseReeducationCamps = new JCheckBox(); + chkEnableOverrideRequirements = new JCheckBox(); + chkShowIneligibleAcademies = new JCheckBox(); + lblEntranceExamBaseTargetNumber = new JLabel(); + spnEntranceExamBaseTargetNumber = new JSpinner(); + + pnlEnableStandardSets = new JPanel(); + chkEnableLocalAcademies = new JCheckBox(); + chkEnablePrestigiousAcademies = new JCheckBox(); + chkEnableUnitEducation = new JCheckBox(); + + pnlXpAndSkillBonuses = new JPanel(); + chkEnableBonuses = new JCheckBox(); + lblFacultyXpMultiplier = new JLabel(); + spnFacultyXpMultiplier = new JSpinner(); + + pnlDropoutChance = new JPanel(); + lblAdultDropoutChance = new JLabel(); + spnAdultDropoutChance = new JSpinner(); + lblChildrenDropoutChance = new JLabel(); + spnChildrenDropoutChance = new JSpinner(); + + pnlAccidentsAndEvents = new JPanel(); + chkAllAges = new JCheckBox(); + lblMilitaryAcademyAccidents = new JLabel(); + spnMilitaryAcademyAccidents = new JSpinner(); + } + + /** + * Initializes the Death tab, focusing on: + *
      + *
    • Allowing configuration of random death probabilities for personnel.
    • + *
    • Customizing age-group-specific death settings.
    • + *
    • Selecting methods for random deaths (e.g., natural causes or accidents).
    • + *
    + */ + private void initializeDeathTab() { + chkKeepMarriedNameUponSpouseDeath = new JCheckBox(); + lblRandomDeathMethod = new JLabel(); + comboRandomDeathMethod = new MMComboBox<>("comboRandomDeathMethod", RandomDeathMethod.values()); + chkUseRandomClanPersonnelDeath = new JCheckBox(); + chkUseRandomPrisonerDeath = new JCheckBox(); + chkUseRandomDeathSuicideCause = new JCheckBox(); + lblPercentageRandomDeathChance = new JLabel(); + spnPercentageRandomDeathChance = new JSpinner(); + + pnlDeathAgeGroup = new JPanel(); + chkEnabledRandomDeathAgeGroups = new HashMap<>(); + } + + /** + * Initializes the Backgrounds tab, which handles: + *

    + *

  13. Randomized background settings for characters.
  14. + *
  15. Options for specifying origins (e.g., faction-specific planetary systems).
  16. + *
  17. Custom search radius and distance scaling for randomized origins.
  18. + *

    + */ + private void initializeBackgroundsTab() { + pnlRandomBackgrounds = new JPanel(); + chkUseRandomPersonalities = new JCheckBox(); + chkUseRandomPersonalityReputation = new JCheckBox(); + chkUseIntelligenceXpMultiplier = new JCheckBox(); + chkUseSimulatedRelationships = new JCheckBox(); + + pnlRandomOriginOptions = new JPanel(); + chkRandomizeOrigin = new JCheckBox(); + chkRandomizeDependentsOrigin = new JCheckBox(); + chkRandomizeAroundSpecifiedPlanet = new JCheckBox(); + chkSpecifiedSystemFactionSpecific = new JCheckBox(); + lblSpecifiedSystem = new JLabel(); + comboSpecifiedSystem = new MMComboBox<>("comboSpecifiedSystem"); + lblSpecifiedPlanet = new JLabel(); + comboSpecifiedPlanet = new MMComboBox<>("comboSpecifiedPlanet"); + lblOriginSearchRadius = new JLabel(); + spnOriginSearchRadius = new JSpinner(); + lblOriginDistanceScale = new JLabel(); + spnOriginDistanceScale = new JSpinner(); + chkAllowClanOrigins = new JCheckBox(); + chkExtraRandomOrigin = new JCheckBox(); + } + + /** + * Initializes the General tab, which provides options for: + *

    + *

  19. General gameplay settings such as gender distribution sliders.
  20. + *
  21. Configuration of familial display levels and other general campaign settings.
  22. + *
  23. Annunciation of anniversaries, recruitment dates, and officer-related milestones.
  24. + *

    + */ + private void initializeGeneralTab() { + chkUseDylansRandomXP = new JCheckBox(); + lblGender = new JLabel(); + sldGender = new JSlider(); + lblNonBinaryDiceSize = new JLabel(); + spnNonBinaryDiceSize = new JSpinner(); + lblFamilyDisplayLevel = new JLabel(); + comboFamilyDisplayLevel = new MMComboBox<>("comboFamilyDisplayLevel", + FamilialRelationshipDisplayLevel.values()); + + pnlAnniversariesPanel = new JPanel(); + chkAnnounceOfficersOnly = new JCheckBox(); + chkAnnounceBirthdays = new JCheckBox(); + chkAnnounceChildBirthdays = new JCheckBox(); + chkAnnounceRecruitmentAnniversaries = new JCheckBox(); + } + + /** + * Builds and returns a `DefaultComboBoxModel` containing the names of all available factions. + * + * @return A `DefaultComboBoxModel` populated with faction names for random name generation rules. + */ + private static DefaultComboBoxModel getFactionNamesModel() { + DefaultComboBoxModel factionNamesModel = new DefaultComboBoxModel<>(); + for (final String faction : RandomNameGenerator.getInstance().getFactions()) { + factionNamesModel.addElement(faction); + } + return factionNamesModel; + } + + /** + * Creates and lays out the General tab, including its components like: + *
      + *
    • Checkboxes for random XP distribution.
    • + *
    • Sliders for gender representation customization.
    • + *
    • Combo boxes for family display level settings within the GUI.
    • + *
    + * + * @return A `JPanel` representing the General tab in the campaign options dialog. + */ + public JPanel createGeneralTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("BiographyGeneralTab", + getImageDirectory() + "logo_clan_blood_spirit.png"); + + // Contents + chkUseDylansRandomXP = new CampaignOptionsCheckBox("UseDylansRandomXP"); + + lblGender = new CampaignOptionsLabel("Gender"); + sldGender = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 50); + sldGender.setMajorTickSpacing(25); + sldGender.setPaintTicks(true); + sldGender.setPaintLabels(true); + + lblNonBinaryDiceSize = new CampaignOptionsLabel("NonBinaryDiceSize"); + spnNonBinaryDiceSize = new CampaignOptionsSpinner("NonBinaryDiceSize", + 60, 0, 100000, 1); + + lblFamilyDisplayLevel = new CampaignOptionsLabel("FamilyDisplayLevel"); + + pnlAnniversariesPanel = createAnniversariesPanel(); + + // Layout the Panel + final JPanel panelLeft = new CampaignOptionsStandardPanel("BiographyGeneralTabLeft", true); + final GridBagConstraints layoutLeft = new CampaignOptionsGridBagConstraints(panelLeft); + + layoutLeft.gridy = 0; + layoutLeft.gridx = 0; + layoutLeft.gridwidth = 1; + panelLeft.add(chkUseDylansRandomXP, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblGender, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(sldGender, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblNonBinaryDiceSize, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnNonBinaryDiceSize, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblFamilyDisplayLevel, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(comboFamilyDisplayLevel, layoutLeft); + + final JPanel panelParent = new CampaignOptionsStandardPanel("BiographyGeneralTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(panelLeft, layoutParent); + layoutParent.gridx++; + panelParent.add(pnlAnniversariesPanel, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "BiographyGeneralTab"); + } + + /** + * Creates the Anniversaries panel within the General tab for managing announcement-related settings: + *

    + *

  25. Enabling birthday and recruitment anniversary announcements.
  26. + *
  27. Specifying whether such announcements should be limited to officers.
  28. + *

    + * + * @return A `JPanel` containing the UI components for defining anniversary-related settings. + */ + private JPanel createAnniversariesPanel() { + // Contents + chkAnnounceBirthdays = new CampaignOptionsCheckBox("AnnounceBirthdays"); + chkAnnounceRecruitmentAnniversaries = new CampaignOptionsCheckBox("AnnounceRecruitmentAnniversaries"); + chkAnnounceOfficersOnly = new CampaignOptionsCheckBox("AnnounceOfficersOnly"); + chkAnnounceChildBirthdays = new CampaignOptionsCheckBox("AnnounceChildBirthdays"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AnniversariesPanel", true, + "AnniversariesPanel"); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panel.add(chkAnnounceBirthdays, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(chkAnnounceRecruitmentAnniversaries, layoutParent); + + layoutParent.gridy++; + panel.add(chkAnnounceOfficersOnly, layoutParent); + + layoutParent.gridy++; + panel.add(chkAnnounceChildBirthdays, layoutParent); + + return panel; + } + + /** + * Creates and lays out the Backgrounds tab, which includes: + *
      + *
    • Settings for enabling randomized personalities and relationships.
    • + *
    • Random origin configurations such as faction specificity and distance scaling.
    • + *
    + * + * @return A `JPanel` representing the Backgrounds tab in the campaign options dialog. + */ + public JPanel createBackgroundsTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("BackgroundsTab", + getImageDirectory() + "logo_nueva_castile.png"); + + // Contents + pnlRandomOriginOptions = createRandomOriginOptionsPanel(); + pnlRandomBackgrounds = createRandomBackgroundsPanel(); + + // Layout the Panels + final JPanel panel = new CampaignOptionsStandardPanel("BackgroundsTab", true); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(pnlRandomOriginOptions, layout); + + layout.gridx++; + panel.add(pnlRandomBackgrounds, layout); + + // Create Parent Panel and return + return createParentPanel(panel, "BackgroundsTab"); + } + + /** + * Creates the panel for configuring random background options in the campaign. + *

    + * This includes controls to enable or disable features such as: + *

    + *

  29. Random personalities for characters.
  30. + *
  31. Random personality reputation.
  32. + *
  33. Intelligence XP multipliers.
  34. + *
  35. Simulated relationships.
  36. + *

    + * + * @return A {@code JPanel} representing the random background configuration UI. + */ + JPanel createRandomBackgroundsPanel() { + // Contents + chkUseRandomPersonalities = new CampaignOptionsCheckBox("UseRandomPersonalities"); + chkUseRandomPersonalityReputation = new CampaignOptionsCheckBox("UseRandomPersonalityReputation"); + chkUseIntelligenceXpMultiplier = new CampaignOptionsCheckBox("UseIntelligenceXpMultiplier"); + chkUseSimulatedRelationships = new CampaignOptionsCheckBox("UseSimulatedRelationships"); + + // Layout the Panels + final JPanel panel = new CampaignOptionsStandardPanel("RandomBackgroundsPanel", true, + "RandomBackgroundsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(chkUseRandomPersonalities, layout); + + layout.gridy++; + panel.add(chkUseRandomPersonalityReputation, layout); + + layout.gridy++; + panel.add(chkUseIntelligenceXpMultiplier, layout); + + layout.gridy++; + panel.add(chkUseSimulatedRelationships, layout); + + return panel; + } + + /** + * Creates and returns a panel for random origin options. This includes: + *

    + *

  37. Controls to enable or disable randomization of origins.
  38. + *
  39. Options for selecting specific planetary systems or factions for origin determination.
  40. + *
  41. Search radius and distance scaling fields to tweak origin calculations.
  42. + *

    + * + * @return A `JPanel` for managing random origin settings. + */ + private JPanel createRandomOriginOptionsPanel() { + // Contents + chkRandomizeOrigin = new CampaignOptionsCheckBox("RandomizeOrigin"); + chkRandomizeDependentsOrigin = new CampaignOptionsCheckBox("RandomizeDependentsOrigin"); + chkRandomizeAroundSpecifiedPlanet = new CampaignOptionsCheckBox("RandomizeAroundSpecifiedPlanet"); + + chkSpecifiedSystemFactionSpecific = new CampaignOptionsCheckBox("SpecifiedSystemFactionSpecific"); + chkSpecifiedSystemFactionSpecific.addActionListener(evt -> { + final PlanetarySystem planetarySystem = comboSpecifiedSystem.getSelectedItem(); + if ((planetarySystem == null) + || !planetarySystem.getFactionSet(campaign.getLocalDate()).contains(campaign.getFaction())) { + restoreComboSpecifiedSystem(); + } + }); + + + lblSpecifiedSystem = new CampaignOptionsLabel("SpecifiedSystem"); + comboSpecifiedSystem.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof PlanetarySystem) { + setText(((PlanetarySystem) value).getName(campaign.getLocalDate())); + } + return this; + } + }); + comboSpecifiedSystem.addActionListener(evt -> { + final PlanetarySystem planetarySystem = comboSpecifiedSystem.getSelectedItem(); + final Planet planet = comboSpecifiedPlanet.getSelectedItem(); + if ((planetarySystem == null) + || ((planet != null) && !planet.getParentSystem().equals(planetarySystem))) { + restoreComboSpecifiedPlanet(); + } + }); + + lblSpecifiedPlanet = new CampaignOptionsLabel("SpecifiedPlanet"); + comboSpecifiedPlanet.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof Planet) { + setText(((Planet) value).getName(campaign.getLocalDate())); + } + return this; + } + }); + + lblOriginSearchRadius = new CampaignOptionsLabel("OriginSearchRadius"); + spnOriginSearchRadius = new CampaignOptionsSpinner("OriginSearchRadius", + 0, 0, 2000, 25); + + lblOriginDistanceScale = new CampaignOptionsLabel("OriginDistanceScale"); + spnOriginDistanceScale = new CampaignOptionsSpinner("OriginDistanceScale", + 0.6, 0.1, 2.0, 0.1); + + chkAllowClanOrigins = new CampaignOptionsCheckBox("AllowClanOrigins"); + chkExtraRandomOrigin = new CampaignOptionsCheckBox("ExtraRandomOrigin"); + + // Layout the Panel + final JPanel panelSystemPlanetOrigins = new CampaignOptionsStandardPanel( + "RandomOriginOptionsPanelSystemPlanetOrigins", false, ""); + final GridBagConstraints layoutSystemPlanetOrigins = new CampaignOptionsGridBagConstraints(panelSystemPlanetOrigins); + + layoutSystemPlanetOrigins.gridwidth = 1; + layoutSystemPlanetOrigins.gridx = 0; + layoutSystemPlanetOrigins.gridy = 0; + panelSystemPlanetOrigins.add(lblSpecifiedSystem, layoutSystemPlanetOrigins); + layoutSystemPlanetOrigins.gridx++; + panelSystemPlanetOrigins.add(comboSpecifiedSystem, layoutSystemPlanetOrigins); + layoutSystemPlanetOrigins.gridx++; + panelSystemPlanetOrigins.add(lblSpecifiedPlanet, layoutSystemPlanetOrigins); + layoutSystemPlanetOrigins.gridx++; + panelSystemPlanetOrigins.add(comboSpecifiedPlanet, layoutSystemPlanetOrigins); + + layoutSystemPlanetOrigins.gridx = 0; + layoutSystemPlanetOrigins.gridy++; + panelSystemPlanetOrigins.add(lblOriginSearchRadius, layoutSystemPlanetOrigins); + layoutSystemPlanetOrigins.gridx++; + panelSystemPlanetOrigins.add(spnOriginSearchRadius, layoutSystemPlanetOrigins); + layoutSystemPlanetOrigins.gridx++; + panelSystemPlanetOrigins.add(lblOriginDistanceScale, layoutSystemPlanetOrigins); + layoutSystemPlanetOrigins.gridx++; + panelSystemPlanetOrigins.add(spnOriginDistanceScale, layoutSystemPlanetOrigins); + + final JPanel panel = new CampaignOptionsStandardPanel("RandomOriginOptionsPanel", true, + "RandomOriginOptionsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(chkRandomizeOrigin, layout); + + layout.gridy++; + panel.add(chkRandomizeDependentsOrigin, layout); + + layout.gridy++; + panel.add(chkRandomizeAroundSpecifiedPlanet, layout); + + layout.gridy++; + panel.add(chkSpecifiedSystemFactionSpecific, layout); + + layout.gridy++; + panel.add(panelSystemPlanetOrigins, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkAllowClanOrigins, layout); + + layout.gridy++; + panel.add(chkExtraRandomOrigin, layout); + + return panel; + } + + /** + * Resets the planet combo box to show only the planets matching the currently selected planetary system. + */ + private void restoreComboSpecifiedPlanet() { + final PlanetarySystem planetarySystem = comboSpecifiedSystem.getSelectedItem(); + + if (planetarySystem == null) { + comboSpecifiedPlanet.removeAllItems(); + } else { + comboSpecifiedPlanet.setModel(new DefaultComboBoxModel<>( + planetarySystem.getPlanets().toArray(new Planet[] {}))); + comboSpecifiedPlanet.setSelectedItem(planetarySystem.getPrimaryPlanet()); + } + } + + /** + * Resets the system combo box to show only the planetary systems that match the current faction, if applicable. + */ + private void restoreComboSpecifiedSystem() { + comboSpecifiedSystem.removeAllItems(); + + comboSpecifiedSystem.setModel(new DefaultComboBoxModel<>(getPlanetarySystems( + chkSpecifiedSystemFactionSpecific.isSelected() ? campaign.getFaction() : null))); + + restoreComboSpecifiedPlanet(); + } + + /** + * Filters planetary systems based on a given faction (if specified) and returns a sorted array of matches. + * + * @param faction The faction to filter planetary systems by (nullable). If `null`, all systems are included. + * @return An array of `PlanetarySystem` objects meeting the filter criteria. + */ + private PlanetarySystem[] getPlanetarySystems(final @Nullable Faction faction) { + ArrayList systems = campaign.getSystems(); + ArrayList filteredSystems = new ArrayList<>(); + + // Filter systems + for (PlanetarySystem planetarySystem : systems) { + if ((faction == null) || planetarySystem.getFactionSet(campaign.getLocalDate()).contains(faction)) { + filteredSystems.add(planetarySystem); + } + } + + // Sort systems + filteredSystems.sort(Comparator.comparing(p -> p.getName(campaign.getLocalDate()))); + + // Convert to array + return filteredSystems.toArray(new PlanetarySystem[0]); + } + + /** + * Configures and creates the Death tab. This includes options like: + *
      + *
    • Methods for random death.
    • + *
    • Percentage-based chances for random death events.
    • + *
    • Check boxes to enable or disable age-specific death events.
    • + *
    + * + * @return A `JPanel` representing the Death tab. + */ + public JPanel createDeathTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("DeathTab", + getImageDirectory() + "logo_clan_fire_mandrills.png"); + + // Contents + chkKeepMarriedNameUponSpouseDeath = new CampaignOptionsCheckBox("KeepMarriedNameUponSpouseDeath"); + + lblRandomDeathMethod = new CampaignOptionsLabel("RandomDeathMethod"); + comboRandomDeathMethod.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof RandomDeathMethod) { + list.setToolTipText(((RandomDeathMethod) value).getToolTipText()); + } + return this; + } + }); + + chkUseRandomClanPersonnelDeath = new CampaignOptionsCheckBox("UseRandomClanPersonnelDeath"); + chkUseRandomPrisonerDeath = new CampaignOptionsCheckBox("UseRandomPrisonerDeath"); + chkUseRandomDeathSuicideCause = new CampaignOptionsCheckBox("UseRandomDeathSuicideCause"); + + lblPercentageRandomDeathChance = new CampaignOptionsLabel("PercentageRandomDeathChance"); + spnPercentageRandomDeathChance = new CampaignOptionsSpinner("PercentageRandomDeathChance", + 0, 0, 100, 0.000001); + NumberEditor editor = new NumberEditor(spnPercentageRandomDeathChance, + "0.000000"); + spnPercentageRandomDeathChance.setEditor(editor); + + pnlDeathAgeGroup = createDeathAgeGroupsPanel(); + + // Layout the Panel + final JPanel panelLeft = new CampaignOptionsStandardPanel("DeathTabLeft", true); + final GridBagConstraints layoutLeft = new CampaignOptionsGridBagConstraints(panelLeft); + + layoutLeft.gridy = 0; + layoutLeft.gridx = 0; + layoutLeft.gridwidth = 2; + panelLeft.add(chkKeepMarriedNameUponSpouseDeath, layoutLeft); + + layoutLeft.gridy++; + layoutLeft.gridwidth = 1; + panelLeft.add(lblRandomDeathMethod, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(comboRandomDeathMethod, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(chkUseRandomClanPersonnelDeath, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkUseRandomPrisonerDeath, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkUseRandomDeathSuicideCause, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(lblPercentageRandomDeathChance, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnPercentageRandomDeathChance, layoutLeft); + + final JPanel panelParent = new CampaignOptionsStandardPanel("DeathTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(panelLeft, layoutParent); + + layoutParent.gridx++; + panelParent.add(pnlDeathAgeGroup, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "DeathTab"); + } + + /** + * Configures and creates a panel where users can enable or disable random death probabilities for specific age groups. + * + * @return A `JPanel` containing the random death age group options. + */ + private JPanel createDeathAgeGroupsPanel() { + final AgeGroup[] ageGroups = AgeGroup.values(); + + // Create the Panel + final JPanel panel = new CampaignOptionsStandardPanel("DeathAgeGroupsPanel", true, + "DeathAgeGroupsPanel"); + panel.setLayout(new GridLayout((ageGroups.length / 2) + 1, 1)); + + // Contents + for (final AgeGroup ageGroup : ageGroups) { + final JCheckBox checkBox = new JCheckBox(ageGroup.toString()); + checkBox.setToolTipText(ageGroup.getToolTipText()); + checkBox.setName("chk" + ageGroup); + + panel.add(checkBox); + chkEnabledRandomDeathAgeGroups.put(ageGroup, checkBox); + } + + return panel; + } + + /** + * Creates the Education tab, which allows managing educational settings within the campaign. + *

    + * This includes: + *

      + *
    • Setting curriculum XP rates.
    • + *
    • Configuring academy requirements and override options.
    • + *
    • Managing dropout chances for adults and children.
    • + *
    • Enabling or disabling the use of reeducation camps, accidents, and events.
    • + *
    + * + * @return A {@code JPanel} representing the Education tab in the campaign UI. + */ + public JPanel createEducationTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("EducationTab", + getImageDirectory() + "logo_taurian_concordat.png"); + + // Contents + chkUseEducationModule = new CampaignOptionsCheckBox("UseEducationModule"); + + lblCurriculumXpRate = new CampaignOptionsLabel("CurriculumXpRate"); + spnCurriculumXpRate = new CampaignOptionsSpinner("CurriculumXpRate", + 3, 1, 10, 1); + + lblMaximumJumpCount = new CampaignOptionsLabel("MaximumJumpCount"); + spnMaximumJumpCount = new CampaignOptionsSpinner("MaximumJumpCount", + 5, 1, 200, 1); + + chkUseReeducationCamps = new CampaignOptionsCheckBox("UseReeducationCamps"); + + chkEnableOverrideRequirements = new CampaignOptionsCheckBox("EnableOverrideRequirements"); + + chkShowIneligibleAcademies = new CampaignOptionsCheckBox("ShowIneligibleAcademies"); + + lblEntranceExamBaseTargetNumber = new CampaignOptionsLabel("EntranceExamBaseTargetNumber"); + spnEntranceExamBaseTargetNumber = new CampaignOptionsSpinner("EntranceExamBaseTargetNumber", + 14, 0, 20, 1); + + pnlEnableStandardSets = createEnableStandardSetsPanel(); + + pnlXpAndSkillBonuses = createXpAndSkillBonusesPanel(); + + pnlDropoutChance = createDropoutChancePanel(); + + pnlAccidentsAndEvents = createAccidentsAndEventsPanel(); + + // Layout the Panels + final JPanel panelLeft = new CampaignOptionsStandardPanel("EducationTabLeft"); + final GridBagConstraints layoutLeft = new CampaignOptionsGridBagConstraints(panelLeft); + + layoutLeft.gridwidth = 5; + layoutLeft.gridx = 0; + layoutLeft.gridy = 0; + panelLeft.add(headerPanel, layoutLeft); + + layoutLeft.gridy++; + layoutLeft.gridwidth = 1; + panelLeft.add(chkUseEducationModule, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(lblCurriculumXpRate, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnCurriculumXpRate, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblMaximumJumpCount, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnMaximumJumpCount, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(chkUseReeducationCamps, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkEnableOverrideRequirements, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkShowIneligibleAcademies, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(lblEntranceExamBaseTargetNumber, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnEntranceExamBaseTargetNumber, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(pnlEnableStandardSets, layoutLeft); + + final JPanel panelRight = new CampaignOptionsStandardPanel("EducationTabRight"); + final GridBagConstraints layoutRight = new CampaignOptionsGridBagConstraints(panelRight); + + layoutRight.gridy++; + layoutRight.gridwidth = 1; + panelRight.add(panelLeft, layoutRight); + layoutRight.gridy++; + panelRight.add(pnlXpAndSkillBonuses, layoutRight); + + layoutRight.gridy++; + panelRight.add(pnlDropoutChance, layoutRight); + + layoutRight.gridy++; + panelRight.add(pnlAccidentsAndEvents, layoutRight); + + final JPanel panelParent = new CampaignOptionsStandardPanel("EducationTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(panelLeft, layoutParent); + + layoutParent.gridx++; + panelParent.add(panelRight, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "EducationTab"); + } + + /** + * Creates a panel for enabling different education-related academy sets. + *

    + * This includes options to toggle various academy types: + *

    + *

  43. Local academies.
  44. + *
  45. Prestigious academies.
  46. + *
  47. Unit-based education academies.
  48. + *

    + * + * @return A {@code JPanel} containing the Enable Standard Sets UI components. + */ + private JPanel createEnableStandardSetsPanel() { + chkEnableLocalAcademies = new CampaignOptionsCheckBox("EnableLocalAcademies"); + chkEnablePrestigiousAcademies = new CampaignOptionsCheckBox("EnablePrestigiousAcademies"); + chkEnableUnitEducation = new CampaignOptionsCheckBox("EnableUnitEducation"); + + final JPanel panel = new CampaignOptionsStandardPanel("EnableStandardSetsPanel", true, + "EnableStandardSetsPanel"); + + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(chkEnableLocalAcademies, layout); + + layout.gridy++; + panel.add(chkEnablePrestigiousAcademies, layout); + + layout.gridy++; + panel.add(chkEnableUnitEducation, layout); + + return panel; + } + + /** + * Creates a panel for configuring experience gain and skill bonuses. + *

    + * This includes: + *

    + *

  49. Option to enable or disable bonuses.
  50. + *
  51. Setting the faculty XP multiplier.
  52. + *

    + * + * @return A {@code JPanel} for managing XP rates and skill bonuses. + */ + private JPanel createXpAndSkillBonusesPanel() { + // Contents + chkEnableBonuses = new CampaignOptionsCheckBox("EnableBonuses"); + lblFacultyXpMultiplier = new CampaignOptionsLabel("FacultyXpMultiplier"); + spnFacultyXpMultiplier = new CampaignOptionsSpinner("FacultyXpMultiplier", + 1.00, 0.00, 10.00, 0.01); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("XpAndSkillBonusesPanel", true, + "XpAndSkillBonusesPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + panel.add(chkEnableBonuses, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblFacultyXpMultiplier, layout); + layout.gridx++; + panel.add(spnFacultyXpMultiplier, layout); + + return panel; + } + + /** + * Creates a panel for configuring dropout chances for academies. + *

    + * This includes: + *

    + *

  53. Setting the dropout chance for adults.
  54. + *
  55. Setting the dropout chance for children.
  56. + *

    + * + * @return A {@code JPanel} for managing dropout chance settings. + */ + private JPanel createDropoutChancePanel() { + // Contents + lblAdultDropoutChance = new CampaignOptionsLabel("AdultDropoutChance"); + spnAdultDropoutChance = new CampaignOptionsSpinner("AdultDropoutChance", + 1000, 0, 100000, 1); + lblChildrenDropoutChance = new CampaignOptionsLabel("ChildrenDropoutChance"); + spnChildrenDropoutChance = new CampaignOptionsSpinner("ChildrenDropoutChance", + 10000, 0, 100000, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("DropoutChancePanel", true, + "DropoutChancePanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(lblAdultDropoutChance, layout); + layout.gridx++; + panel.add(spnAdultDropoutChance, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblChildrenDropoutChance, layout); + layout.gridx++; + panel.add(spnChildrenDropoutChance, layout); + + return panel; + } + + /** + * Creates a panel for configuring accidents and events related to military academies. + *

    + * This includes: + *

    + *

  57. Toggling settings for all-age accidents.
  58. + *
  59. Configuring the frequency of military academy accidents.
  60. + *

    + * + * @return A {@code JPanel} containing the accidents and events configuration UI. + */ + private JPanel createAccidentsAndEventsPanel() { + // Contents + chkAllAges = new CampaignOptionsCheckBox("AllAges"); + lblMilitaryAcademyAccidents = new CampaignOptionsLabel("MilitaryAcademyAccidents"); + spnMilitaryAcademyAccidents = new CampaignOptionsSpinner("MilitaryAcademyAccidents", + 10000, 0, 100000, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AccidentsAndEventsPanel", true, + "AccidentsAndEventsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + panel.add(chkAllAges, layout); + + layout.gridy++; + panel.add(lblMilitaryAcademyAccidents, layout); + layout.gridx++; + panel.add(spnMilitaryAcademyAccidents, layout); + + return panel; + } + + /** + * Creates the Name and Portrait Generation tab for the campaign options. + *

    + * This tab allows users to: + *

    + *
      + *
    • Enable or disable the use of origin factions for name generation.
    • + *
    • Assign portraits to personnel upon role changes.
    • + *
    • Customize which portraits are randomly used for specific roles.
    • + *
    + * + * @return A {@code JPanel} representing the Name and Portrait Generation tab. + */ + public JPanel createNameAndPortraitGenerationTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("NameAndPortraitGenerationTab", + getImageDirectory() + "logo_clan_nova_cat.png"); + + // Contents + chkAssignPortraitOnRoleChange = new CampaignOptionsCheckBox("AssignPortraitOnRoleChange"); + + chkUseOriginFactionForNames = new CampaignOptionsCheckBox("UseOriginFactionForNames"); + + lblFactionNames = new CampaignOptionsLabel("FactionNames"); + + pnlRandomPortrait = createRandomPortraitPanel(); + + // Layout the Panels + final JPanel panelTop = new CampaignOptionsStandardPanel("NameAndPortraitGenerationTab"); + final GridBagConstraints layoutTop = new CampaignOptionsGridBagConstraints(panelTop); + + layoutTop.gridwidth = 1; + layoutTop.gridx = 0; + layoutTop.gridy = 0; + panelTop.add(chkAssignPortraitOnRoleChange, layoutTop); + + layoutTop.gridy++; + panelTop.add(chkUseOriginFactionForNames, layoutTop); + layoutTop.gridx++; + panelTop.add(Box.createHorizontalStrut(UIUtil.scaleForGUI(25))); + layoutTop.gridx++; + panelTop.add(lblFactionNames, layoutTop); + layoutTop.gridx++; + panelTop.add(comboFactionNames, layoutTop); + + final JPanel panel = new CampaignOptionsStandardPanel("NameAndPortraitGenerationTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panel.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(panelTop, layoutParent); + + layoutParent.gridy++; + panel.add(pnlRandomPortrait, layoutParent); + + + // Create Parent Panel and return + return createParentPanel(panel, "NameAndPortraitGenerationTab"); + } + + /** + * Creates a panel for customizing random portrait generation for personnel roles. + *

    + * This includes: + *

    + *

  61. Options to enable or disable the use of role-specific portraits.
  62. + *
  63. Buttons to toggle all or no portrait options collectively.
  64. + *

    + * + * @return A {@code JPanel} containing the random portrait generation configuration UI. + */ + private JPanel createRandomPortraitPanel() { + // Contents + final PersonnelRole[] personnelRoles = PersonnelRole.values(); + + chkUsePortrait = new JCheckBox[personnelRoles.length]; + + btnEnableAllPortraits = new CampaignOptionsButton("EnableAllPortraits"); + btnEnableAllPortraits.addActionListener(evt -> { + for (JCheckBox checkBox : chkUsePortrait) { + checkBox.setSelected(true); + } + }); + + btnDisableAllPortraits = new CampaignOptionsButton("DisableAllPortraits"); + btnDisableAllPortraits.addActionListener(evt -> { + for (JCheckBox checkBox : chkUsePortrait) { + checkBox.setSelected(false); + } + }); + + // Layout the Panel + JPanel panel = new JPanel( + new GridLayout((int) Math.ceil((personnelRoles.length + 2) / 5.0), 5)); + panel.setBorder(BorderFactory.createTitledBorder( + String.format(String.format("%s", + resources.getString("lblRandomPortraitPanel.text"))))); + + panel.add(btnEnableAllPortraits); + panel.add(btnDisableAllPortraits); + + // Add remaining checkboxes + JCheckBox jCheckBox; + for (final PersonnelRole role : PersonnelRole.values()) { + jCheckBox = new JCheckBox(role.toString()); + panel.add(jCheckBox); + chkUsePortrait[role.ordinal()] = jCheckBox; + } + + return panel; + } + + /** + * Creates the Rank tab for configuring rank systems within the campaign. + *

    + * This tab provides options for: + *

      + *
    • Managing rank systems for personnel in the campaign.
    • + *
    • Displaying rank-related UI components for user configuration.
    • + *
    + * + * @return A {@code JPanel} representing the Rank tab in the campaign configuration. + */ + public JPanel createRankTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("RankTab", + getImageDirectory() + "logo_umayyad_caliphate.png", true); + + // Contents + Component rankSystemsViewport = rankSystemsPane.getViewport().getView(); + rankSystemsPane.applyToCampaign(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("RankTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panel.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(rankSystemsViewport, layoutParent); + + + // Create Parent Panel and return + return createParentPanel(panel, "RankTab"); + } + + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null, null, null); + } + + /** + * Loads values from campaign options, optionally integrating with presets for default settings. + * + * @param presetCampaignOptions Optional preset campaign options, or `null` to use the campaign's active settings. + * @param presetRandomOriginOptions Optional random origin options, or `null` to use the default origin settings. + * @param presetRankSystem Optional rank system, or `null` to use the default system. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions, + @Nullable RandomOriginOptions presetRandomOriginOptions, + @Nullable RankSystem presetRankSystem) { + CampaignOptions options = presetCampaignOptions; + if (options == null) { + options = this.campaignOptions; + } + + RandomOriginOptions originOptions = presetRandomOriginOptions; + if (originOptions == null) { + originOptions = this.randomOriginOptions; + } + + RankSystem rankSystem = presetRankSystem; + if (rankSystem == null) { + rankSystem = campaign.getRankSystem(); + } + + // General + chkUseDylansRandomXP.setSelected(options.isUseDylansRandomXP()); + sldGender.setValue(getPercentFemale()); // 'getPercentFemale' is not stored in a Preset + spnNonBinaryDiceSize.setValue(options.getNonBinaryDiceSize()); + comboFamilyDisplayLevel.setSelectedItem(options.getFamilyDisplayLevel()); + chkAnnounceOfficersOnly.setSelected(options.isAnnounceOfficersOnly()); + chkAnnounceBirthdays.setSelected(options.isAnnounceBirthdays()); + chkAnnounceChildBirthdays.setSelected(options.isAnnounceChildBirthdays()); + chkAnnounceRecruitmentAnniversaries.setSelected(options.isAnnounceRecruitmentAnniversaries()); + + // Backgrounds + chkUseRandomPersonalities.setSelected(options.isUseRandomPersonalities()); + chkUseRandomPersonalityReputation.setSelected(options.isUseRandomPersonalityReputation()); + chkUseIntelligenceXpMultiplier.setSelected(options.isUseIntelligenceXpMultiplier()); + chkUseSimulatedRelationships.setSelected(options.isUseSimulatedRelationships()); + chkRandomizeOrigin.setSelected(originOptions.isRandomizeOrigin()); + chkRandomizeDependentsOrigin.setSelected(originOptions.isRandomizeDependentOrigin()); + chkRandomizeAroundSpecifiedPlanet.setSelected(originOptions.isRandomizeAroundSpecifiedPlanet()); + comboSpecifiedPlanet.setSelectedItem(originOptions.getSpecifiedPlanet()); + spnOriginSearchRadius.setValue(originOptions.getOriginSearchRadius()); + spnOriginDistanceScale.setValue(originOptions.getOriginDistanceScale()); + chkAllowClanOrigins.setSelected(originOptions.isAllowClanOrigins()); + chkExtraRandomOrigin.setSelected(originOptions.isExtraRandomOrigin()); + + // Death + chkKeepMarriedNameUponSpouseDeath.setSelected(options.isKeepMarriedNameUponSpouseDeath()); + comboRandomDeathMethod.setSelectedItem(options.getRandomDeathMethod()); + chkUseRandomClanPersonnelDeath.setSelected(options.isUseRandomClanPersonnelDeath()); + chkUseRandomPrisonerDeath.setSelected(options.isUseRandomPrisonerDeath()); + chkUseRandomDeathSuicideCause.setSelected(options.isUseRandomDeathSuicideCause()); + spnPercentageRandomDeathChance.setValue(options.getPercentageRandomDeathChance()); + + Map deathAgeGroups = options.getEnabledRandomDeathAgeGroups(); + for (final AgeGroup ageGroup : AgeGroup.values()) { + chkEnabledRandomDeathAgeGroups.get(ageGroup).setSelected(deathAgeGroups.get(ageGroup)); + } + + // Education + chkUseEducationModule.setSelected(options.isUseEducationModule()); + spnCurriculumXpRate.setValue(options.getCurriculumXpRate()); + spnMaximumJumpCount.setValue(options.getMaximumJumpCount()); + chkUseReeducationCamps.setSelected(options.isUseReeducationCamps()); + chkEnableOverrideRequirements.setSelected(options.isEnableOverrideRequirements()); + chkShowIneligibleAcademies.setSelected(options.isEnableShowIneligibleAcademies()); + spnEntranceExamBaseTargetNumber.setValue(options.getEntranceExamBaseTargetNumber()); + chkEnableLocalAcademies.setSelected(options.isEnableLocalAcademies()); + chkEnablePrestigiousAcademies.setSelected(options.isEnablePrestigiousAcademies()); + chkEnableUnitEducation.setSelected(options.isEnableUnitEducation()); + chkEnableBonuses.setSelected(options.isEnableBonuses()); + spnFacultyXpMultiplier.setValue(options.getFacultyXpRate()); + spnAdultDropoutChance.setValue(options.getAdultDropoutChance()); + spnChildrenDropoutChance.setValue(options.getChildrenDropoutChance()); + chkAllAges.setSelected(options.isAllAges()); + spnMilitaryAcademyAccidents.setValue(options.getMilitaryAcademyAccidents()); + + // Name and Portraits + chkUseOriginFactionForNames.setSelected(options.isUseOriginFactionForNames()); + // 'RandomNameGenerator' is not stored in a Preset + comboFactionNames.setSelectedItem(RandomNameGenerator.getInstance().getChosenFaction()); + chkAssignPortraitOnRoleChange.setSelected(options.isAssignPortraitOnRoleChange()); + + final boolean[] usePortraitForRole = options.isUsePortraitForRoles(); + for (int i = 0; i < chkUsePortrait.length; i++) { + chkUsePortrait[i].setSelected(usePortraitForRole[i]); + } + + // Ranks + rankSystemsPane.getComboRankSystems().setSelectedItem(rankSystem); + } + + /** + * Applies the current settings from the market UI components back into + * the {@link CampaignOptions} of the associated campaign. + *

    + * If a preset options object is provided, the changes are applied there. + * Otherwise, they are applied to the current campaign's options. + * + * @param presetCampaignOptions A {@link CampaignOptions} object to update + * with the current UI settings, or {@code null} + * to apply changes to the campaign's options directly. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + RandomOriginOptions originOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + originOptions = this.randomOriginOptions; + } else { + originOptions = options.getRandomOriginOptions(); + } + + // General + options.setUseDylansRandomXP(chkUseDylansRandomXP.isSelected()); + RandomGenderGenerator.setPercentFemale(sldGender.getValue()); + options.setNonBinaryDiceSize((int) spnNonBinaryDiceSize.getValue()); + options.setFamilyDisplayLevel(comboFamilyDisplayLevel.getSelectedItem()); + options.setAnnounceOfficersOnly(chkAnnounceOfficersOnly.isSelected()); + options.setAnnounceBirthdays(chkAnnounceBirthdays.isSelected()); + options.setAnnounceChildBirthdays(chkAnnounceChildBirthdays.isSelected()); + options.setAnnounceRecruitmentAnniversaries(chkAnnounceRecruitmentAnniversaries.isSelected()); + + // Backgrounds + options.setUseRandomPersonalities(chkUseRandomPersonalities.isSelected()); + options.setUseRandomPersonalityReputation(chkUseRandomPersonalityReputation.isSelected()); + options.setUseIntelligenceXpMultiplier(chkUseIntelligenceXpMultiplier.isSelected()); + options.setUseSimulatedRelationships(chkUseSimulatedRelationships.isSelected()); + + originOptions.setRandomizeOrigin(chkRandomizeOrigin.isSelected()); + originOptions.setRandomizeDependentOrigin(chkRandomizeDependentsOrigin.isSelected()); + originOptions.setRandomizeAroundSpecifiedPlanet(chkRandomizeAroundSpecifiedPlanet.isSelected()); + originOptions.setSpecifiedPlanet(comboSpecifiedPlanet.getSelectedItem()); + originOptions.setOriginSearchRadius((int) spnOriginSearchRadius.getValue()); + originOptions.setOriginDistanceScale((double) spnOriginDistanceScale.getValue()); + originOptions.setAllowClanOrigins(chkAllowClanOrigins.isSelected()); + originOptions.setExtraRandomOrigin(chkExtraRandomOrigin.isSelected()); + options.setRandomOriginOptions(originOptions); + + // Death + options.setKeepMarriedNameUponSpouseDeath(chkKeepMarriedNameUponSpouseDeath.isSelected()); + options.setRandomDeathMethod(comboRandomDeathMethod.getSelectedItem()); + options.setUseRandomClanPersonnelDeath(chkUseRandomClanPersonnelDeath.isSelected()); + options.setUseRandomPrisonerDeath(chkUseRandomPrisonerDeath.isSelected()); + options.setUseRandomDeathSuicideCause(chkUseRandomDeathSuicideCause.isSelected()); + options.setPercentageRandomDeathChance((double) spnPercentageRandomDeathChance.getValue()); + for (final AgeGroup ageGroup : AgeGroup.values()) { + options.getEnabledRandomDeathAgeGroups().put(ageGroup, + chkEnabledRandomDeathAgeGroups.get(ageGroup).isSelected()); + } + + // Education + options.setUseEducationModule(chkUseEducationModule.isSelected()); + options.setCurriculumXpRate((int) spnCurriculumXpRate.getValue()); + options.setMaximumJumpCount((int) spnMaximumJumpCount.getValue()); + options.setUseReeducationCamps(chkUseReeducationCamps.isSelected()); + options.setEnableOverrideRequirements(chkEnableOverrideRequirements.isSelected()); + options.setEnableShowIneligibleAcademies(chkShowIneligibleAcademies.isSelected()); + options.setEntranceExamBaseTargetNumber((int) spnEntranceExamBaseTargetNumber.getValue()); + options.setEnableLocalAcademies(chkEnableLocalAcademies.isSelected()); + options.setEnablePrestigiousAcademies(chkEnablePrestigiousAcademies.isSelected()); + options.setEnableUnitEducation(chkEnableUnitEducation.isSelected()); + options.setEnableBonuses(chkEnableBonuses.isSelected()); + options.setFacultyXpRate((double) spnFacultyXpMultiplier.getValue()); + options.setAdultDropoutChance((int) spnAdultDropoutChance.getValue()); + options.setChildrenDropoutChance((int) spnChildrenDropoutChance.getValue()); + options.setAllAges(chkAllAges.isSelected()); + options.setMilitaryAcademyAccidents((int) spnMilitaryAcademyAccidents.getValue()); + + // Name and Portraits + options.setUseOriginFactionForNames(chkUseOriginFactionForNames.isSelected()); + options.setAssignPortraitOnRoleChange(chkAssignPortraitOnRoleChange.isSelected()); + RandomNameGenerator.getInstance().setChosenFaction(comboFactionNames.getSelectedItem()); + for (int i = 0; i < chkUsePortrait.length; i++) { + options.setUsePortraitForRole(i, chkUsePortrait[i].isSelected()); + } + + // Ranks + rankSystemsPane.applyToCampaign(); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/EquipmentAndSuppliesTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/EquipmentAndSuppliesTab.java new file mode 100644 index 00000000000..f06ea8c1a1a --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/EquipmentAndSuppliesTab.java @@ -0,0 +1,1197 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.EquipmentType; +import megamek.common.ITechnology; +import megamek.common.annotations.Nullable; +import mekhq.MekHQ; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.enums.PlanetaryAcquisitionFactionLimit; +import mekhq.campaign.personnel.SkillType; +import mekhq.gui.campaignOptions.components.*; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; + +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * The {@code EquipmentAndSuppliesTab} class represents a graphical user interface (GUI) + * tab containing various options and settings related to equipment and supplies in a campaign simulation. + * This class is responsible for building and managing multiple sub-tabs and panels for customization purposes, + * including acquisition settings, delivery settings, planetary acquisition settings, and more. + * It also provides methods to initialize, create, and manage these different components. + *

    + * Fields in this class include labels, spinners, combo boxes, checkboxes, and panels used for displaying + * and managing options in the tab. They allow the user to configure various parameters like transit times, + * penalties, acquisition limits, faction-specific settings, and modifiers to influence game mechanics. + * Multiple constants are defined for units of time, representing days, weeks, and months, among others. + *

    + * The class includes initialization methods for different sections of the tab, as well as methods + * to create panels for specific functionality. Utility methods are also provided for configuring spinners + * and combo boxes or formatting options and labels. + */ +public class EquipmentAndSuppliesTab { + // region Variable Declarations + private static String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE, + MekHQ.getMHQOptions().getLocale()); + + private CampaignOptions campaignOptions; + + //start Acquisition Tab + private JPanel pnlAcquisitions; + private JLabel lblChoiceAcquireSkill; + private MMComboBox choiceAcquireSkill; + private JCheckBox chkSupportStaffOnly; + private JLabel lblAcquireClanPenalty; + private JSpinner spnAcquireClanPenalty; + private JLabel lblAcquireIsPenalty; + private JSpinner spnAcquireIsPenalty; + private JLabel lblAcquireWaitingPeriod; + private JSpinner spnAcquireWaitingPeriod; + private JLabel lblMaxAcquisitions; + private JSpinner spnMaxAcquisitions; + //end Acquisition Tab + + //start autoLogistics Tab + private JPanel pnlAutoLogistics; + private JLabel lblAutoLogisticsHeatSink; + private JSpinner spnAutoLogisticsHeatSink; + private JLabel lblAutoLogisticsMekHead; + private JSpinner spnAutoLogisticsMekHead; + private JLabel lblAutoLogisticsMekLocation; + private JSpinner spnAutoLogisticsMekLocation; + private JLabel lblAutoLogisticsNonRepairableLocation; + private JSpinner spnAutoLogisticsNonRepairableLocation; + private JLabel lblAutoLogisticsArmor; + private JSpinner spnAutoLogisticsArmor; + private JLabel lblAutoLogisticsAmmunition; + private JSpinner spnAutoLogisticsAmmunition; + private JLabel lblAutoLogisticsOther; + private JSpinner spnAutoLogisticsOther; + //end autoLogistics Tab + + //start Delivery Tab + private JPanel pnlDeliveries; + private JLabel lblNDiceTransitTime; + private JSpinner spnNDiceTransitTime; + private JLabel lblConstantTransitTime; + private JSpinner spnConstantTransitTime; + private JLabel lblAcquireMosBonus; + private JSpinner spnAcquireMosBonus; + private JLabel lblAcquireMinimum; + private JSpinner spnAcquireMinimum; + private MMComboBox choiceTransitTimeUnits; + private MMComboBox choiceAcquireMosUnits; + private MMComboBox choiceAcquireMinimumUnit; + private static final int TRANSIT_UNIT_DAY = 0; + private static final int TRANSIT_UNIT_WEEK = 1; + private static final int TRANSIT_UNIT_MONTH = 2; + private static final int TRANSIT_UNIT_NUM = 3; + //end Delivery Tab + + //start Planetary Acquisition Tab + private JCheckBox usePlanetaryAcquisitions; + private JLabel lblMaxJumpPlanetaryAcquisitions; + private JSpinner spnMaxJumpPlanetaryAcquisitions; + private JLabel lblPlanetaryAcquisitionsFactionLimits; + private MMComboBox comboPlanetaryAcquisitionsFactionLimits; + private JCheckBox disallowClanPartsFromIS; + private JCheckBox disallowPlanetaryAcquisitionClanCrossover; + private JLabel lblPenaltyClanPartsFromIS; + private JSpinner spnPenaltyClanPartsFromIS; + private JCheckBox usePlanetaryAcquisitionsVerbose; + + private JPanel pnlTechModifiers; + private JLabel[] lblPlanetAcquireTechBonus; + private JSpinner[] spnPlanetAcquireTechBonus; + + private JPanel pnlIndustryModifiers; + private JLabel[] lblPlanetAcquireIndustryBonus; + private JSpinner[] spnPlanetAcquireIndustryBonus; + + private JPanel pnlOutputModifiers; + private JLabel[] lblPlanetAcquireOutputBonus; + private JSpinner[] spnPlanetAcquireOutputBonus; + //end Planetary Acquisition Tab + + //start Tech Limits Tab + private JCheckBox limitByYearBox; + private JCheckBox disallowExtinctStuffBox; + private JCheckBox allowClanPurchasesBox; + private JCheckBox allowISPurchasesBox; + private JCheckBox allowCanonOnlyBox; + private JCheckBox allowCanonRefitOnlyBox; + private JLabel lblChoiceTechLevel; + private MMComboBox choiceTechLevel; + private JCheckBox variableTechLevelBox; + private JCheckBox useAmmoByTypeBox; + //end Tech Limits Tab + + /** + * Constructs the EquipmentAndSuppliesTab with the given campaign options. + * + * @param campaignOptions the {@link CampaignOptions} object containing + * configuration settings for the campaign + */ + public EquipmentAndSuppliesTab(CampaignOptions campaignOptions) { + this.campaignOptions = campaignOptions; + + initialize(); + } + + /** + * Initializes the EquipmentAndSuppliesTab by configuring its various components and panels. + * This includes setting up the acquisitions tab, delivery tab, planetary acquisitions tab, + * and tech limits tab. + */ + void initialize() { + initializeAcquisitionTab(); + initializeAutoLogisticsTab(); + initializeDelivery(); + initializePlanetaryAcquisitionsTab(); + initializeTechLimitsTab(); + } + + /** + * Initializes the components and configurations for the Planetary Acquisitions tab in the + * EquipmentAndSuppliesTab. This includes setting up options, labels, spinners, combo boxes, + * checkboxes, and panels related to planetary acquisitions. + *

    + * The method sets up the following: + *

  65. Configuration options for planetary acquisitions such as enabling/disabling planetary acquisitions, + * setting faction limits, and managing clan/Inner Sphere specific rules.
  66. + *
  67. Panels and components for modifiers, including technology bonuses, industry bonuses, + * and output bonuses.
  68. + */ + private void initializePlanetaryAcquisitionsTab() { + // Options + usePlanetaryAcquisitions = new JCheckBox(); + + lblMaxJumpPlanetaryAcquisitions = new JLabel(); + spnMaxJumpPlanetaryAcquisitions = new JSpinner(); + + lblPlanetaryAcquisitionsFactionLimits = new JLabel(); + comboPlanetaryAcquisitionsFactionLimits = new MMComboBox<>("comboPlanetaryAcquisitionsFactionLimits", + PlanetaryAcquisitionFactionLimit.values()); + + disallowPlanetaryAcquisitionClanCrossover = new JCheckBox(); + + disallowClanPartsFromIS = new JCheckBox(); + + lblPenaltyClanPartsFromIS = new JLabel(); + spnPenaltyClanPartsFromIS = new JSpinner(); + + usePlanetaryAcquisitionsVerbose = new JCheckBox(); + + // Modifiers + pnlTechModifiers = new JPanel(); + lblPlanetAcquireTechBonus = new JLabel[6]; + spnPlanetAcquireTechBonus = new JSpinner[6]; + + pnlIndustryModifiers = new JPanel(); + lblPlanetAcquireIndustryBonus = new JLabel[6]; + spnPlanetAcquireIndustryBonus = new JSpinner[6]; + + pnlOutputModifiers = new JPanel(); + lblPlanetAcquireOutputBonus = new JLabel[6]; + spnPlanetAcquireOutputBonus = new JSpinner[6]; + } + + /** + * Initializes the components and configurations for the delivery tab within the + * EquipmentAndSuppliesTab. This method sets up panels, labels, spinners, and combo boxes required + * for managing delivery-related settings and options. + *

    + * The setup includes: + *

  69. The main delivery panel.
  70. + *
  71. Spinners for numeric inputs such as transit time settings and acquisition modifiers.
  72. + *
  73. Labels for providing descriptions for components.
  74. + *
  75. Combo boxes for selecting unit options related to transit times and acquisitions.
  76. + */ + private void initializeDelivery() { + pnlDeliveries = new JPanel(); + lblNDiceTransitTime = new JLabel(); + spnNDiceTransitTime = new JSpinner(); + lblConstantTransitTime = new JLabel(); + spnConstantTransitTime = new JSpinner(); + choiceTransitTimeUnits = new MMComboBox<>("choiceTransitTimeUnits", getTransitUnitOptions()); + + lblAcquireMosBonus = new JLabel(); + spnAcquireMosBonus = new JSpinner(); + choiceAcquireMosUnits = new MMComboBox<>("choiceAcquireMosUnits", getTransitUnitOptions()); + + lblAcquireMinimum = new JLabel(); + spnAcquireMinimum = new JSpinner(); + choiceAcquireMinimumUnit = new MMComboBox<>("choiceAcquireMinimumUnit", getTransitUnitOptions()); + } + + /** + * Initializes the components and settings for the acquisitions tab in the EquipmentAndSuppliesTab. + * This method sets up the GUI components required for configuring acquisition-related options, + * including panels, labels, spinners, combo boxes, and checkboxes. + */ + private void initializeAcquisitionTab() { + pnlAcquisitions = new JPanel(); + lblChoiceAcquireSkill = new JLabel(); + choiceAcquireSkill = new MMComboBox<>("choiceAcquireSkill", buildAcquireSkillComboOptions()); + + chkSupportStaffOnly = new JCheckBox(); + + lblAcquireClanPenalty = new JLabel(); + spnAcquireClanPenalty = new JSpinner(); + + lblAcquireIsPenalty = new JLabel(); + spnAcquireIsPenalty = new JSpinner(); + + lblAcquireWaitingPeriod = new JLabel(); + spnAcquireWaitingPeriod = new JSpinner(); + + lblMaxAcquisitions = new JLabel(); + spnMaxAcquisitions = new JSpinner(); + } + + /** + * Initializes the components and settings for the autoLogistics tab in the EquipmentAndSuppliesTab. + * This method sets up the GUI components required for configuring acquisition-related options, + * including panels, labels, spinners, combo boxes, and checkboxes. + */ + private void initializeAutoLogisticsTab() { + pnlAutoLogistics = new JPanel(); + lblAutoLogisticsHeatSink = new JLabel(); + spnAutoLogisticsHeatSink = new JSpinner(); + lblAutoLogisticsMekHead = new JLabel(); + spnAutoLogisticsMekHead = new JSpinner(); + lblAutoLogisticsMekLocation = new JLabel(); + spnAutoLogisticsMekLocation = new JSpinner(); + lblAutoLogisticsNonRepairableLocation = new JLabel(); + spnAutoLogisticsNonRepairableLocation = new JSpinner(); + lblAutoLogisticsArmor = new JLabel(); + spnAutoLogisticsArmor = new JSpinner(); + lblAutoLogisticsAmmunition = new JLabel(); + spnAutoLogisticsAmmunition = new JSpinner(); + lblAutoLogisticsOther = new JLabel(); + spnAutoLogisticsOther = new JSpinner(); + } + + /** + * Initializes the components and settings for the Tech Limits tab in the EquipmentAndSuppliesTab. + * This method sets up a series of checkboxes, labels, and combo boxes to configure technology-related + * limits and options. + */ + private void initializeTechLimitsTab() { + limitByYearBox = new JCheckBox(); + disallowExtinctStuffBox = new JCheckBox(); + allowClanPurchasesBox = new JCheckBox(); + allowISPurchasesBox = new JCheckBox(); + allowCanonOnlyBox = new JCheckBox(); + allowCanonRefitOnlyBox = new JCheckBox(); + lblChoiceTechLevel = new JLabel(); + choiceTechLevel = new MMComboBox<>("choiceTechLevel", getMaximumTechLevelOptions()); + variableTechLevelBox = new JCheckBox(); + useAmmoByTypeBox = new JCheckBox(); + } + + /** + * Creates and configures the acquisition tab panel for the user interface. + * This method initializes and organizes the components such as the header, + * acquisition panel, and delivery panel, and then returns the fully constructed + * acquisition tab panel. + * + * @return A {@code JPanel} instance representing the complete acquisition tab. + */ + public JPanel createAcquisitionTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("AcquisitionTab", + getImageDirectory() + "logo_clan_cloud_cobra.png"); + + pnlAcquisitions = createAcquisitionPanel(); + pnlAutoLogistics = createAutoLogisticsPanel(); + pnlDeliveries = createDeliveryPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("acquisitionTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panel.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(pnlAcquisitions, layoutParent); + + layoutParent.gridx++; + panel.add(pnlDeliveries, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + panel.add(pnlAutoLogistics, layoutParent); + + + // Create Parent Panel and return + return createParentPanel(panel, "acquisitionsTab"); + } + + /** + * Creates and returns a {@code JPanel} for configuring acquisition-related options. + * This panel includes various components such as labels, checkboxes, and + * spinners to allow users to set values for acquisition settings, including + * penalties, waiting periods, maximum acquisitions, and stock percentages. + * + * @return A {@code JPanel} populated with acquisition configuration components and their layout. + */ + private JPanel createAcquisitionPanel() { + // Content + lblChoiceAcquireSkill = new CampaignOptionsLabel("ChoiceAcquireSkill"); + + chkSupportStaffOnly = new CampaignOptionsCheckBox("SupportStaffOnly"); + + lblAcquireClanPenalty = new CampaignOptionsLabel("AcquireClanPenalty"); + spnAcquireClanPenalty = new CampaignOptionsSpinner("AcquireClanPenalty", + 0, 0, 13, 1); + + lblAcquireIsPenalty = new CampaignOptionsLabel("AcquireISPenalty"); + spnAcquireIsPenalty = new CampaignOptionsSpinner("AcquireISPenalty", + 0, 0, 13, 1); + + lblAcquireWaitingPeriod = new CampaignOptionsLabel("AcquireWaitingPeriod"); + spnAcquireWaitingPeriod = new CampaignOptionsSpinner("AcquireWaitingPeriod", + 1, 1, 365, 1); + + lblMaxAcquisitions = new CampaignOptionsLabel("MaxAcquisitions"); + spnMaxAcquisitions = new CampaignOptionsSpinner("MaxAcquisitions", + 0,0, 100, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AcquisitionPanel", true, + "AcquisitionPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblChoiceAcquireSkill, layout); + layout.gridx++; + panel.add(choiceAcquireSkill, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkSupportStaffOnly, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblAcquireClanPenalty, layout); + layout.gridx++; + panel.add(spnAcquireClanPenalty, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAcquireIsPenalty, layout); + layout.gridx++; + panel.add(spnAcquireIsPenalty, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAcquireWaitingPeriod, layout); + layout.gridx++; + panel.add(spnAcquireWaitingPeriod, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblMaxAcquisitions, layout); + layout.gridx++; + panel.add(spnMaxAcquisitions, layout); + + return panel; + } + + /** + * Creates and returns a {@code JPanel} for configuring autoLogistics-related options. + * This panel includes various components such as labels, checkboxes, and + * spinners to allow users to set values for acquisition settings, including + * penalties, waiting periods, maximum acquisitions, and stock percentages. + * + * @return A {@code JPanel} populated with autoLogistics configuration components and their layout. + */ + private JPanel createAutoLogisticsPanel() { + // Content + lblAutoLogisticsMekHead = new CampaignOptionsLabel("AutoLogisticsMekHead"); + spnAutoLogisticsMekHead = new CampaignOptionsSpinner("AutoLogisticsMekHead", + 200, 0, 10000, 1); + + lblAutoLogisticsMekLocation = new CampaignOptionsLabel("AutoLogisticsMekLocation"); + spnAutoLogisticsMekLocation = new CampaignOptionsSpinner("AutoLogisticsMekLocation", + 100, 0, 10000, 1); + + lblAutoLogisticsNonRepairableLocation = new CampaignOptionsLabel("AutoLogisticsNonRepairableLocation"); + spnAutoLogisticsNonRepairableLocation = new CampaignOptionsSpinner("AutoLogisticsNonRepairableLocation", + 0, 0, 10000, 1); + + lblAutoLogisticsArmor = new CampaignOptionsLabel("AutoLogisticsArmor"); + spnAutoLogisticsArmor = new CampaignOptionsSpinner("AutoLogisticsArmor", + 500, 0, 10000, 1); + + lblAutoLogisticsAmmunition = new CampaignOptionsLabel("AutoLogisticsAmmunition"); + spnAutoLogisticsAmmunition = new CampaignOptionsSpinner("AutoLogisticsAmmunition", + 500, 0, 10000, 1); + + lblAutoLogisticsHeatSink = new CampaignOptionsLabel("AutoLogisticsHeatSink"); + spnAutoLogisticsHeatSink = new CampaignOptionsSpinner("AutoLogisticsHeatSink", + 250, 0, 10000, 1); + + lblAutoLogisticsOther = new CampaignOptionsLabel("AutoLogisticsOther"); + spnAutoLogisticsOther = new CampaignOptionsSpinner("AutoLogisticsOther", + 50, 0, 10000, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AutoLogisticsPanel", true, + "AutoLogisticsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblAutoLogisticsMekHead, layout); + layout.gridx++; + panel.add(spnAutoLogisticsMekHead, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAutoLogisticsMekLocation, layout); + layout.gridx++; + panel.add(spnAutoLogisticsMekLocation, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAutoLogisticsNonRepairableLocation, layout); + layout.gridx++; + panel.add(spnAutoLogisticsNonRepairableLocation, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAutoLogisticsHeatSink, layout); + layout.gridx++; + panel.add(spnAutoLogisticsHeatSink, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAutoLogisticsArmor, layout); + layout.gridx++; + panel.add(spnAutoLogisticsArmor, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAutoLogisticsAmmunition, layout); + layout.gridx++; + panel.add(spnAutoLogisticsAmmunition, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAutoLogisticsOther, layout); + layout.gridx++; + panel.add(spnAutoLogisticsOther, layout); + + return panel; + } + + /** + * Creates and configures a delivery panel composed of multiple sub-panels for managing transit time and delivery-related settings. + * The panel includes components such as labels, spinners, and choice units, and ensures proper layout and structure. + * + * @return a {@code JPanel} instance representing the delivery panel with all configured sub-panels and components. + */ + private JPanel createDeliveryPanel() { + lblNDiceTransitTime = new CampaignOptionsLabel("NDiceTransitTime"); + spnNDiceTransitTime = new CampaignOptionsSpinner("NDiceTransitTime", 0, + 0, 365, 1); + + lblConstantTransitTime = new CampaignOptionsLabel("ConstantTransitTime"); + spnConstantTransitTime = new CampaignOptionsSpinner("ConstantTransitTime", + 0, 0, 365, 1); + + lblAcquireMosBonus = new CampaignOptionsLabel("AcquireMosBonus"); + spnAcquireMosBonus = new CampaignOptionsSpinner("AcquireMosBonus", + 0, 0, 365, 1); + + lblAcquireMinimum = new CampaignOptionsLabel("AcquireMinimum"); + spnAcquireMinimum = new CampaignOptionsSpinner("AcquireMinimum", + 0, 0, 365, 1); + + // Layout the Panel + final JPanel panelTransit = new CampaignOptionsStandardPanel("DeliveryPanelTransit"); + final GridBagConstraints layoutTransit = new CampaignOptionsGridBagConstraints(panelTransit); + + layoutTransit.gridy = 0; + layoutTransit.gridx = 0; + layoutTransit.gridwidth = 1; + panelTransit.add(lblNDiceTransitTime, layoutTransit); + layoutTransit.gridx++; + panelTransit.add(spnNDiceTransitTime, layoutTransit); + layoutTransit.gridx++; + panelTransit.add(lblConstantTransitTime, layoutTransit); + layoutTransit.gridx++; + panelTransit.add(spnConstantTransitTime, layoutTransit); + layoutTransit.gridx++; + panelTransit.add(choiceTransitTimeUnits, layoutTransit); + + // Layout the Panel + final JPanel panelDeliveries = new CampaignOptionsStandardPanel("DeliveryPanelDeliveries"); + final GridBagConstraints layoutDeliveries = new CampaignOptionsGridBagConstraints(panelDeliveries); + + layoutDeliveries.gridy = 0; + layoutDeliveries.gridx = 0; + layoutDeliveries.gridwidth = 1; + panelDeliveries.add(lblAcquireMosBonus, layoutDeliveries); + layoutDeliveries.gridx++; + panelDeliveries.add(spnAcquireMosBonus, layoutDeliveries); + layoutDeliveries.gridx++; + panelDeliveries.add(choiceAcquireMosUnits, layoutDeliveries); + + layoutDeliveries.gridx = 0; + layoutDeliveries.gridy++; + panelDeliveries.add(lblAcquireMinimum, layoutDeliveries); + layoutDeliveries.gridx++; + panelDeliveries.add(spnAcquireMinimum, layoutDeliveries); + layoutDeliveries.gridx++; + panelDeliveries.add(choiceAcquireMinimumUnit, layoutDeliveries); + + final JPanel panelParent = new CampaignOptionsStandardPanel("DeliveryPanel", true, + "DeliveryPanel"); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridy = 0; + layoutParent.gridx = 0; + layoutParent.gridwidth = 2; + panelParent.add(panelTransit, layoutParent); + layoutParent.gridy++; + panelParent.add(panelDeliveries, layoutParent); + + return panelParent; + } + + /** + * Creates and configures the planetary acquisition tab panel in a campaign options interface. + * The panel includes a header, options, and modifiers section, arranged using + * layout constraints. Once configured, it is wrapped within a parent panel and returned. + * + * @return a {@code JPanel} object representing the planetary acquisition tab with its configured components and layout. + */ + public JPanel createPlanetaryAcquisitionTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("PlanetaryAcquisitionTab", + getImageDirectory() + "logo_rim_worlds_republic.png"); + + // Sub-Panels + JPanel options = createOptionsPanel(); + JPanel modifiers = createModifiersPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PlanetaryAcquisitionTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridy = 0; + panel.add(headerPanel, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(options, layoutParent); + + layoutParent.gridx++; + panel.add(modifiers, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panel, "PlanetaryAcquisitionTab"); + } + + + /** + * Creates and returns a {@code JPanel} containing the components necessary + * for configuring campaign options related to planetary acquisitions. + * This panel includes various labels, checkboxes, and spinners + * for setting and adjusting relevant options. + * + * @return a {@code JPanel} containing the campaign options panel for planetary acquisitions. + */ + private JPanel createOptionsPanel() { + usePlanetaryAcquisitions = new CampaignOptionsCheckBox("UsePlanetaryAcquisitions"); + + lblMaxJumpPlanetaryAcquisitions = new CampaignOptionsLabel("MaxJumpPlanetaryAcquisitions"); + spnMaxJumpPlanetaryAcquisitions = new CampaignOptionsSpinner("MaxJumpPlanetaryAcquisitions", + 2, 0, 5, 1); + + lblPlanetaryAcquisitionsFactionLimits = new CampaignOptionsLabel("PlanetaryAcquisitionsFactionLimits"); + + disallowPlanetaryAcquisitionClanCrossover = new CampaignOptionsCheckBox("DisallowPlanetaryAcquisitionClanCrossover"); + + disallowClanPartsFromIS = new CampaignOptionsCheckBox("DisallowClanPartsFromIS"); + + lblPenaltyClanPartsFromIS = new CampaignOptionsLabel("PenaltyClanPartsFromIS"); + spnPenaltyClanPartsFromIS = new CampaignOptionsSpinner("PenaltyClanPartsFromIS", + 0, 0, 12, 1); + + usePlanetaryAcquisitionsVerbose = new CampaignOptionsCheckBox("UsePlanetaryAcquisitionsVerbose"); + + // Layout the Panel + final JPanel panel = new JPanel(); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + panel.add(usePlanetaryAcquisitions, layout); + layout.gridx++; + panel.add(usePlanetaryAcquisitionsVerbose, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblMaxJumpPlanetaryAcquisitions, layout); + layout.gridx++; + panel.add(spnMaxJumpPlanetaryAcquisitions, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblPlanetaryAcquisitionsFactionLimits, layout); + layout.gridx++; + panel.add(comboPlanetaryAcquisitionsFactionLimits, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(disallowPlanetaryAcquisitionClanCrossover, layout); + + layout.gridy++; + panel.add(disallowClanPartsFromIS, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblPenaltyClanPartsFromIS, layout); + layout.gridx++; + panel.add(spnPenaltyClanPartsFromIS, layout); + + return panel; + } + + /** + * Creates and returns a panel that organizes and displays the planetary acquisition + * modifiers for technology, industry, and output. This method sets up spinners and + * labels for each equipment type rating (A through F) to adjust acquisition bonuses. + *

    + * The method initializes modifier spinners for technology, industry, and output + * acquisition bonuses, creates separate panels for each category, and combines them + * into a single panel using a grid layout. + * + * @return a {@code JPanel} representing the planetary acquisition modifiers panel, including + * elements for adjusting technology, industry, and output modifiers. + */ + private JPanel createModifiersPanel() { + // Modifier Spinners + for (int i = EquipmentType.RATING_A; i <= EquipmentType.RATING_F; i++) { + String modifierLabel = getModifierLabel(i); + + lblPlanetAcquireTechBonus[i] = new JLabel(String.format("%s", + modifierLabel)); + spnPlanetAcquireTechBonus[i] = new JSpinner(new SpinnerNumberModel( + 0, -12, 12, 1)); + setSpinnerWidth(spnPlanetAcquireTechBonus[i]); + + lblPlanetAcquireIndustryBonus[i] = new JLabel(String.format("%s", + modifierLabel)); + spnPlanetAcquireIndustryBonus[i] = new JSpinner(new SpinnerNumberModel( + 0, -12, 12, 1)); + setSpinnerWidth(spnPlanetAcquireIndustryBonus[i]); + + lblPlanetAcquireOutputBonus[i] = new JLabel(String.format("%s", + modifierLabel)); + spnPlanetAcquireOutputBonus[i] = new JSpinner(new SpinnerNumberModel( + 0, -12, 12, 1)); + setSpinnerWidth(spnPlanetAcquireOutputBonus[i]); + } + + // Panels + pnlIndustryModifiers = createIndustryModifiersPanel(); + pnlTechModifiers = createTechModifiersPanel(); + pnlOutputModifiers = createOutputModifiersPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PlanetaryAcquisitionTabModifiers", + true, "ModifiersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + panel.add(pnlIndustryModifiers, layout); + layout.gridx++; + panel.add(pnlTechModifiers, layout); + layout.gridx++; + panel.add(pnlOutputModifiers, layout); + + return panel; + } + + /** + * Creates and returns a {@code JPanel} layout containing components for configuring + * technology-related modifiers in a campaign setting. The panel includes + * labels and corresponding input components (spinners) arranged in a + * grid layout. + * + * @return a {@code JPanel} containing the layout for technology modifiers configuration + */ + private JPanel createTechModifiersPanel() { + JLabel techLabel = new CampaignOptionsLabel("TechLabel"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("createTechModifiersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + // Add the label + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + layout.weightx = 1.0; + panel.add(techLabel, layout); + + // Add the other elements + for (int i = 0; i < 6; i++) { + layout.gridx = 0; + layout.gridy = i + 1; + layout.gridwidth = 1; + layout.weightx = 0; + layout.anchor = GridBagConstraints.WEST; + panel.add(lblPlanetAcquireTechBonus[i], layout); + + layout.gridx++; + panel.add(spnPlanetAcquireTechBonus[i], layout); + } + + return panel; + } + + /** + * Creates and configures a {@code JPanel} that serves as the Industry Modifiers Panel. + * The panel contains labels and spinners arranged in a grid layout to display + * and allow modification of industry bonuses. + * + * @return a {@code JPanel} component configured as the Industry Modifiers Panel with + * labels and spinners for industry adjustment. + */ + private JPanel createIndustryModifiersPanel() { + JLabel industryLabel = new CampaignOptionsLabel("IndustryLabel"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("IndustryModifiersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + // Add the label + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + layout.weightx = 1.0; + panel.add(industryLabel, layout); + + // Add the other elements + for (int i = 0; i < 6; i++) { + layout.gridx = 0; + layout.gridy = i + 1; + layout.gridwidth = 1; + layout.weightx = 0; + layout.anchor = GridBagConstraints.WEST; + panel.add(lblPlanetAcquireIndustryBonus[i], layout); + + layout.gridx++; + panel.add(spnPlanetAcquireIndustryBonus[i], layout); + } + + return panel; + } + + /** + * Creates and configures a {@code JPanel} for displaying and adjusting output modifiers. + * The panel includes labels and corresponding spinner components to modify + * planet acquisition output bonuses. + * + * @return a {@code JPanel} configured with labels and spinners for planet output modifiers + */ + private JPanel createOutputModifiersPanel() { + JLabel outputLabel = new CampaignOptionsLabel("OutputLabel"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("OutputModifiersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + // Add the label + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + layout.weightx = 1.0; + panel.add(outputLabel, layout); + + // Add the other elements + for (int i = 0; i < 6; i++) { + layout.gridx = 0; + layout.gridy = i + 1; + layout.gridwidth = 1; + layout.weightx = 0; + layout.anchor = GridBagConstraints.WEST; + panel.add(lblPlanetAcquireOutputBonus[i], layout); + + layout.gridx++; + panel.add(spnPlanetAcquireOutputBonus[i], layout); + } + + return panel; + } + + /** + * Sets the minimum width of the specified JSpinner component by scaling its preferred size. + * + * @param spinner the JSpinner component whose minimum width is to be set + */ + private void setSpinnerWidth(JSpinner spinner) { + Dimension size = spinner.getPreferredSize(); + spinner.setMinimumSize(UIUtil.scaleForGUI(size.width, size.height)); + } + + /** + * Determines the modifier label based on the provided quality rating. + * + * @param quality the integer representing the quality rating, corresponding to predefined + * constants in EquipmentType. + * @return the string label associated with the provided quality rating. Returns "A" for RATING_A, + * "B" for RATING_B, etc., or "ERROR" if the quality does not match any predefined ratings. + */ + private String getModifierLabel(int quality) { + return switch (quality) { + case EquipmentType.RATING_A -> "A"; + case EquipmentType.RATING_B -> "B"; + case EquipmentType.RATING_C -> "C"; + case EquipmentType.RATING_D -> "D"; + case EquipmentType.RATING_E -> "E"; + case EquipmentType.RATING_F -> "F"; + default -> "ERROR"; + }; + } + + /** + * Creates and returns a DefaultComboBoxModel containing the transit unit options. + * + * @return a DefaultComboBoxModel populated with transit unit names based on TRANSIT_UNIT_NUM. + */ + private static DefaultComboBoxModel getTransitUnitOptions() { + DefaultComboBoxModel transitUnitModel = new DefaultComboBoxModel<>(); + + for (int i = 0; i < TRANSIT_UNIT_NUM; i++) { + transitUnitModel.addElement(getTransitUnitName(i)); + } + return transitUnitModel; + } + + /** + * Retrieves the name of the transit unit based on the provided unit value. + * + * @param unit the integer value representing the transit unit (e.g., day, week, month) + * @return the name of the transit unit as a string, or "ERROR" if the unit is not recognized + */ + private static String getTransitUnitName(final int unit) { + return switch (unit) { + case TRANSIT_UNIT_DAY -> resources.getString("transitUnitNamesDays.text"); + case TRANSIT_UNIT_WEEK -> resources.getString("transitUnitNamesWeeks.text"); + case TRANSIT_UNIT_MONTH -> resources.getString("transitUnitNamesMonths.text"); + default -> "ERROR"; + }; + } + + /** + * Builds a DefaultComboBoxModel containing a predefined set of skill options + * that can be acquired. The options include technical, administrative, + * scrounge, negotiation, and auto skills. + * + * @return a DefaultComboBoxModel containing the skill options as string elements. + */ + private static DefaultComboBoxModel buildAcquireSkillComboOptions() { + DefaultComboBoxModel acquireSkillModel = new DefaultComboBoxModel<>(); + + acquireSkillModel.addElement(CampaignOptions.S_TECH); + acquireSkillModel.addElement(SkillType.S_ADMIN); + acquireSkillModel.addElement(SkillType.S_SCROUNGE); + acquireSkillModel.addElement(SkillType.S_NEG); + acquireSkillModel.addElement(CampaignOptions.S_AUTO); + + return acquireSkillModel; + } + + /** + * Creates and initializes the "Tech Limits" tab panel within a user interface. + * The tab includes various settings and options related to technical limitations, + * such as limiting by year, disallowing extinct technologies, allowing faction-specific purchases, + * enabling canon-only restrictions, setting maximum tech levels, and more. + * The method arranges the components in a structured layout and constructs the required parent panel. + * + * @return the {@code JPanel} representing the "Tech Limits" tab, fully configured with its components and layout. + */ + public JPanel createTechLimitsTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("TechLimitsTab", + getImageDirectory() + "logo_clan_ghost_bear.png"); + + limitByYearBox = new CampaignOptionsCheckBox("LimitByYearBox"); + + disallowExtinctStuffBox = new CampaignOptionsCheckBox("DisallowExtinctStuffBox"); + + allowClanPurchasesBox = new CampaignOptionsCheckBox("AllowClanPurchasesBox"); + allowISPurchasesBox = new CampaignOptionsCheckBox("AllowISPurchasesBox"); + + // Canon Purchases/Refits + allowCanonOnlyBox = new CampaignOptionsCheckBox("AllowCanonOnlyBox"); + allowCanonRefitOnlyBox = new CampaignOptionsCheckBox("AllowCanonRefitOnlyBox"); + + // Maximum Tech Level + lblChoiceTechLevel = new CampaignOptionsLabel("ChoiceTechLevel"); + choiceTechLevel = new MMComboBox<>("choiceTechLevel", getMaximumTechLevelOptions()); + choiceTechLevel.setToolTipText(String.format("%s", + resources.getString("lblChoiceTechLevel.tooltip"))); + + // Variable Tech Level + variableTechLevelBox = new CampaignOptionsCheckBox("VariableTechLevelBox"); + + // Ammo by Type + useAmmoByTypeBox = new CampaignOptionsCheckBox("UseAmmoByTypeBox"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("TechLimitsTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panel.add(headerPanel, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(lblChoiceTechLevel, layoutParent); + layoutParent.gridx++; + panel.add(choiceTechLevel, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + panel.add(limitByYearBox, layoutParent); + layoutParent.gridx++; + panel.add(disallowExtinctStuffBox, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + panel.add(allowClanPurchasesBox, layoutParent); + layoutParent.gridx++; + panel.add(allowISPurchasesBox, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + panel.add(allowCanonOnlyBox, layoutParent); + layoutParent.gridx++; + panel.add(allowCanonRefitOnlyBox, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + panel.add(variableTechLevelBox, layoutParent); + layoutParent.gridx++; + panel.add(useAmmoByTypeBox, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panel, "TechLimitsTab"); + } + + /** + * Creates and returns a DefaultComboBoxModel containing the available + * options for maximum technology levels. + * + * @return A DefaultComboBoxModel populated with the list of + * technology level names corresponding to the defined constants + * in CampaignOptions (e.g., TECH_INTRO, TECH_STANDARD, etc.). + */ + private static DefaultComboBoxModel getMaximumTechLevelOptions() { + DefaultComboBoxModel maximumTechLevelModel = new DefaultComboBoxModel<>(); + + maximumTechLevelModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_INTRO)); + maximumTechLevelModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_STANDARD)); + maximumTechLevelModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_ADVANCED)); + maximumTechLevelModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_EXPERIMENTAL)); + maximumTechLevelModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_UNOFFICIAL)); + + return maximumTechLevelModel; + } + + /** + * Applies the given campaign options to the campaign or uses default options if none are provided. + * This method updates the campaign settings for acquisitions, deliveries, planetary acquisitions, + * and technological limits to customize campaign behavior. + * + * @param presetCampaignOptions the campaign options to apply; if null, default campaign options + * are used instead + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Acquisitions + options.setAcquisitionSkill(choiceAcquireSkill.getSelectedItem()); + options.setAcquisitionSupportStaffOnly(chkSupportStaffOnly.isSelected()); + options.setClanAcquisitionPenalty((int) spnAcquireClanPenalty.getValue()); + options.setIsAcquisitionPenalty((int) spnAcquireIsPenalty.getValue()); + options.setWaitingPeriod((int) spnAcquireWaitingPeriod.getValue()); + options.setMaxAcquisitions((int) spnMaxAcquisitions.getValue()); + + // autoLogistics + options.setAutoLogisticsMekHead((int) spnAutoLogisticsMekHead.getValue()); + options.setAutoLogisticsMekLocation((int) spnAutoLogisticsMekLocation.getValue()); + options.setAutoLogisticsNonRepairableLocation((int) spnAutoLogisticsNonRepairableLocation.getValue()); + options.setAutoLogisticsArmor((int) spnAutoLogisticsArmor.getValue()); + options.setAutoLogisticsAmmunition((int) spnAutoLogisticsAmmunition.getValue()); + options.setAutoLogisticsHeatSink((int) spnAutoLogisticsHeatSink.getValue()); + options.setAutoLogisticsOther((int) spnAutoLogisticsOther.getValue()); + + // Delivery + options.setNDiceTransitTime((int) spnNDiceTransitTime.getValue()); + options.setConstantTransitTime((int) spnConstantTransitTime.getValue()); + options.setUnitTransitTime(choiceTransitTimeUnits.getSelectedIndex()); + options.setAcquireMosBonus((int) spnAcquireMosBonus.getValue()); + options.setAcquireMosUnit(choiceAcquireMosUnits.getSelectedIndex()); + options.setAcquireMinimumTime((int) spnAcquireMinimum.getValue()); + options.setAcquireMinimumTimeUnit(choiceAcquireMinimumUnit.getSelectedIndex()); + + // Planetary Acquisitions + options.setPlanetaryAcquisition(usePlanetaryAcquisitions.isSelected()); + options.setMaxJumpsPlanetaryAcquisition((int) spnMaxJumpPlanetaryAcquisitions.getValue()); + options.setPlanetAcquisitionFactionLimit(comboPlanetaryAcquisitionsFactionLimits.getSelectedItem()); + options.setDisallowPlanetAcquisitionClanCrossover(disallowPlanetaryAcquisitionClanCrossover.isSelected()); + options.setDisallowClanPartsFromIS(disallowClanPartsFromIS.isSelected()); + options.setPenaltyClanPartsFromIS((int) spnPenaltyClanPartsFromIS.getValue()); + options.setPlanetAcquisitionVerboseReporting(usePlanetaryAcquisitionsVerbose.isSelected()); + for (int i = ITechnology.RATING_A; i <= ITechnology.RATING_F; i++) { + options.setPlanetTechAcquisitionBonus((int) spnPlanetAcquireTechBonus[i].getValue(), i); + options.setPlanetIndustryAcquisitionBonus( + (int) spnPlanetAcquireIndustryBonus[i].getValue(), i); + options.setPlanetOutputAcquisitionBonus((int) spnPlanetAcquireOutputBonus[i].getValue(), + i); + } + + // Tech Limits + options.setLimitByYear(limitByYearBox.isSelected()); + options.setDisallowExtinctStuff(disallowExtinctStuffBox.isSelected()); + options.setAllowClanPurchases(allowClanPurchasesBox.isSelected()); + options.setAllowISPurchases(allowISPurchasesBox.isSelected()); + options.setAllowCanonOnly(allowCanonOnlyBox.isSelected()); + options.setAllowCanonRefitOnly(allowCanonRefitOnlyBox.isSelected()); + options.setTechLevel(choiceTechLevel.getSelectedIndex()); + options.setVariableTechLevel(variableTechLevelBox.isSelected()); + options.setUseAmmoByType(useAmmoByTypeBox.isSelected()); + } + + /** + * Loads values from the campaign options. This method serves as a convenience + * method that calls the overloaded version of {@code loadValuesFromCampaignOptions} + * with a {@code null} parameter. + *

    + * This method is typically used to initialize or update certain settings or + * configurations based on the campaign options when no specific options are provided. + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null); + } + + /** + * Loads values from the provided CampaignOptions instance into the UI components. + * If the provided CampaignOptions instance is null, it defaults to using the internal campaignOptions instance. + * + * @param presetCampaignOptions the CampaignOptions instance containing the preset values to load, + * or null to use the default internal campaignOptions. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Acquisitions + choiceAcquireSkill.setSelectedItem(options.getAcquisitionSkill()); + chkSupportStaffOnly.setSelected(options.isAcquisitionSupportStaffOnly()); + spnAcquireClanPenalty.setValue(options.getClanAcquisitionPenalty()); + spnAcquireIsPenalty.setValue(options.getIsAcquisitionPenalty()); + spnAcquireWaitingPeriod.setValue(options.getWaitingPeriod()); + spnMaxAcquisitions.setValue(options.getMaxAcquisitions()); + + // autoLogistics + spnAutoLogisticsMekHead.setValue(options.getAutoLogisticsMekHead()); + spnAutoLogisticsMekLocation.setValue(options.getAutoLogisticsMekLocation()); + spnAutoLogisticsNonRepairableLocation.setValue(options.getAutoLogisticsNonRepairableLocation()); + spnAutoLogisticsArmor.setValue(options.getAutoLogisticsArmor()); + spnAutoLogisticsAmmunition.setValue(options.getAutoLogisticsAmmunition()); + spnAutoLogisticsHeatSink.setValue(options.getAutoLogisticsHeatSink()); + spnAutoLogisticsOther.setValue(options.getAutoLogisticsOther()); + + // Delivery + spnNDiceTransitTime.setValue(options.getNDiceTransitTime()); + spnConstantTransitTime.setValue(options.getConstantTransitTime()); + choiceTransitTimeUnits.setSelectedIndex(options.getUnitTransitTime()); + + spnAcquireMosBonus.setValue(options.getAcquireMosBonus()); + choiceAcquireMosUnits.setSelectedIndex(options.getAcquireMosUnit()); + + spnAcquireMinimum.setValue(options.getAcquireMinimumTime()); + choiceAcquireMinimumUnit.setSelectedIndex(options.getAcquireMinimumTimeUnit()); + + // Planetary Acquisitions + usePlanetaryAcquisitions.setSelected(options.isUsePlanetaryAcquisition()); + spnMaxJumpPlanetaryAcquisitions.setValue(options.getMaxJumpsPlanetaryAcquisition()); + comboPlanetaryAcquisitionsFactionLimits.setSelectedItem(options.getPlanetAcquisitionFactionLimit()); + disallowPlanetaryAcquisitionClanCrossover.setSelected(options.isPlanetAcquisitionNoClanCrossover()); + disallowClanPartsFromIS.setSelected(options.isNoClanPartsFromIS()); + spnPenaltyClanPartsFromIS.setValue(options.getPenaltyClanPartsFromIS()); + usePlanetaryAcquisitionsVerbose.setSelected(options.isPlanetAcquisitionVerbose()); + for (int i = EquipmentType.RATING_A; i <= EquipmentType.RATING_F; i++) { + spnPlanetAcquireTechBonus[i].setValue(options.getPlanetTechAcquisitionBonus(i)); + spnPlanetAcquireIndustryBonus[i].setValue(options.getPlanetIndustryAcquisitionBonus(i)); + spnPlanetAcquireOutputBonus[i].setValue(options.getPlanetOutputAcquisitionBonus(i)); + } + + // Tech Limits + limitByYearBox.setSelected(options.isLimitByYear()); + disallowExtinctStuffBox.setSelected(options.isDisallowExtinctStuff()); + allowClanPurchasesBox.setSelected(options.isAllowClanPurchases()); + allowISPurchasesBox.setSelected(options.isAllowISPurchases()); + allowCanonOnlyBox.setSelected(options.isAllowCanonOnly()); + allowCanonRefitOnlyBox.setSelected(options.isAllowCanonRefitOnly()); + choiceTechLevel.setSelectedIndex(options.getTechLevel()); + variableTechLevelBox.setSelected(options.isVariableTechLevel()); + useAmmoByTypeBox.setSelected(options.isUseAmmoByType()); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/FinancesTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/FinancesTab.java new file mode 100644 index 00000000000..02b5260ab84 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/FinancesTab.java @@ -0,0 +1,854 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.common.annotations.Nullable; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.finances.enums.FinancialYearDuration; +import mekhq.campaign.parts.enums.PartQuality; +import mekhq.gui.campaignOptions.components.*; + +import javax.swing.*; +import javax.swing.JSpinner.DefaultEditor; +import javax.swing.JSpinner.NumberEditor; +import java.awt.*; + +import static mekhq.campaign.parts.enums.PartQuality.QUALITY_F; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * The FinancesTab class represents a UI tab within a larger financial management system + * for a campaign. It provides panels, checkboxes, spinners, combo boxes, and other controls + * to manage and configure various financial options, payments, sales, taxes, shares, + * and price multipliers for the campaign. + *

    + * It is primarily composed of multiple `JPanel` sections organized using + * `GroupLayout` for modularity and clarity. + */ +public class FinancesTab { + private final Campaign campaign; + private final CampaignOptions campaignOptions; + + //start General Options + private JPanel pnlGeneralOptions; + private JCheckBox useLoanLimitsBox; + private JCheckBox usePercentageMaintenanceBox; + private JCheckBox useExtendedPartsModifierBox; + private JCheckBox usePeacetimeCostBox; + private JCheckBox showPeacetimeCostBox; + private JLabel lblFinancialYearDuration; + private MMComboBox comboFinancialYearDuration; + private JCheckBox newFinancialYearFinancesToCSVExportBox; + + private JPanel pnlPayments; + private JCheckBox payForPartsBox; + private JCheckBox payForRepairsBox; + private JCheckBox payForUnitsBox; + private JCheckBox payForSalariesBox; + private JCheckBox payForOverheadBox; + private JCheckBox payForMaintainBox; + private JCheckBox payForTransportBox; + private JCheckBox payForRecruitmentBox; + + + private JPanel pnlSales; + private JCheckBox sellUnitsBox; + private JCheckBox sellPartsBox; + + private JPanel pnlOtherSystems; + + private JPanel pnlTaxes; + private JCheckBox chkUseTaxes; + private JLabel lblTaxesPercentage; + private JSpinner spnTaxesPercentage; + + private JPanel pnlShares; + private JCheckBox chkUseShareSystem; + private JCheckBox chkSharesForAll; + //end General Options + + //start Price Multipliers + private JPanel pnlGeneralMultipliers; + private JLabel lblCommonPartPriceMultiplier; + private JSpinner spnCommonPartPriceMultiplier; + private JLabel lblInnerSphereUnitPriceMultiplier; + private JSpinner spnInnerSphereUnitPriceMultiplier; + private JLabel lblInnerSpherePartPriceMultiplier; + private JSpinner spnInnerSpherePartPriceMultiplier; + private JLabel lblClanUnitPriceMultiplier; + private JSpinner spnClanUnitPriceMultiplier; + private JLabel lblClanPartPriceMultiplier; + private JSpinner spnClanPartPriceMultiplier; + private JLabel lblMixedTechUnitPriceMultiplier; + private JSpinner spnMixedTechUnitPriceMultiplier; + + private JPanel pnlUsedPartsMultipliers; + private JLabel[] lblUsedPartPriceMultipliers; + private JSpinner[] spnUsedPartPriceMultipliers; + + private JPanel pnlOtherMultipliers; + private JLabel lblDamagedPartsValueMultiplier; + private JSpinner spnDamagedPartsValueMultiplier; + private JLabel lblUnrepairablePartsValueMultiplier; + private JSpinner spnUnrepairablePartsValueMultiplier; + private JLabel lblCancelledOrderRefundMultiplier; + private JSpinner spnCancelledOrderRefundMultiplier; + //end Price Multipliers + + /** + * Constructs a `FinancesTab` instance which manages the financial settings + * and configurations for a specific campaign. + * + * @param campaign The `Campaign` object that this `FinancesTab` will be associated with. + * Provides access to campaign-related options and data. + */ + public FinancesTab(Campaign campaign) { + this.campaign = campaign; + this.campaignOptions = campaign.getCampaignOptions(); + + initialize(); + } + + /** + * Initializes the primary components and subcomponents of the `FinancesTab`. + * Specifically, sets up the 'General Options' and 'Price Multipliers' tabs + * through their respective initialization methods. + * This method ensures that the tabs are prepared prior to being displayed or used. + */ + private void initialize() { + initializeGeneralOptionsTab(); + initializePriceMultipliersTab(); + } + + /** + * Initializes the General Options tab within the application's UI. + *

    + * This method sets up various UI components and panels that + * provide configurable options for general settings, payments, + * sales, other systems, taxes, and shares. Components include + * checkboxes, labels, spinners, and combo boxes that allow + * the user to interact with and configure these settings. + *

    + * All UI components are initialized, but additional configuration + * such as layout placements, listeners, or actual visibility might + * need to be completed separately. + */ + private void initializeGeneralOptionsTab() { + // General Options + pnlGeneralOptions = new JPanel(); + useLoanLimitsBox = new JCheckBox(); + usePercentageMaintenanceBox = new JCheckBox(); + useExtendedPartsModifierBox = new JCheckBox(); + usePeacetimeCostBox = new JCheckBox(); + showPeacetimeCostBox = new JCheckBox(); + + lblFinancialYearDuration = new JLabel(); + comboFinancialYearDuration = new MMComboBox<>("comboFinancialYearDuration", + FinancialYearDuration.values()); + + newFinancialYearFinancesToCSVExportBox = new JCheckBox(); + + // Payments + pnlPayments = new JPanel(); + payForPartsBox = new JCheckBox(); + payForRepairsBox = new JCheckBox(); + payForUnitsBox = new JCheckBox(); + payForSalariesBox = new JCheckBox(); + payForOverheadBox = new JCheckBox(); + payForMaintainBox = new JCheckBox(); + payForTransportBox = new JCheckBox(); + payForRecruitmentBox = new JCheckBox(); + + // Sales + pnlSales = new JPanel(); + sellUnitsBox = new JCheckBox(); + sellPartsBox = new JCheckBox(); + + pnlOtherSystems = new JPanel(); + + // Taxes + pnlTaxes = new JPanel(); + chkUseTaxes = new JCheckBox(); + lblTaxesPercentage = new JLabel(); + spnTaxesPercentage = new JSpinner(); + + // Shares + pnlShares= new JPanel(); + chkUseShareSystem = new JCheckBox(); + chkSharesForAll = new JCheckBox(); + } + + /** + * Creates and configures the Finances General Options tab, assembling its components, + * layout, and panels which include general options, other systems, payments, and sales. + * This method initializes required sub-panels and arranges them within the overall + * structure to create a fully constructed tab for financial general options. + * + * @return A fully configured JPanel representing the Finances General Options tab. + */ + public JPanel createFinancesGeneralOptionsTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("FinancesGeneralTab", + getImageDirectory() + "logo_star_league.png"); + + // Contents + pnlGeneralOptions = createGeneralOptionsPanel(); + pnlOtherSystems = createOtherSystemsPanel(); + + pnlPayments = createPaymentsPanel(); + pnlSales = createSalesPanel(); + + // Layout the Panel + final JPanel panelTransactions = new CampaignOptionsStandardPanel("FinancesGeneralTabTransactions"); + GridBagConstraints layoutTransactions = new CampaignOptionsGridBagConstraints(panelTransactions); + + layoutTransactions.gridwidth = 2; + layoutTransactions.gridy = 0; + layoutTransactions.gridx = 0; + panelTransactions.add(pnlPayments, layoutTransactions); + layoutTransactions.gridx += 2; + panelTransactions.add(pnlSales, layoutTransactions); + + final JPanel panel = new CampaignOptionsStandardPanel("FinancesGeneralTab", true); + GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridy = 0; + panel.add(headerPanel, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(pnlGeneralOptions, layoutParent); + layoutParent.gridx++; + panel.add(pnlOtherSystems, layoutParent); + + layoutParent.gridwidth = 2; + layoutParent.gridx = 0; + layoutParent.gridy++; + panel.add(panelTransactions, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panel, "FinancesGeneralTab"); + } + + /** + * Creates and configures a payments panel with various checkbox options for payment categories such as + * parts, repairs, units, salaries, overhead, maintenance, transport, and recruitment. The layout of + * the panel organizes the checkboxes in a grid-based format. + * + * @return a JPanel instance containing the configured payment options checkboxes. + */ + private JPanel createPaymentsPanel() { + // Contents + payForPartsBox = new CampaignOptionsCheckBox("PayForPartsBox"); + payForRepairsBox = new CampaignOptionsCheckBox("PayForRepairsBox"); + payForUnitsBox = new CampaignOptionsCheckBox("PayForUnitsBox"); + payForSalariesBox = new CampaignOptionsCheckBox("PayForSalariesBox"); + payForOverheadBox = new CampaignOptionsCheckBox("PayForOverheadBox"); + payForMaintainBox = new CampaignOptionsCheckBox("PayForMaintainBox"); + payForTransportBox = new CampaignOptionsCheckBox("PayForTransportBox"); + payForRecruitmentBox = new CampaignOptionsCheckBox("PayForRecruitmentBox"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PaymentsPanel", true, + "PaymentsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(payForPartsBox, layout); + layout.gridx++; + panel.add(payForRepairsBox, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(payForUnitsBox, layout); + layout.gridx++; + panel.add(payForSalariesBox, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(payForOverheadBox, layout); + layout.gridx++; + panel.add(payForMaintainBox, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(payForTransportBox, layout); + layout.gridx++; + panel.add(payForRecruitmentBox, layout); + + return panel; + } + + /** + * Constructs and returns a {@link JPanel} for the 'Other Systems Panel'. + * This panel combines two sub-panels: 'Taxes Panel' and 'Shares Panel'. + * Each sub-panel is added sequentially to the main panel using a grid-bag layout. + * These panels are organized vertically in the resulting panel. + * + * @return {@link JPanel} representing the 'Other Systems Panel', containing the + * 'Taxes Panel' and 'Shares Panel'. + */ + private JPanel createOtherSystemsPanel() { + // Contents + pnlTaxes = createTaxesPanel(); + pnlShares = createSharesPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("OtherSystemsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(pnlTaxes, layout); + + layout.gridy++; + panel.add(pnlShares, layout); + + return panel; + } + + /** + * Creates and initializes the General Options Panel with various configurable + * options related to loan limits, maintenance, parts modifiers, peacetime costs, + * and financial year settings. The panel includes checkboxes and labels for easy + * user interaction and configuration of these parameters. + * + * @return A JPanel containing the general options components laid out in a + * structured format. + */ + private JPanel createGeneralOptionsPanel() { + // Contents + useLoanLimitsBox = new CampaignOptionsCheckBox("UseLoanLimitsBox"); + usePercentageMaintenanceBox = new CampaignOptionsCheckBox("UsePercentageMaintenanceBox"); + useExtendedPartsModifierBox = new CampaignOptionsCheckBox("UseExtendedPartsModifierBox"); + usePeacetimeCostBox = new CampaignOptionsCheckBox("UsePeacetimeCostBox"); + showPeacetimeCostBox = new CampaignOptionsCheckBox("ShowPeacetimeCostBox"); + + lblFinancialYearDuration = new CampaignOptionsLabel("FinancialYearDuration"); + + newFinancialYearFinancesToCSVExportBox = new CampaignOptionsCheckBox("NewFinancialYearFinancesToCSVExportBox"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("GeneralOptionsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + panel.add(useLoanLimitsBox, layout); + + layout.gridy++; + panel.add(usePercentageMaintenanceBox, layout); + + layout.gridy++; + panel.add(useExtendedPartsModifierBox, layout); + + layout.gridy++; + panel.add(usePeacetimeCostBox, layout); + + layout.gridy++; + panel.add(showPeacetimeCostBox, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblFinancialYearDuration, layout); + layout.gridx++; + panel.add(comboFinancialYearDuration, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(newFinancialYearFinancesToCSVExportBox, layout); + + return panel; + } + + /** + * Creates and configures the sales panel within the finance tab. + * The panel contains checkboxes for options related to sales, including + * "Sell Units" and "Sell Parts". These checkboxes are added to a layout + * that organizes the components vertically. + * + * @return A JPanel instance containing the configured sales options. + */ + private JPanel createSalesPanel() { + // Contents + sellUnitsBox = new CampaignOptionsCheckBox("SellUnitsBox"); + sellPartsBox = new CampaignOptionsCheckBox("SellPartsBox"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("SalesPanel", true, + "SalesPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(sellUnitsBox, layout); + + layout.gridy++; + panel.add(sellPartsBox, layout); + + return panel; + } + + /** + * Creates and returns a JPanel representing the taxes panel in the campaign options. + * This panel includes a checkbox to enable or disable taxes and a spinner + * to set the percentage of taxes, along with corresponding labels. + * + * @return the configured JPanel containing the components for the taxes panel. + */ + private JPanel createTaxesPanel() { + // Contents + chkUseTaxes = new CampaignOptionsCheckBox("UseTaxesBox"); + + lblTaxesPercentage = new CampaignOptionsLabel("TaxesPercentage"); + spnTaxesPercentage = new CampaignOptionsSpinner("TaxesPercentage", + 30, 1, 100, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("TaxesPanel", true, + "TaxesPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + panel.add(chkUseTaxes, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblTaxesPercentage, layout); + layout.gridx++; + panel.add(spnTaxesPercentage, layout); + + return panel; + } + + /** + * Creates and returns a JPanel representing the 'Shares Panel' within the finance tab. + *

    + * The panel is laid out using grid-based constraints to position the components + * in a structured vertical arrangement. + * + * @return A JPanel containing the configured components for the 'Shares Panel'. + */ + private JPanel createSharesPanel() { + // Contents + chkUseShareSystem = new CampaignOptionsCheckBox("UseShareSystem"); + chkSharesForAll = new CampaignOptionsCheckBox("SharesForAll"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("SharesPanel", true, + "SharesPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(chkUseShareSystem, layout); + + layout.gridy++; + panel.add(chkSharesForAll, layout); + + return panel; + } + + /** + * Initializes the components and layout for the price multipliers tab. + * This tab includes controls for setting various price multipliers such as + * - General multipliers for unit and part prices. + * - Multipliers for used parts. + * - Miscellaneous multipliers for damaged, unrepairable parts, and order refunds. + *

    + * The method creates and assigns UI components including panels, labels, and spinners + * to their respective class fields. Each field corresponds to a specific category + * of price multiplier. + */ + private void initializePriceMultipliersTab() { + pnlGeneralMultipliers = new JPanel(); + lblCommonPartPriceMultiplier = new JLabel(); + spnCommonPartPriceMultiplier = new JSpinner(); + lblInnerSphereUnitPriceMultiplier = new JLabel(); + spnInnerSphereUnitPriceMultiplier = new JSpinner(); + lblInnerSpherePartPriceMultiplier = new JLabel(); + spnInnerSpherePartPriceMultiplier = new JSpinner(); + lblClanUnitPriceMultiplier = new JLabel(); + spnClanUnitPriceMultiplier = new JSpinner(); + lblClanPartPriceMultiplier = new JLabel(); + spnClanPartPriceMultiplier = new JSpinner(); + lblMixedTechUnitPriceMultiplier = new JLabel(); + spnMixedTechUnitPriceMultiplier = new JSpinner(); + + pnlUsedPartsMultipliers = new JPanel(); + lblUsedPartPriceMultipliers = new JLabel[1]; // we initialize this properly later + spnUsedPartPriceMultipliers = new JSpinner[1]; // we initialize this properly later + + pnlOtherMultipliers = new JPanel(); + lblDamagedPartsValueMultiplier = new JLabel(); + spnDamagedPartsValueMultiplier = new JSpinner(); + lblUnrepairablePartsValueMultiplier = new JLabel(); + spnUnrepairablePartsValueMultiplier = new JSpinner(); + lblCancelledOrderRefundMultiplier = new JLabel(); + spnCancelledOrderRefundMultiplier = new JSpinner(); + } + + /** + * Creates and returns a JPanel representing the "Price Multipliers" tab in the user interface. + * The method includes a header section, general multipliers panel, used parts multipliers panel, + * and other multipliers panel. These components are arranged using a specific layout and added + * to a parent panel. + * + * @return a JPanel representing the "Price Multipliers" tab with all its components and layout configured + */ + public JPanel createPriceMultipliersTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("PriceMultipliersTab", + getImageDirectory() + "logo_clan_stone_lion.png", true); + + // Contents + pnlGeneralMultipliers = createGeneralMultipliersPanel(); + pnlUsedPartsMultipliers = createUsedPartsMultiplierPanel(); + pnlOtherMultipliers = createOtherMultipliersPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PriceMultipliersTab", true); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(pnlGeneralMultipliers, layout); + layout.gridx++; + panel.add(pnlUsedPartsMultipliers, layout); + layout.gridx++; + panel.add(pnlOtherMultipliers, layout); + + // Create Parent Panel and return + return createParentPanel(panel, "PriceMultipliersTab"); + } + + /** + * Creates and configures the general multipliers panel, which includes labels + * and spinners for various pricing multipliers such as common parts, Inner Sphere + * units, Inner Sphere parts, Clan units, Clan parts, and mixed tech units. + * The panel is structured using a grid layout for organized placement of components. + * + * @return a JPanel containing the components for setting general multipliers. + */ + private JPanel createGeneralMultipliersPanel() { + // Contents + lblCommonPartPriceMultiplier = new CampaignOptionsLabel("CommonPartPriceMultiplier"); + spnCommonPartPriceMultiplier = new CampaignOptionsSpinner("CommonPartPriceMultiplier", + 1.0, 0.1, 100, 0.1); + + lblInnerSphereUnitPriceMultiplier = new CampaignOptionsLabel("InnerSphereUnitPriceMultiplier"); + spnInnerSphereUnitPriceMultiplier = new CampaignOptionsSpinner("InnerSphereUnitPriceMultiplier", + 1.0, 0.1, 100, 0.1); + + lblInnerSpherePartPriceMultiplier = new CampaignOptionsLabel("InnerSpherePartPriceMultiplier"); + spnInnerSpherePartPriceMultiplier = new CampaignOptionsSpinner("InnerSpherePartPriceMultiplier", + 1.0, 0.1, 100, 0.1); + + lblClanUnitPriceMultiplier = new CampaignOptionsLabel("ClanUnitPriceMultiplier"); + spnClanUnitPriceMultiplier = new CampaignOptionsSpinner("ClanUnitPriceMultiplier", + 1.0, 0.1, 100, 0.1); + + lblClanPartPriceMultiplier = new CampaignOptionsLabel("ClanPartPriceMultiplier"); + spnClanPartPriceMultiplier = new CampaignOptionsSpinner("ClanPartPriceMultiplier", + 1.0, 0.1, 100, 0.1); + + lblMixedTechUnitPriceMultiplier = new CampaignOptionsLabel("MixedTechUnitPriceMultiplier"); + spnMixedTechUnitPriceMultiplier = new CampaignOptionsSpinner("MixedTechUnitPriceMultiplier", + 1.0, 0.1, 100, 0.1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("GeneralMultipliersPanel", true, + "GeneralMultipliersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(lblCommonPartPriceMultiplier, layout); + layout.gridx++; + panel.add(spnCommonPartPriceMultiplier, layout); + layout.gridx++; + panel.add(lblMixedTechUnitPriceMultiplier, layout); + layout.gridx++; + panel.add(spnMixedTechUnitPriceMultiplier, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblInnerSphereUnitPriceMultiplier, layout); + layout.gridx++; + panel.add(spnInnerSphereUnitPriceMultiplier, layout); + layout.gridx++; + panel.add(lblInnerSpherePartPriceMultiplier, layout); + layout.gridx++; + panel.add(spnInnerSpherePartPriceMultiplier, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblClanUnitPriceMultiplier, layout); + layout.gridx++; + panel.add(spnClanUnitPriceMultiplier, layout); + layout.gridx++; + panel.add(lblClanPartPriceMultiplier, layout); + layout.gridx++; + panel.add(spnClanPartPriceMultiplier, layout); + + return panel; + } + + /** + * Creates and returns a JPanel for configuring used parts price multipliers + * based on part quality. Each part quality level is represented with a label + * and a spinner for adjusting the multiplier value. + *

    + * The spinners are initialized with a range of values from 0.00 to 1.00, + * incrementing by 0.05, and include formatting for two decimal places. + * Additionally, the alignment of the spinner text fields is set to left. + *

    + * The panel is arranged using GridBagLayout to ensure proper alignment + * between labels and spinners for each quality level. + * + * @return A JPanel containing labels and spinners for used parts price multipliers. + */ + private JPanel createUsedPartsMultiplierPanel() { + boolean reverseQualities = campaign.getCampaignOptions().isReverseQualityNames(); + + // Contents + lblUsedPartPriceMultipliers = new JLabel[QUALITY_F.ordinal() + 1]; + spnUsedPartPriceMultipliers = new JSpinner[QUALITY_F.ordinal() + 1]; + + for (PartQuality partQuality : PartQuality.values()) { + final String qualityLevel = partQuality.toName(reverseQualities); + int ordinal = partQuality.ordinal(); + + lblUsedPartPriceMultipliers[ordinal] = new JLabel(qualityLevel); + lblUsedPartPriceMultipliers[ordinal].setName("lbl" + qualityLevel); + + spnUsedPartPriceMultipliers[ordinal] = new JSpinner( + new SpinnerNumberModel(0.00, 0.00, 1.00, 0.05)); + spnUsedPartPriceMultipliers[ordinal].setName("spn" + qualityLevel); + spnUsedPartPriceMultipliers[ordinal] + .setEditor(new NumberEditor(spnUsedPartPriceMultipliers[ordinal], "0.00")); + + DefaultEditor editor = (DefaultEditor) spnUsedPartPriceMultipliers[ordinal].getEditor(); + editor.getTextField().setHorizontalAlignment(JTextField.LEFT); + } + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("UsedPartsMultiplierPanel", true, + "UsedPartsMultiplierPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 1; + + for (int i = 0; i < 6; i++) { + layout.gridx = 0; + layout.gridy = i; + panel.add(lblUsedPartPriceMultipliers[i], layout); + layout.gridx++; + panel.add(spnUsedPartPriceMultipliers[i], layout); + } + + return panel; + } + + /** + * Creates and returns a JPanel configured with components for adjusting + * multipliers related to damaged parts value, unrepairable parts value, + * and cancelled order refunds. Each multiplier is represented with a label + * and an associated configurable spinner control. + * + * @return a JPanel instance containing the components for configuring the multipliers. + */ + private JPanel createOtherMultipliersPanel() { + // Contents + lblDamagedPartsValueMultiplier = new CampaignOptionsLabel("DamagedPartsValueMultiplier"); + spnDamagedPartsValueMultiplier = new CampaignOptionsSpinner("DamagedPartsValueMultiplier", + 0.33, 0.00, 1.00, 0.05); + + lblUnrepairablePartsValueMultiplier = new CampaignOptionsLabel("UnrepairablePartsValueMultiplier"); + spnUnrepairablePartsValueMultiplier = new CampaignOptionsSpinner("UnrepairablePartsValueMultiplier", + 0.10, 0.00, 1.00, 0.05); + + lblCancelledOrderRefundMultiplier = new CampaignOptionsLabel("CancelledOrderRefundMultiplier"); + spnCancelledOrderRefundMultiplier = new CampaignOptionsSpinner("CancelledOrderRefundMultiplier", + 0.50, 0.00, 1.00, 0.05); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("OtherMultipliersPanel", true, + "OtherMultipliersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(lblDamagedPartsValueMultiplier, layout); + layout.gridx++; + panel.add(spnDamagedPartsValueMultiplier, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblUnrepairablePartsValueMultiplier, layout); + layout.gridx++; + panel.add(spnUnrepairablePartsValueMultiplier, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblCancelledOrderRefundMultiplier, layout); + layout.gridx++; + panel.add(spnCancelledOrderRefundMultiplier, layout); + + return panel; + } + + /** + * Applies the specified campaign options to the corresponding campaign settings. + * If no campaign options are provided, default options are used instead. + * + * @param presetCampaignOptions + * The campaign options to be applied. If null, default campaign options + * are applied. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // General Options + options.setLoanLimits(useLoanLimitsBox.isSelected()); + options.setUsePercentageMaint(usePercentageMaintenanceBox.isSelected()); + options.setUseExtendedPartsModifier(useExtendedPartsModifierBox.isSelected()); + options.setUsePeacetimeCost(usePeacetimeCostBox.isSelected()); + options.setShowPeacetimeCost(showPeacetimeCostBox.isSelected()); + options.setFinancialYearDuration(comboFinancialYearDuration.getSelectedItem()); + options.setNewFinancialYearFinancesToCSVExport(newFinancialYearFinancesToCSVExportBox.isSelected()); + options.setPayForParts(payForPartsBox.isSelected()); + options.setPayForRepairs(payForRepairsBox.isSelected()); + options.setPayForUnits(payForUnitsBox.isSelected()); + options.setPayForSalaries(payForSalariesBox.isSelected()); + options.setPayForOverhead(payForOverheadBox.isSelected()); + options.setPayForMaintain(payForMaintainBox.isSelected()); + options.setPayForTransport(payForTransportBox.isSelected()); + options.setPayForRecruitment(payForRecruitmentBox.isSelected()); + options.setSellUnits(sellUnitsBox.isSelected()); + options.setSellParts(sellPartsBox.isSelected()); + options.setUseTaxes(chkUseTaxes.isSelected()); + options.setTaxesPercentage((int) spnTaxesPercentage.getValue()); + options.setUseShareSystem(chkUseShareSystem.isSelected()); + options.setSharesForAll(chkSharesForAll.isSelected()); + + // Price Multipliers + options.setCommonPartPriceMultiplier((double) spnCommonPartPriceMultiplier.getValue()); + options.setInnerSphereUnitPriceMultiplier((double) spnInnerSphereUnitPriceMultiplier.getValue()); + options.setInnerSpherePartPriceMultiplier((double) spnInnerSpherePartPriceMultiplier.getValue()); + options.setClanUnitPriceMultiplier((double) spnClanUnitPriceMultiplier.getValue()); + options.setClanPartPriceMultiplier((double) spnClanPartPriceMultiplier.getValue()); + options.setMixedTechUnitPriceMultiplier((double) spnMixedTechUnitPriceMultiplier.getValue()); + for (int i = 0; i < spnUsedPartPriceMultipliers.length; i++) { + options.getUsedPartPriceMultipliers()[i] = (Double) spnUsedPartPriceMultipliers[i] + .getValue(); + } + options.setDamagedPartsValueMultiplier((double) spnDamagedPartsValueMultiplier.getValue()); + options.setUnrepairablePartsValueMultiplier((double) spnUnrepairablePartsValueMultiplier.getValue()); + options.setCancelledOrderRefundMultiplier((double) spnCancelledOrderRefundMultiplier.getValue()); + } + + /** + * Loads configuration values from the current campaign options to populate + * the financial settings and related UI components in the `FinancesTab`. + *

    + * This method is a convenience overload that invokes the overloaded + * {@link #loadValuesFromCampaignOptions(CampaignOptions)} method with a + * `null` parameter, ensuring that default campaign options will be loaded. + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null); + } + + /** + * Loads and applies the values from the provided campaign options or the default campaign options + * if the provided options are null. Updates various UI components and internal variables based + * on the configuration of the campaign options. + * + * @param presetCampaignOptions the campaign options to load values from; if null, the default + * campaign options will be used + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // General Options + useLoanLimitsBox.setSelected(options.isUseLoanLimits()); + usePercentageMaintenanceBox.setSelected(options.isUsePercentageMaint()); + useExtendedPartsModifierBox.setSelected(options.isUseExtendedPartsModifier()); + usePeacetimeCostBox.setSelected(options.isUsePeacetimeCost()); + showPeacetimeCostBox.setSelected(options.isShowPeacetimeCost()); + comboFinancialYearDuration.setSelectedItem(options.getFinancialYearDuration()); + newFinancialYearFinancesToCSVExportBox.setSelected(options.isNewFinancialYearFinancesToCSVExport()); + payForPartsBox.setSelected(options.isPayForParts()); + payForRepairsBox.setSelected(options.isPayForRepairs()); + payForUnitsBox.setSelected(options.isPayForUnits()); + payForSalariesBox.setSelected(options.isPayForSalaries()); + payForOverheadBox.setSelected(options.isPayForOverhead()); + payForMaintainBox.setSelected(options.isPayForMaintain()); + payForTransportBox.setSelected(options.isPayForTransport()); + payForRecruitmentBox.setSelected(options.isPayForRecruitment()); + sellUnitsBox.setSelected(options.isSellUnits()); + sellPartsBox.setSelected(options.isSellParts()); + chkUseTaxes.setSelected(options.isUseTaxes()); + spnTaxesPercentage.setValue(options.getTaxesPercentage()); + chkUseShareSystem.setSelected(options.isUseShareSystem()); + chkSharesForAll.setSelected(options.isSharesForAll()); + + // Price Multipliers + spnCommonPartPriceMultiplier.setValue(options.getCommonPartPriceMultiplier()); + spnInnerSphereUnitPriceMultiplier.setValue(options.getInnerSphereUnitPriceMultiplier()); + spnInnerSpherePartPriceMultiplier.setValue(options.getInnerSpherePartPriceMultiplier()); + spnClanUnitPriceMultiplier.setValue(options.getClanUnitPriceMultiplier()); + spnClanPartPriceMultiplier.setValue(options.getClanPartPriceMultiplier()); + spnMixedTechUnitPriceMultiplier.setValue(options.getMixedTechUnitPriceMultiplier()); + for (int i = 0; i < spnUsedPartPriceMultipliers.length; i++) { + spnUsedPartPriceMultipliers[i].setValue(options.getUsedPartPriceMultipliers()[i]); + } + spnDamagedPartsValueMultiplier.setValue(options.getDamagedPartsValueMultiplier()); + spnUnrepairablePartsValueMultiplier.setValue(options.getUnrepairablePartsValueMultiplier()); + spnCancelledOrderRefundMultiplier.setValue(options.getCancelledOrderRefundMultiplier()); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/GeneralTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/GeneralTab.java new file mode 100644 index 00000000000..ebbbca42c14 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/GeneralTab.java @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.client.ui.dialogs.CamoChooserDialog; +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; +import megamek.common.icons.Camouflage; +import megamek.common.options.OptionsConstants; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.icons.StandardForceIcon; +import mekhq.campaign.personnel.backgrounds.BackgroundsController; +import mekhq.campaign.rating.UnitRatingMethod; +import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; +import mekhq.gui.baseComponents.AbstractMHQScrollablePanel; +import mekhq.gui.baseComponents.AbstractMHQTabbedPane; +import mekhq.gui.baseComponents.DefaultMHQScrollablePanel; +import mekhq.gui.campaignOptions.components.*; +import mekhq.gui.dialog.DateChooser; +import mekhq.gui.dialog.iconDialogs.UnitIconDialog; +import mekhq.gui.displayWrappers.FactionDisplay; + +import javax.swing.*; +import javax.swing.GroupLayout.Alignment; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.time.LocalDate; +import java.util.ResourceBundle; + +import static megamek.client.ui.swing.util.FlatLafStyleBuilder.setFontScaling; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createGroupLayout; + +/** + * Represents a tab within the campaign options UI that allows the user to configure + * general campaign settings. This includes options for: + *

      + *
    • Configuring the campaign name
    • + *
    • Setting the faction and faction-related options
    • + *
    • Adjusting reputation and manual unit rating modifiers
    • + *
    • Specifying the campaign start date
    • + *
    • Choosing a camouflage pattern and unit icon
    • + *
    + * + * This class extends the user interface features provided by {@link AbstractMHQTabbedPane}. + */ +public class GeneralTab { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + private final JFrame frame; + private final Campaign campaign; + private final CampaignOptions campaignOptions; + + private JLabel lblName; + private JTextField txtName; + private JButton btnNameGenerator; + private JLabel lblFaction; + private MMComboBox comboFaction; + private JLabel lblReputation; + private MMComboBox unitRatingMethodCombo; + private JLabel lblManualUnitRatingModifier; + private JSpinner manualUnitRatingModifier; + private JLabel lblDate; + private JButton btnDate; + private LocalDate date; + private JLabel lblCamo; + private JButton btnCamo; + private Camouflage camouflage; + private JLabel lblIcon; + private JButton btnIcon; + private StandardForceIcon unitIcon; + + /** + * Constructs a new instance of the {@code GeneralTab} using the provided {@link Campaign} and {@link JFrame}. + * + * @param campaign The {@link Campaign} associated with this tab, which contains the core game data. + * @param frame The parent {@link JFrame} used to display this tab. + */ + public GeneralTab(Campaign campaign, JFrame frame) { + // region Variable Declarations + this.frame = frame; + this.campaign = campaign; + this.campaignOptions = campaign.getCampaignOptions(); + this.date = campaign.getLocalDate(); + this.camouflage = campaign.getCamouflage(); + this.unitIcon = campaign.getUnitIcon(); + + initialize(); + } + + /** + * Creates the UI components displayed in the general tab. + *

    + * The general tab includes various configurable fields and panels: + *

    + *
      + *
    • An editable text field for setting the campaign name
    • + *
    • A dropdown for selecting the campaign's faction
    • + *
    • Controls for managing reputation and manual rating modifiers
    • + *
    • Date selection associated with the campaign
    • + *
    • Buttons for choosing camouflage and unit icons
    • + *
    + * + * @return An {@link AbstractMHQScrollablePanel} containing the general tab content. + */ + public AbstractMHQScrollablePanel createGeneralTab() { + // Header + JPanel headerPanel = createGeneralHeader(); + + // Campaign name + lblName = new CampaignOptionsLabel("Name"); + txtName = new CampaignOptionsTextField("Name"); + + // Generate new random campaign name + btnNameGenerator = new CampaignOptionsButton("NameGenerator"); + btnNameGenerator.addActionListener(e -> txtName.setText(BackgroundsController + .randomMercenaryCompanyNameGenerator(campaign.getFlaggedCommander()))); + + // Campaign faction + lblFaction = new CampaignOptionsLabel("Faction"); + comboFaction.setSelectedItem(new FactionDisplay(campaign.getFaction(), campaign.getLocalDate())); + comboFaction.setToolTipText(String.format("%s", + resources.getString("lblFaction.tooltip"))); + + // Reputation + lblReputation = new CampaignOptionsLabel("Reputation"); + unitRatingMethodCombo.setToolTipText(String.format("%s", + resources.getString("lblReputation.tooltip"))); + lblManualUnitRatingModifier = new CampaignOptionsLabel("ManualUnitRatingModifier"); + manualUnitRatingModifier = new CampaignOptionsSpinner("ManualUnitRatingModifier", + 0, -200, 200, 1); + + // Date + lblDate = new CampaignOptionsLabel("Date"); + btnDate = new CampaignOptionsButton("Date"); + btnDate.setText(MekHQ.getMHQOptions().getDisplayFormattedDate(date)); + btnDate.addActionListener(this::btnDateActionPerformed); + + // Camouflage + lblCamo = new CampaignOptionsLabel("Camo"); + btnCamo.setName("btnCamo"); + btnCamo.addActionListener(this::btnCamoActionPerformed); + btnCamo.setIcon(camouflage.getImageIcon(UIUtil.scaleForGUI(75))); + + // Unit icon + lblIcon = new CampaignOptionsLabel("Icon"); + btnIcon.setName("btnIcon"); + btnIcon.addActionListener(this::btnIconActionPerformed); + btnIcon.setIcon(unitIcon.getImageIcon(UIUtil.scaleForGUI(75))); + + // Initialize the parent panel + AbstractMHQScrollablePanel generalPanel = new DefaultMHQScrollablePanel(frame, + "generalPanel", new GridBagLayout()); + + // Layout the Panel + JPanel panel = new JPanel(); + GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridwidth = 5; + panel.add(headerPanel, layout); + + layout.gridwidth = 1; + layout.gridy++; + panel.add(lblDate, layout); + panel.add(btnDate, layout); + + layout.gridy++; + panel.add(lblName, layout); + + layout.gridwidth = 2; + layout.weightx = 1; + panel.add(txtName, layout); + + layout.gridwidth = 1; + layout.weightx = 0; + layout.fill = GridBagConstraints.NONE; + panel.add(btnNameGenerator, layout); + layout.fill = GridBagConstraints.HORIZONTAL; + + layout.gridy++; + panel.add(lblFaction, layout); + layout.gridwidth = 2; + panel.add(comboFaction, layout); + + layout.gridwidth = 1; + layout.gridy++; + panel.add(lblReputation, layout); + layout.gridwidth = 2; + panel.add(unitRatingMethodCombo, layout); + + layout.gridwidth = 1; + layout.gridy++; + panel.add(lblManualUnitRatingModifier, layout); + panel.add(manualUnitRatingModifier, layout); + + layout.gridy++; + layout.gridwidth = 5; + layout.gridx = GridBagConstraints.RELATIVE; + + JPanel iconsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + iconsPanel.setBorder(BorderFactory.createEtchedBorder()); + iconsPanel.setMinimumSize(UIUtil.scaleForGUI(0, 120)); + + iconsPanel.add(lblIcon); + iconsPanel.add(btnIcon); + iconsPanel.add(Box.createHorizontalStrut(UIUtil.scaleForGUI(50))); + iconsPanel.add(lblCamo); + iconsPanel.add(btnCamo); + + panel.add(iconsPanel, layout); + layout.gridy++; + panel.add(createFurtherReadingPanel(), layout); + generalPanel.add(panel); + + return generalPanel; + } + + /** + * Creates a header panel for the general tab, which includes: + *

    + *

  77. An image representing the campaign options
  78. + *
  79. A title for the general tab
  80. + *
  81. A description of the general tab functionalities
  82. + *

    + * + * @return A {@link JPanel} containing the general tab header. + */ + private static JPanel createGeneralHeader() { + ImageIcon imageIcon = new ImageIcon("data/images/misc/MekHQ.png"); + JLabel imageLabel = new JLabel(imageIcon); + + final JLabel lblHeader = new JLabel(resources.getString("lblGeneral.text"), SwingConstants.CENTER); + setFontScaling(lblHeader, true, 2); + lblHeader.setName("lblGeneral"); + + JLabel lblBody = new JLabel(String.format("%s", + resources.getString("lblGeneralBody.text")), SwingConstants.CENTER); + lblBody.setName("lblGeneralHeaderBody"); + + final JPanel panel = new CampaignOptionsStandardPanel("pnlGeneralHeaderPanel"); + final GroupLayout layout = createGroupLayout(panel); + panel.setLayout(layout); + + layout.setVerticalGroup( + layout.createSequentialGroup() + .addComponent(lblHeader) + .addComponent(imageLabel) + .addComponent(lblBody) + .addGap(UIUtil.scaleForGUI(20))); + + layout.setHorizontalGroup( + layout.createParallelGroup(Alignment.CENTER) + .addComponent(lblHeader) + .addComponent(imageLabel) + .addComponent(lblBody)); + + return panel; + } + + /** + * Initializes the UI components used throughout the general tab. + *

    + * This method sets up the components for all editable campaign settings, including: + *

    + *

  83. Labels, text fields, dropdowns, and buttons for campaign settings
  84. + *
  85. Default values fetched from the campaign instance
  86. + *

    + */ + private void initialize() { + lblName = new JLabel(); + txtName = new JTextField(); + + btnNameGenerator = new JButton(); + + lblFaction = new JLabel(); + comboFaction = new MMComboBox<>("comboFaction", buildFactionDisplayOptions()); + + lblReputation = new JLabel(); + unitRatingMethodCombo = new MMComboBox<>("unitRatingMethodCombo", UnitRatingMethod.values()); + + lblManualUnitRatingModifier = new JLabel(); + manualUnitRatingModifier = new JSpinner(); + + lblDate = new JLabel(); + btnDate = new JButton(); + + lblCamo = new JLabel(); + btnCamo = new JButton() { + @Override + public Dimension getPreferredSize() { + return UIUtil.scaleForGUI(100, 100); + } + }; + + lblIcon = new JLabel(); + btnIcon = new JButton() { + @Override + public Dimension getPreferredSize() { + return UIUtil.scaleForGUI(100, 100); + } + }; + } + + /** + * Builds a {@link DefaultComboBoxModel} containing faction options based on the current campaign data. + * These options allow users to choose valid factions appropriate to the campaign's start date. + * + * @return A {@link DefaultComboBoxModel} populated with available {@link FactionDisplay} options. + */ + private DefaultComboBoxModel buildFactionDisplayOptions() { + DefaultComboBoxModel factionModel = new DefaultComboBoxModel<>(); + + factionModel.addAll(FactionDisplay.getSortedValidFactionDisplays( + Factions.getInstance().getChoosableFactions(), campaign.getLocalDate())); + + return factionModel; + } + + /** + * Handles the "Date" button action, which triggers a date selection via a {@link DateChooser} dialog. + * If the user confirms their date choice, it updates the campaign's start date accordingly. + * + * @param actionEvent The {@link ActionEvent} triggered by the "Date" button. + */ + private void btnDateActionPerformed(ActionEvent actionEvent) { + // show the date chooser + DateChooser dateChooser = new DateChooser(frame, date); + // user can either choose a date or cancel by closing + if (dateChooser.showDateChooser() == DateChooser.OK_OPTION) { + setDate(dateChooser.getDate()); + } + } + + /** + * Updates the campaign start date and refreshes the dependent UI components based on the changes. + * + * @param date The selected {@link LocalDate} to set as the campaign start date. Can be null. + */ + private void setDate(final @Nullable LocalDate date) { + if (date == null) { + return; + } + + this.date = date; + btnDate.setText(MekHQ.getMHQOptions().getDisplayFormattedDate(date)); + btnDate.revalidate(); + btnDate.repaint(); + + final FactionDisplay factionDisplay = comboFaction.getSelectedItem(); + comboFaction.removeAllItems(); + ((DefaultComboBoxModel) comboFaction.getModel()).addAll(FactionDisplay + .getSortedValidFactionDisplays(Factions.getInstance().getChoosableFactions(), date)); + comboFaction.setSelectedItem(factionDisplay); + } + + /** + * Handles the "Camouflage" button action, which opens a {@link CamoChooserDialog}. + * If the user confirms their camouflage selection, it updates the button icon to display the + * chosen camouflage. + * + * @param actionEvent The {@link ActionEvent} triggered by the "Camouflage" button. + */ + private void btnCamoActionPerformed(ActionEvent actionEvent) { + CamoChooserDialog camoChooserDialog = new CamoChooserDialog(frame, camouflage); + if (camoChooserDialog.showDialog().isConfirmed()) { + camouflage = camoChooserDialog.getSelectedItem(); + btnCamo.setIcon(camouflage.getImageIcon()); + } + } + + /** + * Handles the "Unit Icon" button action, which opens a {@link UnitIconDialog}. + * If the user selects a new unit icon and confirms, this method updates the button icon to + * reflect the selection. + * + * @param actionEvent The {@link ActionEvent} triggered by the "Unit Icon" button. + */ + private void btnIconActionPerformed(ActionEvent actionEvent) { + final UnitIconDialog unitIconDialog = new UnitIconDialog(frame, unitIcon); + if (unitIconDialog.showDialog().isConfirmed() && (unitIconDialog.getSelectedItem() != null)) { + unitIcon = unitIconDialog.getSelectedItem(); + btnIcon.setIcon(unitIcon.getImageIcon(UIUtil.scaleForGUI(75))); + } + } + + /** + * Creates a "Further Reading" panel that provides links or additional details to guide users + * in understanding the campaign options. + *

    + * The panel may include references to: + *

    + *

  87. BattleMech Manual (BMM)
  88. + *
  89. Total Warfare rules
  90. + *
  91. Campaign Operations documentation
  92. + *

    + * + * @return A {@link JPanel} containing additional informational components. + */ + private JPanel createFurtherReadingPanel() { + // Contents + JPanel headerPanelBMM = new CampaignOptionsHeaderPanel("BMMPanel", "", + true); + + JPanel headerPanelTotalWarfare = new CampaignOptionsHeaderPanel("TotalWarfarePanel", + "", true); + + JPanel headerPanelCampaignOperations = new CampaignOptionsHeaderPanel("CampaignOperationsPanel", + "", true); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("FurtherReadingPanel", + true, "FurtherReadingPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + panel.add(headerPanelBMM, layout); + + layout.gridy++; + panel.add(headerPanelTotalWarfare, layout); + + layout.gridy++; + panel.add(headerPanelCampaignOperations, layout); + + return panel; + } + + /** + * Loads the values from the current campaign's {@link CampaignOptions} and updates the user interface components. + *

    + * This is a convenience method that uses the default campaign options, default date, and default faction + * to populate the relevant data fields in the user interface. It essentially delegates the work to the overloaded + * method {@link #loadValuesFromCampaignOptions(CampaignOptions, LocalDate, Faction)} with all parameters set to {@code null}. + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null, null, null); + } + + /** + * Loads values from the specified {@link CampaignOptions}, date, and faction into the user interface components. + *

    + * This method updates the UI fields (e.g., text fields, combo boxes, and buttons) with the corresponding values + * from the provided options or defaults to the current campaign's settings if no presets are provided. + *

    + * Specific actions include: + *

      + *
    • Setting the campaign name and faction in the respective fields.
    • + *
    • Updating the unit rating method and manual unit rating modifier based on the campaign + * options.
    • + *
    • Synchronizing the date to the UI, accounting for a preset date if provided.
    • + *
    • Setting the camouflage pattern and unit icon to align with the campaign's default or + * custom configuration.
    • + *
    • Performing required UI updates (e.g., repainting date labels).
    • + *
    + * + * @param presetCampaignOptions Optional {@link CampaignOptions} used to populate values. + * If {@code null}, the current campaign options are used. + * @param presetDate Optional {@link LocalDate} to be used as the active date. + * If {@code null}, the campaign's current date is used. + * @param presetFaction Optional {@link Faction} to be used in the faction selection field. + * If {@code null}, the campaign's default faction is used. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions, + @Nullable LocalDate presetDate, + @Nullable Faction presetFaction) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + txtName.setText(campaign.getName()); + + comboFaction.setSelectedItem(campaign.getFaction()); + if (presetFaction != null) { + comboFaction.setSelectedItem(new FactionDisplay(presetFaction, date)); + } + + unitRatingMethodCombo.setSelectedItem(options.getUnitRatingMethod()); + manualUnitRatingModifier.setValue(options.getManualUnitRatingModifier()); + + date = campaign.getLocalDate(); + if (presetDate != null) { + date = presetDate; + } + // Button labels are not updated when we repaint, so we have to specifically call it here + btnDate.setText(MekHQ.getMHQOptions().getDisplayFormattedDate(date)); + + camouflage = campaign.getCamouflage(); + unitIcon = campaign.getUnitIcon(); + } + + /** + * Applies the updated campaign options from the general tab's UI components to the {@link Campaign}. + * This method ensures that any changes made in the UI are reflected in the campaign's settings. + * + * @param presetCampaignOptions An optional {@link CampaignOptions} to apply instead of the campaign's current options. + * @param isStartUp A boolean indicating if the campaign is in a startup state. + * @param isSaveAction A boolean indicating if this is a save action. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions, + boolean isStartUp, boolean isSaveAction) { + // First, we apply any updates to the campaign + if (!isSaveAction) { + campaign.setName(txtName.getText()); + + if (isStartUp) { + campaign.getForces().setName(campaign.getName()); + } + campaign.setLocalDate(date); + + if ((campaign.getCampaignStartDate() == null) + || (campaign.getCampaignStartDate().isAfter(campaign.getLocalDate()))) { + campaign.setCampaignStartDate(date); + } + // Ensure that the MegaMek year GameOption matches the campaign year + campaign.getGameOptions().getOption(OptionsConstants.ALLOWED_YEAR).setValue(campaign.getGameYear()); + + // Null state handled during validation + FactionDisplay newFaction = comboFaction.getSelectedItem(); + if (newFaction != null) { + campaign.setFaction(comboFaction.getSelectedItem().getFaction()); + } + + campaign.setCamouflage(camouflage); + campaign.setUnitIcon(unitIcon); + } + + // Then updates to Campaign Options + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + options.setUnitRatingMethod(unitRatingMethodCombo.getSelectedItem()); + options.setManualUnitRatingModifier((int) manualUnitRatingModifier.getValue()); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/MarketsTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/MarketsTab.java new file mode 100644 index 00000000000..c6da392f771 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/MarketsTab.java @@ -0,0 +1,816 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.common.annotations.Nullable; +import megamek.common.enums.SkillLevel; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.market.enums.ContractMarketMethod; +import mekhq.campaign.market.enums.UnitMarketMethod; +import mekhq.campaign.personnel.Skills; +import mekhq.gui.campaignOptions.components.*; +import mekhq.module.PersonnelMarketServiceManager; +import mekhq.module.api.PersonnelMarketMethod; + +import javax.swing.*; +import javax.swing.JSpinner.DefaultEditor; +import java.awt.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.ResourceBundle; + +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * The {@code MarketsTab} class represents the campaign options tab related to market settings. + * This tab provides configurations for three key market areas: + *
      + *
    • Personnel Market: Settings for managing personnel hiring, removal targets, and market types.
    • + *
    • Unit Market: Configurations for purchasing units, special unit chances, rarity modifiers, etc.
    • + *
    • Contract Market: Options for contract acquisition, such as market methods, search radius, + * and payment settings.
    • + *
    + *

    + * The class initializes the UI components for these three market types and provides methods to + * load data into the UI or apply changes from the UI to the campaign settings. + *

    + *

    + * This class interacts with {@link CampaignOptions} to retrieve or update the persistent campaign settings. + * It also utilizes Swing components for building the UI. + *

    + */ +public class MarketsTab { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + private final Campaign campaign; + private final CampaignOptions campaignOptions; + + //start Personnel Market + private JPanel pnlPersonnelMarketGeneralOptions; + private JLabel lblPersonnelMarketType; + private MMComboBox comboPersonnelMarketType; + private JCheckBox chkPersonnelMarketReportRefresh; + private JCheckBox chkUsePersonnelHireHiringHallOnly; + + private JPanel pnlRemovalTargets; + private JLabel lblPersonnelMarketDylansWeight; + private JSpinner spnPersonnelMarketDylansWeight; + private Map lblPersonnelMarketRandomRemovalTargets; + private Map spnPersonnelMarketRandomRemovalTargets; + //end Personnel Market + + //start Unit Market + private JLabel lblUnitMarketMethod; + private MMComboBox comboUnitMarketMethod; + private JCheckBox chkUnitMarketRegionalMekVariations; + private JLabel lblUnitMarketSpecialUnitChance; + private JSpinner spnUnitMarketSpecialUnitChance; + private JLabel lblUnitMarketRarityModifier; + private JSpinner spnUnitMarketRarityModifier; + private JCheckBox chkInstantUnitMarketDelivery; + private JCheckBox chkUnitMarketReportRefresh; + //end Unit Market + + //start Contract Market + private JPanel pnlContractMarketGeneralOptions; + private JLabel lblContractMarketMethod; + private MMComboBox comboContractMarketMethod; + private JLabel lblContractSearchRadius; + private JSpinner spnContractSearchRadius; + private JCheckBox chkVariableContractLength; + private JCheckBox chkContractMarketReportRefresh; + private JLabel lblCoontractMaxSalvagePercentage; + private JSpinner spnContractMaxSalvagePercentage; + private JLabel lblDropShipBonusPercentage; + private JSpinner spnDropShipBonusPercentage; + + private JPanel pnlContractPay; + private JRadioButton btnContractEquipment; + private JLabel lblEquipPercent; + private JSpinner spnEquipPercent; + private JCheckBox chkEquipContractSaleValue; + private JLabel lblDropShipPercent; + private JSpinner spnDropShipPercent; + private JLabel lblJumpShipPercent; + private JSpinner spnJumpShipPercent; + private JLabel lblWarShipPercent; + private JSpinner spnWarShipPercent; + private JRadioButton btnContractPersonnel; + private JCheckBox useInfantryDoseNotCountBox; + private JCheckBox chkMercSizeLimited; + private JCheckBox chkBLCSaleValue; + private JCheckBox chkOverageRepaymentInFinalPayment; + //end Contract Market + + /** + * Constructs a {@code MarketsTab} with the provided campaign. Initializes the market + * configuration options based on the settings of the given {@link Campaign}. + * + * @param campaign The {@link Campaign} associated with this market tab. This campaign + * is used to retrieve and modify {@link CampaignOptions}. + */ + public MarketsTab(Campaign campaign) { + this.campaign = campaign; + this.campaignOptions = campaign.getCampaignOptions(); + + initialize(); + } + + /** + * Initializes the market-related options tabs by setting up configurations + * for the Personnel Market, Unit Market, and Contract Market. + *

    + * This method is invoked internally within the constructor to prepare the + * various market configurations for use in the UI. + */ + private void initialize() { + initializePersonnelMarket(); + initializeUnitMarket(); + initializeContractMarket(); + } + + /** + * Initializes the settings and UI components related to the Personnel Market. + *

    + * This includes setting up labels, combo boxes for selecting the personnel market type, + * checkboxes for additional options, and spinners for configuring removal targets. + */ + private void initializePersonnelMarket() { + pnlPersonnelMarketGeneralOptions = new JPanel(); + lblPersonnelMarketType = new JLabel(); + comboPersonnelMarketType = new MMComboBox<>("comboPersonnelMarketType", + getPersonnelMarketTypeOptions()); + chkPersonnelMarketReportRefresh = new JCheckBox(); + chkUsePersonnelHireHiringHallOnly = new JCheckBox(); + + pnlRemovalTargets = new JPanel(); + lblPersonnelMarketDylansWeight = new JLabel(); + spnPersonnelMarketDylansWeight = new JSpinner(); + lblPersonnelMarketRandomRemovalTargets = new HashMap<>(); + spnPersonnelMarketRandomRemovalTargets = new HashMap<>(); + } + + /** + * Retrieves the available personnel market type options for display in a combo box. + *

    + * These types are fetched from the {@link PersonnelMarketServiceManager} and represent + * the available personnel market methods configured for the campaign. + * + * @return A {@link DefaultComboBoxModel} containing the personnel market type options. + */ + private static DefaultComboBoxModel getPersonnelMarketTypeOptions() { + final DefaultComboBoxModel personnelMarketTypeModel = new DefaultComboBoxModel<>(); + for (final PersonnelMarketMethod method : PersonnelMarketServiceManager.getInstance() + .getAllServices(true)) { + personnelMarketTypeModel.addElement(method.getModuleName()); + } + return personnelMarketTypeModel; + } + + /** + * Creates and returns the JPanel representing the Personnel Market configuration tab. + *

    + * This tab includes general personnel market settings, as well as removal target + * configuration options for various skill levels. + * + * @return A {@link JPanel} for the Personnel Market configuration tab. + */ + public JPanel createPersonnelMarketTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("PersonnelMarketTab", + getImageDirectory() + "logo_st_ives_compact.png"); + + // Contents + pnlPersonnelMarketGeneralOptions = createPersonnelMarketGeneralOptionsPanel(); + pnlRemovalTargets = createPersonnelMarketRemovalOptionsPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PersonnelMarketTab", true, + ""); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(pnlPersonnelMarketGeneralOptions, layout); + layout.gridx++; + panel.add(pnlRemovalTargets, layout); + + // Create Parent Panel and return + return createParentPanel(panel, "PersonnelMarketTab"); + + } + + /** + * Builds the general options panel for the Personnel Market tab, which includes settings + * such as the personnel market type, Dylan's weight, and options like report refresh toggles. + *

    + * These components are laid out into a panel and returned for use in the UI. + * + * @return A {@link JPanel} representing the general options within the Personnel Market tab. + */ + private JPanel createPersonnelMarketGeneralOptionsPanel() { + // Contents + lblPersonnelMarketType = new CampaignOptionsLabel("PersonnelMarketType"); + comboPersonnelMarketType = new MMComboBox<>("comboPersonnelMarketType", + getPersonnelMarketTypeOptions()); + + lblPersonnelMarketDylansWeight = new CampaignOptionsLabel("PersonnelMarketDylansWeight"); + spnPersonnelMarketDylansWeight = new CampaignOptionsSpinner("PersonnelMarketDylansWeight", + 0.3, 0, 1, 0.1); + + chkPersonnelMarketReportRefresh = new CampaignOptionsCheckBox("PersonnelMarketReportRefresh"); + + chkUsePersonnelHireHiringHallOnly = new CampaignOptionsCheckBox("UsePersonnelHireHiringHallOnly"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PersonnelMarketGeneralOptionsPanel", false, + ""); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(lblPersonnelMarketType, layout); + layout.gridx++; + panel.add(comboPersonnelMarketType, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblPersonnelMarketDylansWeight, layout); + layout.gridx++; + panel.add(spnPersonnelMarketDylansWeight, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkPersonnelMarketReportRefresh, layout); + + layout.gridy++; + panel.add(chkUsePersonnelHireHiringHallOnly, layout); + + return panel; + } + + /** + * Creates and configures the removal options panel for the Personnel Market tab. + *

    + * This panel includes settings for removal targets, which are based on various + * {@link SkillLevel} entries. Each skill level configuration includes both a label + * and an associated spinner for setting values. + * + * @return A {@link JPanel} containing removal options for the Personnel Market. + */ + private JPanel createPersonnelMarketRemovalOptionsPanel() { + // Contents + for (final SkillLevel skillLevel : Skills.SKILL_LEVELS) { + final JLabel jLabel = new JLabel(skillLevel.toString()); + lblPersonnelMarketRandomRemovalTargets.put(skillLevel, jLabel); + + final JSpinner jSpinner = new JSpinner( + new SpinnerNumberModel(0, 0, 12, 1)); + + DefaultEditor editor = (DefaultEditor) jSpinner.getEditor(); + editor.getTextField().setHorizontalAlignment(JTextField.LEFT); + + spnPersonnelMarketRandomRemovalTargets.put(skillLevel, jSpinner); + } + + // Layout the Panels + final JPanel panel = new CampaignOptionsStandardPanel("PersonnelMarketRemovalOptionsPanel", + true, "PersonnelMarketRemovalOptionsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(lblPersonnelMarketRandomRemovalTargets.get(SkillLevel.NONE), layout); + layout.gridx++; + panel.add(spnPersonnelMarketRandomRemovalTargets.get(SkillLevel.NONE), layout); + layout.gridx++; + panel.add(lblPersonnelMarketRandomRemovalTargets.get(SkillLevel.VETERAN), layout); + layout.gridx++; + panel.add(spnPersonnelMarketRandomRemovalTargets.get(SkillLevel.VETERAN), layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblPersonnelMarketRandomRemovalTargets.get(SkillLevel.ULTRA_GREEN), layout); + layout.gridx++; + panel.add(spnPersonnelMarketRandomRemovalTargets.get(SkillLevel.ULTRA_GREEN), layout); + layout.gridx++; + panel.add(lblPersonnelMarketRandomRemovalTargets.get(SkillLevel.ELITE), layout); + layout.gridx++; + panel.add(spnPersonnelMarketRandomRemovalTargets.get(SkillLevel.ELITE), layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblPersonnelMarketRandomRemovalTargets.get(SkillLevel.GREEN), layout); + layout.gridx++; + panel.add(spnPersonnelMarketRandomRemovalTargets.get(SkillLevel.GREEN), layout); + layout.gridx++; + panel.add(lblPersonnelMarketRandomRemovalTargets.get(SkillLevel.HEROIC), layout); + layout.gridx++; + panel.add(spnPersonnelMarketRandomRemovalTargets.get(SkillLevel.HEROIC), layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblPersonnelMarketRandomRemovalTargets.get(SkillLevel.REGULAR), layout); + layout.gridx++; + panel.add(spnPersonnelMarketRandomRemovalTargets.get(SkillLevel.REGULAR), layout); + layout.gridx++; + panel.add(lblPersonnelMarketRandomRemovalTargets.get(SkillLevel.LEGENDARY), layout); + layout.gridx++; + panel.add(spnPersonnelMarketRandomRemovalTargets.get(SkillLevel.LEGENDARY), layout); + + return panel; + } + + /** + * Initializes the settings and UI components related to the Unit Market tab. + *

    + * This includes various elements such as labels, combo boxes, checkboxes, and + * spinners for settings like unit market methods, rarity modifiers, and delivery options. + */ + private void initializeUnitMarket() { + lblUnitMarketMethod = new JLabel(); + comboUnitMarketMethod = new MMComboBox<>("comboUnitMarketMethod", UnitMarketMethod.values()); + chkUnitMarketRegionalMekVariations = new JCheckBox(); + lblUnitMarketSpecialUnitChance = new JLabel(); + spnUnitMarketSpecialUnitChance = new JSpinner(); + lblUnitMarketRarityModifier = new JLabel(); + spnUnitMarketRarityModifier = new JSpinner(); + chkInstantUnitMarketDelivery = new JCheckBox(); + chkUnitMarketReportRefresh = new JCheckBox(); + } + + /** + * Creates and returns the JPanel representing the Unit Market configuration tab. + *

    + * This tab includes options such as unit market methods, rarity modifiers, + * special unit chance settings, and more. + * + * @return A {@link JPanel} for the Unit Market configuration tab. + */ + public JPanel createUnitMarketTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("UnitMarketTab", + getImageDirectory() + "logo_clan_ice_hellion.png"); + + // Contents + lblUnitMarketMethod = new CampaignOptionsLabel("UnitMarketMethod"); + comboUnitMarketMethod = new MMComboBox<>("comboUnitMarketMethod", UnitMarketMethod.values()); + + chkUnitMarketRegionalMekVariations = new CampaignOptionsCheckBox("UnitMarketRegionalMekVariations"); + + lblUnitMarketSpecialUnitChance = new CampaignOptionsLabel("UnitMarketSpecialUnitChance"); + spnUnitMarketSpecialUnitChance = new CampaignOptionsSpinner("UnitMarketSpecialUnitChance", + 30, 0, 100, 1); + + lblUnitMarketRarityModifier = new CampaignOptionsLabel("UnitMarketRarityModifier"); + spnUnitMarketRarityModifier = new CampaignOptionsSpinner("UnitMarketRarityModifier", + 0, -10, 10, 1); + + chkInstantUnitMarketDelivery = new CampaignOptionsCheckBox("InstantUnitMarketDelivery"); + + chkUnitMarketReportRefresh = new CampaignOptionsCheckBox("UnitMarketReportRefresh"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("UnitMarketTab", true, + ""); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 5; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy++; + panel.add(lblUnitMarketMethod, layout); + layout.gridx++; + panel.add(comboUnitMarketMethod, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkUnitMarketRegionalMekVariations, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblUnitMarketSpecialUnitChance, layout); + layout.gridx++; + panel.add(spnUnitMarketSpecialUnitChance, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblUnitMarketRarityModifier, layout); + layout.gridx++; + panel.add(spnUnitMarketRarityModifier, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkInstantUnitMarketDelivery, layout); + + layout.gridy++; + panel.add(chkUnitMarketReportRefresh, layout); + + // Create Parent Panel and return + return createParentPanel(panel, "UnitMarketTab"); + } + + /** + * Initializes the settings and UI components related to the Contract Market. + *

    + * This includes options for contract market methods, payment settings, salvage percentages, + * and other contract-specific configurations. + */ + private void initializeContractMarket() { + pnlContractMarketGeneralOptions = new JPanel(); + lblContractMarketMethod = new JLabel(); + comboContractMarketMethod = new MMComboBox<>("comboContractMarketMethod"); + lblContractSearchRadius = new JLabel(); + spnContractSearchRadius = new JSpinner(); + chkVariableContractLength = new JCheckBox(); + chkContractMarketReportRefresh = new JCheckBox(); + lblCoontractMaxSalvagePercentage = new JLabel(); + spnContractMaxSalvagePercentage = new JSpinner(); + lblDropShipBonusPercentage = new JLabel(); + spnDropShipBonusPercentage = new JSpinner(); + + pnlContractPay = new JPanel(); + btnContractEquipment = new JRadioButton(); + lblEquipPercent = new JLabel(); + spnEquipPercent = new JSpinner(); + chkEquipContractSaleValue = new JCheckBox(); + lblDropShipPercent = new JLabel(); + spnDropShipPercent = new JSpinner(); + lblJumpShipPercent = new JLabel(); + spnJumpShipPercent = new JSpinner(); + lblWarShipPercent = new JLabel(); + spnWarShipPercent = new JSpinner(); + btnContractPersonnel = new JRadioButton(); + useInfantryDoseNotCountBox = new JCheckBox(); + chkMercSizeLimited = new JCheckBox(); + chkBLCSaleValue = new JCheckBox(); + chkOverageRepaymentInFinalPayment = new JCheckBox(); + } + + /** + * Creates and returns the JPanel representing the Contract Market configuration tab. + *

    + * This tab includes settings for configuring various aspects of contract acquisition, + * such as methods, search radius, payment options, and variable contract length. + * + * @return A {@link JPanel} for the Contract Market configuration tab. + */ + public JPanel createContractMarketTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("ContractMarketTab", + getImageDirectory() + "logo_federated_suns.png"); + + // Contents + pnlContractMarketGeneralOptions = createContractMarketGeneralOptionsPanel(); + pnlContractPay = createContractPayPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("ContractMarketTab", true, + ""); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(pnlContractMarketGeneralOptions, layout); + layout.gridx++; + panel.add(pnlContractPay, layout); + + // Create Parent Panel and return + return createParentPanel(panel, "ContractMarketTab"); + } + + /** + * Builds the general settings panel for the Contract Market tab, which includes + * options for the contract market method, search radius, salvage percentages, and + * other general configurations. + * + * @return A {@link JPanel} representing general options within the Contract Market tab. + */ + private JPanel createContractMarketGeneralOptionsPanel() { + // Contents + lblContractMarketMethod = new CampaignOptionsLabel("ContractMarketMethod"); + comboContractMarketMethod = new MMComboBox<>("comboContractMarketMethod"); + DefaultComboBoxModel model = new DefaultComboBoxModel<>(ContractMarketMethod.values()); + model.removeElement(ContractMarketMethod.CAM_OPS); + comboContractMarketMethod.setModel(model); + + lblContractSearchRadius = new CampaignOptionsLabel("ContractSearchRadius"); + spnContractSearchRadius = new CampaignOptionsSpinner("ContractSearchRadius", + 300, 100, 2500, 100); + + chkVariableContractLength = new CampaignOptionsCheckBox("VariableContractLength"); + + chkContractMarketReportRefresh = new CampaignOptionsCheckBox("ContractMarketReportRefresh"); + + lblCoontractMaxSalvagePercentage = new CampaignOptionsLabel("CoontractMaxSalvagePercentage"); + spnContractMaxSalvagePercentage = new CampaignOptionsSpinner("CoontractMaxSalvagePercentage", + 100, 0, 100, 10); + + lblDropShipBonusPercentage = new CampaignOptionsLabel("DropShipBonusPercentage"); + spnDropShipBonusPercentage = new CampaignOptionsSpinner("DropShipBonusPercentage", + 0, 0, 20, 5); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("ContractMarketGeneralOptionsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(lblContractMarketMethod, layout); + layout.gridx++; + panel.add(comboContractMarketMethod, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblContractSearchRadius, layout); + layout.gridx++; + panel.add(spnContractSearchRadius, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkVariableContractLength, layout); + + layout.gridy++; + panel.add(chkContractMarketReportRefresh, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblCoontractMaxSalvagePercentage, layout); + layout.gridx++; + panel.add(spnContractMaxSalvagePercentage, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblDropShipBonusPercentage, layout); + layout.gridx++; + panel.add(spnDropShipBonusPercentage, layout); + + return panel; + } + + /** + * Creates the panel for configuring payment settings in the Contract Market tab. + *

    + * This panel contains options for configuring equipment-based payment percentages, + * override repayment rules, and toggles for contract payment methods. + * + * @return A {@link JPanel} containing payment configuration settings for the Contract Market. + */ + private JPanel createContractPayPanel() { + // Contents + btnContractEquipment = new JRadioButton(resources.getString("lblContractEquipment.text")); + btnContractEquipment.setToolTipText(resources.getString("lblContractEquipment.tooltip")); + + btnContractPersonnel = new JRadioButton(resources.getString("lblContractPersonnel.text")); + btnContractPersonnel.setToolTipText(resources.getString("lblContractPersonnel.tooltip")); + + // Create a ButtonGroup to link the buttons + ButtonGroup contractGroup = new ButtonGroup(); + contractGroup.add(btnContractEquipment); + contractGroup.add(btnContractPersonnel); + + // Add other components here... + chkEquipContractSaleValue = new CampaignOptionsCheckBox("EquipContractSaleValue"); + + lblEquipPercent = new CampaignOptionsLabel("EquipPercent"); + spnEquipPercent = new CampaignOptionsSpinner("EquipPercent", + 0.1, 0, CampaignOptions.MAXIMUM_COMBAT_EQUIPMENT_PERCENT, 0.1); + + lblDropShipPercent = new CampaignOptionsLabel("DropShipPercent"); + spnDropShipPercent = new CampaignOptionsSpinner("DropShipPercent", + 0.1, 0, CampaignOptions.MAXIMUM_COMBAT_EQUIPMENT_PERCENT, 0.1); + + lblJumpShipPercent = new CampaignOptionsLabel("JumpShipPercent"); + spnJumpShipPercent = new CampaignOptionsSpinner("JumpShipPercent", + 0.1, 0, CampaignOptions.MAXIMUM_COMBAT_EQUIPMENT_PERCENT, 0.1); + + lblWarShipPercent = new CampaignOptionsLabel("WarShipPercent"); + spnWarShipPercent = new CampaignOptionsSpinner("WarShipPercent", + 0.1, 0, CampaignOptions.MAXIMUM_COMBAT_EQUIPMENT_PERCENT, 0.1); + + chkBLCSaleValue = new CampaignOptionsCheckBox("BLCSaleValue"); + + useInfantryDoseNotCountBox = new CampaignOptionsCheckBox("UseInfantryDoseNotCountBox"); + + chkMercSizeLimited = new CampaignOptionsCheckBox("MercSizeLimited"); + + chkOverageRepaymentInFinalPayment = new CampaignOptionsCheckBox("OverageRepaymentInFinalPayment"); + + // Layout the Panel + final JPanel panelValuePercent = new CampaignOptionsStandardPanel("ContractPayPanelValuePercent", + false); + final GridBagConstraints layoutValuePercent = new CampaignOptionsGridBagConstraints(panelValuePercent); + + layoutValuePercent.gridx = 0; + layoutValuePercent.gridy = 0; + layoutValuePercent.gridwidth = 1; + panelValuePercent.add(chkEquipContractSaleValue, layoutValuePercent); + + layoutValuePercent.gridy++; + panelValuePercent.add(lblEquipPercent, layoutValuePercent); + layoutValuePercent.gridx++; + panelValuePercent.add(spnEquipPercent, layoutValuePercent); + + layoutValuePercent.gridx = 0; + layoutValuePercent.gridy++; + panelValuePercent.add(lblDropShipPercent, layoutValuePercent); + layoutValuePercent.gridx++; + panelValuePercent.add(spnDropShipPercent, layoutValuePercent); + + layoutValuePercent.gridx = 0; + layoutValuePercent.gridy++; + panelValuePercent.add(lblJumpShipPercent, layoutValuePercent); + layoutValuePercent.gridx++; + panelValuePercent.add(spnJumpShipPercent, layoutValuePercent); + + layoutValuePercent.gridx = 0; + layoutValuePercent.gridy++; + panelValuePercent.add(lblWarShipPercent, layoutValuePercent); + layoutValuePercent.gridx++; + panelValuePercent.add(spnWarShipPercent, layoutValuePercent); + + final JPanel panel = new CampaignOptionsStandardPanel("ContractPayPanel", + true, "ContractPayPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(btnContractEquipment, layout); + + layout.gridy++; + panel.add(panelValuePercent, layout); + + layout.gridy++; + panel.add(btnContractPersonnel, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkBLCSaleValue, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(useInfantryDoseNotCountBox, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkMercSizeLimited, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkOverageRepaymentInFinalPayment, layout); + + return panel; + } + + + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null); + } + + /** + * Loads the campaign options from the associated {@link Campaign} into + * the UI components of the market tabs. This includes personnel, unit, + * and contract market settings. + *

    + * If no preset options are provided, the current campaign options are loaded. + * + * @param presetCampaignOptions A {@link CampaignOptions} object with previously + * configured settings, or {@code null} to use the + * current campaign's options. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Personnel Market + comboPersonnelMarketType.setSelectedItem(options.getPersonnelMarketName()); + chkPersonnelMarketReportRefresh.setSelected(options.isContractMarketReportRefresh()); + chkUsePersonnelHireHiringHallOnly.setSelected(options.isUsePersonnelHireHiringHallOnly()); + spnPersonnelMarketDylansWeight.setValue(options.getPersonnelMarketDylansWeight()); + for (final Entry entry : spnPersonnelMarketRandomRemovalTargets.entrySet()) { + entry.getValue().setValue(options.getPersonnelMarketRandomRemovalTargets().get(entry.getKey())); + } + + // Unit Market + comboUnitMarketMethod.setSelectedItem(options.getUnitMarketMethod()); + chkUnitMarketRegionalMekVariations.setSelected(options.isRegionalMekVariations()); + spnUnitMarketSpecialUnitChance.setValue(options.getUnitMarketSpecialUnitChance()); + spnUnitMarketRarityModifier.setValue(options.getUnitMarketRarityModifier()); + chkInstantUnitMarketDelivery.setSelected(options.isInstantUnitMarketDelivery()); + chkUnitMarketReportRefresh.setSelected(options.isContractMarketReportRefresh()); + + // Contract Market + comboContractMarketMethod.setSelectedItem(options.getContractMarketMethod()); + spnContractSearchRadius.setValue(options.getContractSearchRadius()); + chkVariableContractLength.setSelected(options.isVariableContractLength()); + chkContractMarketReportRefresh.setSelected(options.isContractMarketReportRefresh()); + spnContractMaxSalvagePercentage.setValue(options.getContractMaxSalvagePercentage()); + spnDropShipBonusPercentage.setValue(options.getDropShipBonusPercentage()); + if (options.isEquipmentContractBase()) { + btnContractEquipment.setSelected(true); + } else { + btnContractPersonnel.setSelected(true); + } + spnEquipPercent.setValue(options.getEquipmentContractPercent()); + chkEquipContractSaleValue.setSelected(options.isEquipmentContractSaleValue()); + spnDropShipPercent.setValue(options.getDropShipContractPercent()); + spnJumpShipPercent.setValue(options.getJumpShipContractPercent()); + spnWarShipPercent.setValue(options.getWarShipContractPercent()); + useInfantryDoseNotCountBox.setSelected(options.isInfantryDontCount()); + chkMercSizeLimited.setSelected(options.isMercSizeLimited()); + chkBLCSaleValue.setSelected(options.isBLCSaleValue()); + chkOverageRepaymentInFinalPayment.setSelected(options.isOverageRepaymentInFinalPayment()); + } + + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Personnel Market + options.setPersonnelMarketName(comboPersonnelMarketType.getSelectedItem()); + if (Objects.equals(comboPersonnelMarketType.getSelectedItem(), "Campaign Ops")) { + campaign.getPersonnelMarket().setPaidRecruitment(false); + } + options.setPersonnelMarketDylansWeight((double) spnPersonnelMarketDylansWeight.getValue()); + options.setUsePersonnelHireHiringHallOnly(chkUsePersonnelHireHiringHallOnly.isSelected()); + options.setPersonnelMarketReportRefresh(chkPersonnelMarketReportRefresh.isSelected()); + for (final Entry entry : spnPersonnelMarketRandomRemovalTargets + .entrySet()) { + options.getPersonnelMarketRandomRemovalTargets().put(entry.getKey(), + (int) entry.getValue().getValue()); + } + + // Unit Market + options.setUnitMarketMethod(comboUnitMarketMethod.getSelectedItem()); + options.setUnitMarketRegionalMekVariations(chkUnitMarketRegionalMekVariations.isSelected()); + options.setUnitMarketSpecialUnitChance((int) spnUnitMarketSpecialUnitChance.getValue()); + options.setUnitMarketRarityModifier((int) spnUnitMarketRarityModifier.getValue()); + options.setInstantUnitMarketDelivery(chkInstantUnitMarketDelivery.isSelected()); + options.setUnitMarketReportRefresh(chkUnitMarketReportRefresh.isSelected()); + + // Contract Market + options.setContractMarketMethod(comboContractMarketMethod.getSelectedItem()); + options.setContractSearchRadius((int) spnContractSearchRadius.getValue()); + options.setVariableContractLength(chkVariableContractLength.isSelected()); + options.setContractMarketReportRefresh(chkContractMarketReportRefresh.isSelected()); + options.setContractMaxSalvagePercentage((int) spnContractMaxSalvagePercentage.getValue()); + options.setDropShipBonusPercentage((int) spnDropShipBonusPercentage.getValue()); + options.setEquipmentContractBase(btnContractEquipment.isSelected()); + options.setEquipmentContractPercent((double) spnEquipPercent.getValue()); + options.setDropShipContractPercent((double) spnDropShipPercent.getValue()); + options.setJumpShipContractPercent((double) spnJumpShipPercent.getValue()); + options.setWarShipContractPercent((double) spnWarShipPercent.getValue()); + options.setEquipmentContractSaleValue(chkEquipContractSaleValue.isSelected()); + options.setMercSizeLimited(chkMercSizeLimited.isSelected()); + options.setBLCSaleValue(chkBLCSaleValue.isSelected()); + options.setUseInfantryDontCount(useInfantryDoseNotCountBox.isSelected()); + options.setOverageRepaymentInFinalPayment(chkOverageRepaymentInFinalPayment.isSelected()); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/PersonnelTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/PersonnelTab.java new file mode 100644 index 00000000000..33cef6e378b --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/PersonnelTab.java @@ -0,0 +1,1580 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; +import megamek.common.enums.SkillLevel; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.personnel.Skills; +import mekhq.campaign.personnel.enums.*; +import mekhq.gui.campaignOptions.components.*; + +import javax.swing.*; +import javax.swing.GroupLayout.Alignment; +import javax.swing.GroupLayout.Group; +import javax.swing.GroupLayout.ParallelGroup; +import javax.swing.GroupLayout.SequentialGroup; +import javax.swing.JSpinner.DefaultEditor; +import java.awt.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.ResourceBundle; + +import static megamek.client.ui.WrapLayout.wordWrap; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createGroupLayout; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * The {@code PersonnelTab} class represents the user interface components + * for configuring personnel-related options in the MekHQ Campaign Options dialog. + * This class handles the initialization, layout, and logic for various personnel + * settings spanning multiple tabs, such as general personnel options, personnel + * logs, information, awards, medical options, salaries, and prisoners and dependents. + *

    + * The class is organized into multiple tabs that encapsulate settings under specific categories: + *

    + *
      + *
    • General Tab: General settings for personnel management such as tactics, + * initiative bonus, toughness, and edge settings.
    • + *
    • Personnel Logs Tab: Settings for logging activities like skill or ability + * gains, personnel transfers, and kill records.
    • + *
    • Personnel Information Tab: Configuration of options for displaying + * personnel details like time in service, time in rank, and earnings tracking.
    • + *
    • Awards Tab: Options for managing awards given during play, including + * auto-awards, tier size configurations, and specific award filters.
    • + *
    • Medical Tab: Medical-related settings such as healing time, + * advanced medical usage, and tougher healing options.
    • + *
    • Prisoners and Dependents Tab: Configuration of prisoner handling + * and dependent-related rules.
    • + *
    • Salaries Tab: Configuration of salaries based on roles, experience + * multipliers, and base salary rates.
    • + *
    + * + *

    + * This class serves as the main controller for the UI components of the Personnel Tab, + * bridging the user interface with the {@link CampaignOptions} and ensuring the appropriate + * application of configuration settings. + *

    + */ +public class PersonnelTab { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + private final CampaignOptions campaignOptions; + + //start General Tab + private JPanel pnlPersonnelGeneralOptions; + private JCheckBox chkUseTactics; + private JCheckBox chkUseInitiativeBonus; + private JCheckBox chkUseToughness; + private JCheckBox chkUseRandomToughness; + private JCheckBox chkUseArtillery; + private JCheckBox chkUseAbilities; + private JCheckBox chkUseEdge; + private JCheckBox chkUseSupportEdge; + private JCheckBox chkUseImplants; + private JCheckBox chkUseAlternativeQualityAveraging; + + private JPanel pnlPersonnelCleanup; + private JCheckBox chkUsePersonnelRemoval; + private JCheckBox chkUseRemovalExemptCemetery; + private JCheckBox chkUseRemovalExemptRetirees; + + private JPanel pnlAdministrators; + private JCheckBox chkAdminsHaveNegotiation; + private JCheckBox chkAdminExperienceLevelIncludeNegotiation; + private JCheckBox chkAdminsHaveScrounge; + private JCheckBox chkAdminExperienceLevelIncludeScrounge; + //end General Tab + + //start Personnel Logs Tab + private JCheckBox chkUseTransfers; + private JCheckBox chkUseExtendedTOEForceName; + private JCheckBox chkPersonnelLogSkillGain; + private JCheckBox chkPersonnelLogAbilityGain; + private JCheckBox chkPersonnelLogEdgeGain; + private JCheckBox chkDisplayPersonnelLog; + private JCheckBox chkDisplayScenarioLog; + private JCheckBox chkDisplayKillRecord; + //end Personnel Logs Tab + + //start Personnel Information Tab + private JCheckBox chkUseTimeInService; + private JLabel lblTimeInServiceDisplayFormat; + private MMComboBox comboTimeInServiceDisplayFormat; + private JCheckBox chkUseTimeInRank; + private JLabel lblTimeInRankDisplayFormat; + private MMComboBox comboTimeInRankDisplayFormat; + private JCheckBox chkTrackTotalEarnings; + private JCheckBox chkTrackTotalXPEarnings; + private JCheckBox chkShowOriginFaction; + //end Personnel Information Tab + + //start Awards Tab + private JPanel pnlAwardsGeneralOptions; + private JLabel lblAwardBonusStyle; + private MMComboBox comboAwardBonusStyle; + private JLabel lblAwardTierSize; + private JSpinner spnAwardTierSize; + private JCheckBox chkEnableAutoAwards; + private JCheckBox chkIssuePosthumousAwards; + private JCheckBox chkIssueBestAwardOnly; + private JCheckBox chkIgnoreStandardSet; + + private JPanel pnlAutoAwardsFilter; + private JCheckBox chkEnableContractAwards; + private JCheckBox chkEnableFactionHunterAwards; + private JCheckBox chkEnableInjuryAwards; + private JCheckBox chkEnableIndividualKillAwards; + private JCheckBox chkEnableFormationKillAwards; + private JCheckBox chkEnableRankAwards; + private JCheckBox chkEnableScenarioAwards; + private JCheckBox chkEnableSkillAwards; + private JCheckBox chkEnableTheatreOfWarAwards; + private JCheckBox chkEnableTimeAwards; + private JCheckBox chkEnableTrainingAwards; + private JCheckBox chkEnableMiscAwards; + private JTextArea txtAwardSetFilterList; + //end Awards Tab + + //start Medical Tab + private JCheckBox chkUseAdvancedMedical; + private JLabel lblHealWaitingPeriod; + private JSpinner spnHealWaitingPeriod; + private JLabel lblNaturalHealWaitingPeriod; + private JSpinner spnNaturalHealWaitingPeriod; + private JLabel lblMinimumHitsForVehicles; + private JSpinner spnMinimumHitsForVehicles; + private JCheckBox chkUseRandomHitsForVehicles; + private JCheckBox chkUseTougherHealing; + private JLabel lblMaximumPatients; + private JSpinner spnMaximumPatients; + //end Medical Tab + + //start Prisoners and Dependents Tab + private JPanel prisonerPanel; + private JLabel lblPrisonerCaptureStyle; + private MMComboBox comboPrisonerCaptureStyle; + private JLabel lblPrisonerStatus; + private MMComboBox comboPrisonerStatus; + private JCheckBox chkPrisonerBabyStatus; + private JCheckBox chkAtBPrisonerDefection; + private JCheckBox chkAtBPrisonerRansom; + + private JPanel dependentsPanel; + private JCheckBox chkUseRandomDependentAddition; + private JCheckBox chkUseRandomDependentRemoval; + //end Prisoners and Dependents Tab + + //start Salaries Tab + private JCheckBox chkDisableSecondaryRoleSalary; + + private JPanel pnlSalaryMultipliersPanel; + private JLabel lblAntiMekSalary; + private JSpinner spnAntiMekSalary; + private JLabel lblSpecialistInfantrySalary; + private JSpinner spnSpecialistInfantrySalary; + + private JPanel pnlSalaryExperienceMultipliersPanel; + private Map lblSalaryExperienceMultipliers; + private Map spnSalaryExperienceMultipliers; + + private JPanel pnlSalaryBaseSalaryPanel; + private JLabel[] lblBaseSalary; + private JSpinner[] spnBaseSalary; + //end Salaries Tab + + /** + * Constructs the {@code PersonnelTab} object with the given campaign options. + * + * @param campaignOptions the {@link CampaignOptions} instance to be used for initializing and managing personnel options. + */ + public PersonnelTab(CampaignOptions campaignOptions) { + this.campaignOptions = campaignOptions; + + initialize(); + } + + /** + * Initializes all tabs and their components within the PersonnelTab. + */ + private void initialize() { + initializeGeneralTab(); + initializePersonnelLogsTab(); + initializePersonnelInformationTab(); + initializeAwardsTab(); + initializeMedicalTab(); + initializePrisonersAndDependentsTab(); + initializeSalariesTab(); + } + + /** + * Initializes the components of the Salaries Tab. This includes settings for + * personnel salaries, such as multipliers and base salary rates. + */ + private void initializeSalariesTab() { + chkDisableSecondaryRoleSalary = new JCheckBox(); + + pnlSalaryMultipliersPanel = new JPanel(); + + lblAntiMekSalary = new JLabel(); + spnAntiMekSalary = new JSpinner(); + + lblSpecialistInfantrySalary = new JLabel(); + spnSpecialistInfantrySalary = new JSpinner(); + + pnlSalaryExperienceMultipliersPanel = new JPanel(); + lblSalaryExperienceMultipliers = new HashMap<>(); + spnSalaryExperienceMultipliers = new HashMap<>(); + + pnlSalaryBaseSalaryPanel = new JPanel(); + lblBaseSalary = new JLabel[29]; + spnBaseSalary = new JSpinner[29]; + } + + /** + * Initializes the components of the Prisoners and Dependents Tab. This includes + * settings related to prisoners and handling of dependents. + */ + private void initializePrisonersAndDependentsTab() { + prisonerPanel = new JPanel(); + lblPrisonerCaptureStyle = new JLabel(); + comboPrisonerCaptureStyle = new MMComboBox<>("comboPrisonerCaptureStyle", + PrisonerCaptureStyle.values()); + + lblPrisonerStatus = new JLabel(); + comboPrisonerStatus = new MMComboBox<>("comboPrisonerStatus", + getPrisonerStatusOptions()); + + chkPrisonerBabyStatus = new JCheckBox(); + chkAtBPrisonerDefection = new JCheckBox(); + chkAtBPrisonerRansom = new JCheckBox(); + + dependentsPanel = new JPanel(); + chkUseRandomDependentAddition = new JCheckBox(); + chkUseRandomDependentRemoval = new JCheckBox(); + } + + /** + * Initializes the components of the Medical Tab. This includes medical-related options + * such as recovery time, random hits for vehicles, and limits on patients. + */ + private void initializeMedicalTab() { + chkUseAdvancedMedical = new JCheckBox(); + + lblHealWaitingPeriod = new JLabel(); + spnHealWaitingPeriod = new JSpinner(); + + lblNaturalHealWaitingPeriod = new JLabel(); + spnNaturalHealWaitingPeriod = new JSpinner(); + + lblMinimumHitsForVehicles = new JLabel(); + spnMinimumHitsForVehicles = new JSpinner(); + + chkUseRandomHitsForVehicles = new JCheckBox(); + chkUseTougherHealing = new JCheckBox(); + + lblMaximumPatients = new JLabel(); + spnMaximumPatients = new JSpinner(); + } + + /** + * Initializes the components of the Awards Tab. This includes settings for managing + * awards, such as automatic awards issuance, tier configurations, and award filters. + */ + private void initializeAwardsTab() { + pnlAwardsGeneralOptions = new JPanel(); + lblAwardBonusStyle = new JLabel(); + comboAwardBonusStyle = new MMComboBox<>("comboAwardBonusStyle", AwardBonus.values()); + + lblAwardTierSize = new JLabel(); + spnAwardTierSize = new JSpinner(); + chkEnableAutoAwards = new JCheckBox(); + chkIssuePosthumousAwards = new JCheckBox(); + chkIssueBestAwardOnly = new JCheckBox(); + chkIgnoreStandardSet = new JCheckBox(); + chkEnableContractAwards = new JCheckBox(); + chkEnableFactionHunterAwards = new JCheckBox(); + chkEnableInjuryAwards = new JCheckBox(); + chkEnableIndividualKillAwards = new JCheckBox(); + chkEnableFormationKillAwards = new JCheckBox(); + chkEnableRankAwards = new JCheckBox(); + chkEnableScenarioAwards = new JCheckBox(); + chkEnableSkillAwards = new JCheckBox(); + chkEnableTheatreOfWarAwards = new JCheckBox(); + chkEnableTimeAwards = new JCheckBox(); + chkEnableTrainingAwards = new JCheckBox(); + chkEnableMiscAwards = new JCheckBox(); + + pnlAutoAwardsFilter = new JPanel(); + txtAwardSetFilterList = new JTextArea(); + } + + /** + * Initializes the components of the Personnel Information Tab. This includes + * settings for tracking and displaying information like service time, rank time, and earnings. + */ + private void initializePersonnelInformationTab() { + chkUseTimeInService = new JCheckBox(); + + lblTimeInServiceDisplayFormat = new JLabel(); + comboTimeInServiceDisplayFormat = new MMComboBox<>("comboTimeInServiceDisplayFormat", + TimeInDisplayFormat.values()); + + chkUseTimeInRank = new JCheckBox(); + + lblTimeInRankDisplayFormat = new JLabel(); + comboTimeInRankDisplayFormat = new MMComboBox<>("comboTimeInRankDisplayFormat", + TimeInDisplayFormat.values()); + + chkTrackTotalEarnings = new JCheckBox(); + chkTrackTotalXPEarnings = new JCheckBox(); + chkShowOriginFaction = new JCheckBox(); + } + + /** + * Initializes the components of the Personnel Logs Tab. This includes options + * for personnel log-keeping, such as tracking skill and ability gains, as well as transfers. + */ + private void initializePersonnelLogsTab() { + chkUseTransfers = new JCheckBox(); + chkUseExtendedTOEForceName = new JCheckBox(); + chkPersonnelLogSkillGain = new JCheckBox(); + chkPersonnelLogAbilityGain = new JCheckBox(); + chkPersonnelLogEdgeGain = new JCheckBox(); + chkDisplayPersonnelLog = new JCheckBox(); + chkDisplayScenarioLog = new JCheckBox(); + chkDisplayKillRecord = new JCheckBox(); + } + + /** + * Initializes the components of the General Tab. This includes general personnel-related + * options, such as tactics, edge, initiative bonuses, and personnel cleanup settings. + */ + private void initializeGeneralTab() { + pnlPersonnelGeneralOptions = new JPanel(); + chkUseTactics = new JCheckBox(); + chkUseInitiativeBonus = new JCheckBox(); + chkUseToughness = new JCheckBox(); + chkUseRandomToughness = new JCheckBox(); + chkUseArtillery = new JCheckBox(); + chkUseAbilities = new JCheckBox(); + chkUseEdge = new JCheckBox(); + chkUseSupportEdge = new JCheckBox(); + chkUseImplants = new JCheckBox(); + chkUseAlternativeQualityAveraging = new JCheckBox(); + + pnlPersonnelCleanup = new JPanel(); + chkUsePersonnelRemoval = new JCheckBox(); + chkUseRemovalExemptCemetery = new JCheckBox(); + chkUseRemovalExemptRetirees = new JCheckBox(); + + pnlAdministrators = new JPanel(); + chkAdminsHaveNegotiation = new JCheckBox(); + chkAdminExperienceLevelIncludeNegotiation = new JCheckBox(); + chkAdminsHaveScrounge = new JCheckBox(); + chkAdminExperienceLevelIncludeScrounge = new JCheckBox(); + } + + /** + * Retrieves a {@link DefaultComboBoxModel} containing all valid {@link PrisonerStatus} options, + * except for the {@code PrisonerStatus.FREE} enumeration. + * + * @return a {@link DefaultComboBoxModel} containing the prisoner status options. + */ + private DefaultComboBoxModel getPrisonerStatusOptions() { + final DefaultComboBoxModel prisonerStatusModel = new DefaultComboBoxModel<>( + PrisonerStatus.values()); + // we don't want this as a standard use case for prisoners + prisonerStatusModel.removeElement(PrisonerStatus.FREE); + + return prisonerStatusModel; + } + + /** + * Creates the components and layout for the General Tab, + * organizing personnel management settings into specific groups. + * + * @return a {@link JPanel} representing the General Tab. + */ + public JPanel createGeneralTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("PersonnelGeneralTab", + getImageDirectory() + "logo_clan_wolverine.png"); + + // Contents + pnlPersonnelGeneralOptions = createGeneralOptionsPanel(); + pnlPersonnelCleanup = createPersonnelCleanUpPanel(); + pnlAdministrators = createAdministratorsPanel(); + + // Layout the Panels + final JPanel panelRight = new CampaignOptionsStandardPanel("RightPanel"); + GridBagConstraints layoutRight = new CampaignOptionsGridBagConstraints(panelRight); + + layoutRight.gridwidth = 1; + layoutRight.gridx = 0; + layoutRight.gridy = 0; + panelRight.add(pnlPersonnelCleanup, layoutRight); + + layoutRight.gridy++; + panelRight.add(pnlAdministrators, layoutRight); + + final JPanel panelParent = new CampaignOptionsStandardPanel("PersonnelGeneralTab", true); + GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(pnlPersonnelGeneralOptions, layoutParent); + + layoutParent.gridx++; + panelParent.add(panelRight, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "PersonnelGeneralTab"); + } + + /** + * Creates the panel for general personnel options in the General Tab. + * + * @return a {@link JPanel} containing checkboxes for various personnel management settings. + */ + private JPanel createGeneralOptionsPanel() { + // Contents + chkUseTactics = new CampaignOptionsCheckBox("UseTactics"); + chkUseInitiativeBonus = new CampaignOptionsCheckBox("UseInitiativeBonus"); + chkUseToughness = new CampaignOptionsCheckBox("UseToughness"); + chkUseRandomToughness = new CampaignOptionsCheckBox("UseRandomToughness"); + chkUseArtillery = new CampaignOptionsCheckBox("UseArtillery"); + chkUseAbilities = new CampaignOptionsCheckBox("UseAbilities"); + chkUseEdge = new CampaignOptionsCheckBox("UseEdge"); + chkUseSupportEdge = new CampaignOptionsCheckBox("UseSupportEdge"); + chkUseImplants = new CampaignOptionsCheckBox("UseImplants"); + chkUseAlternativeQualityAveraging = new CampaignOptionsCheckBox("UseAlternativeQualityAveraging"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PersonnelGeneralTab"); + GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(chkUseTactics, layout); + + layout.gridy++; + panel.add(chkUseInitiativeBonus, layout); + + layout.gridy++; + panel.add(chkUseToughness, layout); + + layout.gridy++; + panel.add(chkUseRandomToughness, layout); + + layout.gridy++; + panel.add(chkUseArtillery, layout); + + layout.gridy++; + panel.add(chkUseAbilities, layout); + + layout.gridy++; + panel.add(chkUseEdge, layout); + + layout.gridy++; + panel.add(chkUseSupportEdge, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkUseImplants, layout); + + layout.gridy++; + panel.add(chkUseImplants, layout); + + layout.gridy++; + panel.add(chkUseAlternativeQualityAveraging, layout); + + return panel; + } + + /** + * Creates the panel for personnel cleanup options in the General Tab. + * + * @return a {@link JPanel} containing options for personnel cleanup, such as removal exemptions. + */ + private JPanel createPersonnelCleanUpPanel() { + // Contents + chkUsePersonnelRemoval = new CampaignOptionsCheckBox("UsePersonnelRemoval"); + chkUseRemovalExemptCemetery = new CampaignOptionsCheckBox("UseRemovalExemptCemetery"); + chkUseRemovalExemptRetirees = new CampaignOptionsCheckBox("UseRemovalExemptRetirees"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PersonnelCleanUpPanel", true, + "PersonnelCleanUpPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel, null, GridBagConstraints.NONE); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(chkUsePersonnelRemoval, layout); + layout.gridx++; + panel.add(Box.createHorizontalStrut(UIUtil.scaleForGUI(35))); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkUseRemovalExemptCemetery, layout); + layout.gridx++; + panel.add(Box.createHorizontalStrut(UIUtil.scaleForGUI(35))); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkUseRemovalExemptRetirees, layout); + layout.gridx++; + panel.add(Box.createHorizontalStrut(UIUtil.scaleForGUI(35))); + + return panel; + } + + /** + * Creates the panel for administrative settings in the General Tab. + * + * @return a {@link JPanel} containing settings related to administrators, such as + * negotiation and scrounge options. + */ + private JPanel createAdministratorsPanel() { + // Contents + chkAdminsHaveNegotiation = new CampaignOptionsCheckBox("AdminsHaveNegotiation"); + chkAdminExperienceLevelIncludeNegotiation = new CampaignOptionsCheckBox("AdminExperienceLevelIncludeNegotiation"); + chkAdminsHaveScrounge = new CampaignOptionsCheckBox("AdminsHaveScrounge"); + chkAdminExperienceLevelIncludeScrounge = new CampaignOptionsCheckBox("AdminExperienceLevelIncludeScrounge"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AdministratorsPanel", true, + "AdministratorsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(chkAdminsHaveNegotiation, layout); + + layout.gridy++; + panel.add(chkAdminExperienceLevelIncludeNegotiation, layout); + + layout.gridy++; + panel.add(chkAdminsHaveScrounge, layout); + + layout.gridy++; + panel.add(chkAdminExperienceLevelIncludeScrounge, layout); + + return panel; + } + + /** + * Creates the panels and layout for the Awards Tab, including its general and filter components. + * + * @return a {@link JPanel} representing the Awards Tab. + */ + public JPanel createAwardsTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("AwardsTab", + getImageDirectory() + "logo_outworld_alliance.png"); + + // Contents + pnlAwardsGeneralOptions = createAwardsGeneralOptionsPanel(); + pnlAutoAwardsFilter = createAutoAwardsFilterPanel(); + + txtAwardSetFilterList = new JTextArea(10, 60); + txtAwardSetFilterList.setLineWrap(true); + txtAwardSetFilterList.setWrapStyleWord(true); + txtAwardSetFilterList.setToolTipText( + wordWrap(resources.getString("lblAwardSetFilterList.tooltip"))); + txtAwardSetFilterList.setName("txtAwardSetFilterList"); + txtAwardSetFilterList.setText(""); + JScrollPane scrollAwardSetFilterList = new JScrollPane(txtAwardSetFilterList); + scrollAwardSetFilterList.setHorizontalScrollBarPolicy( + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollAwardSetFilterList.setVerticalScrollBarPolicy( + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + + // Layout the Panel + final JPanel panelRight = new CampaignOptionsStandardPanel("AwardsTabRight"); + final GridBagConstraints layoutRight = new CampaignOptionsGridBagConstraints(panelRight); + + layoutRight.gridy = 0; + layoutRight.gridwidth = 1; + layoutRight.gridy++; + panelRight.add(pnlAutoAwardsFilter, layoutRight); + + final JPanel panelBottom = new CampaignOptionsStandardPanel("AwardsTabBottom", true, + "AwardsTabBottom"); + final GridBagConstraints layoutBottom = new CampaignOptionsGridBagConstraints(panelBottom, + null, GridBagConstraints.HORIZONTAL); + + layoutBottom.gridx = 0; + layoutBottom.gridy++; + panelBottom.add(txtAwardSetFilterList, layoutBottom); + + final JPanel panelParent = new CampaignOptionsStandardPanel("AwardsTabRight", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(pnlAwardsGeneralOptions, layoutParent); + + layoutParent.gridx++; + panelParent.add(panelRight, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + layoutParent.gridwidth = 2; + panelParent.add(panelBottom, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "AwardsTab"); + } + + /** + * Creates the panel for general award configuration settings in the Awards Tab. + * + * @return a {@link JPanel} containing settings for awards, such as bonus style and auto awards. + */ + JPanel createAwardsGeneralOptionsPanel() { + // Contents + lblAwardBonusStyle = new CampaignOptionsLabel("AwardBonusStyle"); + comboAwardBonusStyle.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof AwardBonus) { + list.setToolTipText(((AwardBonus) value).getToolTipText()); + } + return this; + } + }); + + lblAwardTierSize = new CampaignOptionsLabel("AwardTierSize"); + spnAwardTierSize = new CampaignOptionsSpinner("AwardTierSize", + 5, 1, 100, 1); + + chkEnableAutoAwards = new CampaignOptionsCheckBox("EnableAutoAwards"); + + chkIssuePosthumousAwards = new CampaignOptionsCheckBox("IssuePosthumousAwards"); + + chkIssueBestAwardOnly = new CampaignOptionsCheckBox("IssueBestAwardOnly"); + + chkIgnoreStandardSet = new CampaignOptionsCheckBox("IgnoreStandardSet"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AwardsGeneralOptionsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + panel.add(lblAwardBonusStyle, layout); + layout.gridx++; + panel.add(comboAwardBonusStyle, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAwardTierSize, layout); + layout.gridx++; + panel.add(spnAwardTierSize, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkEnableAutoAwards, layout); + + layout.gridy++; + panel.add(chkIssuePosthumousAwards, layout); + + layout.gridy++; + panel.add(chkIssueBestAwardOnly, layout); + + layout.gridy++; + panel.add(chkIgnoreStandardSet, layout); + + return panel; + } + + /** + * Creates the panel for filtering auto-awards settings in the Awards Tab. + * + * @return a {@link JPanel} containing checkboxes for various award filters. + */ + private JPanel createAutoAwardsFilterPanel() { + // Contents + chkEnableContractAwards = new CampaignOptionsCheckBox("EnableContractAwards"); + chkEnableFactionHunterAwards = new CampaignOptionsCheckBox("EnableFactionHunterAwards"); + chkEnableInjuryAwards = new CampaignOptionsCheckBox("EnableInjuryAwards"); + chkEnableIndividualKillAwards = new CampaignOptionsCheckBox("EnableIndividualKillAwards"); + chkEnableFormationKillAwards = new CampaignOptionsCheckBox("EnableFormationKillAwards"); + chkEnableRankAwards = new CampaignOptionsCheckBox("EnableRankAwards"); + chkEnableScenarioAwards = new CampaignOptionsCheckBox("EnableScenarioAwards"); + chkEnableSkillAwards = new CampaignOptionsCheckBox("EnableSkillAwards"); + chkEnableTheatreOfWarAwards = new CampaignOptionsCheckBox("EnableTheatreOfWarAwards"); + chkEnableTimeAwards = new CampaignOptionsCheckBox("EnableTimeAwards"); + chkEnableTrainingAwards = new CampaignOptionsCheckBox("EnableTrainingAwards"); + chkEnableMiscAwards = new CampaignOptionsCheckBox("EnableMiscAwards"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AutoAwardsFilterPanel", true, "AutoAwardsFilterPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(chkEnableContractAwards, layout); + layout.gridx++; + panel.add(chkEnableFactionHunterAwards, layout); + layout.gridx++; + panel.add(chkEnableInjuryAwards, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkEnableIndividualKillAwards, layout); + layout.gridx++; + panel.add(chkEnableFormationKillAwards, layout); + layout.gridx++; + panel.add(chkEnableRankAwards, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkEnableScenarioAwards, layout); + layout.gridx++; + panel.add(chkEnableSkillAwards, layout); + layout.gridx++; + panel.add(chkEnableTheatreOfWarAwards, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkEnableTimeAwards, layout); + layout.gridx++; + panel.add(chkEnableTrainingAwards, layout); + layout.gridx++; + panel.add(chkEnableMiscAwards, layout); + + return panel; + } + + /** + * Creates the layout for the Medical Tab, combining components related to medical settings. + * + * @return a {@link JPanel} containing medical-related settings. + */ + public JPanel createMedicalTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("MedicalTab", + getImageDirectory() + "logo_duchy_of_tamarind_abbey.png"); + + // Contents + chkUseAdvancedMedical = new CampaignOptionsCheckBox("UseAdvancedMedical"); + + lblHealWaitingPeriod = new CampaignOptionsLabel("HealWaitingPeriod"); + spnHealWaitingPeriod = new CampaignOptionsSpinner("HealWaitingPeriod", + 1, 1, 30, 1); + + lblNaturalHealWaitingPeriod = new CampaignOptionsLabel("NaturalHealWaitingPeriod"); + spnNaturalHealWaitingPeriod = new CampaignOptionsSpinner("NaturalHealWaitingPeriod", + 1, 1, 365, 1); + + lblMinimumHitsForVehicles = new CampaignOptionsLabel("MinimumHitsForVehicles"); + spnMinimumHitsForVehicles = new CampaignOptionsSpinner("MinimumHitsForVehicles", + 1, 1, 5, 1); + + chkUseRandomHitsForVehicles = new CampaignOptionsCheckBox("UseRandomHitsForVehicles"); + + chkUseTougherHealing = new CampaignOptionsCheckBox("UseTougherHealing"); + + lblMaximumPatients = new CampaignOptionsLabel("MaximumPatients"); + spnMaximumPatients = new CampaignOptionsSpinner("MaximumPatients", + 25, 1, 100, 1); + + final JPanel panelLeft = new CampaignOptionsStandardPanel("MedicalTabLeft"); + final GridBagConstraints layoutLeft = new CampaignOptionsGridBagConstraints(panelLeft); + + layoutLeft.gridy = 0; + layoutLeft.gridx = 0; + layoutLeft.gridwidth = 1; + panelLeft.add(lblMaximumPatients, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnMaximumPatients, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblHealWaitingPeriod, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnHealWaitingPeriod, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblNaturalHealWaitingPeriod, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnNaturalHealWaitingPeriod, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(chkUseRandomHitsForVehicles, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblMinimumHitsForVehicles, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnMinimumHitsForVehicles, layoutLeft); + + // Layout the Panels + final JPanel panelRight = new CampaignOptionsStandardPanel("MedicalTabRight"); + final GridBagConstraints layoutRight = new CampaignOptionsGridBagConstraints(panelRight); + + layoutRight.gridy++; + layoutRight.gridwidth = 1; + panelRight.add(chkUseAdvancedMedical, layoutRight); + + layoutRight.gridx = 0; + layoutRight.gridy++; + panelRight.add(chkUseTougherHealing, layoutRight); + + // Layout the Panels + final JPanel panelParent = new CampaignOptionsStandardPanel("MedicalTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(panelLeft, layoutParent); + + layoutParent.gridx++; + panelParent.add(panelRight, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "MedicalTab"); + } + + /** + * Creates the layout for the Personnel Information Tab, + * including its components for displaying personnel information and logs. + * + * @return a {@link JPanel} representing the Personnel Information Tab. + */ + public JPanel createPersonnelInformationTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("PersonnelInformation", + getImageDirectory() + "logo_rasalhague_dominion.png"); + + // Contents + chkUseTimeInService = new CampaignOptionsCheckBox("UseTimeInService"); + lblTimeInServiceDisplayFormat = new CampaignOptionsLabel("TimeInServiceDisplayFormat"); + chkUseTimeInRank = new CampaignOptionsCheckBox("UseTimeInRank"); + lblTimeInRankDisplayFormat = new CampaignOptionsLabel("TimeInRankDisplayFormat"); + chkTrackTotalEarnings = new CampaignOptionsCheckBox("TrackTotalEarnings"); + chkTrackTotalXPEarnings = new CampaignOptionsCheckBox("TrackTotalXPEarnings"); + chkShowOriginFaction = new CampaignOptionsCheckBox("ShowOriginFaction"); + + JPanel pnlPersonnelLogs = createPersonnelLogsPanel(); + + // Layout the Panel + final JPanel panelLeft = new CampaignOptionsStandardPanel("PersonnelInformationLeft"); + final GridBagConstraints layoutLeft = new CampaignOptionsGridBagConstraints(panelLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy = 0; + layoutLeft.gridwidth = 1; + panelLeft.add(chkUseTimeInService, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(lblTimeInServiceDisplayFormat, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(comboTimeInServiceDisplayFormat, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(chkUseTimeInRank, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblTimeInRankDisplayFormat, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(comboTimeInRankDisplayFormat, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(chkTrackTotalEarnings, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkTrackTotalXPEarnings, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkShowOriginFaction, layoutLeft); + + final JPanel panelParent = new CampaignOptionsStandardPanel("PersonnelInformation", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(panelLeft, layoutParent); + + layoutParent.gridx++; + panelParent.add(pnlPersonnelLogs, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "PersonnelInformation"); + } + + /** + * Creates a sub-panel for managing personnel log settings within the Personnel Information Tab. + * + * @return a {@link JPanel} containing log settings for personnel activities. + */ + JPanel createPersonnelLogsPanel() { + // Contents + chkUseTransfers = new CampaignOptionsCheckBox("UseTransfers"); + chkUseExtendedTOEForceName = new CampaignOptionsCheckBox("UseExtendedTOEForceName"); + chkPersonnelLogSkillGain = new CampaignOptionsCheckBox("PersonnelLogSkillGain"); + chkPersonnelLogAbilityGain = new CampaignOptionsCheckBox("PersonnelLogAbilityGain"); + chkPersonnelLogEdgeGain = new CampaignOptionsCheckBox("PersonnelLogEdgeGain"); + chkDisplayPersonnelLog = new CampaignOptionsCheckBox("DisplayPersonnelLog"); + chkDisplayScenarioLog = new CampaignOptionsCheckBox("DisplayScenarioLog"); + chkDisplayKillRecord = new CampaignOptionsCheckBox("DisplayKillRecord"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PersonnelLogsPanel", true, + "PersonnelLogsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(chkUseTransfers, layout); + + layout.gridy++; + panel.add(chkUseExtendedTOEForceName, layout); + + layout.gridy++; + panel.add(chkPersonnelLogSkillGain, layout); + + layout.gridy++; + panel.add(chkPersonnelLogAbilityGain, layout); + + layout.gridy++; + panel.add(chkPersonnelLogEdgeGain, layout); + + layout.gridy++; + panel.add(chkDisplayPersonnelLog, layout); + + layout.gridy++; + panel.add(chkDisplayScenarioLog, layout); + + layout.gridy++; + panel.add(chkDisplayKillRecord, layout); + + return panel; + } + + /** + * Creates the layout for the Prisoners and Dependents Tab, + * organizing settings for prisoner handling and dependent management. + * + * @return a {@link JPanel} containing the Prisoners and Dependents Tab components. + */ + public JPanel createPrisonersAndDependentsTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("PrisonersAndDependentsTab", + getImageDirectory() + "logo_illyrian_palatinate.png"); + + // Contents + prisonerPanel = createPrisonersPanel(); + dependentsPanel = createDependentsPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PrisonersAndDependentsTab", true, + ""); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panel.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(prisonerPanel, layoutParent); + + layoutParent.gridx++; + panel.add(dependentsPanel, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panel, "PrisonersAndDependentsTab"); + } + + /** + * Creates the panel for configuring prisoner settings in the Prisoners and Dependents Tab. + * + * @return a {@link JPanel} containing prisoner-related options such as capture style and status. + */ + private JPanel createPrisonersPanel() { + // Contents + lblPrisonerCaptureStyle = new CampaignOptionsLabel("PrisonerCaptureStyle"); + comboPrisonerCaptureStyle.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof PrisonerCaptureStyle) { + list.setToolTipText(((PrisonerCaptureStyle) value).getToolTipText()); + } + return this; + } + }); + + lblPrisonerStatus = new CampaignOptionsLabel("PrisonerStatus"); + + chkPrisonerBabyStatus = new CampaignOptionsCheckBox("PrisonerBabyStatus"); + chkAtBPrisonerDefection = new CampaignOptionsCheckBox("AtBPrisonerDefection"); + chkAtBPrisonerRansom = new CampaignOptionsCheckBox("AtBPrisonerRansom"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PrisonersPanel", true, + "PrisonersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblPrisonerCaptureStyle, layout); + layout.gridx++; + panel.add(comboPrisonerCaptureStyle, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblPrisonerStatus, layout); + layout.gridx++; + panel.add(comboPrisonerStatus, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkPrisonerBabyStatus, layout); + + layout.gridy++; + panel.add(chkAtBPrisonerDefection, layout); + + layout.gridy++; + panel.add(chkAtBPrisonerRansom, layout); + + return panel; + } + + /** + * Creates the panel for configuring dependent settings in the Prisoners and Dependents Tab. + * + * @return a {@link JPanel} containing dependent management options. + */ + private JPanel createDependentsPanel() { + // Contents + chkUseRandomDependentAddition = new CampaignOptionsCheckBox("UseRandomDependentAddition"); + chkUseRandomDependentRemoval = new CampaignOptionsCheckBox("UseRandomDependentRemoval"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("DependentsPanel", true, "DependentsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(chkUseRandomDependentAddition, layout); + + layout.gridy++; + panel.add(chkUseRandomDependentRemoval, layout); + + return panel; + } + + /** + * Creates the layout for the Salaries Tab, including components for salary multipliers + * and base salary settings. + * + * @return a {@link JPanel} representing the Salaries Tab. + */ + public JPanel createSalariesTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("SalariesTab", + getImageDirectory() + "logo_clan_coyote.png"); + + // Contents + chkDisableSecondaryRoleSalary = new CampaignOptionsCheckBox("DisableSecondaryRoleSalary"); + pnlSalaryMultipliersPanel = createSalaryMultipliersPanel(); + pnlSalaryExperienceMultipliersPanel = createExperienceMultipliersPanel(); + pnlSalaryBaseSalaryPanel = createBaseSalariesPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("SalariesTab", true); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 5; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(chkDisableSecondaryRoleSalary, layout); + + layout.gridy++; + panel.add(pnlSalaryMultipliersPanel, layout); + layout.gridx++; + panel.add(pnlSalaryExperienceMultipliersPanel, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(pnlSalaryBaseSalaryPanel, layout); + + // Create Parent Panel and return + return createParentPanel(panel, "SalariesTab"); + } + + /** + * Creates the panel for configuring salary multipliers for specific roles in the Salaries Tab. + * + * @return a {@link JPanel} containing salary multiplier options. + */ + private JPanel createSalaryMultipliersPanel() { + // Contents + lblAntiMekSalary = new CampaignOptionsLabel("AntiMekSalary"); + spnAntiMekSalary = new CampaignOptionsSpinner("AntiMekSalary", + 0, 0, 100, 0.01); + + lblSpecialistInfantrySalary = new CampaignOptionsLabel("SpecialistInfantrySalary"); + spnSpecialistInfantrySalary = new CampaignOptionsSpinner("SpecialistInfantrySalary", + 0, 0, 100, 0.01); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("SalaryMultipliersPanel", true, + "SalaryMultipliersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblAntiMekSalary, layout); + layout.gridx++; + panel.add(spnAntiMekSalary, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblSpecialistInfantrySalary, layout); + layout.gridx++; + panel.add(spnSpecialistInfantrySalary, layout); + + return panel; + } + + /** + * Creates the panel for configuring experience multipliers based on skill levels in the Salaries Tab. + * + * @return a {@link JPanel} containing settings for skill-based experience multipliers. + */ + private JPanel createExperienceMultipliersPanel() { + // Contents + for (final SkillLevel skillLevel : Skills.SKILL_LEVELS) { + final JLabel label = new CampaignOptionsLabel("SkillLevel" + skillLevel.toString(), + null, true); + label.setToolTipText(resources.getString("lblSkillLevelMultiplier.tooltip")); + lblSalaryExperienceMultipliers.put(skillLevel, label); + + final JSpinner spinner = new CampaignOptionsSpinner("SkillLevel" + skillLevel, + null, 0, 0, 100, 0.1, true); + spinner.setToolTipText(resources.getString("lblSkillLevelMultiplier.tooltip")); + spnSalaryExperienceMultipliers.put(skillLevel, spinner); + + } + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("ExperienceMultipliersPanel", + true, "ExperienceMultipliersPanel"); + final GroupLayout layout = createGroupLayout(panel); + panel.setLayout(layout); + + SkillLevel[] skillLevels = SkillLevel.values(); + int rows = 2; + int columns = 4; + + SequentialGroup horizontalGroups = layout.createSequentialGroup(); + ParallelGroup[] verticalGroups = new ParallelGroup[rows]; + + for (int j = 0; j < rows; j++) { + verticalGroups[j] = layout.createParallelGroup(Alignment.BASELINE); + } + + for (int i = 0; i < columns; i++) { + ParallelGroup horizontalParallelGroup = layout.createParallelGroup(); + + for (int j = 0; j < rows; j++) { + int index = i * rows + j; + + SequentialGroup horizontalSequentialGroup = layout.createSequentialGroup(); + horizontalSequentialGroup.addComponent(lblSalaryExperienceMultipliers.get(skillLevels[index])); + horizontalSequentialGroup.addComponent(spnSalaryExperienceMultipliers.get(skillLevels[index])); + if (i != (columns - 1)) { + horizontalSequentialGroup.addGap(10); + } + + horizontalParallelGroup.addGroup(horizontalSequentialGroup); + verticalGroups[j].addComponent(lblSalaryExperienceMultipliers.get(skillLevels[index])); + verticalGroups[j].addComponent(spnSalaryExperienceMultipliers.get(skillLevels[index])); + } + + horizontalGroups.addGroup(horizontalParallelGroup); + } + + layout.setHorizontalGroup(horizontalGroups); + SequentialGroup verticalGroup = layout.createSequentialGroup(); + for (Group group: verticalGroups) { + verticalGroup.addGroup(group); + } + layout.setVerticalGroup(verticalGroup); + + return panel; + } + + /** + * Creates the panel for configuring base salaries for various personnel roles in the Salaries Tab. + * + * @return a {@link JPanel} containing settings for base salaries. + */ + private JPanel createBaseSalariesPanel() { + // Contents + for (final PersonnelRole personnelRole : PersonnelRole.values()) { + String componentName = personnelRole.toString().replaceAll(" ", ""); + + // JLabel + JLabel jLabel = new JLabel(personnelRole.toString()); + jLabel.setName("lbl" + componentName); + + Dimension labelSize = jLabel.getPreferredSize(); + jLabel.setMinimumSize(UIUtil.scaleForGUI(labelSize.width, labelSize.height)); + + // JSpinner + JSpinner jSpinner = new JSpinner(); + jSpinner.setModel(new SpinnerNumberModel(0.0, 0.0, 1000000, 10.0)); + jSpinner.setName("spn" + componentName); + + DefaultEditor editor = (DefaultEditor) jSpinner.getEditor(); + editor.getTextField().setHorizontalAlignment(JTextField.LEFT); + + Dimension spinnerSize = jSpinner.getPreferredSize(); + jSpinner.setMinimumSize(UIUtil.scaleForGUI(spinnerSize.width, spinnerSize.height)); + + // Component Tracking Assignment + lblBaseSalary[personnelRole.ordinal()] = jLabel; + spnBaseSalary[personnelRole.ordinal()] = jSpinner; + } + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("BaseSalariesPanel", true, + "BaseSalariesPanel"); + final GroupLayout layout = createGroupLayout(panel); + panel.setLayout(layout); + + SequentialGroup mainHorizontalGroup = layout.createSequentialGroup(); + SequentialGroup mainVerticalGroup = layout.createSequentialGroup(); + + int columns = 3; + int rows = (int) Math.ceil((double) lblBaseSalary.length / columns); + + // Create an array to store ParallelGroups for each column + ParallelGroup[] columnGroups = new ParallelGroup[columns]; + for (int i = 0; i < columns; i++) { + columnGroups[i] = layout.createParallelGroup(); + } + + for (int j = 0; j < rows; j++) { + ParallelGroup verticalGroup = layout.createParallelGroup(Alignment.BASELINE); + + for (int i = 0; i < columns; i++) { + int index = i * rows + j; + + if (index < lblBaseSalary.length) { + // Create a SequentialGroup for the label and spinner + SequentialGroup horizontalSequentialGroup = layout.createSequentialGroup(); + + horizontalSequentialGroup.addComponent(lblBaseSalary[index]); + horizontalSequentialGroup.addComponent(spnBaseSalary[index]); + if (i != (columns - 1)) { + horizontalSequentialGroup.addGap(10); + } + + // Add the SequentialGroup to the column's ParallelGroup + columnGroups[i].addGroup(horizontalSequentialGroup); + + verticalGroup.addComponent(lblBaseSalary[index]); + verticalGroup.addComponent(spnBaseSalary[index]); + } + } + mainVerticalGroup.addGroup(verticalGroup); + } + for (ParallelGroup columnGroup : columnGroups) { + mainHorizontalGroup.addGroup(columnGroup); + } + + layout.setHorizontalGroup(mainHorizontalGroup); + layout.setVerticalGroup(mainVerticalGroup); + + return panel; + } + + /** + * Shortcut method to load default {@link CampaignOptions} values into the tab components. + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null); + } + + /** + * Loads the values from the provided {@link CampaignOptions} into the Personnel Tab components. + * If no preset options are provided, the current {@link CampaignOptions} instance is used. + * + * @param presetCampaignOptions optional custom {@link CampaignOptions} to load into the tab. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // General + chkUseTactics.setSelected(options.isUseTaxes()); + chkUseInitiativeBonus.setSelected(options.isUseInitiativeBonus()); + chkUseToughness.setSelected(options.isUseToughness()); + chkUseRandomToughness.setSelected(options.isUseRandomToughness()); + chkUseArtillery.setSelected(options.isUseArtillery()); + chkUseAbilities.setSelected(options.isUseAbilities()); + chkUseEdge.setSelected(options.isUseEdge()); + chkUseSupportEdge.setSelected(options.isUseSupportEdge()); + chkUseImplants.setSelected(options.isUseImplants()); + chkUseAlternativeQualityAveraging.setSelected(options.isAlternativeQualityAveraging()); + chkUsePersonnelRemoval.setSelected(options.isUsePersonnelRemoval()); + chkUseRemovalExemptCemetery.setSelected(options.isUseRemovalExemptCemetery()); + chkUseRemovalExemptRetirees.setSelected(options.isUseRemovalExemptRetirees()); + chkAdminsHaveNegotiation.setSelected(options.isAdminsHaveNegotiation()); + chkAdminExperienceLevelIncludeNegotiation.setSelected(options.isAdminExperienceLevelIncludeNegotiation()); + chkAdminsHaveScrounge.setSelected(options.isAdminsHaveScrounge()); + chkAdminExperienceLevelIncludeScrounge.setSelected(options.isAdminExperienceLevelIncludeScrounge()); + + // Personnel Log + chkUseTransfers.setSelected(options.isUseTransfers()); + chkUseExtendedTOEForceName.setSelected(options.isUseExtendedTOEForceName()); + chkPersonnelLogSkillGain.setSelected(options.isPersonnelLogSkillGain()); + chkPersonnelLogAbilityGain.setSelected(options.isPersonnelLogAbilityGain()); + chkPersonnelLogEdgeGain.setSelected(options.isPersonnelLogEdgeGain()); + chkDisplayPersonnelLog.setSelected(options.isDisplayPersonnelLog()); + chkDisplayScenarioLog.setSelected(options.isDisplayScenarioLog()); + chkDisplayKillRecord.setSelected(options.isDisplayKillRecord()); + + // Personnel Information + chkUseTimeInService.setSelected(options.isUseTimeInService()); + comboTimeInServiceDisplayFormat.setSelectedItem(options.getTimeInServiceDisplayFormat()); + chkUseTimeInRank.setSelected(options.isUseTimeInRank()); + comboTimeInRankDisplayFormat.setSelectedItem(options.getTimeInRankDisplayFormat()); + chkTrackTotalEarnings.setSelected(options.isTrackTotalEarnings()); + chkTrackTotalXPEarnings.setSelected(options.isTrackTotalXPEarnings()); + chkShowOriginFaction.setSelected(options.isShowOriginFaction()); + + // Awards + comboAwardBonusStyle.setSelectedItem(options.getAwardBonusStyle()); + spnAwardTierSize.setValue(options.getAwardTierSize()); + chkEnableAutoAwards.setSelected(options.isEnableAutoAwards()); + chkIssuePosthumousAwards.setSelected(options.isIssuePosthumousAwards()); + chkIssueBestAwardOnly.setSelected(options.isIssueBestAwardOnly()); + chkIgnoreStandardSet.setSelected(options.isIgnoreStandardSet()); + chkEnableContractAwards.setSelected(options.isEnableContractAwards()); + chkEnableFactionHunterAwards.setSelected(options.isEnableFactionHunterAwards()); + chkEnableInjuryAwards.setSelected(options.isEnableInjuryAwards()); + chkEnableIndividualKillAwards.setSelected(options.isEnableIndividualKillAwards()); + chkEnableFormationKillAwards.setSelected(options.isEnableFormationKillAwards()); + chkEnableRankAwards.setSelected(options.isEnableRankAwards()); + chkEnableScenarioAwards.setSelected(options.isEnableScenarioAwards()); + chkEnableSkillAwards.setSelected(options.isEnableSkillAwards()); + chkEnableTheatreOfWarAwards.setSelected(options.isEnableTheatreOfWarAwards()); + chkEnableTimeAwards.setSelected(options.isEnableTimeAwards()); + chkEnableTrainingAwards.setSelected(options.isEnableTrainingAwards()); + chkEnableMiscAwards.setSelected(options.isEnableMiscAwards()); + txtAwardSetFilterList.setText(options.getAwardSetFilterList()); + + // Medical + chkUseAdvancedMedical.setSelected(options.isUseAdvancedMedical()); + spnHealWaitingPeriod.setValue(options.getHealingWaitingPeriod()); + spnNaturalHealWaitingPeriod.setValue(options.getNaturalHealingWaitingPeriod()); + spnMinimumHitsForVehicles.setValue(options.getMinimumHitsForVehicles()); + chkUseRandomHitsForVehicles.setSelected(options.isUseRandomHitsForVehicles()); + chkUseTougherHealing.setSelected(options.isTougherHealing()); + spnMaximumPatients.setValue(options.getMaximumPatients()); + + // Prisoners and Dependents + comboPrisonerCaptureStyle.setSelectedItem(options.getPrisonerCaptureStyle()); + comboPrisonerStatus.setSelectedItem(options.getDefaultPrisonerStatus()); + chkPrisonerBabyStatus.setSelected(options.isPrisonerBabyStatus()); + chkAtBPrisonerDefection.setSelected(options.isUseAtBPrisonerDefection()); + chkAtBPrisonerRansom.setSelected(options.isUseAtBPrisonerRansom()); + chkUseRandomDependentAddition.setSelected(options.isUseRandomDependentAddition()); + chkUseRandomDependentRemoval.setSelected(options.isUseRandomDependentRemoval()); + + // Salaries + chkDisableSecondaryRoleSalary.setSelected(options.isDisableSecondaryRoleSalary()); + spnAntiMekSalary.setValue(options.getSalaryAntiMekMultiplier()); + spnSpecialistInfantrySalary.setValue(options.getSalarySpecialistInfantryMultiplier()); + for (final Entry entry : spnSalaryExperienceMultipliers.entrySet()) { + entry.getValue().setValue(options.getSalaryXPMultipliers().get(entry.getKey())); + } + for (int i = 0; i < spnBaseSalary.length; i++) { + spnBaseSalary[i].setValue(options.getRoleBaseSalaries()[i].getAmount().doubleValue()); + } + } + + /** + * Applies the modified personnel tab settings to the repository's campaign options. + * If no preset {@link CampaignOptions} is provided, the changes are applied to the current options. + * + * @param presetCampaignOptions optional custom {@link CampaignOptions} to apply changes to. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // General + options.setUseTaxes(chkUseTactics.isSelected()); + options.setUseInitiativeBonus(chkUseInitiativeBonus.isSelected()); + options.setUseToughness(chkUseToughness.isSelected()); + options.setUseRandomToughness(chkUseRandomToughness.isSelected()); + options.setUseArtillery(chkUseArtillery.isSelected()); + options.setUseAbilities(chkUseAbilities.isSelected()); + options.setUseEdge(chkUseEdge.isSelected()); + options.setUseSupportEdge(chkUseSupportEdge.isSelected()); + options.setUseImplants(chkUseImplants.isSelected()); + options.setAlternativeQualityAveraging(chkUseAlternativeQualityAveraging.isSelected()); + options.setUsePersonnelRemoval(chkUsePersonnelRemoval.isSelected()); + options.setUseRemovalExemptCemetery(chkUseRemovalExemptCemetery.isSelected()); + options.setUseRemovalExemptRetirees(chkUseRemovalExemptRetirees.isSelected()); + options.setAdminsHaveNegotiation(chkAdminsHaveNegotiation.isSelected()); + options.setAdminExperienceLevelIncludeNegotiation(chkAdminExperienceLevelIncludeNegotiation.isSelected()); + options.setAdminsHaveScrounge(chkAdminsHaveScrounge.isSelected()); + options.setAdminExperienceLevelIncludeScrounge(chkAdminExperienceLevelIncludeScrounge.isSelected()); + + // Personnel Log + options.setUseTransfers(chkUseTransfers.isSelected()); + options.setUseExtendedTOEForceName(chkUseExtendedTOEForceName.isSelected()); + options.setPersonnelLogSkillGain(chkPersonnelLogSkillGain.isSelected()); + options.setPersonnelLogAbilityGain(chkPersonnelLogAbilityGain.isSelected()); + options.setPersonnelLogEdgeGain(chkPersonnelLogEdgeGain.isSelected()); + options.setDisplayPersonnelLog(chkDisplayPersonnelLog.isSelected()); + options.setDisplayScenarioLog(chkDisplayScenarioLog.isSelected()); + options.setDisplayKillRecord(chkDisplayKillRecord.isSelected()); + + // Personnel Information + options.setUseTimeInService(chkUseTimeInService.isSelected()); + options.setTimeInServiceDisplayFormat(comboTimeInServiceDisplayFormat.getSelectedItem()); + options.setUseTimeInRank(chkUseTimeInRank.isSelected()); + options.setTimeInRankDisplayFormat(comboTimeInRankDisplayFormat.getSelectedItem()); + options.setTrackTotalEarnings(chkTrackTotalEarnings.isSelected()); + options.setTrackTotalXPEarnings(chkTrackTotalXPEarnings.isSelected()); + options.setShowOriginFaction(chkShowOriginFaction.isSelected()); + + // Awards + options.setAwardBonusStyle(comboAwardBonusStyle.getSelectedItem()); + options.setAwardTierSize((int) spnAwardTierSize.getValue()); + options.setEnableAutoAwards(chkEnableAutoAwards.isSelected()); + options.setIssuePosthumousAwards(chkIssuePosthumousAwards.isSelected()); + options.setIssueBestAwardOnly(chkIssueBestAwardOnly.isSelected()); + options.setIgnoreStandardSet(chkIgnoreStandardSet.isSelected()); + options.setEnableContractAwards(chkEnableContractAwards.isSelected()); + options.setEnableFactionHunterAwards(chkEnableFactionHunterAwards.isSelected()); + options.setEnableInjuryAwards(chkEnableInjuryAwards.isSelected()); + options.setEnableIndividualKillAwards(chkEnableIndividualKillAwards.isSelected()); + options.setEnableFormationKillAwards(chkEnableFormationKillAwards.isSelected()); + options.setEnableRankAwards(chkEnableRankAwards.isSelected()); + options.setEnableScenarioAwards(chkEnableScenarioAwards.isSelected()); + options.setEnableSkillAwards(chkEnableSkillAwards.isSelected()); + options.setEnableTheatreOfWarAwards(chkEnableTheatreOfWarAwards.isSelected()); + options.setEnableTimeAwards(chkEnableTimeAwards.isSelected()); + options.setEnableTrainingAwards(chkEnableTrainingAwards.isSelected()); + options.setEnableMiscAwards(chkEnableMiscAwards.isSelected()); + options.setAwardSetFilterList(txtAwardSetFilterList.getText()); + + // Medical + options.setUseAdvancedMedical(chkUseAdvancedMedical.isSelected()); + options.setHealingWaitingPeriod((int) spnHealWaitingPeriod.getValue()); + options.setNaturalHealingWaitingPeriod((int) spnNaturalHealWaitingPeriod.getValue()); + options.setMinimumHitsForVehicles((int) spnMinimumHitsForVehicles.getValue()); + options.setUseRandomHitsForVehicles(chkUseRandomHitsForVehicles.isSelected()); + options.setTougherHealing(chkUseTougherHealing.isSelected()); + options.setMaximumPatients((int) spnMaximumPatients.getValue()); + + // Prisoners and Dependents + options.setPrisonerCaptureStyle(comboPrisonerCaptureStyle.getSelectedItem()); + options.setDefaultPrisonerStatus(comboPrisonerStatus.getSelectedItem()); + options.setPrisonerBabyStatus(chkPrisonerBabyStatus.isSelected()); + options.setUseAtBPrisonerDefection(chkAtBPrisonerDefection.isSelected()); + options.setUseAtBPrisonerRansom(chkAtBPrisonerRansom.isSelected()); + options.setUseRandomDependentAddition(chkUseRandomDependentAddition.isSelected()); + options.setUseRandomDependentRemoval(chkUseRandomDependentRemoval.isSelected()); + + // Salaries + options.setDisableSecondaryRoleSalary(chkDisableSecondaryRoleSalary.isSelected()); + options.setSalaryAntiMekMultiplier((double) spnAntiMekSalary.getValue()); + options.setSalarySpecialistInfantryMultiplier((double) spnSpecialistInfantrySalary.getValue()); + + options.setDisableSecondaryRoleSalary(chkDisableSecondaryRoleSalary.isSelected()); + options.setSalaryAntiMekMultiplier((double) spnAntiMekSalary.getValue()); + options.setSalarySpecialistInfantryMultiplier((double) spnSpecialistInfantrySalary.getValue()); + + for (final Entry entry : spnSalaryExperienceMultipliers.entrySet()) { + options.getSalaryXPMultipliers().put(entry.getKey(), + (Double) entry.getValue().getValue()); + } + + for (final PersonnelRole personnelRole : PersonnelRole.values()) { + options.setRoleBaseSalary(personnelRole, + (double) spnBaseSalary[personnelRole.ordinal()].getValue()); + } + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/RelationshipsTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/RelationshipsTab.java new file mode 100644 index 00000000000..d3df36a5f39 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/RelationshipsTab.java @@ -0,0 +1,866 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.common.annotations.Nullable; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.personnel.enums.BabySurnameStyle; +import mekhq.campaign.personnel.enums.RandomDivorceMethod; +import mekhq.campaign.personnel.enums.RandomMarriageMethod; +import mekhq.campaign.personnel.enums.RandomProcreationMethod; +import mekhq.gui.campaignOptions.components.*; + +import javax.swing.*; +import java.awt.*; + +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * Represents a tab in the campaign options UI for configuring relationship-related options, + * such as marriage, divorce, and procreation settings. + *

    + * This tab allows users to manage manual and random settings for the relationships + * between personnel in a campaign, applying user-defined rules and configurations. + * The class generates UI components for the respective configurations and interacts + * with {@link CampaignOptions} to store and apply these settings. + *

    + *

    + * The tab is divided into three main sections: + *

    + *
      + *
    • Marriage Tab: Manages configurations for manual and random marriage settings.
    • + *
    • Divorce Tab: Manages configurations for manual and random divorce settings.
    • + *
    • Procreation Tab: Manages configurations for manual and random procreation settings.
    • + *
    + */ +public class RelationshipsTab { + private final CampaignOptions campaignOptions; + + //start Marriage Tab + private JPanel pnlMarriageGeneralOptions; + private JCheckBox chkUseManualMarriages; + private JCheckBox chkUseClanPersonnelMarriages; + private JCheckBox chkUsePrisonerMarriages; + private JLabel lblNoInterestInMarriageDiceSize; + private JSpinner spnNoInterestInMarriageDiceSize; + private JLabel lblCheckMutualAncestorsDepth; + private JSpinner spnCheckMutualAncestorsDepth; + private JCheckBox chkLogMarriageNameChanges; + + private JPanel pnlRandomMarriage; + private JLabel lblRandomMarriageMethod; + private MMComboBox comboRandomMarriageMethod; + private JCheckBox chkUseRandomClanPersonnelMarriages; + private JCheckBox chkUseRandomPrisonerMarriages; + private JLabel lblRandomMarriageAgeRange; + private JSpinner spnRandomMarriageAgeRange; + private JLabel lblRandomMarriageOppositeSexDiceSize; + private JSpinner spnRandomMarriageDiceSize; + private JLabel lblRandomSameSexMarriageDiceSize; + private JSpinner spnRandomSameSexMarriageDiceSize; + private JLabel lblRandomNewDependentMarriage; + private JSpinner spnRandomNewDependentMarriage; + //end Marriage Tab + + //start Divorce Tab + private JCheckBox chkUseManualDivorce; + private JCheckBox chkUseClanPersonnelDivorce; + private JCheckBox chkUsePrisonerDivorce; + + private JPanel pnlRandomDivorce; + private JLabel lblRandomDivorceMethod; + private MMComboBox comboRandomDivorceMethod; + private JCheckBox chkUseRandomOppositeSexDivorce; + private JCheckBox chkUseRandomSameSexDivorce; + private JCheckBox chkUseRandomClanPersonnelDivorce; + private JCheckBox chkUseRandomPrisonerDivorce; + private JLabel lblRandomDivorceDiceSize; + private JSpinner spnRandomDivorceDiceSize; + //end Divorce Tab + + //start Procreation Tab + private JCheckBox chkUseManualProcreation; + private JCheckBox chkUseClanPersonnelProcreation; + private JCheckBox chkUsePrisonerProcreation; + private JLabel lblMultiplePregnancyOccurrences; + private JSpinner spnMultiplePregnancyOccurrences; + private JLabel lblBabySurnameStyle; + private MMComboBox comboBabySurnameStyle; + private JCheckBox chkAssignNonPrisonerBabiesFounderTag; + private JCheckBox chkAssignChildrenOfFoundersFounderTag; + private JCheckBox chkDetermineFatherAtBirth; + private JCheckBox chkDisplayTrueDueDate; + private JLabel lblNoInterestInChildrenDiceSize; + private JSpinner spnNoInterestInChildrenDiceSize; + private JCheckBox chkUseMaternityLeave; + private JCheckBox chkLogProcreation; + + private JPanel pnlProcreationGeneralOptionsPanel; + private JPanel pnlRandomProcreationPanel; + private JLabel lblRandomProcreationMethod; + private MMComboBox comboRandomProcreationMethod; + private JCheckBox chkUseRelationshiplessRandomProcreation; + private JCheckBox chkUseRandomClanPersonnelProcreation; + private JCheckBox chkUseRandomPrisonerProcreation; + private JLabel lblRandomProcreationRelationshipDiceSize; + private JSpinner spnRandomProcreationRelationshipDiceSize; + private JLabel lblRandomProcreationRelationshiplessDiceSize; + private JSpinner spnRandomProcreationRelationshiplessDiceSize; + //end Procreation Tab + + /** + * Constructs a {@code RelationshipsTab} instance for configuring relationships-related campaign options. + * + * @param campaignOptions the {@link CampaignOptions} instance to be used for managing relationship settings. + */ + public RelationshipsTab(CampaignOptions campaignOptions) { + this.campaignOptions = campaignOptions; + + initialize(); + } + + /** + * Initializes the various tabs within the RelationshipsTab, including Marriage, Divorce, and Procreation Tabs. + */ + private void initialize() { + initializeMarriageTab(); + initializeDivorceTab(); + initializeProcreationTab(); + } + + /** + * Initializes the Procreation Tab and its components. + * This tab controls general procreation settings and allows configuring random procreation options. + */ + private void initializeProcreationTab() { + pnlProcreationGeneralOptionsPanel = new JPanel(); + chkUseManualProcreation = new JCheckBox(); + chkUseClanPersonnelProcreation = new JCheckBox(); + chkUsePrisonerProcreation = new JCheckBox(); + lblMultiplePregnancyOccurrences = new JLabel(); + spnMultiplePregnancyOccurrences = new JSpinner(); + lblBabySurnameStyle = new JLabel(); + comboBabySurnameStyle = new MMComboBox<>("comboBabySurnameStyle", BabySurnameStyle.values()); + chkAssignNonPrisonerBabiesFounderTag = new JCheckBox(); + chkAssignChildrenOfFoundersFounderTag = new JCheckBox(); + chkDetermineFatherAtBirth = new JCheckBox(); + chkDisplayTrueDueDate = new JCheckBox(); + lblNoInterestInChildrenDiceSize = new JLabel(); + spnNoInterestInChildrenDiceSize = new JSpinner(); + chkUseMaternityLeave = new JCheckBox(); + chkLogProcreation = new JCheckBox(); + + pnlRandomProcreationPanel = new JPanel(); + lblRandomProcreationMethod = new JLabel(); + comboRandomProcreationMethod = new MMComboBox<>("comboRandomProcreationMethod", + RandomProcreationMethod.values()); + chkUseRelationshiplessRandomProcreation = new JCheckBox(); + chkUseRandomClanPersonnelProcreation = new JCheckBox(); + chkUseRandomPrisonerProcreation = new JCheckBox(); + lblRandomProcreationRelationshipDiceSize = new JLabel(); + spnRandomProcreationRelationshipDiceSize = new JSpinner(); + lblRandomProcreationRelationshiplessDiceSize = new JLabel(); + spnRandomProcreationRelationshiplessDiceSize = new JSpinner(); + } + + /** + * Initializes the Divorce Tab and its components. + * This tab controls general divorce settings and allows configuring random divorce options. + */ + private void initializeDivorceTab() { + chkUseManualDivorce = new JCheckBox(); + chkUseClanPersonnelDivorce = new JCheckBox(); + chkUsePrisonerDivorce = new JCheckBox(); + + pnlRandomDivorce = new JPanel(); + lblRandomDivorceMethod = new JLabel(); + comboRandomDivorceMethod = new MMComboBox<>("comboRandomDivorceMethod", RandomDivorceMethod.values()); + chkUseRandomOppositeSexDivorce = new JCheckBox(); + chkUseRandomSameSexDivorce = new JCheckBox(); + chkUseRandomClanPersonnelDivorce = new JCheckBox(); + chkUseRandomPrisonerDivorce = new JCheckBox(); + lblRandomDivorceDiceSize = new JLabel(); + spnRandomDivorceDiceSize = new JSpinner(); + } + + /** + * Initializes the Marriage Tab and its components. + * This tab controls general marriage settings and allows configuring random marriage options. + */ + private void initializeMarriageTab() { + pnlMarriageGeneralOptions = new JPanel(); + chkUseManualMarriages = new JCheckBox(); + chkUseClanPersonnelMarriages = new JCheckBox(); + chkUsePrisonerMarriages = new JCheckBox(); + lblNoInterestInMarriageDiceSize = new JLabel(); + spnNoInterestInMarriageDiceSize = new JSpinner(); + lblCheckMutualAncestorsDepth = new JLabel(); + spnCheckMutualAncestorsDepth = new JSpinner(); + chkLogMarriageNameChanges = new JCheckBox(); + + pnlRandomMarriage = new JPanel(); + comboRandomMarriageMethod = new MMComboBox<>("comboRandomMarriageMethod", + RandomMarriageMethod.values()); + + pnlRandomMarriage = new JPanel(); + lblRandomMarriageMethod = new JLabel(); + comboRandomMarriageMethod = new MMComboBox<>("comboRandomMarriageMethod", + RandomMarriageMethod.values()); + chkUseRandomClanPersonnelMarriages = new JCheckBox(); + chkUseRandomPrisonerMarriages = new JCheckBox(); + lblRandomMarriageAgeRange = new JLabel(); + spnRandomMarriageAgeRange = new JSpinner(); + + lblRandomMarriageOppositeSexDiceSize = new JLabel(); + spnRandomMarriageDiceSize = new JSpinner(); + lblRandomSameSexMarriageDiceSize = new JLabel(); + spnRandomSameSexMarriageDiceSize = new JSpinner(); + lblRandomNewDependentMarriage = new JLabel(); + spnRandomNewDependentMarriage = new JSpinner(); + } + + /** + * Creates the UI for the Marriage Tab, including components for managing manual and random marriage options. + * + * @return a {@link JPanel} representing the Marriage Tab. + */ + public JPanel createMarriageTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("MarriageTab", + getImageDirectory() + "logo_morgrains_valkyrate.png"); + + // Contents + pnlMarriageGeneralOptions = createMarriageGeneralOptionsPanel(); + pnlRandomMarriage = createRandomMarriagePanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("MarriageTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panel.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(pnlMarriageGeneralOptions, layoutParent); + + layoutParent.gridx++; + panel.add(pnlRandomMarriage, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panel, "MarriageTab"); + } + + /** + * Creates the panel for general marriage settings. + * This panel includes controls like manual marriage toggles and ancestor checks. + * + * @return a {@link JPanel} containing general marriage options. + */ + private JPanel createMarriageGeneralOptionsPanel() { + // Contents + chkUseManualMarriages = new CampaignOptionsCheckBox("UseManualMarriages"); + chkUseClanPersonnelMarriages = new CampaignOptionsCheckBox("UseClanPersonnelMarriages"); + chkUsePrisonerMarriages = new CampaignOptionsCheckBox("UsePrisonerMarriages"); + + lblNoInterestInMarriageDiceSize = new CampaignOptionsLabel("NoInterestInMarriageDiceSize"); + spnNoInterestInMarriageDiceSize = new CampaignOptionsSpinner("NoInterestInMarriageDiceSize", + 10, 1, 100000, 1); + + lblCheckMutualAncestorsDepth = new CampaignOptionsLabel("CheckMutualAncestorsDepth"); + spnCheckMutualAncestorsDepth = new CampaignOptionsSpinner("CheckMutualAncestorsDepth", + 4, 0, 20, 1); + + chkLogMarriageNameChanges = new CampaignOptionsCheckBox("LogMarriageNameChanges"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("MarriageGeneralOptionsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(chkUseManualMarriages, layout); + + layout.gridy++; + panel.add(chkUseClanPersonnelMarriages, layout); + + layout.gridy++; + panel.add(chkUsePrisonerMarriages, layout); + + layout.gridy++; + panel.add(lblNoInterestInMarriageDiceSize, layout); + layout.gridx++; + panel.add(spnNoInterestInMarriageDiceSize, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblCheckMutualAncestorsDepth, layout); + layout.gridx++; + panel.add(spnCheckMutualAncestorsDepth, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkLogMarriageNameChanges, layout); + + return panel; + } + + /** + * Creates the panel for configuring random marriage settings. + * Options include random clan marriages, prisoner marriages, and other random marriage rules. + * + * @return a {@link JPanel} containing random marriage settings. + */ + private JPanel createRandomMarriagePanel() { + // Contents + lblRandomMarriageMethod = new CampaignOptionsLabel("RandomMarriageMethod"); + comboRandomMarriageMethod.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof RandomMarriageMethod) { + list.setToolTipText(((RandomMarriageMethod) value).getToolTipText()); + } + return this; + } + }); + + chkUseRandomClanPersonnelMarriages = new CampaignOptionsCheckBox("UseRandomClanPersonnelMarriages"); + chkUseRandomPrisonerMarriages = new CampaignOptionsCheckBox("UseRandomPrisonerMarriages"); + + lblRandomMarriageAgeRange = new CampaignOptionsLabel("RandomMarriageAgeRange"); + spnRandomMarriageAgeRange = new CampaignOptionsSpinner("RandomMarriageAgeRange", + 10, 0, 100, 1); + + lblRandomMarriageOppositeSexDiceSize = new CampaignOptionsLabel("RandomMarriageOppositeSexDiceSize"); + spnRandomMarriageDiceSize = new CampaignOptionsSpinner("RandomMarriageOppositeSexDiceSize", + 5000, 0, 100000, 1); + + lblRandomSameSexMarriageDiceSize = new CampaignOptionsLabel("RandomSameSexMarriageDiceSize"); + spnRandomSameSexMarriageDiceSize = new CampaignOptionsSpinner("RandomSameSexMarriageDiceSize", + 14, 0, 100000, 1); + + lblRandomNewDependentMarriage = new CampaignOptionsLabel("RandomNewDependentMarriage"); + spnRandomNewDependentMarriage = new CampaignOptionsSpinner("RandomNewDependentMarriage", + 20, 0, 100000, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("RandomMarriages", true, + "RandomMarriages"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblRandomMarriageMethod, layout); + layout.gridx++; + panel.add(comboRandomMarriageMethod, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkUseRandomClanPersonnelMarriages, layout); + + layout.gridy++; + panel.add(chkUseRandomPrisonerMarriages, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblRandomMarriageAgeRange, layout); + layout.gridx++; + panel.add(spnRandomMarriageAgeRange, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblRandomMarriageOppositeSexDiceSize, layout); + layout.gridx++; + panel.add(spnRandomMarriageDiceSize, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblRandomSameSexMarriageDiceSize, layout); + layout.gridx++; + panel.add(spnRandomSameSexMarriageDiceSize, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblRandomNewDependentMarriage, layout); + layout.gridx++; + panel.add(spnRandomNewDependentMarriage, layout); + + return panel; + } + + /** + * Creates the UI for the Divorce Tab, including components for managing manual and random divorce options. + * + * @return a {@link JPanel} representing the Divorce Tab. + */ + public JPanel createDivorceTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("DivorceTab", + getImageDirectory() + "logo_escorpion_imperio.png"); + + // Contents + chkUseManualDivorce = new CampaignOptionsCheckBox("UseManualDivorce"); + chkUseClanPersonnelDivorce = new CampaignOptionsCheckBox("UseClanPersonnelDivorce"); + chkUsePrisonerDivorce = new CampaignOptionsCheckBox("UsePrisonerDivorce"); + + pnlRandomDivorce = createRandomDivorcePanel(); + + // Layout the Panel + final JPanel panelLeft = new CampaignOptionsStandardPanel("DivorceTabLeft"); + final GridBagConstraints layoutLeft = new CampaignOptionsGridBagConstraints(panelLeft); + + layoutLeft.gridwidth = 1; + layoutLeft.gridx = 0; + layoutLeft.gridy = 0; + panelLeft.add(chkUseManualDivorce, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkUseClanPersonnelDivorce, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkUsePrisonerDivorce, layoutLeft); + + final JPanel panelParent = new CampaignOptionsStandardPanel("DivorceTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(panelLeft, layoutParent); + + layoutParent.gridx++; + panelParent.add(pnlRandomDivorce, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "DivorceTab"); + } + + /** + * Creates the panel for configuring random divorce settings. + * Options include toggles for random same-sex and opposite-sex divorces. + * + * @return a {@link JPanel} containing random divorce settings. + */ + private JPanel createRandomDivorcePanel() { + // Contents + lblRandomDivorceMethod = new CampaignOptionsLabel("RandomDivorceMethod"); + comboRandomDivorceMethod.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof RandomDivorceMethod) { + list.setToolTipText(((RandomDivorceMethod) value).getToolTipText()); + } + return this; + } + }); + + chkUseRandomOppositeSexDivorce = new CampaignOptionsCheckBox("UseRandomOppositeSexDivorce"); + chkUseRandomSameSexDivorce = new CampaignOptionsCheckBox("UseRandomSameSexDivorce"); + chkUseRandomClanPersonnelDivorce = new CampaignOptionsCheckBox("UseRandomClanPersonnelDivorce"); + chkUseRandomPrisonerDivorce = new CampaignOptionsCheckBox("UseRandomPrisonerDivorce"); + + lblRandomDivorceDiceSize = new CampaignOptionsLabel("RandomDivorceDiceSize"); + spnRandomDivorceDiceSize = new CampaignOptionsSpinner("RandomDivorceDiceSize", + 900, 0, 100000, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("RandomDivorcePanel", true, + "RandomDivorcePanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblRandomDivorceMethod, layout); + layout.gridx++; + panel.add(comboRandomDivorceMethod, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkUseRandomOppositeSexDivorce, layout); + + layout.gridy++; + panel.add(chkUseRandomSameSexDivorce, layout); + + layout.gridy++; + panel.add(chkUseRandomClanPersonnelDivorce, layout); + + layout.gridy++; + panel.add(chkUseRandomPrisonerDivorce, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblRandomDivorceDiceSize, layout); + layout.gridx++; + panel.add(spnRandomDivorceDiceSize, layout); + + return panel; + } + + /** + * Creates the UI for the Procreation Tab, including components for managing manual and random procreation options. + * + * @return a {@link JPanel} representing the Procreation Tab. + */ + public JPanel createProcreationTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("ProcreationTab", + getImageDirectory() + "logo_hanseatic_league.png"); + + // Contents + pnlProcreationGeneralOptionsPanel = createProcreationGeneralOptionsPanel(); + pnlRandomProcreationPanel = createRandomProcreationPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("ProcreationTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panel); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panel.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panel.add(pnlProcreationGeneralOptionsPanel, layoutParent); + + layoutParent.gridx++; + panel.add(pnlRandomProcreationPanel, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panel, "ProcreationTab"); + } + + /** + * Creates the panel for general procreation settings. + * This panel includes controls for determining maternity leave, surname styles, and logging options. + * + * @return a {@link JPanel} containing general procreation options. + */ + private JPanel createProcreationGeneralOptionsPanel() { + // Contents + chkUseManualProcreation = new CampaignOptionsCheckBox("UseManualProcreation"); + chkUseClanPersonnelProcreation = new CampaignOptionsCheckBox("UseClanPersonnelProcreation"); + chkUsePrisonerProcreation = new CampaignOptionsCheckBox("UsePrisonerProcreation"); + + lblMultiplePregnancyOccurrences = new CampaignOptionsLabel("MultiplePregnancyOccurrences"); + spnMultiplePregnancyOccurrences = new CampaignOptionsSpinner("MultiplePregnancyOccurrences", + 50, 1, 1000, 1); + + lblBabySurnameStyle = new CampaignOptionsLabel("BabySurnameStyle"); + comboBabySurnameStyle.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof BabySurnameStyle) { + list.setToolTipText(((BabySurnameStyle) value).getToolTipText()); + } + return this; + } + }); + + chkAssignNonPrisonerBabiesFounderTag = new CampaignOptionsCheckBox("AssignNonPrisonerBabiesFounderTag"); + chkAssignChildrenOfFoundersFounderTag = new CampaignOptionsCheckBox("AssignChildrenOfFoundersFounderTag"); + chkDetermineFatherAtBirth = new CampaignOptionsCheckBox("DetermineFatherAtBirth"); + chkDisplayTrueDueDate = new CampaignOptionsCheckBox("DisplayTrueDueDate"); + + lblNoInterestInChildrenDiceSize = new CampaignOptionsLabel("NoInterestInChildrenDiceSize"); + spnNoInterestInChildrenDiceSize = new CampaignOptionsSpinner("NoInterestInChildrenDiceSize", + 3, 1, 100000, 1); + + chkUseMaternityLeave = new CampaignOptionsCheckBox("UseMaternityLeave"); + chkLogProcreation = new CampaignOptionsCheckBox("LogProcreation"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("ProcreationGeneralOptionsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(chkUseManualProcreation, layout); + + layout.gridy++; + panel.add(chkUseClanPersonnelProcreation, layout); + + layout.gridy++; + panel.add(chkUsePrisonerProcreation, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblMultiplePregnancyOccurrences, layout); + layout.gridx++; + panel.add(spnMultiplePregnancyOccurrences, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblBabySurnameStyle, layout); + layout.gridx++; + panel.add(comboBabySurnameStyle, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkAssignNonPrisonerBabiesFounderTag, layout); + + layout.gridy++; + panel.add(chkAssignChildrenOfFoundersFounderTag, layout); + + layout.gridy++; + panel.add(chkDetermineFatherAtBirth, layout); + + layout.gridy++; + panel.add(chkDisplayTrueDueDate, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblNoInterestInChildrenDiceSize, layout); + layout.gridx++; + panel.add(spnNoInterestInChildrenDiceSize, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(chkUseMaternityLeave, layout); + + layout.gridy++; + panel.add(chkLogProcreation, layout); + + return panel; + } + + /** + * Creates the panel for configuring random procreation options. + * Options include toggles for relationshipless procreation and dice settings. + * + * @return a {@link JPanel} containing random procreation settings. + */ + private JPanel createRandomProcreationPanel() { + // Contents + lblRandomProcreationMethod = new CampaignOptionsLabel("RandomProcreationMethod"); + comboRandomProcreationMethod.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof RandomProcreationMethod) { + list.setToolTipText(((RandomProcreationMethod) value).getToolTipText()); + } + return this; + } + }); + + chkUseRelationshiplessRandomProcreation =new CampaignOptionsCheckBox("UseRelationshiplessRandomProcreation"); + chkUseRandomClanPersonnelProcreation = new CampaignOptionsCheckBox("UseRandomClanPersonnelProcreation"); + chkUseRandomPrisonerProcreation = new CampaignOptionsCheckBox("UseRandomPrisonerProcreation"); + + lblRandomProcreationRelationshipDiceSize = new CampaignOptionsLabel("RandomProcreationRelationshipDiceSize"); + spnRandomProcreationRelationshipDiceSize = new CampaignOptionsSpinner("RandomProcreationRelationshipDiceSize", + 621, 0, 100000, 1); + + lblRandomProcreationRelationshiplessDiceSize = new CampaignOptionsLabel("RandomProcreationRelationshiplessDiceSize"); + spnRandomProcreationRelationshiplessDiceSize = new CampaignOptionsSpinner("RandomProcreationRelationshiplessDiceSize", + 1861, 0, 100000, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("RandomProcreationPanel", true, + "RandomProcreationPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblRandomProcreationMethod, layout); + layout.gridx++; + panel.add(comboRandomProcreationMethod, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkUseRelationshiplessRandomProcreation, layout); + + layout.gridy++; + panel.add(chkUseRandomClanPersonnelProcreation, layout); + + layout.gridy++; + panel.add(chkUseRandomPrisonerProcreation, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblRandomProcreationRelationshipDiceSize, layout); + layout.gridx++; + panel.add(spnRandomProcreationRelationshipDiceSize, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblRandomProcreationRelationshiplessDiceSize, layout); + layout.gridx++; + panel.add(spnRandomProcreationRelationshiplessDiceSize, layout); + + return panel; + } + + /** + * Loads the default {@link CampaignOptions} values into the RelationshipsTab components. + * This is a shortcut for calling {@link #loadValuesFromCampaignOptions(CampaignOptions)} with {@code null}. + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null); + } + + /** + * Loads values from the specified {@link CampaignOptions} instance into the RelationshipsTab components. + * If no custom options are provided, the current {@link CampaignOptions} instance is used. + * + * @param presetCampaignOptions optional custom {@link CampaignOptions} to load. If {@code null}, default options are used. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Marriage + chkUseManualMarriages.setSelected(options.isUseManualMarriages()); + chkUseClanPersonnelMarriages.setSelected(options.isUseClanPersonnelMarriages()); + chkUsePrisonerMarriages.setSelected(options.isUsePrisonerMarriages()); + spnNoInterestInMarriageDiceSize.setValue(options.getNoInterestInMarriageDiceSize()); + spnCheckMutualAncestorsDepth.setValue(options.getCheckMutualAncestorsDepth()); + chkLogMarriageNameChanges.setSelected(options.isLogMarriageNameChanges()); + comboRandomMarriageMethod.setSelectedItem(options.getRandomMarriageMethod()); + chkUseRandomClanPersonnelMarriages.setSelected(options.isUseRandomClanPersonnelMarriages()); + chkUseRandomPrisonerMarriages.setSelected(options.isUsePrisonerMarriages()); + spnRandomMarriageAgeRange.setValue(options.getRandomMarriageAgeRange()); + spnRandomMarriageDiceSize.setValue(options.getRandomMarriageDiceSize()); + spnRandomSameSexMarriageDiceSize.setValue(options.getRandomSameSexMarriageDiceSize()); + spnRandomNewDependentMarriage.setValue(options.getRandomNewDependentMarriage()); + + // Divorce + chkUseManualDivorce.setSelected(options.isUseManualDivorce()); + chkUseClanPersonnelDivorce.setSelected(options.isUseClanPersonnelDivorce()); + chkUsePrisonerDivorce.setSelected(options.isUsePrisonerDivorce()); + comboRandomDivorceMethod.setSelectedItem(options.getRandomDivorceMethod()); + chkUseRandomOppositeSexDivorce.setSelected(options.isUseRandomOppositeSexDivorce()); + chkUseRandomSameSexDivorce.setSelected(options.isUseRandomSameSexDivorce()); + chkUseRandomClanPersonnelDivorce.setSelected(options.isUseClanPersonnelDivorce()); + chkUseRandomPrisonerDivorce.setSelected(options.isUsePrisonerDivorce()); + spnRandomDivorceDiceSize.setValue(options.getRandomDivorceDiceSize()); + + // Procreation + chkUseManualProcreation.setSelected(options.isUseManualProcreation()); + chkUseClanPersonnelProcreation.setSelected(options.isUseClanPersonnelProcreation()); + chkUsePrisonerProcreation.setSelected(options.isUsePrisonerProcreation()); + spnMultiplePregnancyOccurrences.setValue(options.getMultiplePregnancyOccurrences()); + comboBabySurnameStyle.setSelectedItem(options.getBabySurnameStyle()); + chkAssignNonPrisonerBabiesFounderTag.setSelected(options.isAssignNonPrisonerBabiesFounderTag()); + 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()); + chkUseRelationshiplessRandomProcreation.setSelected(options.isUseRelationshiplessRandomProcreation()); + chkUseRandomClanPersonnelProcreation.setSelected(options.isUseRandomClanPersonnelProcreation()); + chkUseRandomPrisonerProcreation.setSelected(options.isUseRandomPrisonerProcreation()); + spnRandomProcreationRelationshipDiceSize.setValue(options.getRandomProcreationRelationshipDiceSize()); + spnRandomProcreationRelationshiplessDiceSize.setValue(options.getRandomProcreationRelationshiplessDiceSize()); + } + + /** + * Applies the current settings from the RelationshipsTab components to the specified {@link CampaignOptions}. + * If no custom options are provided, changes are applied to the current {@link CampaignOptions} instance. + * + * @param presetCampaignOptions optional custom {@link CampaignOptions} to apply changes to. If {@code null}, default options are used. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Marriage + options.setUseManualMarriages(chkUseManualMarriages.isSelected()); + options.setUseClanPersonnelMarriages(chkUseClanPersonnelMarriages.isSelected()); + options.setUsePrisonerMarriages(chkUsePrisonerMarriages.isSelected()); + options.setNoInterestInMarriageDiceSize((int) spnNoInterestInMarriageDiceSize.getValue()); + options.setCheckMutualAncestorsDepth((int) spnCheckMutualAncestorsDepth.getValue()); + options.setLogMarriageNameChanges(chkLogMarriageNameChanges.isSelected()); + options.setRandomMarriageMethod(comboRandomMarriageMethod.getSelectedItem()); + options.setUseRandomClanPersonnelMarriages(chkUseRandomClanPersonnelMarriages.isSelected()); + options.setUseRandomPrisonerMarriages(chkUseRandomPrisonerMarriages.isSelected()); + options.setRandomMarriageAgeRange((int) spnRandomMarriageAgeRange.getValue()); + options.setRandomMarriageDiceSize((int) spnRandomMarriageDiceSize.getValue()); + options.setRandomSameSexMarriageDiceSize((int) spnRandomSameSexMarriageDiceSize.getValue()); + options.setRandomNewDependentMarriage((int) spnRandomNewDependentMarriage.getValue()); + + // Divorce + options.setUseManualDivorce(chkUseManualDivorce.isSelected()); + options.setUseClanPersonnelDivorce(chkUseClanPersonnelDivorce.isSelected()); + options.setUsePrisonerDivorce(chkUsePrisonerDivorce.isSelected()); + options.setRandomDivorceMethod(comboRandomDivorceMethod.getSelectedItem()); + options.setUseRandomOppositeSexDivorce(chkUseRandomOppositeSexDivorce.isSelected()); + options.setUseRandomSameSexDivorce(chkUseRandomSameSexDivorce.isSelected()); + options.setUseRandomClanPersonnelDivorce(chkUseRandomClanPersonnelDivorce.isSelected()); + options.setUseRandomPrisonerDivorce(chkUseRandomPrisonerDivorce.isSelected()); + options.setRandomDivorceDiceSize((int) spnRandomDivorceDiceSize.getValue()); + + // Procreation + options.setUseManualProcreation(chkUseManualProcreation.isSelected()); + options.setUseClanPersonnelProcreation(chkUseClanPersonnelProcreation.isSelected()); + options.setUsePrisonerProcreation(chkUsePrisonerProcreation.isSelected()); + options.setMultiplePregnancyOccurrences((int) spnMultiplePregnancyOccurrences.getValue()); + options.setBabySurnameStyle(comboBabySurnameStyle.getSelectedItem()); + options.setAssignNonPrisonerBabiesFounderTag(chkAssignNonPrisonerBabiesFounderTag.isSelected()); + options.setAssignChildrenOfFoundersFounderTag(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.setRandomProcreationRelationshipDiceSize((int) spnRandomProcreationRelationshipDiceSize.getValue()); + options.setRandomProcreationRelationshiplessDiceSize((int) spnRandomProcreationRelationshiplessDiceSize.getValue()); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/RepairAndMaintenanceTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/RepairAndMaintenanceTab.java new file mode 100644 index 00000000000..7d1ce179e05 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/RepairAndMaintenanceTab.java @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.common.annotations.Nullable; +import mekhq.campaign.CampaignOptions; +import mekhq.gui.campaignOptions.components.*; + +import javax.swing.*; +import java.awt.*; + +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * Represents a tab in the campaign options UI used to configure settings related to + * repair and maintenance in a campaign. + *

    + * This tab is divided into two sections: + *

    + *
      + *
    • Repair Tab: Manages options for era modifications, tech assignments, + * equipment quirks handling, destruction margins, and more.
    • + *
    • Maintenance Tab: Handles maintenance settings such as cycle frequency, quality standards, + * planetary modifiers, and unofficial maintenance rules.
    • + *
    + *

    + * The class interacts with {@link CampaignOptions}, enabling the retrieval, storage, + * and application of repair and maintenance configuration settings. + *

    + */ +public class RepairAndMaintenanceTab { + private final CampaignOptions campaignOptions; + + //start Repair Tab + private JCheckBox useEraModsCheckBox; + private JCheckBox assignedTechFirstCheckBox; + private JCheckBox resetToFirstTechCheckBox; + private JCheckBox useQuirksBox; + private JCheckBox useAeroSystemHitsBox; + private JCheckBox useDamageMargin; + private JLabel lblDamageMargin; + private JSpinner spnDamageMargin; + private JLabel lblDestroyPartTarget; + private JSpinner spnDestroyPartTarget; + //end Repair Tab + + //start Maintenance Tab + private JCheckBox checkMaintenance; + private JLabel lblMaintenanceDays; + private JSpinner spnMaintenanceDays; + private JLabel lblMaintenanceBonus; + private JSpinner spnMaintenanceBonus; + private JLabel lblDefaultMaintenanceTime; + private JSpinner spnDefaultMaintenanceTime; + private JCheckBox useQualityMaintenance; + private JCheckBox reverseQualityNames; + private JCheckBox chkUseRandomUnitQualities; + private JCheckBox chkUsePlanetaryModifiers; + private JCheckBox useUnofficialMaintenance; + private JCheckBox logMaintenance; + //end Maintenance Tab + + /** + * Constructs a {@code RepairAndMaintenanceTab} instance for configuring + * repair and maintenance-related settings. + * + * @param campaignOptions the {@link CampaignOptions} object to be used + * for managing repair and maintenance options. + */ + public RepairAndMaintenanceTab(CampaignOptions campaignOptions) { + this.campaignOptions = campaignOptions; + + initialize(); + } + + /** + * Initializes the components of the tab, including both the Repair and Maintenance sections. + */ + void initialize() { + initializeRepairTab(); + initializeMaintenanceTab(); + } + + /** + * Initializes the components for the Repair Tab. + *

    + * The Repair Tab includes settings for era-based modifications, technician assignment, + * equipment quirks, damage margins, and destruction thresholds. + *

    + */ + private void initializeRepairTab() { + useEraModsCheckBox = new JCheckBox(); + + assignedTechFirstCheckBox = new JCheckBox(); + + resetToFirstTechCheckBox = new JCheckBox(); + + useQuirksBox = new JCheckBox(); + + useAeroSystemHitsBox = new JCheckBox(); + + useDamageMargin = new JCheckBox(); + lblDamageMargin = new JLabel(); + spnDamageMargin = new JSpinner(); + + lblDestroyPartTarget = new JLabel(); + spnDestroyPartTarget = new JSpinner(); + } + + /** + * Initializes the components for the Maintenance Tab. + *

    + * The Maintenance Tab includes settings for maintenance scheduling, quality rules, + * randomization factors, and logging options. + *

    + */ + private void initializeMaintenanceTab() { + checkMaintenance = new JCheckBox(); + + lblMaintenanceDays = new JLabel(); + spnMaintenanceDays = new JSpinner(); + + lblMaintenanceBonus = new JLabel(); + spnMaintenanceBonus = new JSpinner(); + + lblDefaultMaintenanceTime = new JLabel(); + spnDefaultMaintenanceTime = new JSpinner(); + + useQualityMaintenance = new JCheckBox(); + + reverseQualityNames = new JCheckBox(); + + chkUseRandomUnitQualities = new JCheckBox(); + + chkUsePlanetaryModifiers = new JCheckBox(); + + useUnofficialMaintenance = new JCheckBox(); + + logMaintenance = new JCheckBox(); + } + + /** + * Creates the panel for the Repair Tab. + *

    + * This tab provides configurable options for managing repair rules, handling quirks, + * setting margins for equipment survival, and incorporating era modifications. + *

    + * + * @return a {@link JPanel} representing the Repair Tab. + */ + public JPanel createRepairTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("RepairTab", + getImageDirectory() + "logo_clan_burrock.png"); + + // Era Mods + useEraModsCheckBox = new CampaignOptionsCheckBox("UseEraModsCheckBox"); + + // Tech Placement + assignedTechFirstCheckBox = new CampaignOptionsCheckBox("AssignedTechFirstCheckBox"); + resetToFirstTechCheckBox = new CampaignOptionsCheckBox("ResetToFirstTechCheckBox"); + + // Use Quirks + useQuirksBox = new CampaignOptionsCheckBox("UseQuirksBox"); + + // Aero System Damage + useAeroSystemHitsBox = new CampaignOptionsCheckBox("UseAeroSystemHitsBox"); + + // Damage by Margin + useDamageMargin = new CampaignOptionsCheckBox("UseDamageMargin"); + useDamageMargin.addActionListener(evt -> spnDamageMargin.setEnabled(useDamageMargin.isSelected())); + + lblDamageMargin = new CampaignOptionsLabel("DamageMargin"); + spnDamageMargin = new CampaignOptionsSpinner("DamageMargin", + 1, 1, 20, 1); + + // Equipment Survival + lblDestroyPartTarget = new CampaignOptionsLabel("DestroyPartTarget"); + spnDestroyPartTarget = new CampaignOptionsSpinner("DestroyPartTarget", + 2, 2, 13, 1); + + // Layout the Panel + final JPanel panelLeft = new CampaignOptionsStandardPanel("repairTabLeft"); + final GridBagConstraints layoutLeft = new CampaignOptionsGridBagConstraints(panelLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy = 0; + layoutLeft.gridwidth = 1; + panelLeft.add(useEraModsCheckBox, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(assignedTechFirstCheckBox, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(resetToFirstTechCheckBox, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(useQuirksBox, layoutLeft); + + final JPanel panelRight = new CampaignOptionsStandardPanel("RepairTabRight", true, + "RepairTabRight"); + final GridBagConstraints layoutRight = new CampaignOptionsGridBagConstraints(panelRight); + + layoutRight.gridx = 0; + layoutRight.gridy = 0; + layoutRight.gridwidth = 2; + panelRight.add(useAeroSystemHitsBox, layoutRight); + + layoutRight.gridy++; + panelRight.add(useDamageMargin, layoutRight); + + layoutRight.gridy++; + layoutRight.gridwidth = 1; + panelRight.add(lblDamageMargin, layoutRight); + layoutRight.gridx++; + panelRight.add(spnDamageMargin, layoutRight); + + layoutRight.gridx = 0; + layoutRight.gridy++; + panelRight.add(lblDestroyPartTarget, layoutRight); + layoutRight.gridx++; + panelRight.add(spnDestroyPartTarget, layoutRight); + + final JPanel panelParent = new CampaignOptionsStandardPanel("RepairTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(panelLeft, layoutParent); + + layoutParent.gridx++; + panelParent.add(panelRight, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "repairTab"); + } + + /** + * Creates the panel for the Maintenance Tab. + *

    + * This tab provides configurable options for managing maintenance cycles, + * quality standards, planetary effects, and custom rules for units' upkeep. + *

    + * + * @return a {@link JPanel} representing the Maintenance Tab. + */ + public JPanel createMaintenanceTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("MaintenanceTab", + getImageDirectory() + "logo_magistracy_of_canopus.png"); + + // Contents + checkMaintenance = new CampaignOptionsCheckBox("CheckMaintenance"); + + lblMaintenanceDays = new CampaignOptionsLabel("MaintenanceDays"); + spnMaintenanceDays = new CampaignOptionsSpinner("MaintenanceDays", + 7, 1, 365, 1); + + lblMaintenanceBonus = new CampaignOptionsLabel("MaintenanceBonus"); + spnMaintenanceBonus = new CampaignOptionsSpinner("MaintenanceBonus", + 0, -13, 13, 1); + + lblDefaultMaintenanceTime = new CampaignOptionsLabel("DefaultMaintenanceTime"); + spnDefaultMaintenanceTime = new CampaignOptionsSpinner("DefaultMaintenanceTime", + 1, 1, 4, 1); + + useQualityMaintenance = new CampaignOptionsCheckBox("UseQualityMaintenance"); + + reverseQualityNames = new CampaignOptionsCheckBox("ReverseQualityNames"); + + chkUseRandomUnitQualities = new CampaignOptionsCheckBox("UseRandomUnitQualities"); + + chkUsePlanetaryModifiers = new CampaignOptionsCheckBox("UsePlanetaryModifiers"); + + useUnofficialMaintenance = new CampaignOptionsCheckBox("UseUnofficialMaintenance"); + + logMaintenance = new CampaignOptionsCheckBox("LogMaintenance"); + + // Layout the Panel + final JPanel panelLeft = new CampaignOptionsStandardPanel("repairTabLeft"); + GridBagConstraints layoutLeft = new CampaignOptionsGridBagConstraints(panelLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy = 0; + layoutLeft.gridwidth = 1; + panelLeft.add(checkMaintenance, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(lblMaintenanceDays, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnMaintenanceDays, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblMaintenanceBonus, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnMaintenanceBonus, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + panelLeft.add(lblDefaultMaintenanceTime, layoutLeft); + layoutLeft.gridx++; + panelLeft.add(spnDefaultMaintenanceTime, layoutLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy++; + layoutLeft.gridwidth = 2; + panelLeft.add(logMaintenance, layoutLeft); + + final JPanel panelRight = new CampaignOptionsStandardPanel("repairTabRight", true); + GridBagConstraints layoutRight = new CampaignOptionsGridBagConstraints(panelRight); + + layoutLeft.gridx = 0; + layoutLeft.gridy = 0; + layoutLeft.gridwidth = 1; + panelRight.add(useQualityMaintenance, layoutRight); + + layoutRight.gridy++; + panelRight.add(reverseQualityNames, layoutRight); + + layoutRight.gridy++; + panelRight.add(chkUseRandomUnitQualities, layoutRight); + + layoutRight.gridy++; + panelRight.add(chkUseRandomUnitQualities, layoutRight); + + layoutRight.gridy++; + panelRight.add(chkUsePlanetaryModifiers, layoutRight); + + layoutRight.gridy++; + panelRight.add(useUnofficialMaintenance, layoutRight); + + final JPanel panelParent = new CampaignOptionsStandardPanel("repairTab", true); + GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridx = 0; + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(panelLeft, layoutParent); + layoutParent.gridx++; + panelParent.add(panelRight, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "maintenanceTab"); + } + + /** + * Applies the current tab's repair and maintenance settings from the UI components + * to the provided {@link CampaignOptions}. + *

    + * If no custom {@link CampaignOptions} are provided, uses the default + * {@link CampaignOptions} associated with this tab. + *

    + * + * @param presetCampaignOptions optional custom {@link CampaignOptions} object to + * apply the current settings to. If {@code null}, the default options are modified. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Repair + options.setEraMods(useEraModsCheckBox.isSelected()); + options.setAssignedTechFirst(assignedTechFirstCheckBox.isSelected()); + options.setResetToFirstTech(resetToFirstTechCheckBox.isSelected()); + options.setQuirks(useQuirksBox.isSelected()); + options.setUseAeroSystemHits(useAeroSystemHitsBox.isSelected()); + options.setDestroyByMargin(useDamageMargin.isSelected()); + options.setDestroyMargin((int) spnDamageMargin.getValue()); + options.setDestroyPartTarget((int) spnDestroyPartTarget.getValue()); + + // Maintenance + options.setCheckMaintenance(checkMaintenance.isSelected()); + options.setMaintenanceCycleDays((int) spnMaintenanceDays.getValue()); + options.setMaintenanceBonus((int) spnMaintenanceBonus.getValue()); + options.setDefaultMaintenanceTime((int) spnDefaultMaintenanceTime.getValue()); + options.setUseQualityMaintenance(useQualityMaintenance.isSelected()); + options.setReverseQualityNames(reverseQualityNames.isSelected()); + options.setUseRandomUnitQualities(chkUseRandomUnitQualities.isSelected()); + options.setUsePlanetaryModifiers(chkUsePlanetaryModifiers.isSelected()); + options.setUseUnofficialMaintenance(useUnofficialMaintenance.isSelected()); + options.setLogMaintenance(logMaintenance.isSelected()); + } + + /** + * Loads the repair and maintenance settings from the default {@link CampaignOptions} + * into the tab's UI components. + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null); + } + + /** + * Loads the repair and maintenance settings from the {@link CampaignOptions} object + * into the tab's UI components. + *

    + * If no custom {@link CampaignOptions} are provided, the default + * {@link CampaignOptions} associated with this tab is used. + *

    + * + * @param presetCampaignOptions optional custom {@link CampaignOptions} object to load settings from. + * If {@code null}, the default options are used. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Repair + useEraModsCheckBox.setSelected(options.isUseEraMods()); + assignedTechFirstCheckBox.setSelected(options.isAssignedTechFirst()); + resetToFirstTechCheckBox.setSelected(options.isResetToFirstTech()); + useQuirksBox.setSelected(options.isUseQuirks()); + useAeroSystemHitsBox.setSelected(options.isUseAeroSystemHits()); + useDamageMargin.setSelected(options.isDestroyByMargin()); + spnDamageMargin.setValue(options.getDestroyMargin()); + spnDestroyPartTarget.setValue(options.getDestroyPartTarget()); + + // Maintenance + checkMaintenance.setSelected(options.isCheckMaintenance()); + spnMaintenanceDays.setValue(options.getMaintenanceCycleDays()); + spnMaintenanceBonus.setValue(options.getMaintenanceBonus()); + spnDefaultMaintenanceTime.setValue(options.getDefaultMaintenanceTime()); + useQualityMaintenance.setSelected(options.isUseQualityMaintenance()); + reverseQualityNames.setSelected(options.isReverseQualityNames()); + chkUseRandomUnitQualities.setSelected(options.isUseRandomUnitQualities()); + chkUsePlanetaryModifiers.setSelected(options.isUsePlanetaryModifiers()); + useUnofficialMaintenance.setSelected(options.isUseUnofficialMaintenance()); + logMaintenance.setSelected(options.isLogMaintenance()); + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/RulesetsTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/RulesetsTab.java new file mode 100644 index 00000000000..4749b272ec2 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/RulesetsTab.java @@ -0,0 +1,1176 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.ui.baseComponents.FileNameComboBoxModel; +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.client.ui.swing.GUIPreferences; +import megamek.common.annotations.Nullable; +import megamek.common.enums.SkillLevel; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.autoresolve.AutoResolveMethod; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.enums.CombatRole; +import mekhq.campaign.personnel.Skills; +import mekhq.gui.campaignOptions.components.*; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; +import java.io.File; +import java.util.ResourceBundle; + +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; +import static org.apache.commons.lang3.ObjectUtils.firstNonNull; + +/** + * Represents a tab in the campaign options UI for managing ruleset configurations in campaigns. + *

    + * This class organizes and manages options related to universal rules, legacy AtB rules (Against the Bot), + * and StratCon (Strategic Context) settings. It provides a UI to customize configurations such as + * opponent force generation, scenario rules, equipment behavior, and campaign-specific variations. + *

    + * + * Tab Sections: + *
      + *
    • Universal Options: Handles features applicable to all campaigns, + * such as skill levels, unit ratios, map conditions, and auto-resolve settings.
    • + *
    • Legacy AtB: Legacy-specific rules for opponent force generation, + * scenario generation probabilities, and battle intensity configurations.
    • + *
    • StratCon: Settings for Strategic Context campaigns, including BV usage + * (Battle Values) and verbose bidding options.
    • + *
    + */ +public class RulesetsTab { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + private final CampaignOptions campaignOptions; + + //start Universal Options + private JLabel lblSkillLevel; + private MMComboBox comboSkillLevel; + private JPanel pnlScenarioGenerationPanel; + private JPanel pnlCampaignOptions; + + private JPanel pnlUnitRatioPanel; + private JLabel lblOpForLanceTypeMeks; + private JSpinner spnOpForLanceTypeMeks; + private JLabel lblOpForLanceTypeMixed; + private JSpinner spnOpForLanceTypeMixed; + private JLabel lblOpForLanceTypeVehicle; + private JSpinner spnOpForLanceTypeVehicles; + + private JCheckBox chkUseDropShips; + private JCheckBox chkOpForUsesVTOLs; + + private JCheckBox chkClanVehicles; + private JCheckBox chkRegionalMekVariations; + + private JCheckBox chkAttachedPlayerCamouflage; + private JCheckBox chkPlayerControlsAttachedUnits; + private JLabel lblSPAUpgradeIntensity; + private JSpinner spnSPAUpgradeIntensity; + private JCheckBox chkAutoConfigMunitions; + + private JPanel pnlScenarioModifiers; + private JLabel lblScenarioModMax; + private JSpinner spnScenarioModMax; + private JLabel lblScenarioModChance; + private JSpinner spnScenarioModChance; + private JLabel lblScenarioModBV; + private JSpinner spnScenarioModBV; + + private JPanel pnlMapGenerationPanel; + private JCheckBox chkUseWeatherConditions; + private JCheckBox chkUseLightConditions; + private JCheckBox chkUsePlanetaryConditions; + private JLabel lblFixedMapChance; + private JSpinner spnFixedMapChance; + + private JPanel pnlPartsPanel; + private JCheckBox chkRestrictPartsByMission; + + private JPanel pnlLancePanel; + private JCheckBox chkLimitLanceWeight; + private JCheckBox chkLimitLanceNumUnits; + private JCheckBox chkUseStrategy; + private JLabel lblBaseStrategyDeployment; + private JSpinner spnBaseStrategyDeployment; + private JLabel lblAdditionalStrategyDeployment; + private JSpinner spnAdditionalStrategyDeployment; + + private JPanel pnlAutoResolve; + private JLabel lblAutoResolveMethod; + private MMComboBox comboAutoResolveMethod; + private MMComboBox minimapThemeSelector; + private JCheckBox chkAutoResolveVictoryChanceEnabled; + private JCheckBox chkAutoResolveExperimentalPacarGuiEnabled; + private JLabel lblAutoResolveNumberOfScenarios; + private JSpinner spnAutoResolveNumberOfScenarios; + //end Universal Options + + //start Legacy AtB + private JCheckBox chkUseAtB; + + private JPanel pnlLegacyOpForGenerationPanel; + private JCheckBox chkUseVehicles; + private JCheckBox chkDoubleVehicles; + private JCheckBox chkOpForUsesAero; + private JLabel lblOpForAeroChance; + private JSpinner spnOpForAeroChance; + private JCheckBox chkOpForUsesLocalForces; + private JCheckBox chkAdjustPlayerVehicles; + + private JPanel pnlLegacyScenarioGenerationPanel; + private JLabel lblIntensity; + private JSpinner spnAtBBattleIntensity; + private JLabel lblFightChance; + private JLabel lblDefendChance; + private JLabel lblScoutChance; + private JLabel lblTrainingChance; + private JSpinner[] spnAtBBattleChance; + private JButton btnIntensityUpdate; + private JCheckBox chkGenerateChases; + //end Legacy AtB + + //start StratCon + private JCheckBox chkUseStratCon; + private JCheckBox chkUseGenericBattleValue; + private JCheckBox chkUseVerboseBidding; + //end StratCon + + /** + * Constructs a {@code RulesetsTab} instance for managing ruleset options. + * + * @param campaignOptions the {@link CampaignOptions} object to manage repair, maintenance, and other ruleset options. + */ + public RulesetsTab(CampaignOptions campaignOptions) { + this.campaignOptions = campaignOptions; + + initialize(); + } + + /** + * Initializes the tab by setting up all three sections: + *

    + *

  93. Universal Options
  94. + *
  95. StratCon Tab
  96. + *
  97. Legacy Tab
  98. + *

    + */ + private void initialize() { + initializeUniversalOptions(); + initializeStratConTab(); + initializeLegacyTab(); + } + + /** + * Initializes the universal options section of the tab. + *

    + * Universal options include settings like skill levels, scenario modifiers, + * map generation parameters, and auto-resolve behavior. + *

    + */ + private void initializeUniversalOptions() { + // General + lblSkillLevel = new JLabel(); + comboSkillLevel = new MMComboBox<>("comboSkillLevel", getSkillLevelOptions()); + pnlScenarioGenerationPanel = new JPanel(); + + // OpFor Generation + pnlUnitRatioPanel = new JPanel(); + lblOpForLanceTypeMeks = new JLabel(); + spnOpForLanceTypeMeks = new JSpinner(); + lblOpForLanceTypeMixed = new JLabel(); + spnOpForLanceTypeMixed = new JSpinner(); + lblOpForLanceTypeVehicle = new JLabel(); + spnOpForLanceTypeVehicles = new JSpinner(); + + chkUseDropShips = new JCheckBox(); + chkOpForUsesVTOLs = new JCheckBox(); + chkClanVehicles = new JCheckBox(); + chkRegionalMekVariations = new JCheckBox(); + + chkAttachedPlayerCamouflage = new JCheckBox(); + chkPlayerControlsAttachedUnits = new JCheckBox(); + + lblSPAUpgradeIntensity = new JLabel(); + spnSPAUpgradeIntensity = new JSpinner(); + chkAutoConfigMunitions = new JCheckBox(); + + pnlScenarioModifiers = new JPanel(); + lblScenarioModMax = new JLabel(); + spnScenarioModMax = new JSpinner(); + lblScenarioModChance = new JLabel(); + spnScenarioModChance = new JSpinner(); + lblScenarioModBV = new JLabel(); + spnScenarioModBV = new JSpinner(); + + // Map Generation + pnlMapGenerationPanel = new JPanel(); + chkUseWeatherConditions = new JCheckBox(); + chkUseLightConditions = new JCheckBox(); + chkUsePlanetaryConditions = new JCheckBox(); + lblFixedMapChance = new JLabel(); + spnFixedMapChance = new JSpinner(); + + // Parts + pnlPartsPanel = new JPanel(); + chkRestrictPartsByMission = new JCheckBox(); + + // Lances + pnlLancePanel = new JPanel(); + chkLimitLanceWeight = new JCheckBox(); + chkLimitLanceNumUnits = new JCheckBox(); + chkUseStrategy = new JCheckBox(); + lblBaseStrategyDeployment = new JLabel(); + spnBaseStrategyDeployment = new JSpinner(); + lblAdditionalStrategyDeployment = new JLabel(); + spnAdditionalStrategyDeployment = new JSpinner(); + + // Auto Resolve + pnlAutoResolve = new JPanel(); + lblAutoResolveMethod = new JLabel(); + final DefaultComboBoxModel autoResolveTypeModel = new DefaultComboBoxModel<>( + AutoResolveMethod.values()); + comboAutoResolveMethod = new MMComboBox<>("comboAutoResolveMethod", autoResolveTypeModel); + minimapThemeSelector = new MMComboBox<>("minimapThemeSelector", + new FileNameComboBoxModel(GUIPreferences.getInstance().getMinimapThemes())); + chkAutoResolveVictoryChanceEnabled = new JCheckBox(); + lblAutoResolveNumberOfScenarios = new JLabel(); + spnAutoResolveNumberOfScenarios = new JSpinner(); + chkAutoResolveExperimentalPacarGuiEnabled = new JCheckBox(); + // Here we set up the options, so they can be used across both the AtB and StratCon tabs + substantializeUniversalOptions(); + } + + /** + * Configures and initializes universal options components for use across tabs. + *

    + * This method sets up and organizes the various UI elements for universal options, + * such as skill levels, scenario generation, map generation, and more. These initialized + * components are then used in other methods to build the complete universal options UI. + *

    + */ + private void substantializeUniversalOptions() { + // General + lblSkillLevel = new CampaignOptionsLabel("SkillLevel"); + comboSkillLevel.setToolTipText(resources.getString("lblSkillLevel.tooltip")); + + // OpFor Generation + pnlUnitRatioPanel = createUniversalUnitRatioPanel(); + + chkUseDropShips = new CampaignOptionsCheckBox("UseDropShips"); + chkOpForUsesVTOLs = new CampaignOptionsCheckBox("OpForUsesVTOLs"); + chkClanVehicles = new CampaignOptionsCheckBox("ClanVehicles"); + chkRegionalMekVariations = new CampaignOptionsCheckBox("RegionalMekVariations"); + + chkAttachedPlayerCamouflage = new CampaignOptionsCheckBox("AttachedPlayerCamouflage"); + chkPlayerControlsAttachedUnits = new CampaignOptionsCheckBox("PlayerControlsAttachedUnits"); + lblSPAUpgradeIntensity = new CampaignOptionsLabel("SPAUpgradeIntensity"); + spnSPAUpgradeIntensity = new CampaignOptionsSpinner("SPAUpgradeIntensity", + 0, -1, 3, 1); + chkAutoConfigMunitions = new CampaignOptionsCheckBox("AutoConfigMunitions"); + + // Other + pnlScenarioModifiers = createUniversalModifiersPanel(); + pnlMapGenerationPanel = createUniversalMapGenerationPanel(); + pnlPartsPanel = createUniversalPartsPanel(); + pnlLancePanel = createUniversalLancePanel(); + + pnlScenarioGenerationPanel = createUniversalScenarioGenerationPanel(); + pnlCampaignOptions = createUniversalCampaignOptionsPanel(); + pnlAutoResolve = createAutoResolvePanel(); + } + + /** + * Retrieves the available skill levels as a {@link DefaultComboBoxModel}. + *

    + * Returns the predefined {@link SkillLevel} values, excluding {@link SkillLevel#NONE}. + * Used for populating the skill level selector in the universal options UI. + *

    + * + * @return a {@link DefaultComboBoxModel} containing available {@link SkillLevel} options + */ + private static DefaultComboBoxModel getSkillLevelOptions() { + final DefaultComboBoxModel skillLevelModel = new DefaultComboBoxModel<>( + Skills.SKILL_LEVELS); + + skillLevelModel.removeElement(SkillLevel.NONE); + + return skillLevelModel; + } + + /** + * Creates the UI panel for configuring universal scenario generation options. + *

    + * Allows users to define settings for opponent force configurations, such as + * enabling dropships, VTOLs, and clan vehicles, as well as other universal scenario parameters. + *

    + * + * @return a {@link JPanel} containing controls to configure universal scenario generation + */ + private JPanel createUniversalScenarioGenerationPanel() { + // Layout the panel + final JPanel panel = new CampaignOptionsStandardPanel("UniversalScenarioGenerationPanel", true, + "UniversalScenarioGenerationPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 3; + panel.add(pnlUnitRatioPanel, layout); + + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkUseDropShips, layout); + + layout.gridy++; + panel.add(chkOpForUsesVTOLs, layout); + + layout.gridy++; + panel.add(chkClanVehicles, layout); + + layout.gridy++; + panel.add(chkRegionalMekVariations, layout); + + layout.gridy++; + panel.add(chkAttachedPlayerCamouflage, layout); + + layout.gridy++; + panel.add(chkPlayerControlsAttachedUnits, layout); + + layout.gridy++; + panel.add(chkAutoConfigMunitions, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblSPAUpgradeIntensity, layout); + layout.gridx++; + panel.add(spnSPAUpgradeIntensity, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 3; + panel.add(pnlScenarioModifiers, layout); + + return panel; + } + + /** + * Creates the UI panel for configuring the auto-resolve options in campaigns. + *

    + * Includes controls to set the auto-resolve method, enable victory chance calculation, + * and specify the number of scenarios to consider during auto-resolution. + *

    + * + * @return a {@link JPanel} containing controls to configure auto-resolve behavior + */ + private JPanel createAutoResolvePanel() { + // Content + lblAutoResolveMethod = new CampaignOptionsLabel("AutoResolveMethod"); + lblAutoResolveNumberOfScenarios = new CampaignOptionsLabel("AutoResolveNumberOfScenarios"); + spnAutoResolveNumberOfScenarios = new CampaignOptionsSpinner("AutoResolveNumberOfScenarios", + 250, 10, 1000, 10); + chkAutoResolveVictoryChanceEnabled = new CampaignOptionsCheckBox("AutoResolveVictoryChanceEnabled"); + var lblMinimapTheme = new CampaignOptionsLabel("MinimapTheme"); + chkAutoResolveExperimentalPacarGuiEnabled = new CampaignOptionsCheckBox("AutoResolveExperimentalPacarGuiEnabled"); + + // Layout the panel + final JPanel panel = new CampaignOptionsStandardPanel("AutoResolvePanel", true, + "AutoResolvePanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(lblAutoResolveMethod, layout); + layout.gridx++; + panel.add(comboAutoResolveMethod, layout); + layout.gridx++; + layout.gridwidth = 1; + panel.add(chkAutoResolveVictoryChanceEnabled, layout); + layout.gridx++; + layout.gridwidth = 1; + panel.add(chkAutoResolveExperimentalPacarGuiEnabled, layout); + + layout.gridx = 0; + layout.gridwidth = 1; + layout.gridy++; + panel.add(lblMinimapTheme, layout); + layout.gridx++; + panel.add(minimapThemeSelector, layout); + layout.gridx++; + layout.gridx++; + panel.add(lblAutoResolveNumberOfScenarios, layout); + layout.gridx++; + panel.add(spnAutoResolveNumberOfScenarios, layout); + + return panel; + } + + /** + * Creates the UI panel for configuring unit ratios in universal options. + *

    + * Includes spinners for setting the ratio of various unit types, such as + * mechs, mixed units, and vehicles, for the opponent forces. + *

    + * + * @return a {@link JPanel} containing controls for unit ratio configuration + */ + private JPanel createUniversalUnitRatioPanel() { + // Content + lblOpForLanceTypeMeks = new CampaignOptionsLabel("OpForLanceTypeMeks"); + spnOpForLanceTypeMeks = new CampaignOptionsSpinner("OpForLanceTypeMeks", + 0, 0, 10, 1); + lblOpForLanceTypeMixed = new CampaignOptionsLabel("OpForLanceTypeMixed"); + spnOpForLanceTypeMixed = new CampaignOptionsSpinner("OpForLanceTypeMixed", + 0, 0, 10, 1); + lblOpForLanceTypeVehicle = new CampaignOptionsLabel("OpForLanceTypeVehicle"); + spnOpForLanceTypeVehicles = new CampaignOptionsSpinner("OpForLanceTypeVehicle", + 0, 0, 10, 1); + + // Layout the panel + final JPanel panel = new CampaignOptionsStandardPanel("UniversalUnitRatioPanel", true, + "UniversalUnitRatioPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(lblOpForLanceTypeMeks, layout); + layout.gridx++; + panel.add(spnOpForLanceTypeMeks, layout); + layout.gridx++; + panel.add(lblOpForLanceTypeMixed, layout); + layout.gridx++; + panel.add(spnOpForLanceTypeMixed, layout); + layout.gridx++; + panel.add(lblOpForLanceTypeVehicle, layout); + layout.gridx++; + panel.add(spnOpForLanceTypeVehicles, layout); + + return panel; + } + + /** + * Creates the UI panel for configuring universal scenario modifiers. + *

    + * This panel includes controls to adjust the maximum modifiers for scenario generation, + * modifier chance percentages, and BV (Battle Value) impact. It is designed to provide + * flexible settings for campaign customization. + *

    + * + * @return a {@link JPanel} containing controls to configure universal scenario modifiers + */ + private JPanel createUniversalModifiersPanel() { + //Content + lblScenarioModMax = new CampaignOptionsLabel("ScenarioModMax"); + spnScenarioModMax = new CampaignOptionsSpinner("ScenarioModMax", + 3, 0, 10, 1); + lblScenarioModChance = new CampaignOptionsLabel("ScenarioModChance"); + spnScenarioModChance = new CampaignOptionsSpinner("ScenarioModChance", + 25, 5, 100, 5); + lblScenarioModBV = new CampaignOptionsLabel("ScenarioModBV"); + spnScenarioModBV = new CampaignOptionsSpinner("ScenarioModBV", + 50, 5, 100, 5); + + // Layout the panel + final JPanel panel = new CampaignOptionsStandardPanel("UniversalModifiersPanel", true, + "UniversalModifiersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 1; + panel.add(lblScenarioModMax, layout); + layout.gridx++; + panel.add(spnScenarioModMax, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblScenarioModChance, layout); + layout.gridx++; + panel.add(spnScenarioModChance, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblScenarioModBV, layout); + layout.gridx++; + panel.add(spnScenarioModBV, layout); + + return panel; + } + + /** + * Creates the UI panel for configuring universal map generation settings. + *

    + * Includes options for enabling weather, light, planetary conditions, and fixed map chances, + * with spinners and checkboxes for user input. + *

    + * + * @return a {@link JPanel} containing controls to configure map generation options + */ + private JPanel createUniversalMapGenerationPanel() { + // Content + chkUseWeatherConditions = new CampaignOptionsCheckBox("UseWeatherConditions"); + chkUseLightConditions = new CampaignOptionsCheckBox("UseLightConditions"); + chkUsePlanetaryConditions = new CampaignOptionsCheckBox("UsePlanetaryConditions"); + lblFixedMapChance = new CampaignOptionsLabel("FixedMapChance"); + spnFixedMapChance = new CampaignOptionsSpinner("FixedMapChance", + 0, 0, 100, 1); + + // Layout the panel + final JPanel panel = new CampaignOptionsStandardPanel("UniversalMapGenerationPanel", true, + "UniversalMapGenerationPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + panel.add(chkUseWeatherConditions, layout); + + layout.gridy++; + panel.add(chkUseLightConditions, layout); + + layout.gridy++; + panel.add(chkUsePlanetaryConditions, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblFixedMapChance, layout); + layout.gridx++; + panel.add(spnFixedMapChance, layout); + + return panel; + } + + /** + * Creates the UI panel that consolidates universal campaign options. + *

    + * This panel combines sub-panels like the parts panel, lance panel, and map generation panel + * into a single cohesive UI for configuring general campaign options. + *

    + * + * @return a {@link JPanel} containing all universal campaign options organized in sections + */ + private JPanel createUniversalCampaignOptionsPanel() { + // Layout the panel + final JPanel panel = new CampaignOptionsStandardPanel("UniversalCampaignOptionsPanel"); + GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 2; + layout.gridy = 0; + layout.gridx = 0; + panel.add(pnlPartsPanel, layout); + layout.gridy++; + panel.add(pnlLancePanel, layout); + layout.gridy++; + panel.add(pnlMapGenerationPanel, layout); + + return panel; + } + + /** + * Creates the UI panel for configuring universal parts restrictions during campaigns. + *

    + * Includes settings such as restricting parts availability based on mission requirements. + *

    + * + * @return a {@link JPanel} containing controls to configure parts-related options for campaigns + */ + private JPanel createUniversalPartsPanel() { + // Content + chkRestrictPartsByMission = new CampaignOptionsCheckBox("RestrictPartsByMission"); + + // Layout the panel + final JPanel panel = new CampaignOptionsStandardPanel("UniversalPartsPanel", true, + "UniversalPartsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + panel.add(chkRestrictPartsByMission, layout); + + return panel; + } + + /** + * Creates the UI panel for configuring universal lance options during campaigns. + *

    + * This panel includes settings for limiting lance weight or the number of units, + * managing strategy deployment, and adjusting payment for strategies. It organizes + * these options in a grid layout for clarity and ease of use. + *

    + * + * @return a {@link JPanel} containing controls to configure lance-related options for campaigns + */ + private JPanel createUniversalLancePanel() { + // Content + chkLimitLanceWeight = new CampaignOptionsCheckBox("LimitLanceWeight"); + chkLimitLanceNumUnits = new CampaignOptionsCheckBox("LimitLanceNumUnits"); + chkUseStrategy = new CampaignOptionsCheckBox("UseStrategy"); + lblBaseStrategyDeployment = new CampaignOptionsLabel("BaseStrategyDeployment"); + spnBaseStrategyDeployment = new CampaignOptionsSpinner("BaseStrategyDeployment", + 0, 0, 10, 1); + lblAdditionalStrategyDeployment = new CampaignOptionsLabel("AdditionalStrategyDeployment"); + spnAdditionalStrategyDeployment = new CampaignOptionsSpinner("AdditionalStrategyDeployment", + 0, 0, 10, 1); + + // Layout the panel + final JPanel panel = new CampaignOptionsStandardPanel("UniversalLancePanel", true, + "UniversalLancePanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + panel.add(chkLimitLanceWeight, layout); + + layout.gridy++; + panel.add(chkLimitLanceNumUnits, layout); + + layout.gridy++; + panel.add(chkUseStrategy, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblBaseStrategyDeployment, layout); + layout.gridx++; + panel.add(spnBaseStrategyDeployment, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblAdditionalStrategyDeployment, layout); + layout.gridx++; + panel.add(spnAdditionalStrategyDeployment, layout); + + return panel; + } + + + /** + * Initializes the StratCon (Strategic Context) section of the tab. + */ + private void initializeStratConTab() { + chkUseStratCon = new JCheckBox(); + chkUseGenericBattleValue = new JCheckBox(); + chkUseVerboseBidding = new JCheckBox(); + } + + /** + * Creates the UI panel for the StratCon configuration. + *

    + * This section includes settings for using generic battle values, + * enabling verbose bidding, and other Strategic Conquest-specific rules. + *

    + * + * @return a {@link JPanel} containing all StratCon settings. + */ + public JPanel createStratConTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("StratConTab", + getImageDirectory() + "logo_clan_wolf.png"); + + // Content + chkUseStratCon = new CampaignOptionsCheckBox("UseStratCon"); + chkUseGenericBattleValue = new CampaignOptionsCheckBox("UseGenericBattleValue"); + chkUseVerboseBidding = new CampaignOptionsCheckBox("UseVerboseBidding"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("StratConTab", true); + GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 5; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridwidth = 1; + layout.gridy++; + panel.add(chkUseStratCon, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblSkillLevel, layout); + layout.gridx++; + panel.add(comboSkillLevel, layout); + layout.gridx++; + panel.add(chkUseGenericBattleValue, layout); + layout.gridx++; + panel.add(chkUseVerboseBidding, layout); + + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy++; + panel.add(pnlAutoResolve, layout); + + layout.gridwidth = 2; + layout.gridx = 0; + layout.gridy++; + panel.add(pnlScenarioGenerationPanel, layout); + layout.gridx += 2; + panel.add(pnlCampaignOptions, layout); + + // Create panel and return + return createParentPanel(panel, "StratConTab"); + } + + /** + * Initializes the Legacy AtB (Against the Bot) section of the tab. + */ + private void initializeLegacyTab() { + // General + chkUseAtB = new JCheckBox(); + + // OpFor Generation + pnlLegacyOpForGenerationPanel = new JPanel(); + chkUseVehicles = new JCheckBox(); + chkDoubleVehicles = new JCheckBox(); + chkOpForUsesAero = new JCheckBox(); + lblOpForAeroChance = new JLabel(); + spnOpForAeroChance = new JSpinner(); + chkOpForUsesLocalForces = new JCheckBox(); + chkAdjustPlayerVehicles = new JCheckBox(); + + // Scenarios + pnlLegacyScenarioGenerationPanel = new JPanel(); + chkGenerateChases = new JCheckBox(); + lblIntensity = new JLabel(); + spnAtBBattleIntensity = new JSpinner(); + lblFightChance = new JLabel(); + lblDefendChance = new JLabel(); + lblScoutChance = new JLabel(); + lblTrainingChance = new JLabel(); + spnAtBBattleChance = new JSpinner[CombatRole.values().length - 1]; + btnIntensityUpdate = new JButton(); + } + + /** + * Creates the UI panel for the Legacy AtB configuration. + *

    + * This section configures opponent force generation, scenario generation probabilities, + * and customization of battle intensities for "Against the Bot" campaigns. + *

    + * + * @return a {@link JPanel} containing all Legacy AtB settings. + */ + public JPanel createLegacyTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("LegacyTab", + getImageDirectory() + "logo_free_rasalhague_republic.png", true); + + chkUseAtB = new CampaignOptionsCheckBox("UseAtB"); + pnlLegacyOpForGenerationPanel = createLegacyOpForGenerationPanel(); + pnlLegacyScenarioGenerationPanel = createLegacyScenarioGenerationPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("LegacyTab", true); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 5; + layout.gridx = 0; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(chkUseAtB, layout); + + layout.gridy++; + panel.add(pnlLegacyOpForGenerationPanel, layout); + layout.gridx++; + panel.add(pnlLegacyScenarioGenerationPanel, layout); + + // Create panel and return + return createParentPanel(panel, "LegacyTab"); + } + + /** + * Creates the UI panel for configuring the Legacy AtB opponent force (OpFor) generation settings. + *

    + * Options include enabling vehicle support, aero unit chances, local forces, and player + * vehicle adjustments. The panel provides various checkboxes and spinners for user interaction. + *

    + * + * @return a {@link JPanel} containing controls to configure AtB opponent force generation options + */ + private JPanel createLegacyOpForGenerationPanel() { + // Content + chkUseVehicles = new CampaignOptionsCheckBox("UseVehicles"); + chkDoubleVehicles = new CampaignOptionsCheckBox("DoubleVehicles"); + chkOpForUsesAero = new CampaignOptionsCheckBox("OpForUsesAero"); + lblOpForAeroChance = new CampaignOptionsLabel("OpForAeroChance"); + spnOpForAeroChance = new CampaignOptionsSpinner("OpForAeroChance", + 0, 0, 6, 1); + chkOpForUsesLocalForces = new CampaignOptionsCheckBox("OpForUsesLocalForces"); + chkAdjustPlayerVehicles = new CampaignOptionsCheckBox("AdjustPlayerVehicles"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("LegacyOpForGenerationPanel", true, + "LegacyOpForGenerationPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + panel.add(chkUseVehicles, layout); + + layout.gridy++; + panel.add(chkDoubleVehicles, layout); + + layout.gridy++; + panel.add(chkOpForUsesAero, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblOpForAeroChance, layout); + layout.gridx++; + panel.add(spnOpForAeroChance, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkOpForUsesLocalForces, layout); + + layout.gridy++; + panel.add(chkAdjustPlayerVehicles, layout); + + return panel; + } + + /** + * Creates the UI panel for configuring the Legacy AtB (Against the Bot) scenario generation settings. + *

    + * This panel includes settings for enabling chase generation, adjusting battle role chances + * (Fight, Defend, Scout, Training), and updating these values based on a calculated intensity. + *

    + * + * @return a {@link JPanel} containing controls to configure AtB scenario generation options + */ + private JPanel createLegacyScenarioGenerationPanel() { + // Content + chkGenerateChases = new CampaignOptionsCheckBox("GenerateChases"); + lblIntensity = new CampaignOptionsLabel("AtBBattleIntensity"); + spnAtBBattleIntensity = new CampaignOptionsSpinner("AtBBattleIntensity", + 0.0, 0.0, 100.0, 0.1); + + lblFightChance = new JLabel(CombatRole.MANEUVER.toString()); + lblDefendChance = new JLabel(CombatRole.FRONTLINE.toString()); + lblScoutChance = new JLabel(CombatRole.PATROL.toString()); + lblTrainingChance = new JLabel(CombatRole.TRAINING.toString()); + spnAtBBattleChance = new JSpinner[CombatRole.values().length - 1]; + + for (int i = 0; i < spnAtBBattleChance.length; i++) { + spnAtBBattleChance[i] = new JSpinner( + new SpinnerNumberModel(0, 0, 100, 1)); + } + + btnIntensityUpdate = new CampaignOptionsButton("IntensityUpdate"); + AtBBattleIntensityChangeListener atBBattleIntensityChangeListener = new AtBBattleIntensityChangeListener(); + btnIntensityUpdate.addChangeListener(evt -> { + spnAtBBattleIntensity.removeChangeListener(atBBattleIntensityChangeListener); + spnAtBBattleIntensity.setValue(determineAtBBattleIntensity()); + spnAtBBattleIntensity.addChangeListener(atBBattleIntensityChangeListener); + }); + + // Layout the Panel + final JPanel panelBattleChance = new CampaignOptionsStandardPanel("LegacyScenarioGenerationPanel"); + final GridBagConstraints layoutBattleChance = new CampaignOptionsGridBagConstraints(panelBattleChance); + + layoutBattleChance.gridx = 0; + layoutBattleChance.gridy = 0; + layoutBattleChance.gridwidth = 1; + panelBattleChance.add(lblFightChance, layoutBattleChance); + layoutBattleChance.gridx++; + panelBattleChance.add(spnAtBBattleChance[CombatRole.MANEUVER.ordinal()], layoutBattleChance); + + layoutBattleChance.gridx = 0; + layoutBattleChance.gridy++; + panelBattleChance.add(lblDefendChance, layoutBattleChance); + layoutBattleChance.gridx++; + panelBattleChance.add(spnAtBBattleChance[CombatRole.FRONTLINE.ordinal()], layoutBattleChance); + + layoutBattleChance.gridx = 0; + layoutBattleChance.gridy++; + panelBattleChance.add(lblScoutChance, layoutBattleChance); + layoutBattleChance.gridx++; + panelBattleChance.add(spnAtBBattleChance[CombatRole.PATROL.ordinal()], layoutBattleChance); + + layoutBattleChance.gridx = 0; + layoutBattleChance.gridy++; + panelBattleChance.add(lblTrainingChance, layoutBattleChance); + layoutBattleChance.gridx++; + panelBattleChance.add(spnAtBBattleChance[CombatRole.TRAINING.ordinal()], layoutBattleChance); + + final JPanel panel = new CampaignOptionsStandardPanel("LegacyScenarioGenerationPanel", true, + "LegacyScenarioGenerationPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridx = 0; + layout.gridy = 0; + layout.gridwidth = 2; + panel.add(chkGenerateChases, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblIntensity, layout); + layout.gridx++; + panel.add(spnAtBBattleIntensity, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(panelBattleChance, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(btnIntensityUpdate, layout); + + return panel; + } + + /** + * Determines the AtB (Against the Bot) battle intensity value based on the current + * settings of battle role chance spinners. + *

    + * Each role (e.g., Maneuver, Frontline, Patrol, Training) contributes to the overall + * battle intensity value based on complex formulas. The result is normalized, capped + * at 100.0, and rounded to a single decimal place. + *

    + * + * @return the calculated battle intensity value as a {@code double} + */ + private double determineAtBBattleIntensity() { + double intensity = 0.0; + + int x = (int) spnAtBBattleChance[CombatRole.MANEUVER.ordinal()].getValue(); + intensity += ((-3.0 / 2.0) * (2.0 * x - 1.0)) / (2.0 * x - 201.0); + + x = (int) spnAtBBattleChance[CombatRole.FRONTLINE.ordinal()].getValue(); + intensity += ((-4.0) * (2.0 * x - 1.0)) / (2.0 * x - 201.0); + + x = (int) spnAtBBattleChance[CombatRole.PATROL.ordinal()].getValue(); + intensity += ((-2.0 / 3.0) * (2.0 * x - 1.0)) / (2.0 * x - 201.0); + + x = (int) spnAtBBattleChance[CombatRole.TRAINING.ordinal()].getValue(); + intensity += ((-9.0) * (2.0 * x - 1.0)) / (2.0 * x - 201.0); + + intensity = intensity / 4.0; + + if (intensity > 100.0) { + intensity = 100.0; + } + + return Math.round(intensity * 10.0) / 10.0; + } + + /** + * A listener to manage changes in the AtB (Against the Bot) battle intensity spinner value. + *

    + * It listens for changes in the battle intensity spinner, recalculates the values for different + * battle roles (e.g., Maneuver, Frontline, Patrol, Training), and updates the corresponding spinners + * for the player to see the effects of the intensity change. + *

    + */ + private class AtBBattleIntensityChangeListener implements ChangeListener { + /** + * Called when the state of the AtB battle intensity spinner changes. + *

    + * Updates the battle role chance spinners based on the current value of the battle intensity + * spinner. If the intensity is below the minimum defined in {@link AtBContract#MINIMUM_INTENSITY}, + * all role chance spinners are set to zero. + *

    + * + * @param e the {@link ChangeEvent} triggered when the spinner value changes + */ + @Override + public void stateChanged(ChangeEvent e) { + double intensity = (double) spnAtBBattleIntensity.getValue(); + + if (intensity >= AtBContract.MINIMUM_INTENSITY) { + int value = (int) Math.min( + Math.round(400.0 * intensity / (4.0 * intensity + 6.0) + 0.05), 100); + spnAtBBattleChance[CombatRole.MANEUVER.ordinal()].setValue(value); + value = (int) Math.min(Math.round(200.0 * intensity / (2.0 * intensity + 8.0) + 0.05), + 100); + spnAtBBattleChance[CombatRole.FRONTLINE.ordinal()].setValue(value); + value = (int) Math.min(Math.round(600.0 * intensity / (6.0 * intensity + 4.0) + 0.05), + 100); + spnAtBBattleChance[CombatRole.PATROL.ordinal()].setValue(value); + value = (int) Math.min(Math.round(100.0 * intensity / (intensity + 9.0) + 0.05), 100); + spnAtBBattleChance[CombatRole.TRAINING.ordinal()].setValue(value); + } else { + spnAtBBattleChance[CombatRole.MANEUVER.ordinal()].setValue(0); + spnAtBBattleChance[CombatRole.FRONTLINE.ordinal()].setValue(0); + spnAtBBattleChance[CombatRole.PATROL.ordinal()].setValue(0); + spnAtBBattleChance[CombatRole.TRAINING.ordinal()].setValue(0); + } + } + } + + /** + * Applies the current values configured in the tab back to the provided {@link CampaignOptions}. + *

    + * If no custom {@link CampaignOptions} is provided, it uses the default {@link CampaignOptions} + * associated with the tab. + *

    + * + * @param presetCampaignOptions an optional custom {@link CampaignOptions} object to apply the values to; + * if {@code null}, the default options are used. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Universal + options.setSkillLevel(comboSkillLevel.getSelectedItem()); + options.setOpForLanceTypeMeks((int) spnOpForLanceTypeMeks.getValue()); + options.setOpForLanceTypeMixed((int) spnOpForLanceTypeMixed.getValue()); + options.setOpForLanceTypeVehicles((int) spnOpForLanceTypeVehicles.getValue()); + options.setUseDropShips(chkUseDropShips.isSelected()); + options.setOpForUsesVTOLs(chkOpForUsesVTOLs.isSelected()); + options.setClanVehicles(chkClanVehicles.isSelected()); + options.setRegionalMekVariations(chkRegionalMekVariations.isSelected()); + options.setAttachedPlayerCamouflage(chkAttachedPlayerCamouflage.isSelected()); + options.setPlayerControlsAttachedUnits(chkPlayerControlsAttachedUnits.isSelected()); + options.setSpaUpgradeIntensity((int) spnSPAUpgradeIntensity.getValue()); + options.setAutoConfigMunitions(chkAutoConfigMunitions.isSelected()); + options.setScenarioModMax((int) spnScenarioModMax.getValue()); + options.setScenarioModChance((int) spnScenarioModChance.getValue()); + options.setScenarioModBV((int) spnScenarioModBV.getValue()); + options.setUseWeatherConditions(chkUseWeatherConditions.isSelected()); + options.setUseLightConditions(chkUseLightConditions.isSelected()); + options.setUsePlanetaryConditions(chkUsePlanetaryConditions.isSelected()); + options.setFixedMapChance((int) spnFixedMapChance.getValue()); + options.setRestrictPartsByMission(chkRestrictPartsByMission.isSelected()); + options.setLimitLanceWeight(chkLimitLanceWeight.isSelected()); + options.setLimitLanceNumUnits(chkLimitLanceNumUnits.isSelected()); + options.setUseStrategy(chkUseStrategy.isSelected()); + options.setBaseStrategyDeployment((int) spnBaseStrategyDeployment.getValue()); + options.setAdditionalStrategyDeployment((int) spnAdditionalStrategyDeployment.getValue()); + options.setAutoResolveMethod(comboAutoResolveMethod.getSelectedItem()); + options.setStrategicViewTheme(minimapThemeSelector.getSelectedItem()); + options.setAutoResolveVictoryChanceEnabled(chkAutoResolveVictoryChanceEnabled.isSelected()); + options.setAutoResolveNumberOfScenarios((int) spnAutoResolveNumberOfScenarios.getValue()); + options.setAutoResolveExperimentalPacarGuiEnabled(chkAutoResolveExperimentalPacarGuiEnabled.isSelected()); + + // StratCon + options.setUseStratCon(chkUseStratCon.isSelected()); + options.setUseGenericBattleValue(chkUseGenericBattleValue.isSelected()); + options.setUseVerboseBidding(chkUseVerboseBidding.isSelected()); + + // Legacy + options.setUseAtB(chkUseAtB.isSelected() && !chkUseStratCon.isSelected()); + options.setUseVehicles(chkUseVehicles.isSelected()); + options.setDoubleVehicles(chkDoubleVehicles.isSelected()); + options.setUseAero(chkOpForUsesAero.isSelected()); + options.setOpForAeroChance((int) spnOpForAeroChance.getValue()); + options.setAllowOpForLocalUnits(chkOpForUsesLocalForces.isSelected()); + options.setAdjustPlayerVehicles(chkAdjustPlayerVehicles.isSelected()); + options.setGenerateChases(chkGenerateChases.isSelected()); + + for (int i = 0; i < spnAtBBattleChance.length; i++) { + options.setAtBBattleChance(i, (int) spnAtBBattleChance[i].getValue()); + } + } + + /** + * A convenience method to load values from the default {@link CampaignOptions} instance. + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null); + } + + /** + * Loads the ruleset values from a {@link CampaignOptions} object into the UI components. + *

    + * If no custom {@link CampaignOptions} is provided, it will fetch values from the default + * {@link CampaignOptions} instance. + *

    + * + * @param presetCampaignOptions an optional custom {@link CampaignOptions} object to load values from; + * if {@code null}, the default options are used. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Universal + comboSkillLevel.setSelectedItem(options.getSkillLevel()); + spnOpForLanceTypeMeks.setValue(options.getOpForLanceTypeMeks()); + spnOpForLanceTypeMixed.setValue(options.getOpForLanceTypeMixed()); + spnOpForLanceTypeVehicles.setValue(options.getOpForLanceTypeVehicles()); + chkUseDropShips.setSelected(options.isUseDropShips()); + chkOpForUsesVTOLs.setSelected(options.isOpForUsesVTOLs()); + chkClanVehicles.setSelected(options.isClanVehicles()); + chkRegionalMekVariations.setSelected(options.isRegionalMekVariations()); + chkAttachedPlayerCamouflage.setSelected(options.isAttachedPlayerCamouflage()); + chkPlayerControlsAttachedUnits.setSelected(options.isPlayerControlsAttachedUnits()); + spnSPAUpgradeIntensity.setValue(options.getSpaUpgradeIntensity()); + chkAutoConfigMunitions.setSelected(options.isAutoConfigMunitions()); + spnScenarioModMax.setValue(options.getScenarioModMax()); + spnScenarioModChance.setValue(options.getScenarioModChance()); + spnScenarioModBV.setValue(options.getScenarioModBV()); + chkUseWeatherConditions.setSelected(options.isUseWeatherConditions()); + chkUseLightConditions.setSelected(options.isUseLightConditions()); + chkUsePlanetaryConditions.setSelected(options.isUsePlanetaryConditions()); + spnFixedMapChance.setValue(options.getFixedMapChance()); + chkRestrictPartsByMission.setSelected(options.isRestrictPartsByMission()); + chkLimitLanceWeight.setSelected(options.isLimitLanceWeight()); + chkLimitLanceNumUnits.setSelected(options.isLimitLanceNumUnits()); + chkUseStrategy.setSelected(options.isUseStrategy()); + spnBaseStrategyDeployment.setValue(options.getBaseStrategyDeployment()); + spnAdditionalStrategyDeployment.setValue(options.getAdditionalStrategyDeployment()); + comboAutoResolveMethod.setSelectedItem(options.getAutoResolveMethod()); + minimapThemeSelector.setSelectedItem(options.getStrategicViewTheme().getName()); + chkAutoResolveVictoryChanceEnabled.setSelected(options.isAutoResolveVictoryChanceEnabled()); + chkAutoResolveExperimentalPacarGuiEnabled.setSelected(options.isAutoResolveExperimentalPacarGuiEnabled()); + spnAutoResolveNumberOfScenarios.setValue(options.getAutoResolveNumberOfScenarios()); + + // StratCon + chkUseStratCon.setSelected(options.isUseStratCon()); + chkUseGenericBattleValue.setSelected(options.isUseGenericBattleValue()); + chkUseVerboseBidding.setSelected(options.isUseVerboseBidding()); + + // Legacy + chkUseAtB.setSelected(options.isUseAtB() && !options.isUseStratCon()); + chkUseVehicles.setSelected(options.isUseVehicles()); + chkDoubleVehicles.setSelected(options.isDoubleVehicles()); + chkOpForUsesAero.setSelected(options.isUseAero()); + spnOpForAeroChance.setValue(options.getOpForAeroChance()); + chkOpForUsesLocalForces.setSelected(options.isAllowOpForLocalUnits()); + chkAdjustPlayerVehicles.setSelected(options.isAdjustPlayerVehicles()); + chkGenerateChases.setSelected(options.isGenerateChases()); + for (CombatRole role : CombatRole.values()) { + if (role.ordinal() <= CombatRole.TRAINING.ordinal()) { + spnAtBBattleChance[role.ordinal()].setValue(options.getAtBBattleChance(role)); + } + } + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/SkillsTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/SkillsTab.java new file mode 100644 index 00000000000..8db423cb7e7 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/SkillsTab.java @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.common.annotations.Nullable; +import megamek.common.enums.SkillLevel; +import megamek.logging.MMLogger; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.personnel.SkillType; +import mekhq.gui.campaignOptions.components.*; + +import javax.swing.*; +import java.awt.*; +import java.util.List; +import java.util.*; + +import static java.util.Arrays.sort; +import static megamek.common.enums.SkillLevel.*; +import static mekhq.campaign.personnel.SkillType.isCombatSkill; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * SkillsTab is a component of the campaign options user interface that allows players + * to configure the rules and costs associated with skills in their campaign. + *

    + * This tab can be configured for either combat or support skills. It allows users to: + *

    + *
      + *
    • Set skill target numbers.
    • + *
    • Specify skill costs at different levels.
    • + *
    • Define milestones for the progression of skills by skill levels.
    • + *
    • Copy and paste configurations for skill settings.
    • + *
    + *

    + * The interface is dynamically created to display all relevant skill options for the + * selected tab (combat or support). + *

    + */ +public class SkillsTab { + private static final String RESOURCE_PACKAGE = "mekhq/resources/CampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE); + + private final CampaignOptions campaignOptions; + + private Map allTargetNumbers; + private Map> allSkillLevels; + private Map> allSkillCosts; + private Map>> allSkillMilestones; + private double storedTargetNumber = 0; + private List storedValuesSpinners = new ArrayList<>(); + private List storedValuesComboBoxes = new ArrayList<>(); + + private JPanel pnlEdgeCost; + private JLabel lblEdgeCost; + private JSpinner spnEdgeCost; + + private static final MMLogger logger = MMLogger.create(SkillsTab.class); + + /** + * Constructs a new `SkillsTab` instance and initializes the necessary data + * structures for managing skill configurations. + * + * @param campaignOptions the {@code CampaignOptions} instance that holds the settings + * to be modified or displayed in this tab. + */ + public SkillsTab(CampaignOptions campaignOptions) { + this.campaignOptions = campaignOptions; + initialize(); + } + + /** + * Initializes the SkillsTab by setting up the necessary data structures and + * default values for skill configuration. + */ + private void initialize() { + initializeGeneral(); + } + + /** + * Sets up the general data structures needed for skill configuration in the + * SkillsTab. This includes collections for tracking target numbers, costs, + * and milestones for every skill. + */ + private void initializeGeneral() { + allTargetNumbers = new HashMap<>(); + allSkillLevels = new HashMap<>(); + allSkillCosts = new HashMap<>(); + allSkillMilestones = new HashMap<>(); + storedTargetNumber = 0; + storedValuesSpinners = new ArrayList<>(); + storedValuesComboBoxes = new ArrayList<>(); + + pnlEdgeCost = new JPanel(); + lblEdgeCost = new JLabel(); + spnEdgeCost = new JSpinner(); + } + + /** + * Creates the main panel for the SkillsTab UI. + *

    + * Depending on the `isCombatTab` argument, it creates a tab for either combat + * or support skills. The tab displays skill panels associated with the selected + * category, allowing users to configure their target numbers, costs, and milestones. + *

    + * + * @param isCombatTab a boolean indicating whether this is a combat tab + * (true for combat skills, false for support skills). + * @return a {@link JPanel} containing the dynamically generated skill options + * for the selected tab. + */ + public JPanel createSkillsTab(boolean isCombatTab) { + // Header + JPanel headerPanel; + if (isCombatTab) { + headerPanel = new CampaignOptionsHeaderPanel("CombatSkillsTab", + getImageDirectory() + "logo_clan_diamond_sharks.png"); + } else { + headerPanel = new CampaignOptionsHeaderPanel("SupportSkillsTab", + getImageDirectory() + "logo_free_worlds_league.png"); + } + + // Contents + String[] allSkills = SkillType.getSkillList(); + sort(allSkills); + + List relevantSkills = new ArrayList<>(); + for (String skillName : SkillType.getSkillList()) { + SkillType skill = SkillType.getType(skillName); + boolean isCombatSkill = isCombatSkill(skill); + + if (isCombatSkill == isCombatTab) { + relevantSkills.add(skill); + } + } + + List skillPanels = new ArrayList<>(); + + for (SkillType skill : relevantSkills) { + JPanel skillPanel = createSkillPanel(skill); + skillPanels.add(skillPanel); + } + + // Content + pnlEdgeCost = createEdgeCostPanel(); + pnlEdgeCost.setVisible(!isCombatTab); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel(isCombatTab ? + "CombatSkillsTab" : "SupportSkillsTab", true); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + // Create a button to toggle the table + JButton hideAllButton = new JButton(resources.getString("btnHideAll.text")); + hideAllButton.addActionListener(e -> { + setVisibleForAll(false); + + panel.revalidate(); + panel.repaint(); + }); + + // Create a button to toggle the table + JButton showAllButton = new JButton(resources.getString("btnDisplayAll.text")); + showAllButton.addActionListener(e -> { + setVisibleForAll(true); + + panel.revalidate(); + panel.repaint(); + }); + + layout.gridwidth = 5; + layout.gridy = 0; + panel.add(headerPanel, layout); + + layout.gridwidth = 2; + layout.gridx = 0; + layout.gridy++; + panel.add(pnlEdgeCost, layout); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy++; + panel.add(showAllButton, layout); + layout.gridx++; + panel.add(hideAllButton, layout); + + layout.gridx = 0; + layout.gridy++; + int tableCounter = 0; + for (int i = 0; i < 4; i++) { + layout.gridy++; + layout.gridx = 0; + for (int j = 0; j < 5; j++) { + if (tableCounter < skillPanels.size()) { + panel.add(skillPanels.get(tableCounter), layout); + layout.gridx++; + } + tableCounter++; + } + } + + // Create Parent Panel + return createParentPanel(panel, "CombatSkillsTab"); + } + + /** + * Creates a panel to configure and display the edge cost option in the SkillsTab. + * + * @return A {@link JPanel} containing the label and spinner for the edge cost configuration. + */ + private JPanel createEdgeCostPanel() { + lblEdgeCost = new CampaignOptionsLabel("EdgeCost"); + spnEdgeCost = new CampaignOptionsSpinner("EdgeCost", + 100, 0, 500, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("EdgeCostPanel", + true, "EdgeCostPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridwidth = 1; + layout.gridx = 0; + layout.gridy = 0; + panel.add(lblEdgeCost, layout); + layout.gridx++; + panel.add(spnEdgeCost, layout); + + return panel; + } + + /** + * Toggles the visibility of all components related to skills in the SkillsTab UI. + *

    + * This can be used to either hide or display all components for easier navigation + * or cleaner representation of the tab. + *

    + * + * @param visible a boolean indicating whether to show or hide all components. + */ + private void setVisibleForAll(boolean visible) { + setVisibleForAll(allSkillLevels, visible); + setVisibleForAll(allSkillCosts, visible); + setVisibleForAll(allSkillMilestones, visible); + } + + /** + * Toggles the visibility of a specific category of components in the SkillsTab UI. + *

    + * This method allows visibility control over target numbers, costs, and skill + * level milestones based on the provided component map. + *

    + * + * @param componentsMap a map containing the components to show or hide. + * @param visible a boolean indicating whether to show or hide the components. + * @param the type of the component to toggle visibility for + * (e.g., {@link JSpinner}, {@link JComboBox}). + */ + private void setVisibleForAll(Map> componentsMap, + boolean visible) { + for (String SkillName : componentsMap.keySet()) { + List components = componentsMap.get(SkillName); + if (components != null) { + for (T component : components) { + component.setVisible(visible); + } + } + } + } + + /** + * Creates a dynamic panel for configuring the specified skill. + *

    + * Each skill panel includes controls for: + *

    + *

  99. Setting the target number for the skill.
  100. + *
  101. Configuring costs of the skill at different levels.
  102. + *
  103. Defining milestones for skill progression (e.g., from Green to Veteran).
  104. + *

    + * Copy and paste buttons are available for transferring configurations between skills. + *

    + * + * @param skill the {@link SkillType} object representing the skill to be configured. + * @return a {@link JPanel} containing the UI components for the given skill. + */ + private JPanel createSkillPanel(SkillType skill) { + String panelName = "SkillPanel" + skill.getName().replace(" ", ""); + + // Create the target number spinner + JLabel lblTargetNumber = new CampaignOptionsLabel("SkillPanelTargetNumber"); + JSpinner spnTargetNumber = new CampaignOptionsSpinner("SkillPanelTargetNumber", + 0, 0, 12, 1); + allTargetNumbers.put(skill.getName(), spnTargetNumber); + + List labels = new ArrayList<>(); + List spinners = new ArrayList<>(); + List> comboBoxes = new ArrayList<>(); + + List skillLevels = new ArrayList<>(); + List skillCosts = new ArrayList<>(); + List> skillMilestones = new ArrayList<>(); + for (int i = 0; i < 11; i++) { + JLabel label = new CampaignOptionsLabel("SkillLevel" + i, null, true); + label.setVisible(false); + labels.add(label); + skillLevels.add(label); + + JSpinner spinner = new CampaignOptionsSpinner("SkillLevel" + i, + null, 0, -1, 9999, 1, true); + spinner.setVisible(false); + spinners.add(spinner); + skillCosts.add(spinner); + + JComboBox comboBox = new JComboBox<>(); + comboBox.addItem(ULTRA_GREEN); + comboBox.addItem(GREEN); + comboBox.addItem(REGULAR); + comboBox.addItem(VETERAN); + comboBox.addItem(ELITE); + comboBox.addActionListener(e -> milestoneActionListener(comboBoxes, comboBox)); + comboBox.setVisible(false); + comboBoxes.add(comboBox); + skillMilestones.add(comboBox); + } + + allSkillLevels.put(skill.getName(), skillLevels); + allSkillCosts.put(skill.getName(), skillCosts); + allSkillMilestones.put(skill.getName(), skillMilestones); + + JButton copyButton = new JButton(resources.getString("btnCopy.text")); + copyButton.addActionListener(e -> { + storedTargetNumber = (Double) spnTargetNumber.getValue(); + + storedValuesSpinners = new ArrayList<>(); + storedValuesComboBoxes = new ArrayList<>(); + + for (int i = 0; i < labels.size(); i++) { + storedValuesSpinners.add((Double) spinners.get(i).getValue()); + storedValuesComboBoxes.add((SkillLevel) comboBoxes.get(i).getSelectedItem()); + } + }); + + JButton pasteButton = new JButton(resources.getString("btnPaste.text")); + pasteButton.addActionListener(e -> { + spnTargetNumber.setValue(storedTargetNumber); + + for (int i = 0; i < labels.size(); i++) { + spinners.get(i).setValue(storedValuesSpinners.get(i)); + comboBoxes.get(i).setSelectedItem(storedValuesComboBoxes.get(i)); + } + }); + + final JPanel panel = new CampaignOptionsStandardPanel(panelName, true, panelName); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + // Create a button to toggle the table + JButton toggleButton = new JButton(resources.getString("btnToggle.text")); + toggleButton.addActionListener(e -> { + for (JLabel label : labels) { + label.setVisible(!label.isVisible()); + } + + for (JComboBox comboBox : comboBoxes) { + comboBox.setVisible(!comboBox.isVisible()); + } + + for (JSpinner spinner : spinners) { + spinner.setVisible(!spinner.isVisible()); + } + }); + + layout.gridy = 0; + layout.gridx = 1; + layout.gridwidth = 2; + panel.add(toggleButton, layout); + layout.gridy++; + + layout.gridy++; + layout.gridx = 1; + layout.gridwidth = 1; + panel.add(copyButton, layout); + layout.gridx++; + panel.add(pasteButton, layout); + + layout.gridy++; + layout.gridx = 1; + panel.add(lblTargetNumber, layout); + layout.gridx++; + panel.add(spnTargetNumber, layout); + + for (int i = 0; i < labels.size(); i++) { + layout.gridy++; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(labels.get(i), layout); + layout.gridx++; + panel.add(spinners.get(i), layout); + layout.gridx++; + panel.add(comboBoxes.get(i), layout); + } + + return panel; + } + + /** + * Action listener for milestone combo boxes to synchronize their values sequentially. + *

    + * When the user changes the value of a milestone, subsequent milestones are + * automatically adjusted to ensure logical skill progression (e.g., later + * milestones cannot precede earlier ones). This method enforces such constraints. + *

    + * + * @param comboBoxes the list of combo boxes representing milestones for a single skill. + * @param comboBox the combo box that triggered the action. + */ + private static void milestoneActionListener(List> comboBoxes, JComboBox comboBox) { + int originIndex = comboBoxes.indexOf(comboBox); + + SkillLevel currentSelection = (SkillLevel) comboBox.getSelectedItem(); + + if (currentSelection == null) { + return; + } + + if (originIndex != 0) { + JComboBox previousComboBox = comboBoxes.get(originIndex - 1); + SkillLevel previousSelection = (SkillLevel) previousComboBox.getSelectedItem(); + + if (previousSelection != null) { + if (previousSelection.ordinal() > currentSelection.ordinal()) { + comboBox.setSelectedItem(previousSelection); + } else if (currentSelection.ordinal() > previousSelection.ordinal() + 1) { + comboBox.setSelectedItem(parseFromInteger(previousSelection.ordinal() + 1)); + } + } + } else { + if (comboBox.getSelectedItem() != NONE) { + comboBox.setSelectedItem(ULTRA_GREEN); + } + } + + if (originIndex < comboBoxes.size() - 1) { + originIndex++; + JComboBox nextComboBox = comboBoxes.get(originIndex); + nextComboBox.setSelectedItem(comboBox.getSelectedItem()); + } + } + + /** + * Loads skill values from the default campaign options. + *

    + * A version of this method without parameters that uses default skill values + * defined in the campaign. + *

    + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null, new HashMap<>()); + } + + /** + * Loads skill values into the UI components from the campaign options. + *

    + * The method populates the spinners, labels, and comboboxes for all skills + * with their corresponding properties (e.g., target numbers, costs, and milestones) + * retrieved from the campaign's configuration. + *

    + * + * @param presetSkillValues an optional map of preset skill values. If null + * or empty, default skill values are used instead. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions, + Map presetSkillValues) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + String[] skills = SkillType.getSkillList(); // default skills + + for (String skillName : skills) { + // Fetch the skill, either from presetSkillValues or default + SkillType skill = presetSkillValues.getOrDefault(skillName, SkillType.getType(skillName)); + + // Skip outdated or missing skills + if (allTargetNumbers.get(skillName) == null) { + logger.info(String.format("Skipping outdated or missing skill: %s", skillName)); + continue; + } + + // Update Target Number + JSpinner spinner = allTargetNumbers.get(skillName); + spinner.setValue(skill.getTarget()); + + // Costs + List skillCosts = allSkillCosts.get(skillName); + Integer[] costs = skill.getCosts(); + if (costs != null && skillCosts != null) { + for (int i = 0; i < Math.min(costs.length, skillCosts.size()); i++) { + skillCosts.get(i).setValue(costs[i]); + } + } + + // Milestones + List> milestones = allSkillMilestones.get(skillName); + if (milestones != null) { + int greenIndex = skill.getGreenLevel(); + int regularIndex = skill.getRegularLevel(); + int veteranIndex = skill.getVeteranLevel(); + int eliteIndex = skill.getEliteLevel(); + + for (int i = 0; i < milestones.size(); i++) { + SkillLevel levelToSet = determineMilestoneLevel(i, greenIndex, regularIndex, + veteranIndex, eliteIndex); + milestones.get(i).setSelectedItem(levelToSet); + } + } + } + + // Edge Costs + spnEdgeCost.setValue(options.getEdgeCost()); + } + + /** + * Determines the skill level milestone based on progress indices. + *

    + * It evaluates whether the milestone falls under Green, Regular, Veteran, or Elite + * levels, ensuring proper assignments for milestone thresholds. + *

    + * + * @param index the position in the milestone sequence. + * @param greenIndex the index where Green begins. + * @param regularIndex the index where Regular begins. + * @param veteranIndex the index where Veteran begins. + * @param eliteIndex the index where Elite begins. + * @return the corresponding {@link SkillLevel} for the given milestone. + */ + private SkillLevel determineMilestoneLevel(int index, int greenIndex, int regularIndex, + int veteranIndex, int eliteIndex) { + if (index < greenIndex) { + return ULTRA_GREEN; + } + if (index < regularIndex) { + return GREEN; + } + if (index < veteranIndex) { + return REGULAR; + } + if (index < eliteIndex) { + return VETERAN; + } + return ELITE; + } + + /** + * Transfers the configured skill values from the SkillsTab UI into the campaign's + * underlying data model. + *

    + * The method iterates through all configurable skills, updating the campaign + * with the configured target numbers, costs, and milestones for each skill. + *

    + * + * @param presetCampaignOptions the {@link CampaignOptions} instance to save settings to, + * or {@code null} to update the current campaign options. + * @param presetSkills an optional map of preset skill values. Overrides default + * values if provided. Null values will use the campaign's + * default values. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions, + @Nullable Map presetSkills) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + for (final String skillName : SkillType.getSkillList()) { + SkillType type = SkillType.getType(skillName); + if (presetSkills != null) { + type = presetSkills.get(skillName); + } + + if (type == null) { + logger.info(String.format("Skipping outdated or missing skill: %s", skillName)); + continue; + } + + // Update Target Number + updateTargetNumber(type); + + // Update Skill Costs + updateSkillCosts(skillName); + + // Update Skill Milestones + updateSkillMilestones(type); + } + + // Edge Costs + options.setEdgeCost((int) spnEdgeCost.getValue()); + } + + /** + * Updates the target number for a given skill in the campaign based on the + * corresponding user input from the SkillsTab UI. + *

    + * The target number determines the difficulty level for the specified skill + * in the campaign. This value is fetched from the associated spinner component + * in the UI and is applied to the given {@link SkillType}. + *

    + * + * @param type the {@link SkillType} object representing the skill whose target + * number needs to be updated. + */ + private void updateTargetNumber(SkillType type) { + int targetNumber = (int) allTargetNumbers.get(type.getName()).getValue(); + type.setTarget(targetNumber); + } + + /** + * Updates the costs associated with a given skill based on the user input + * from the SkillsTab UI. + *

    + * For each level of the specified skill, the cost values are retrieved from + * the corresponding spinner components in the UI and stored in the campaign's + * configuration. The costs represent the resource requirements for acquiring + * the skill at various levels. + *

    + * + * @param skillName the name of the skill whose cost values are to be updated. + */ + private void updateSkillCosts(String skillName) { + List costs = allSkillCosts.get(skillName); + + for (int level = 0; level < costs.size(); level++) { + int cost = (int) costs.get(level).getValue(); + SkillType.setCost(skillName, cost, level); + } + } + + /** + * Updates the skill milestones for a given skill in the campaign + * based on user input from the SkillsTab UI. + *

    + * Milestones represent the thresholds required to reach + * certain skill levels (e.g., Green, Regular, Veteran, Elite). + * The method processes these values from the associated combo boxes + * in the UI and applies them to the provided {@link SkillType}. + *

    + * The method ensures logical milestone progression and assigns + * default values if necessary. + *

    + * + * @param type the {@link SkillType} object representing the skill whose + * milestones are to be updated. + */ + private void updateSkillMilestones(SkillType type) { + List> skillMilestones = allSkillMilestones.get(type.getName()); + + // These allow us to ensure the full array of milestones has been assigned + type.setGreenLevel(skillMilestones.size() -4); + type.setRegularLevel(skillMilestones.size() -3); + type.setVeteranLevel(skillMilestones.size() -2); + type.setEliteLevel(skillMilestones.size() -1); + + // Then we overwrite those insurance values with the actual values + SkillLevel lastAssignment = ULTRA_GREEN; + for (int i = 0; i < skillMilestones.size(); i++) { + + JComboBox milestoneCombo = skillMilestones.get(i); + SkillLevel selectedSkillLevel = (SkillLevel) milestoneCombo.getSelectedItem(); + + if (selectedSkillLevel != lastAssignment) { + lastAssignment = selectedSkillLevel; + + if (selectedSkillLevel != null) { + switch (selectedSkillLevel) { + case GREEN -> type.setGreenLevel(i); + case REGULAR -> type.setRegularLevel(i); + case VETERAN -> type.setVeteranLevel(i); + case ELITE -> type.setEliteLevel(i); + default -> {} + } + } + } + } + } +} diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/TurnoverAndRetentionTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/TurnoverAndRetentionTab.java new file mode 100644 index 00000000000..1da953b3079 --- /dev/null +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/TurnoverAndRetentionTab.java @@ -0,0 +1,901 @@ +/* + * Copyright (c) 2024-2025 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.gui.campaignOptions.contents; + +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.common.annotations.Nullable; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.personnel.enums.TurnoverFrequency; +import mekhq.gui.campaignOptions.components.*; + +import javax.swing.*; +import java.awt.*; + +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.createParentPanel; +import static mekhq.gui.campaignOptions.CampaignOptionsUtilities.getImageDirectory; + +/** + * The {@code TurnoverAndRetentionTab} class represents a graphical user interface (GUI) + * configuration tab in the campaign options for managing unit turnover, retention, and fatigue settings. + *

    + * This class provides functionality to define and customize gameplay-related options such as: + *

    + *
      + *
    • Unit turnover settings, including retirement, contract durations, payouts, and modifiers.
    • + *
    • Administrative strain and management skills impacting unit cohesion.
    • + *
    • Fatigue mechanics such as fatigue rates, leave thresholds, and injury fatigue.
    • + *
    + *

    + * The class interacts with a {@link CampaignOptions} object, allowing the user to load and save + * configurations. It consists of two main panels: + *

    + *
      + *
    • Turnover Tab: Controls unit turnover, payouts, and related modifiers.
    • + *
    • Fatigue Tab: Manages fatigue-related options like kitchen capacity + * and fatigue rates.
    • + *
    + */ +public class TurnoverAndRetentionTab { + private final CampaignOptions campaignOptions; + + //start Turnover Tab + private JCheckBox chkUseRandomRetirement; + + private JPanel pnlSettings; + private JLabel lblTurnoverFixedTargetNumber; + private JSpinner spnTurnoverFixedTargetNumber; + private JLabel lblTurnoverFrequency; + private MMComboBox comboTurnoverFrequency; + private JCheckBox chkUseContractCompletionRandomRetirement; + private JCheckBox chkUseRandomFounderTurnover; + private JCheckBox chkTrackOriginalUnit; + private JCheckBox chkAeroRecruitsHaveUnits; + private JCheckBox chkUseSubContractSoldiers; + private JLabel lblServiceContractDuration; + private JSpinner spnServiceContractDuration; + private JLabel lblServiceContractModifier; + private JSpinner spnServiceContractModifier; + private JCheckBox chkPayBonusDefault; + private JLabel lblPayBonusDefaultThreshold; + private JSpinner spnPayBonusDefaultThreshold; + + private JPanel pnlModifiers; + private JCheckBox chkUseCustomRetirementModifiers; + private JCheckBox chkUseFatigueModifiers; + private JCheckBox chkUseSkillModifiers; + private JCheckBox chkUseAgeModifiers; + private JCheckBox chkUseUnitRatingModifiers; + private JCheckBox chkUseFactionModifiers; + private JCheckBox chkUseMissionStatusModifiers; + private JCheckBox chkUseHostileTerritoryModifiers; + private JCheckBox chkUseFamilyModifiers; + private JCheckBox chkUseLoyaltyModifiers; + private JCheckBox chkUseHideLoyalty; + + private JPanel pnlPayout; + private JLabel lblPayoutRateOfficer; + private JSpinner spnPayoutRateOfficer; + private JLabel lblPayoutRateEnlisted; + private JSpinner spnPayoutRateEnlisted; + private JLabel lblPayoutRetirementMultiplier; + private JSpinner spnPayoutRetirementMultiplier; + private JCheckBox chkUsePayoutServiceBonus; + private JLabel lblPayoutServiceBonusRate; + private JSpinner spnPayoutServiceBonusRate; + + private JPanel pnlUnitCohesion; + + private JPanel pnlAdministrativeStrainWrapper; + private JCheckBox chkUseAdministrativeStrain; + + private JPanel pnlAdministrativeStrain; + private JLabel lblAdministrativeCapacity; + private JSpinner spnAdministrativeCapacity; + private JLabel lblMultiCrewStrainDivider; + private JSpinner spnMultiCrewStrainDivider; + + private JPanel pnlManagementSkillWrapper; + private JCheckBox chkUseManagementSkill; + + private JPanel pnlManagementSkill; + private JCheckBox chkUseCommanderLeadershipOnly; + private JLabel lblManagementSkillPenalty; + private JSpinner spnManagementSkillPenalty; + //end Turnover Tab + + //start Fatigue Tab + private JCheckBox chkUseFatigue; + private JLabel lblFatigueRate; + private JSpinner spnFatigueRate; + private JCheckBox chkUseInjuryFatigue; + private JLabel lblFieldKitchenCapacity; + private JSpinner spnFieldKitchenCapacity; + private JCheckBox chkFieldKitchenIgnoreNonCombatants; + private JLabel lblFatigueLeaveThreshold; + private JSpinner spnFatigueLeaveThreshold; + //end Fatigue Tab + + /** + * Constructs a {@code TurnoverAndRetentionTab} and initializes the tab with the given + * {@link CampaignOptions}. This sets up necessary UI components and their default + * configurations. + * + * @param campaignOptions the {@code CampaignOptions} instance that holds the settings + * to be modified or displayed in this tab. + */ + public TurnoverAndRetentionTab(CampaignOptions campaignOptions) { + this.campaignOptions = campaignOptions; + + initialize(); + } + + /** + * Initializes the content and configuration of the turnover and fatigue tabs. + * This method sets up their respective panels and components. + */ + private void initialize() { + initializeTurnoverTab(); + initializeFatigueTab(); + } + + /** + * Initializes the content of the fatigue configuration tab. + * Includes settings such as fatigue rate, injury fatigue, field kitchen capacity, + * and fatigue leave thresholds. + */ + private void initializeFatigueTab() { + chkUseFatigue = new JCheckBox(); + lblFatigueRate = new JLabel(); + spnFatigueRate = new JSpinner(); + chkUseInjuryFatigue = new JCheckBox(); + lblFieldKitchenCapacity = new JLabel(); + spnFieldKitchenCapacity = new JSpinner(); + chkFieldKitchenIgnoreNonCombatants = new JCheckBox(); + lblFatigueLeaveThreshold = new JLabel(); + spnFatigueLeaveThreshold = new JSpinner(); + } + + /** + * Initializes the content of the turnover configuration tab. + * Includes settings such as turnover frequencies, service contract details, + * and retirement/payout modifiers. + */ + private void initializeTurnoverTab() { + chkUseRandomRetirement = new JCheckBox(); + + pnlSettings = new JPanel(); + lblTurnoverFixedTargetNumber = new JLabel(); + spnTurnoverFixedTargetNumber = new JSpinner(); + lblTurnoverFrequency = new JLabel(); + comboTurnoverFrequency = new MMComboBox<>("comboTurnoverFrequency", TurnoverFrequency.values()); + chkUseContractCompletionRandomRetirement = new JCheckBox(); + chkUseRandomFounderTurnover = new JCheckBox(); + chkTrackOriginalUnit = new JCheckBox(); + chkAeroRecruitsHaveUnits = new JCheckBox(); + chkUseSubContractSoldiers = new JCheckBox(); + lblServiceContractDuration = new JLabel(); + spnServiceContractDuration = new JSpinner(); + lblServiceContractModifier = new JLabel(); + spnServiceContractModifier = new JSpinner(); + chkPayBonusDefault = new JCheckBox(); + lblPayBonusDefaultThreshold = new JLabel(); + spnPayBonusDefaultThreshold = new JSpinner(); + + pnlModifiers = new JPanel(); + chkUseCustomRetirementModifiers = new JCheckBox(); + chkUseFatigueModifiers = new JCheckBox(); + chkUseSkillModifiers = new JCheckBox(); + chkUseAgeModifiers = new JCheckBox(); + chkUseUnitRatingModifiers = new JCheckBox(); + chkUseFactionModifiers = new JCheckBox(); + chkUseMissionStatusModifiers = new JCheckBox(); + chkUseHostileTerritoryModifiers = new JCheckBox(); + chkUseFamilyModifiers = new JCheckBox(); + chkUseLoyaltyModifiers = new JCheckBox(); + chkUseHideLoyalty = new JCheckBox(); + + pnlPayout = new JPanel(); + lblPayoutRateOfficer = new JLabel(); + spnPayoutRateOfficer = new JSpinner(); + lblPayoutRateEnlisted = new JLabel(); + spnPayoutRateEnlisted = new JSpinner(); + lblPayoutRetirementMultiplier = new JLabel(); + spnPayoutRetirementMultiplier = new JSpinner(); + chkUsePayoutServiceBonus = new JCheckBox(); + lblPayoutServiceBonusRate = new JLabel(); + spnPayoutServiceBonusRate = new JSpinner(); + + pnlUnitCohesion = new JPanel(); + + pnlAdministrativeStrainWrapper = new JPanel(); + chkUseAdministrativeStrain = new JCheckBox(); + + pnlAdministrativeStrain = new JPanel(); + lblAdministrativeCapacity = new JLabel(); + spnAdministrativeCapacity = new JSpinner(); + lblMultiCrewStrainDivider = new JLabel(); + spnMultiCrewStrainDivider = new JSpinner(); + + pnlManagementSkillWrapper = new JPanel(); + chkUseManagementSkill = new JCheckBox(); + + pnlManagementSkill = new JPanel(); + chkUseCommanderLeadershipOnly = new JCheckBox(); + lblManagementSkillPenalty = new JLabel(); + spnManagementSkillPenalty = new JSpinner(); + } + + /** + * Creates and configures the "Fatigue" tab with its relevant components. + * These include options related to enabling fatigue, fatigue rates, injury fatigue, + * kitchen capacities, and leave thresholds. + * + * @return the {@link JPanel} representing the constructed Fatigue tab. + */ + public JPanel createFatigueTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("FatigueTab", + getImageDirectory() + "logo_clan_mongoose.png", true); + + // Contents + chkUseFatigue = new CampaignOptionsCheckBox("UseFatigue"); + + lblFatigueRate = new CampaignOptionsLabel("FatigueRate"); + spnFatigueRate = new CampaignOptionsSpinner("FatigueRate", + 1, 1, 10, 1); + + chkUseInjuryFatigue = new CampaignOptionsCheckBox("UseInjuryFatigue"); + + lblFieldKitchenCapacity = new CampaignOptionsLabel("FieldKitchenCapacity"); + spnFieldKitchenCapacity = new CampaignOptionsSpinner("FieldKitchenCapacity", + 150, 0, 450, 1); + + chkFieldKitchenIgnoreNonCombatants = new CampaignOptionsCheckBox("FieldKitchenIgnoreNonCombatants"); + + lblFatigueLeaveThreshold = new CampaignOptionsLabel("FatigueLeaveThreshold"); + spnFatigueLeaveThreshold = new CampaignOptionsSpinner("FatigueLeaveThreshold", + 13, 0, 17, 1); + + // Layout the Panels + final JPanel panelLeft = new CampaignOptionsStandardPanel("FatigueTabLeft"); + final GridBagConstraints layoutLeft = new CampaignOptionsGridBagConstraints(panelLeft); + + layoutLeft.gridx = 0; + layoutLeft.gridy = 0; + layoutLeft.gridwidth = 1; + panelLeft.add(chkUseFatigue, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkUseInjuryFatigue, layoutLeft); + + layoutLeft.gridy++; + panelLeft.add(chkFieldKitchenIgnoreNonCombatants, layoutLeft); + + final JPanel panelRight = new CampaignOptionsStandardPanel("FatigueTabRight"); + final GridBagConstraints layoutRight = new CampaignOptionsGridBagConstraints(panelRight); + + layoutRight.gridx = 0; + layoutRight.gridy = 0; + layoutRight.gridwidth = 1; + panelRight.add(lblFatigueRate, layoutRight); + layoutRight.gridx++; + panelRight.add(spnFatigueRate, layoutRight); + + layoutRight.gridx = 0; + layoutRight.gridy++; + panelRight.add(lblFieldKitchenCapacity, layoutRight); + layoutRight.gridx++; + panelRight.add(spnFieldKitchenCapacity, layoutRight); + + layoutRight.gridx = 0; + layoutRight.gridy++; + panelRight.add(lblFatigueLeaveThreshold, layoutRight); + layoutRight.gridx++; + panelRight.add(spnFatigueLeaveThreshold, layoutRight); + + final JPanel panelParent = new CampaignOptionsStandardPanel("FatigueTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridwidth = 1; + layoutParent.gridy++; + panelParent.add(panelLeft, layoutParent); + layoutParent.gridx++; + panelParent.add(panelRight, layoutParent); + + // Create Parent Panel and return + return createParentPanel(panelParent, "FatigueTab"); + } + + /** + * Creates and configures the "Turnover" tab with its relevant components. + * These include options for turnover control, random retirement, payout settings, + * and modifiers for administrative strain and cohesion. + * + * @return the {@link JPanel} representing the constructed Turnover tab. + */ + public JPanel createTurnoverTab() { + // Header + JPanel headerPanel = new CampaignOptionsHeaderPanel("TurnoverTab", + getImageDirectory() + "logo_duchy_of_andurien.png", true); + + // Contents + chkUseRandomRetirement = new CampaignOptionsCheckBox("UseRandomRetirement"); + pnlSettings = createSettingsPanel(); + pnlModifiers = createModifiersPanel(); + pnlPayout = createPayoutsPanel(); + pnlUnitCohesion = createUnitCohesionPanel(); + + // Layout the Panel + final JPanel panelTop = new CampaignOptionsStandardPanel("TurnoverTabTop"); + final GridBagConstraints layoutTop = new CampaignOptionsGridBagConstraints(panelTop); + + layoutTop.gridwidth = 1; + layoutTop.gridx = 0; + layoutTop.gridy = 0; + panelTop.add(chkUseRandomRetirement, layoutTop); + + layoutTop.gridy++; + panelTop.add(pnlSettings, layoutTop); + layoutTop.gridx++; + panelTop.add(pnlModifiers, layoutTop); + layoutTop.gridx++; + panelTop.add(pnlPayout, layoutTop); + + // Layout the Panel + final JPanel panelParent = new CampaignOptionsStandardPanel("TurnoverTab", true); + final GridBagConstraints layoutParent = new CampaignOptionsGridBagConstraints(panelParent); + + layoutParent.gridwidth = 5; + layoutParent.gridx = 0; + layoutParent.gridy = 0; + panelParent.add(headerPanel, layoutParent); + + layoutParent.gridy++; + layoutParent.gridwidth = 1; + panelParent.add(panelTop, layoutParent); + + layoutParent.gridy++; + panelParent.add(pnlUnitCohesion, layoutParent); + + + // Create Parent Panel and return + return createParentPanel(panelParent, "TurnoverTab"); + } + + /** + * Creates the settings panel for the "Turnover" tab, which organizes various + * settings like random retirement, contract durations, and bonuses into a layout. + * + * @return the {@link JPanel} representing the turnover settings. + */ + private JPanel createSettingsPanel() { + // Contents + lblTurnoverFixedTargetNumber = new CampaignOptionsLabel("TurnoverFixedTargetNumber"); + spnTurnoverFixedTargetNumber = new CampaignOptionsSpinner("TurnoverFixedTargetNumber", + 3, 0, 10, 1); + + lblTurnoverFrequency = new CampaignOptionsLabel("TurnoverFrequency"); + comboTurnoverFrequency.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, final Object value, + final int index, final boolean isSelected, + final boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof TurnoverFrequency) { + list.setToolTipText(((TurnoverFrequency) value).getToolTipText()); + } + return this; + } + }); + + chkUseContractCompletionRandomRetirement = new CampaignOptionsCheckBox( + "UseContractCompletionRandomRetirement"); + + chkUseRandomFounderTurnover = new CampaignOptionsCheckBox("UseRandomFounderTurnover"); + + chkTrackOriginalUnit = new CampaignOptionsCheckBox("TrackOriginalUnit"); + + chkAeroRecruitsHaveUnits = new CampaignOptionsCheckBox("AeroRecruitsHaveUnits"); + + chkUseSubContractSoldiers = new CampaignOptionsCheckBox("UseSubContractSoldiers"); + + lblServiceContractDuration = new CampaignOptionsLabel("ServiceContractDuration"); + spnServiceContractDuration = new CampaignOptionsSpinner("ServiceContractDuration", + 36, 0, 120, 1); + + lblServiceContractModifier = new CampaignOptionsLabel("ServiceContractModifier"); + spnServiceContractModifier = new CampaignOptionsSpinner("ServiceContractModifier", + 3, 0, 10, 1); + + chkPayBonusDefault = new CampaignOptionsCheckBox("PayBonusDefault"); + + lblPayBonusDefaultThreshold = new CampaignOptionsLabel("PayBonusDefaultThreshold"); + spnPayBonusDefaultThreshold = new CampaignOptionsSpinner("PayBonusDefaultThreshold", + 3, 0, 12, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("SettingsPanel", true, + "SettingsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblTurnoverFixedTargetNumber, layout); + layout.gridx++; + panel.add(spnTurnoverFixedTargetNumber, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblTurnoverFrequency, layout); + layout.gridx++; + panel.add(comboTurnoverFrequency, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkUseContractCompletionRandomRetirement, layout); + + layout.gridy++; + panel.add(chkUseRandomFounderTurnover, layout); + + layout.gridy++; + panel.add(chkTrackOriginalUnit, layout); + + layout.gridy++; + panel.add(chkAeroRecruitsHaveUnits, layout); + + layout.gridy++; + panel.add(chkUseSubContractSoldiers, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblServiceContractDuration, layout); + layout.gridx++; + panel.add(spnServiceContractDuration, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblServiceContractModifier, layout); + layout.gridx++; + panel.add(spnServiceContractModifier, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkPayBonusDefault, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblPayBonusDefaultThreshold, layout); + layout.gridx++; + panel.add(spnPayBonusDefaultThreshold, layout); + + return panel; + } + + /** + * Creates the modifiers panel for the "Turnover" tab, which contains gameplay + * modifiers such as age, skill, faction, and loyalty. + * + * @return the {@link JPanel} representing the turnover modifiers. + */ + private JPanel createModifiersPanel() { + // Contents + chkUseCustomRetirementModifiers = new CampaignOptionsCheckBox("UseCustomRetirementModifiers"); + chkUseFatigueModifiers = new CampaignOptionsCheckBox("UseFatigueModifiers"); + chkUseSkillModifiers = new CampaignOptionsCheckBox("UseSkillModifiers"); + chkUseAgeModifiers = new CampaignOptionsCheckBox("UseAgeModifiers"); + chkUseUnitRatingModifiers = new CampaignOptionsCheckBox("UseUnitRatingModifiers"); + chkUseFactionModifiers = new CampaignOptionsCheckBox("UseFactionModifiers"); + chkUseMissionStatusModifiers = new CampaignOptionsCheckBox("UseMissionStatusModifiers"); + chkUseHostileTerritoryModifiers = new CampaignOptionsCheckBox("UseHostileTerritoryModifiers"); + chkUseFamilyModifiers = new CampaignOptionsCheckBox("UseFamilyModifiers"); + chkUseLoyaltyModifiers = new CampaignOptionsCheckBox("UseLoyaltyModifiers"); + chkUseHideLoyalty = new CampaignOptionsCheckBox("UseHideLoyalty"); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("TurnoverModifiersPanel", true, + "ModifiersPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(chkUseCustomRetirementModifiers, layout); + + layout.gridy++; + panel.add(chkUseFatigueModifiers, layout); + + layout.gridy++; + panel.add(chkUseSkillModifiers, layout); + + layout.gridy++; + panel.add(chkUseAgeModifiers, layout); + + layout.gridy++; + panel.add(chkUseUnitRatingModifiers, layout); + + layout.gridy++; + panel.add(chkUseFactionModifiers, layout); + + layout.gridy++; + panel.add(chkUseMissionStatusModifiers, layout); + + layout.gridy++; + panel.add(chkUseHostileTerritoryModifiers, layout); + + layout.gridy++; + panel.add(chkUseFamilyModifiers, layout); + + layout.gridy++; + panel.add(chkUseLoyaltyModifiers, layout); + + layout.gridy++; + panel.add(chkUseHideLoyalty, layout); + + return panel; + } + + /** + * Creates the payouts panel for the "Turnover" tab. This panel holds settings related to + * payout rates for officers and enlisted personnel, service bonuses, and retirement multipliers. + * + * @return the {@link JPanel} representing the payout settings. + */ + private JPanel createPayoutsPanel() { + // Contents + lblPayoutRateOfficer = new CampaignOptionsLabel("PayoutRateOfficer"); + spnPayoutRateOfficer = new CampaignOptionsSpinner("PayoutRateOfficer", + 3, 0, 12, 1); + + lblPayoutRateEnlisted = new CampaignOptionsLabel("PayoutRateEnlisted"); + spnPayoutRateEnlisted = new CampaignOptionsSpinner("PayoutRateEnlisted", + 3, 0, 12, 1); + + lblPayoutRetirementMultiplier = new CampaignOptionsLabel("PayoutRetirementMultiplier"); + spnPayoutRetirementMultiplier = new CampaignOptionsSpinner("PayoutRetirementMultiplier", + 24, 1, 120, 1); + + chkUsePayoutServiceBonus = new CampaignOptionsCheckBox("UsePayoutServiceBonus"); + + lblPayoutServiceBonusRate = new CampaignOptionsLabel("PayoutServiceBonusRate"); + spnPayoutServiceBonusRate = new CampaignOptionsSpinner("PayoutServiceBonusRate", + 10, 1, 100, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("PayoutsPanel", true, + "PayoutsPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblPayoutRateOfficer, layout); + layout.gridx++; + panel.add(spnPayoutRateOfficer, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblPayoutRateEnlisted, layout); + layout.gridx++; + panel.add(spnPayoutRateEnlisted, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblPayoutRetirementMultiplier, layout); + layout.gridx++; + panel.add(spnPayoutRetirementMultiplier, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 2; + panel.add(chkUsePayoutServiceBonus, layout); + + layout.gridx = 0; + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblPayoutServiceBonusRate, layout); + layout.gridx++; + panel.add(spnPayoutServiceBonusRate, layout); + + return panel; + } + + /** + * Creates the unit cohesion panel for the "Turnover" tab, which includes settings like + * administrative strain and management skills. + * + * @return the {@link JPanel} containing unit cohesion settings. + */ + private JPanel createUnitCohesionPanel() { + // Contents + pnlAdministrativeStrainWrapper = createAdministrativeStrainWrapperPanel(); + pnlManagementSkillWrapper = createManagementSkillWrapperPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("UnitCohesionPanel", true, + "UnitCohesionPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(pnlAdministrativeStrainWrapper, layout); + layout.gridx++; + panel.add(pnlManagementSkillWrapper, layout); + + return panel; + } + + /** + * Creates the administrative strain wrapper panel. Includes a checkbox to enable + * administrative strain and settings for related capacities and behaviors. + * + * @return the {@link JPanel} for managing administrative strain settings. + */ + private JPanel createAdministrativeStrainWrapperPanel() { + // Contents + chkUseAdministrativeStrain = new CampaignOptionsCheckBox("UseAdministrativeStrain"); + pnlAdministrativeStrain = createAdministrativeStrainPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AdministrativeStrainPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(chkUseAdministrativeStrain, layout); + + layout.gridy++; + panel.add(pnlAdministrativeStrain, layout); + + return panel; + } + + /** + * Creates the panel for administrative strain settings, which contains + * spinners to adjust administrative capacity and multi-crew strain dividers. + * + * @return the {@link JPanel} for administrative strain adjustment. + */ + private JPanel createAdministrativeStrainPanel() { + // Contents + lblAdministrativeCapacity = new CampaignOptionsLabel("AdministrativeCapacity"); + spnAdministrativeCapacity = new CampaignOptionsSpinner("AdministrativeCapacity", + 10, 1, 30, 1); + + lblMultiCrewStrainDivider = new CampaignOptionsLabel("MultiCrewStrainDivider"); + spnMultiCrewStrainDivider = new CampaignOptionsSpinner("MultiCrewStrainDivider", + 5, 1, 25, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("AdministrativeStrain", true, + "AdministrativeStrain"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 1; + panel.add(lblAdministrativeCapacity, layout); + layout.gridx++; + panel.add(spnAdministrativeCapacity, layout); + + layout.gridx = 0; + layout.gridy++; + panel.add(lblMultiCrewStrainDivider, layout); + layout.gridx++; + panel.add(spnMultiCrewStrainDivider, layout); + + return panel; + } + + /** + * Creates the management skill wrapper panel, which contains settings such as enabling + * management skill checks and adjusting penalties. + * + * @return the {@link JPanel} for managing skill configurations. + */ + private JPanel createManagementSkillWrapperPanel() { + // Contents + chkUseManagementSkill = new CampaignOptionsCheckBox("UseManagementSkill"); + pnlManagementSkill = createManagementSkillPanel(); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("UnitCohesionPanel"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 2; + panel.add(chkUseManagementSkill, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(pnlManagementSkill, layout); + + return panel; + } + + /** + * Creates the panel for management skill settings, including options for leadership adjustments + * and penalties. + * + * @return the {@link JPanel} for setting management and leadership skill penalties. + */ + private JPanel createManagementSkillPanel() { + // Contents + chkUseCommanderLeadershipOnly = new CampaignOptionsCheckBox("UseCommanderLeadershipOnly"); + + lblManagementSkillPenalty = new CampaignOptionsLabel("ManagementSkillPenalty"); + spnManagementSkillPenalty = new CampaignOptionsSpinner("ManagementSkillPenalty", + 0, -10, 10, 1); + + // Layout the Panel + final JPanel panel = new CampaignOptionsStandardPanel("ManagementSkill", true, + "ManagementSkill"); + final GridBagConstraints layout = new CampaignOptionsGridBagConstraints(panel); + + layout.gridy = 0; + layout.gridx = 0; + layout.gridwidth = 2; + panel.add(chkUseCommanderLeadershipOnly, layout); + + layout.gridy++; + layout.gridwidth = 1; + panel.add(lblManagementSkillPenalty, layout); + layout.gridx++; + panel.add(spnManagementSkillPenalty, layout); + + return panel; + } + + /** + * Overload of {@code loadValuesFromCampaignOptions} method. + * Loads values from the current {@link CampaignOptions} instance. + */ + public void loadValuesFromCampaignOptions() { + loadValuesFromCampaignOptions(null); + } + + /** + * Loads the current configuration values from the provided {@link CampaignOptions} + * object and updates the associated UI components in both the Turnover and Fatigue tabs. + * If no options are provided, the existing campaign options are used. + * + * @param presetCampaignOptions the {@link CampaignOptions} instance to load settings from, + * or {@code null} to use the current campaign options. + */ + public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Turnover + chkUseRandomRetirement.setSelected(options.isUseRandomRetirement()); + spnTurnoverFixedTargetNumber.setValue(options.getTurnoverFixedTargetNumber()); + comboTurnoverFrequency.setSelectedItem(options.getTurnoverFrequency()); + chkUseContractCompletionRandomRetirement.setSelected(options.isUseContractCompletionRandomRetirement()); + chkUseRandomFounderTurnover.setSelected(options.isUseRandomFounderTurnover()); + chkTrackOriginalUnit.setSelected(options.isTrackOriginalUnit()); + chkAeroRecruitsHaveUnits.setSelected(options.isAeroRecruitsHaveUnits()); + chkUseSubContractSoldiers.setSelected(options.isUseSubContractSoldiers()); + spnServiceContractDuration.setValue(options.getServiceContractDuration()); + spnServiceContractModifier.setValue(options.getServiceContractModifier()); + chkPayBonusDefault.setSelected(options.isPayBonusDefault()); + spnPayBonusDefaultThreshold.setValue(options.getPayBonusDefaultThreshold()); + chkUseCustomRetirementModifiers.setSelected(options.isUseCustomRetirementModifiers()); + chkUseFatigueModifiers.setSelected(options.isUseFatigueModifiers()); + chkUseSkillModifiers.setSelected(options.isUseSkillModifiers()); + chkUseAgeModifiers.setSelected(options.isUseAgeModifiers()); + chkUseUnitRatingModifiers.setSelected(options.isUseUnitRatingModifiers()); + chkUseFactionModifiers.setSelected(options.isUseFactionModifiers()); + chkUseMissionStatusModifiers.setSelected(options.isUseMissionStatusModifiers()); + chkUseHostileTerritoryModifiers.setSelected(options.isUseHostileTerritoryModifiers()); + chkUseFamilyModifiers.setSelected(options.isUseFamilyModifiers()); + chkUseLoyaltyModifiers.setSelected(options.isUseLoyaltyModifiers()); + chkUseHideLoyalty.setSelected(options.isUseHideLoyalty()); + spnPayoutRateOfficer.setValue(options.getPayoutRateOfficer()); + spnPayoutRateEnlisted.setValue(options.getPayoutRateEnlisted()); + spnPayoutRetirementMultiplier.setValue(options.getPayoutRetirementMultiplier()); + chkUsePayoutServiceBonus.setSelected(options.isUsePayoutServiceBonus()); + spnPayoutServiceBonusRate.setValue(options.getPayoutServiceBonusRate()); + chkUseAdministrativeStrain.setSelected(options.isUseAdministrativeStrain()); + spnAdministrativeCapacity.setValue(options.getAdministrativeCapacity()); + spnMultiCrewStrainDivider.setValue(options.getMultiCrewStrainDivider()); + chkUseManagementSkill.setSelected(options.isUseManagementSkill()); + chkUseCommanderLeadershipOnly.setSelected(options.isUseCommanderLeadershipOnly()); + spnManagementSkillPenalty.setValue(options.getManagementSkillPenalty()); + + // Fatigue + chkUseFatigue.setSelected(options.isUseFatigue()); + spnFatigueRate.setValue(options.getFatigueRate()); + chkUseInjuryFatigue.setSelected(options.isUseInjuryFatigue()); + spnFieldKitchenCapacity.setValue(options.getFieldKitchenCapacity()); + chkFieldKitchenIgnoreNonCombatants.setSelected(options.isUseFieldKitchenIgnoreNonCombatants()); + spnFatigueLeaveThreshold.setValue(options.getFatigueLeaveThreshold()); + } + + /** + * Applies the current campaign options based on the configurations in the UI + * to the given {@link CampaignOptions}. If no options are provided, the current + * campaign options are updated. + * + * @param presetCampaignOptions the {@link CampaignOptions} instance to save settings to, + * or {@code null} to update the current campaign options. + */ + public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions) { + CampaignOptions options = presetCampaignOptions; + if (presetCampaignOptions == null) { + options = this.campaignOptions; + } + + // Turnover + options.setUseRandomRetirement(chkUseRandomRetirement.isSelected()); + options.setTurnoverFixedTargetNumber((int) spnTurnoverFixedTargetNumber.getValue()); + options.setTurnoverFrequency(comboTurnoverFrequency.getSelectedItem()); + options.setUseContractCompletionRandomRetirement(chkUseContractCompletionRandomRetirement.isSelected()); + options.setUseRandomFounderTurnover(chkUseRandomFounderTurnover.isSelected()); + options.setTrackOriginalUnit(chkTrackOriginalUnit.isSelected()); + options.setAeroRecruitsHaveUnits(chkAeroRecruitsHaveUnits.isSelected()); + options.setUseSubContractSoldiers(chkUseSubContractSoldiers.isSelected()); + options.setServiceContractDuration((int) spnServiceContractDuration.getValue()); + options.setServiceContractModifier((int) spnServiceContractModifier.getValue()); + options.setPayBonusDefault(chkPayBonusDefault.isSelected()); + options.setPayBonusDefaultThreshold((int) spnPayBonusDefaultThreshold.getValue()); + options.setUseCustomRetirementModifiers(chkUseCustomRetirementModifiers.isSelected()); + options.setUseFatigueModifiers(chkUseFatigueModifiers.isSelected()); + options.setUseSkillModifiers(chkUseSkillModifiers.isSelected()); + options.setUseAgeModifiers(chkUseAgeModifiers.isSelected()); + options.setUseUnitRatingModifiers(chkUseUnitRatingModifiers.isSelected()); + options.setUseFactionModifiers(chkUseFactionModifiers.isSelected()); + options.setUseMissionStatusModifiers(chkUseMissionStatusModifiers.isSelected()); + options.setUseHostileTerritoryModifiers(chkUseHostileTerritoryModifiers.isSelected()); + options.setUseFamilyModifiers(chkUseFamilyModifiers.isSelected()); + options.setUseLoyaltyModifiers(chkUseLoyaltyModifiers.isSelected()); + options.setUseHideLoyalty(chkUseHideLoyalty.isSelected()); + options.setPayoutRateOfficer((int) spnPayoutRateOfficer.getValue()); + options.setPayoutRateEnlisted((int) spnPayoutRateEnlisted.getValue()); + options.setPayoutRetirementMultiplier((int) spnPayoutRetirementMultiplier.getValue()); + options.setUsePayoutServiceBonus(chkUsePayoutServiceBonus.isSelected()); + options.setPayoutServiceBonusRate((int) spnPayoutServiceBonusRate.getValue()); + options.setUseAdministrativeStrain(chkUseAdministrativeStrain.isSelected()); + options.setAdministrativeCapacity((int) spnAdministrativeCapacity.getValue()); + options.setMultiCrewStrainDivider((int) spnMultiCrewStrainDivider.getValue()); + options.setUseManagementSkill(chkUseManagementSkill.isSelected()); + options.setUseCommanderLeadershipOnly(chkUseCommanderLeadershipOnly.isSelected()); + options.setManagementSkillPenalty((int) spnManagementSkillPenalty.getValue()); + + // Fatigue + options.setUseFatigue(chkUseFatigue.isSelected()); + options.setFatigueRate((int) spnFatigueRate.getValue()); + options.setUseInjuryFatigue(chkUseInjuryFatigue.isSelected()); + options.setFieldKitchenCapacity((int) spnFieldKitchenCapacity.getValue()); + options.setFatigueLeaveThreshold((int) spnFatigueLeaveThreshold.getValue()); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/AcquisitionsDialog.java b/MekHQ/src/mekhq/gui/dialog/AcquisitionsDialog.java index 85f83b0de11..aebc99d059a 100644 --- a/MekHQ/src/mekhq/gui/dialog/AcquisitionsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/AcquisitionsDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2017-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,28 +18,6 @@ */ package mekhq.gui.dialog; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Image; -import java.awt.Insets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.swing.BorderFactory; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.WindowConstants; - import megamek.client.ui.preferences.JWindowPreference; import megamek.client.ui.preferences.PreferencesNode; import megamek.codeUtilities.StringUtility; @@ -59,6 +37,12 @@ import mekhq.service.PartsAcquisitionService; import mekhq.service.PartsAcquisitionService.PartCountInfo; +import javax.swing.*; +import java.awt.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * @author Kipsta */ @@ -72,14 +56,10 @@ public class AcquisitionsDialog extends JDialog { private JLabel lblSummary; private JButton btnSummary; - private int numBonusParts = 0; - public AcquisitionsDialog(JFrame parent, boolean modal, CampaignGUI campaignGUI) { super(parent, modal); this.campaignGUI = campaignGUI; - calculateBonusParts(); - initComponents(); setLocationRelativeTo(parent); @@ -173,12 +153,7 @@ private JPanel createSummaryPanel() { } }); btnSummary.addPropertyChangeListener("missingCount", evt -> { - boolean visible = false; - - if ((PartsAcquisitionService.getMissingCount() > 0) - && (PartsAcquisitionService.getMissingCount() > PartsAcquisitionService.getUnavailableCount())) { - visible = true; - } + boolean visible = (PartsAcquisitionService.getMissingCount() > 0) && (PartsAcquisitionService.getMissingCount() > PartsAcquisitionService.getUnavailableCount()); btnSummary.setVisible(visible); }); @@ -251,16 +226,6 @@ private void setUserPreferences() { } } - private void calculateBonusParts() { - numBonusParts = campaignGUI.getCampaign().totalBonusParts(); - - if (partPanelMap != null) { - for (AcquisitionPanel pnl : partPanelMap.values()) { - pnl.refresh(); - } - } - } - public class AcquisitionPanel extends JPanel { private List awList; private int idx; @@ -270,7 +235,6 @@ public class AcquisitionPanel extends JPanel { private PartCountInfo partCountInfo = new PartCountInfo(); private JButton btnOrderAll; - private JButton btnUseBonus; private JButton btnDepod; private JLabel lblText; @@ -296,18 +260,6 @@ public void orderAllMissing() { } } - private void useBonusPart() { - if (targetWork instanceof AmmoBin) { - targetWork = ((AmmoBin) targetWork).getAcquisitionWork(); - } - - campaignGUI.getCampaign().spendBonusPart(targetWork); - - refresh(); - - calculateBonusParts(); - } - private void refresh() { pnlSummary.firePropertyChange("counts", -1, 0); @@ -330,9 +282,6 @@ private void refresh() { } btnDepod.setVisible(partCountInfo.getOmniPodCount() != 0); - - btnUseBonus.setText(String.format("Use Bonus Part (%s)", numBonusParts)); - btnUseBonus.setVisible(numBonusParts > 0); } } @@ -363,7 +312,7 @@ private String generateText() { String countModifier = partCountInfo.getCountModifier(); if (!StringUtility.isNullOrBlank(countModifier)) { - countModifier = " " + countModifier; + countModifier = ' ' + countModifier; } String inventoryInfo = "Inventory: " + partCountInfo.getInTransitCount() + countModifier @@ -459,7 +408,7 @@ private void initComponents() { JPanel pnlUnits = new JPanel(); pnlUnits.setLayout(new GridBagLayout()); - pnlUnits.setBorder(BorderFactory.createTitledBorder("Units requiring this part (" + unitMap.size() + ")")); + pnlUnits.setBorder(BorderFactory.createTitledBorder("Units requiring this part (" + unitMap.size() + ')')); GridBagConstraints cUnits = new GridBagConstraints(); cUnits.gridx = 0; @@ -501,14 +450,6 @@ private JPanel createActionButtons() { gbcActions.insets = new Insets(10, 0, 5, 0); gbcActions.fill = GridBagConstraints.NONE; gbcActions.anchor = GridBagConstraints.NORTHEAST; - - btnUseBonus = new JButton(String.format("Use Bonus Part (%s)", numBonusParts)); - btnUseBonus.setToolTipText("Use a bonus part to acquire this item"); - btnUseBonus.setName("btnUseBonus"); - btnUseBonus.setVisible(numBonusParts > 0); - btnUseBonus.addActionListener(ev -> useBonusPart()); - actionButtons.add(btnUseBonus, gbcActions); - gbcActions.gridy++; JButton btnOrderOne; JButton btnOrderInBulk; if (partCountInfo.isCanBeAcquired()) { @@ -546,7 +487,7 @@ private JPanel createActionButtons() { actionButtons.add(btnOrderInBulk, gbcActions); gbcActions.gridy++; - btnOrderAll = new JButton("Order All (" + partCountInfo.getMissingCount() + ")"); + btnOrderAll = new JButton("Order All (" + partCountInfo.getMissingCount() + ')'); btnOrderAll.setToolTipText("Order all missing"); btnOrderAll.setName("btnOrderAll"); btnOrderAll.setVisible(partCountInfo.getMissingCount() > 1); diff --git a/MekHQ/src/mekhq/gui/dialog/AdvanceDaysDialog.java b/MekHQ/src/mekhq/gui/dialog/AdvanceDaysDialog.java index da1300e7489..acc223e921d 100644 --- a/MekHQ/src/mekhq/gui/dialog/AdvanceDaysDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/AdvanceDaysDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2014-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,25 +18,6 @@ */ package mekhq.gui.dialog; -import java.awt.Container; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.time.LocalDate; -import java.time.temporal.ChronoUnit; -import java.time.temporal.IsoFields; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.GroupLayout; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.SpinnerNumberModel; - import megamek.client.ui.baseComponents.MMButton; import megamek.client.ui.preferences.JIntNumberSpinnerPreference; import megamek.client.ui.preferences.PreferencesNode; @@ -46,9 +27,20 @@ import mekhq.campaign.event.ReportEvent; import mekhq.gui.CampaignGUI; import mekhq.gui.DailyReportLogPanel; -import mekhq.gui.baseComponents.AbstractMHQDialog; +import mekhq.gui.baseComponents.AbstractMHQDialogBasic; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.time.temporal.IsoFields; +import java.util.ArrayList; +import java.util.List; -public class AdvanceDaysDialog extends AbstractMHQDialog { +public class AdvanceDaysDialog extends AbstractMHQDialogBasic { private static final MMLogger logger = MMLogger.create(AdvanceDaysDialog.class); // region Variable Declarations diff --git a/MekHQ/src/mekhq/gui/dialog/AutoAwardsDialog.java b/MekHQ/src/mekhq/gui/dialog/AutoAwardsDialog.java index 29a53276637..534984568a8 100644 --- a/MekHQ/src/mekhq/gui/dialog/AutoAwardsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/AutoAwardsDialog.java @@ -145,7 +145,7 @@ private void initComponents() { AutoAwardsTableModel model = new AutoAwardsTableModel(campaign); // This is where we insert the external data logger.info("Trying to pass data to AutoAwardsTableModel.java"); - logger.info("Data being passed: {}", data); + logger.debug("Data being passed: {}", data); model.setData(data); logger.info("Attempt successful"); personnelTable = new AutoAwardsTable(model); diff --git a/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java index 1b5c27bf19d..49193b9df25 100644 --- a/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java @@ -9,7 +9,6 @@ import mekhq.campaign.Campaign; import mekhq.gui.dialog.helpDialogs.AutoResolveBehaviorSettingsHelpDialog; - import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; @@ -38,7 +37,7 @@ public class AutoResolveBehaviorSettingsDialog * @param campaign The campaign to get the auto resolve behavior settings from. */ public AutoResolveBehaviorSettingsDialog(final JFrame frame, final Campaign campaign) { - super(frame, campaign.getName() + ":AI", campaign.getAutoResolveBehaviorSettings(), null); + super(frame, campaign.getName() + "@AI", campaign.getAutoResolveBehaviorSettings(), null); setAlwaysOnTop(true); setCampaign(campaign); } @@ -78,7 +77,7 @@ private void setCampaign(final Campaign campaign) { private void updateBehaviorSettings() { var autoResolveBehaviorSettings = getBehaviorSettings(); try { - autoResolveBehaviorSettings.setDescription(campaign.getName() + ":AI"); + autoResolveBehaviorSettings.setDescription(campaign.getName() + "@AI"); } catch (PrincessException e) { // This should never happen, but if it does, it is not a critical error. // We set the auto resolve behavior setting, ignore that its description diff --git a/MekHQ/src/mekhq/gui/dialog/AutoResolveChanceDialog.java b/MekHQ/src/mekhq/gui/dialog/AutoResolveChanceDialog.java new file mode 100644 index 00000000000..e69de29bb2d diff --git a/MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java b/MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java index 6b69e9bf946..00476751b70 100644 --- a/MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 - The MegaMek Team. All Rights Reserved + * Copyright (c) 2016-2024 - The MegaMek Team. All Rights Reserved * * This file is part of MekHQ. * @@ -19,7 +19,6 @@ package mekhq.gui.dialog; import megamek.client.ui.models.XTableColumnModel; -import megamek.client.ui.preferences.*; import megamek.codeUtilities.MathUtility; import megamek.common.enums.SkillLevel; import megamek.logging.MMLogger; @@ -91,8 +90,6 @@ public BatchXPDialog(JFrame parent, Campaign campaign) { personnelModel.refreshData(); initComponents(); - - setUserPreferences(); } private void initComponents() { @@ -105,41 +102,6 @@ private void initComponents() { setLocationRelativeTo(getParent()); } - private void setUserPreferences() { - try { - PreferencesNode preferences = MekHQ.getMHQPreferences().forClass(BatchXPDialog.class); - - choiceType.setName("primaryRole"); - preferences.manage(new JComboBoxPreference(choiceType)); - - choiceExp.setName("experienceLevel"); - preferences.manage(new JComboBoxPreference(choiceExp)); - - choiceRank.setName("rank"); - preferences.manage(new JComboBoxPreference(choiceRank)); - - onlyOfficers.setName("onlyOfficers"); - preferences.manage(new JToggleButtonPreference(onlyOfficers)); - - noOfficers.setName("noOfficers"); - preferences.manage(new JToggleButtonPreference(noOfficers)); - - choiceSkill.setName("skill"); - preferences.manage(new JComboBoxPreference(choiceSkill)); - - skillLevel.setName("skillLevel"); - preferences.manage(new JIntNumberSpinnerPreference(skillLevel)); - - allowPrisoners.setName("allowPrisoners"); - preferences.manage(new JToggleButtonPreference(allowPrisoners)); - - this.setName("dialog"); - preferences.manage(new JWindowPreference(this)); - } catch (Exception ex) { - logger.error("Failed to set user preferences", ex); - } - } - private JComponent getPersonnelTable() { personnelTable = new JTable(personnelModel); personnelTable.setCellSelectionEnabled(false); @@ -299,7 +261,7 @@ private JComponent getButtonPanel() { skillLevel.setEnabled(true); ((SpinnerNumberModel) skillLevel.getModel()).setMaximum(maxSkillLevel); skillLevel.getModel().setValue( - MathUtility.clamp((Integer) skillLevel.getModel().getValue(), 0, maxSkillLevel)); + MathUtility.clamp((Integer) skillLevel.getModel().getValue(), 1, maxSkillLevel)); buttonSpendXP.setEnabled(true); } } @@ -310,7 +272,7 @@ private JComponent getButtonPanel() { panel.add(Box.createRigidArea(new Dimension(10, 10))); panel.add(new JLabel(resourceMap.getString("targetSkillLevel.text"))); - skillLevel = new JSpinner(new SpinnerNumberModel(10, 0, 10, 1)); + skillLevel = new JSpinner(new SpinnerNumberModel(10, 1, 10, 1)); skillLevel.setMaximumSize(new Dimension(Short.MAX_VALUE, (int) skillLevel.getPreferredSize().getHeight())); skillLevel.addChangeListener(evt -> { personnelFilter.setMaxSkillLevel((Integer) skillLevel.getModel().getValue()); diff --git a/MekHQ/src/mekhq/gui/dialog/CampaignExportWizard.java b/MekHQ/src/mekhq/gui/dialog/CampaignExportWizard.java index d3e4c5db454..38b8a6ef70b 100644 --- a/MekHQ/src/mekhq/gui/dialog/CampaignExportWizard.java +++ b/MekHQ/src/mekhq/gui/dialog/CampaignExportWizard.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2019-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -46,6 +46,7 @@ import mekhq.campaign.Campaign; import mekhq.campaign.CampaignFactory; import mekhq.campaign.Kill; +import mekhq.campaign.enums.CampaignTransportType; import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.force.Force; @@ -516,11 +517,15 @@ private boolean exportToCampaign(File file) { // Reset any transport assignments, as the export may not contain all transports // and cargo units unit.setTransportShipAssignment(null); + unit.setTransportShipAssignment(null); - if (unit.hasTransportedUnits()) { - unit.unloadTransportShip(); + for (CampaignTransportType campaignTransportType : CampaignTransportType.values()) { + if (unit.hasTransportedUnits(campaignTransportType)) { + unit.unloadTransport(campaignTransportType); + } } + // make an attempt to re-construct the force structure in the destination // campaign // or assign the unit to the same force diff --git a/MekHQ/src/mekhq/gui/dialog/CampaignHasProblemOnLoad.java b/MekHQ/src/mekhq/gui/dialog/CampaignHasProblemOnLoad.java new file mode 100644 index 00000000000..93f49c8f1bf --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/CampaignHasProblemOnLoad.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2025 - 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.gui.dialog; + +import megamek.common.annotations.Nullable; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignFactory.CampaignProblemType; +import mekhq.campaign.personnel.Person; +import mekhq.gui.baseComponents.MHQDialogImmersive; + +import java.util.List; + +import static mekhq.campaign.Campaign.AdministratorSpecialization.COMMAND; +import static mekhq.campaign.CampaignFactory.CampaignProblemType.CANT_LOAD_FROM_NEWER_VERSION; +import static mekhq.utilities.MHQInternationalization.getFormattedTextAt; + +/** + * Dialog to inform and handle campaign-loading problems within MekHQ. + * + *

    This dialog prompts the user with both in-character and out-of-character messages, + * providing actionable options if any issues arise during campaign load, such as version + * incompatibility or missing contracts.

    + * + *

    The dialog presents two options: "Cancel" to abort loading the campaign or + * "Continue Regardless" to proceed despite the detected issues. It dynamically generates + * text based on the problem type and campaign information.

    + */ +public class CampaignHasProblemOnLoad extends MHQDialogImmersive { + private static final String RESOURCE_BUNDLE = "mekhq.resources.CampaignHasProblemOnLoad"; + + /** + * Constructs the dialog to handle campaign load problems. + * + *

    The dialog is built using localized messages for both + * in-character and out-of-character messages, following the detected + * problem type and campaign details. It also sets up predefined + * buttons for user interaction.

    + * + * @param campaign the {@link Campaign} for which the load problem dialog is presented + * @param problemType the {@link CampaignProblemType} specifying the nature of the load problem + */ + public CampaignHasProblemOnLoad(Campaign campaign, CampaignProblemType problemType) { + super(campaign, getSpeaker(campaign), null, createInCharacterMessage(campaign, problemType), + createButtons(problemType), createOutOfCharacterMessage(problemType), null); + } + + /** + * Generates the list of buttons for the dialog based on the specified problem type. + * + *

    Buttons include:

    + *
      + *
    • "Cancel" button: Stops loading the campaign in all scenarios.
    • + *
    • "Continue" button: Allows proceeding with loading the campaign, provided the problem type permits it.
    • + *
    + * + *

    If the problem type is {@code CANT_LOAD_FROM_NEWER_VERSION}, only the "Cancel" button is available + * because proceeding is not allowed for this issue. For other problem types, both "Cancel" and "Continue" + * buttons are included in the dialog.

    + * + * @param problemType the {@link CampaignProblemType} specifying the nature of the issue, which determines + * the buttons to display + * @return a {@link List} of {@link ButtonLabelTooltipPair} objects representing the dialog's buttons + */ + private static List createButtons(CampaignProblemType problemType) { + ButtonLabelTooltipPair btnCancel = new ButtonLabelTooltipPair( + getFormattedTextAt(RESOURCE_BUNDLE, "cancel.button"), null); + + ButtonLabelTooltipPair btnContinue = new ButtonLabelTooltipPair( + getFormattedTextAt(RESOURCE_BUNDLE, "continue.button"), null); + + if (problemType == CANT_LOAD_FROM_NEWER_VERSION) { + return List.of(btnCancel); + } else { + return List.of(btnCancel, btnContinue); + } + } + + /** + * Retrieves the speaker for in-character dialog. + * + *

    The speaker is determined as the senior administrator for the campaign + * with the "Command" specialization. If no such administrator is found, {@code null} + * is returned.

    + * + * @param campaign the {@link Campaign} whose senior administrator is to be retrieved + * @return a {@link Person} representing the senior administrator, or {@code null} if none exists + */ + private static @Nullable Person getSpeaker(Campaign campaign) { + return campaign.getSeniorAdminPerson(COMMAND); + } + + /** + * Creates the in-character message dynamically based on the problem type. + * + *

    This message is localized and assembled using resource bundles, with campaign-specific + * information such as the commander's address.

    + * + * @param campaign the {@link Campaign} for which the in-character message is generated + * @param problemType the {@link CampaignProblemType} specifying the nature of the load problem + * @return a localized {@link String} containing the in-character message + */ + private static String createInCharacterMessage(Campaign campaign, CampaignProblemType problemType) { + String typeKey = problemType.toString(); + String commanderAddress = campaign.getCommanderAddress(false); + + return getFormattedTextAt(RESOURCE_BUNDLE, typeKey + ".message", commanderAddress); + } + + /** + * Creates the out-of-character message dynamically based on the problem type. + * + *

    This message is localized and is more technical or process-oriented, + * explaining the detected issues in plain terms.

    + * + * @param problemType the {@link CampaignProblemType} specifying the nature of the load problem + * @return a localized {@link String} containing the out-of-character message + */ + private static String createOutOfCharacterMessage(CampaignProblemType problemType) { + String typeKey = problemType.toString(); + return getFormattedTextAt(RESOURCE_BUNDLE, typeKey + ".ooc"); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java b/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java deleted file mode 100644 index 899912d1f08..00000000000 --- a/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2009-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 2 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.gui.dialog; - -import megamek.client.ui.baseComponents.MMButton; -import megamek.client.ui.enums.DialogResult; -import megamek.client.ui.enums.ValidationState; -import megamek.common.annotations.Nullable; -import mekhq.MekHQ; -import mekhq.campaign.Campaign; -import mekhq.campaign.CampaignPreset; -import mekhq.gui.FileDialogs; -import mekhq.gui.baseComponents.AbstractMHQValidationButtonDialog; -import mekhq.gui.panes.CampaignOptionsPane; -import mekhq.gui.panes.campaignOptions.SelectPresetDialog; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.util.ResourceBundle; - -import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CANCELLED; - -/** - * @author Jay Lawson (jaylawson39 at yahoo.com) (Original Version, now largely CampaignOptionsPane) - * @author Justin 'Windchild' Bowen (Current Version) - */ -public class CampaignOptionsDialog extends AbstractMHQValidationButtonDialog { - //region Variable Declarations - private final Campaign campaign; - private final boolean startup; - private CampaignOptionsPane campaignOptionsPane; - //endregion Variable Declarations - - //region Constructors - public CampaignOptionsDialog(final JFrame frame, final Campaign campaign, final boolean startup) { - super(frame, true, ResourceBundle.getBundle("mekhq.resources.CampaignOptionsDialog", - MekHQ.getMHQOptions().getLocale()), - "CampaignOptionsDialog", "CampaignOptionsDialog.title"); - this.campaign = campaign; - this.startup = startup; - initialize(); - } - - /** - * Allows dialog to be constructed with an owner being another dialog - */ - public CampaignOptionsDialog(final JDialog owner, final JFrame frame, final Campaign campaign, final boolean startup) { - super(owner, frame, true, ResourceBundle.getBundle("mekhq.resources.CampaignOptionsDialog", - MekHQ.getMHQOptions().getLocale()), - "CampaignOptionsDialog", "CampaignOptionsDialog.title"); - this.campaign = campaign; - this.startup = startup; - initialize(); - } - //endregion Constructors - - //region Getters/Setters - public Campaign getCampaign() { - return campaign; - } - - public boolean isStartup() { - return startup; - } - - public CampaignOptionsPane getCampaignOptionsPane() { - return campaignOptionsPane; - } - - public void setCampaignOptionsPane(final CampaignOptionsPane campaignOptionsPane) { - this.campaignOptionsPane = campaignOptionsPane; - } - //endregion Getters/Setters - - //region Initialization - @Override - protected Container createCenterPane() { - setCampaignOptionsPane(new CampaignOptionsPane(getFrame(), getCampaign(), isStartup())); - return getCampaignOptionsPane(); - } - - @Override - protected JPanel createButtonPanel() { - final JPanel panel = new JPanel(new GridLayout(1, 0)); - - panel.add(new MMButton("btnOkay", resources, "Confirm.text", null, - this::okButtonPerformedShowStratConNotice)); - - panel.add(new MMButton("btnSavePreset", resources, "btnSavePreset.text", - "btnSavePreset.toolTipText", evt -> btnSaveActionPerformed())); - - panel.add(new MMButton("btnLoadPreset", resources, "btnLoadPreset.text", - "btnLoadPreset.toolTipText", evt -> { - final SelectPresetDialog presetSelectionDialog = - new SelectPresetDialog(getFrame(), true, false); - if (presetSelectionDialog.getReturnState() != PRESET_SELECTION_CANCELLED) { - applyPreset(presetSelectionDialog.getSelectedPreset()); - } - })); - - panel.add(new MMButton("btnCancel", resources, "Cancel.text", "Cancel.toolTipText", - this::cancelActionPerformed)); - - return panel; - } - - /** - * Performs the action when the Ok button is clicked, which includes invoking the - * {@link #okButtonActionPerformed(ActionEvent)} method and then the {@link #showStratConNotice()} method. - * - * @param e the ActionEvent triggering this method - */ - private void okButtonPerformedShowStratConNotice(ActionEvent e) { - okButtonActionPerformed(e); - showStratConNotice(); - } - - /** - * Displays a promo introducing users to StratCon. - * This method shows the promo only when the Campaign Options pane is closed - * and the current day is the first day of the campaign. - */ - private void showStratConNotice() { - // we don't store whether this dialog has previously appeared, - // instead we just have it appear only when Campaign Options is closed, - // the current day is the first day of the campaign, and StratCon is enabled - if (!campaign.getCampaignOptions().isUseStratCon() || !campaign.getLocalDate().equals(campaign.getCampaignStartDate())) { - return; - } - - ImageIcon imageIcon = new ImageIcon("data/images/stratcon/stratConPromo.png"); - JLabel imageLabel = new JLabel(imageIcon); - JPanel imagePanel = new JPanel(new GridBagLayout()); - imagePanel.add(imageLabel); - - String title = resources.getString("stratConPromo.title"); - - String message = resources.getString("stratConPromo.message"); - JLabel messageLabel = new JLabel(message); - JPanel messagePanel = new JPanel(new GridBagLayout()); - messagePanel.add(messageLabel); - - JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); - panel.add(imagePanel); - panel.add(messagePanel); - - Object[] options = { - resources.getString("stratConPromo.button") - }; - - JOptionPane.showOptionDialog(null, panel, title, JOptionPane.DEFAULT_OPTION, - JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); - } - - @Override - protected void finalizeInitialization() throws Exception { - getCampaignOptionsPane().setOptions(getCampaign().getCampaignOptions(), - getCampaign().getRandomSkillPreferences()); - super.finalizeInitialization(); - } - //endregion Initialization - - //region Button Actions - @Override - protected void okAction() { - getCampaignOptionsPane().updateOptions(); - } - - @Override - protected ValidationState validateAction(final boolean display) { - return getCampaignOptionsPane().validateOptions(display); - } - - private void btnSaveActionPerformed() { - if (validateAction(true).isFailure()) { - return; - } - getCampaignOptionsPane().updateOptions(); - setResult(DialogResult.CONFIRMED); - - final CreateCampaignPresetDialog createCampaignPresetDialog - = new CreateCampaignPresetDialog(getFrame(), getCampaign(), null); - if (!createCampaignPresetDialog.showDialog().isConfirmed()) { - setVisible(false); - return; - } - final CampaignPreset preset = createCampaignPresetDialog.getPreset(); - if (preset == null) { - setVisible(false); - return; - } - preset.writeToFile(getFrame(), - FileDialogs.saveCampaignPreset(getFrame(), preset).orElse(null)); - setVisible(false); - } - //endregion Button Actions - - public void applyPreset(final @Nullable CampaignPreset preset) { - getCampaignOptionsPane().applyPreset(preset); - } -} diff --git a/MekHQ/src/mekhq/gui/dialog/CampaignPresetSelectionDialog.java b/MekHQ/src/mekhq/gui/dialog/CampaignPresetSelectionDialog.java new file mode 100644 index 00000000000..e69de29bb2d diff --git a/MekHQ/src/mekhq/gui/dialog/ContractAutomationDialog.java b/MekHQ/src/mekhq/gui/dialog/ContractAutomationDialog.java new file mode 100644 index 00000000000..eeafff216e8 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/ContractAutomationDialog.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2024-2025 - 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.gui.dialog; + +import megamek.client.ui.swing.util.UIUtil; +import mekhq.campaign.Campaign; +import mekhq.campaign.Campaign.AdministratorSpecialization; +import mekhq.campaign.personnel.Person; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; + +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerDescription; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +public class ContractAutomationDialog extends JDialog { + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + final int INSERT_SIZE = UIUtil.scaleForGUI(10); + + private boolean dialogConfirmed = false; + + public boolean isDialogConfirmed() { + return dialogConfirmed; + } + + private final static ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.ContractAutomation"); + + public ContractAutomationDialog(Campaign campaign, String message, boolean includeAddendum) { + setTitle(resources.getString("incomingTransmission.title")); + + // Main Panel to hold both boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + // Left box for speaker details + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get speaker details + Person speaker = campaign.getSeniorAdminPerson(AdministratorSpecialization.TRANSPORT); + + String speakerName; + if (speaker != null) { + speakerName = speaker.getFullTitle(); + } else { + speakerName = campaign.getName(); + } + + // Add speaker image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, speaker, speakerName); + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, INSERT_SIZE))); + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + mainPanel.add(leftBox, constraints); + + // Right box: Just a message + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + + JLabel rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, message)); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + constraints.gridx = 1; + constraints.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, constraints); + + add(mainPanel, BorderLayout.CENTER); + + // Create a container panel to hold both the button panel and the new panel + JPanel containerPanel = new JPanel(); + containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS)); // Stack vertically + + // Buttons panel + JPanel buttonPanel = new JPanel(); + + JButton confirmButton = new JButton(resources.getString("generalConfirm.text")); + confirmButton.addActionListener(e -> { + dialogConfirmed = true; + dispose(); + }); + buttonPanel.add(confirmButton); + + JButton declineButton = new JButton(resources.getString("generalDecline.text")); + declineButton.addActionListener(e -> dispose()); + buttonPanel.add(declineButton); + + // Add the button panel to the container + containerPanel.add(buttonPanel); + + if (includeAddendum) { + // New panel (to be added below the button panel) + JPanel infoPanel = new JPanel(new BorderLayout()); + JLabel lblInfo = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH + LEFT_WIDTH, + String.format(resources.getString("mothballDescription.addendum")))); + lblInfo.setHorizontalAlignment(SwingConstants.CENTER); + infoPanel.add(lblInfo, BorderLayout.CENTER); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + + // Add the new panel to the container (below the button panel) + containerPanel.add(infoPanel); + } + + // Add the container panel to the dialog (at the bottom of the layout) + add(containerPanel, BorderLayout.SOUTH); + + // Dialog settings + pack(); + setModal(true); + setLocationRelativeTo(null); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java index 2edd56216f0..772218cad12 100644 --- a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java @@ -34,6 +34,7 @@ import mekhq.campaign.mission.Contract; import mekhq.campaign.universe.Factions; import mekhq.gui.FactionComboBox; +import mekhq.gui.dialog.resupplyAndCaches.DialogContractStart; import mekhq.gui.sorter.FormattedNumberSorter; import mekhq.gui.sorter.IntegerStringSorter; import mekhq.gui.utilities.JScrollPaneWithSpeed; @@ -260,6 +261,11 @@ private void initComponents() { final int days = (int) Math.ceil(path.getTotalTime(c.getStartDate(), campaign.getLocation().getTransitTime())); row.add(Integer.toString(days)); + row.add(String.valueOf(c.getLength())); + row.add(c.getTransportCompString()); + row.add(c.getSalvagePctString()); + row.add(c.getStraightSupportString()); + row.add(c.getBattleLossCompString()); row.add(c.getEstimatedTotalProfit(campaign).toAmountAndSymbolString()); data.add(row); } @@ -269,6 +275,11 @@ private void initComponents() { colNames.add("Enemy"); colNames.add("Mission Type"); colNames.add("Transit Time"); + colNames.add("Contract Length (months)"); + colNames.add("Transport Terms"); + colNames.add("Salvage Rights"); + colNames.add("Straight Support"); + colNames.add("Battle Loss Compensation"); colNames.add("Estimated Profit"); tableContracts = new JTable(); @@ -410,6 +421,11 @@ public boolean isCellEditable(int row, int column) { final int days = (int) Math.ceil(path.getTotalTime(c.getStartDate(), campaign.getLocation().getTransitTime())); row.add(Integer.toString(days)); + row.add(String.valueOf(c.getLength())); + row.add(c.getTransportCompString()); + row.add(c.getSalvagePctString()); + row.add(c.getStraightSupportString()); + row.add(c.getBattleLossCompString()); row.add(c.getEstimatedTotalProfit(campaign).toAmountAndSymbolString()); ((DefaultTableModel) tableContracts.getModel()).addRow(row); }); @@ -480,6 +496,10 @@ private void acceptContract(ActionEvent evt) { if (!triggerConfirmationDialog()) { return; } + + if (campaign.getCampaignOptions().isUseStratCon()) { + new DialogContractStart(campaign, (AtBContract) selectedContract); + } } selectedContract.setName(contractView.getContractName()); diff --git a/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java b/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java index a2c49d2baa8..80cd66abffc 100644 --- a/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java @@ -190,7 +190,7 @@ public Component getListCellRendererComponent(final JList list, final Object JLabel lblEnemyRating = new JLabel(); JLabel lblRequiredLances = new JLabel(); - int requiredLances = contract.getRequiredLances() > 0 ? contract.getRequiredLances() : 1; + int requiredLances = contract.getRequiredCombatTeams() > 0 ? contract.getRequiredCombatTeams() : 1; spnRequiredLances = new JSpinner(new SpinnerNumberModel(requiredLances, 1, null, 1)); JLabel lblEnemyMorale = new JLabel(); @@ -574,7 +574,7 @@ private void btnOKActionPerformed(ActionEvent evt) { contract.setAllyQuality(cbAllyQuality.getSelectedIndex()); contract.setEnemySkill(comboEnemySkill.getSelectedItem()); contract.setEnemyQuality(cbEnemyQuality.getSelectedIndex()); - contract.setRequiredLances((Integer) spnRequiredLances.getValue()); + contract.setRequiredCombatTeams((Integer) spnRequiredLances.getValue()); contract.setMoraleLevel(comboEnemyMorale.getSelectedItem()); contract.setContractScoreArbitraryModifier((Integer) spnContractScoreArbitraryModifier.getValue()); contract.setBaseAmount(txtBasePay.getMoney()); diff --git a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java index d48946b4a2b..c907b99c003 100644 --- a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2009-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,16 +21,18 @@ import megamek.client.generator.RandomCallsignGenerator; import megamek.client.generator.RandomNameGenerator; import megamek.client.ui.swing.util.UIUtil; +import megamek.common.Entity; +import megamek.common.GunEmplacement; import megamek.common.MekSummaryCache; import megamek.common.annotations.Nullable; import megamek.common.options.OptionsConstants; import megamek.logging.MMLogger; +import mekhq.CampaignPreset; import mekhq.MHQStaticDirectoryManager; import mekhq.MekHQ; import mekhq.NullEntityException; import mekhq.campaign.Campaign; import mekhq.campaign.CampaignFactory; -import mekhq.campaign.CampaignPreset; import mekhq.campaign.event.OptionsChangedEvent; import mekhq.campaign.finances.CurrencyManager; import mekhq.campaign.finances.financialInstitutions.FinancialInstitutions; @@ -44,12 +46,15 @@ import mekhq.campaign.rating.CamOpsReputation.ReputationController; import mekhq.campaign.storyarc.StoryArc; import mekhq.campaign.storyarc.StoryArcStub; +import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.RATManager; import mekhq.campaign.universe.Systems; import mekhq.campaign.universe.eras.Eras; -import mekhq.gui.baseComponents.AbstractMHQDialog; -import mekhq.gui.panes.campaignOptions.SelectPresetDialog; +import mekhq.gui.baseComponents.AbstractMHQDialogBasic; +import mekhq.gui.campaignOptions.CampaignOptionsDialog; +import mekhq.gui.campaignOptions.CampaignOptionsDialog.CampaignOptionsDialogMode; +import mekhq.gui.campaignOptions.SelectPresetDialog; import javax.swing.*; import java.awt.*; @@ -58,14 +63,18 @@ import java.io.File; import java.io.FileInputStream; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; -import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CANCELLED; -import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CUSTOMIZE; -import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_SELECT; +import static mekhq.gui.campaignOptions.CampaignOptionsDialog.CampaignOptionsDialogMode.ABRIDGED; +import static mekhq.gui.campaignOptions.CampaignOptionsDialog.CampaignOptionsDialogMode.STARTUP; +import static mekhq.gui.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CANCELLED; +import static mekhq.gui.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CUSTOMIZE; +import static mekhq.gui.campaignOptions.SelectPresetDialog.PRESET_SELECTION_SELECT; -public class DataLoadingDialog extends AbstractMHQDialog implements PropertyChangeListener { +public class DataLoadingDialog extends AbstractMHQDialogBasic implements PropertyChangeListener { private static final MMLogger logger = MMLogger.create(DataLoadingDialog.class); // region Variable Declarations @@ -75,21 +84,25 @@ public class DataLoadingDialog extends AbstractMHQDialog implements PropertyChan private JLabel splash; private JProgressBar progressBar; private StoryArcStub storyArcStub; + private boolean isInAppNewCampaign; + + private final LocalDate DEFAULT_START_DATE = LocalDate.of(3051, 1, 1); // endregion Variable Declarations // region Constructors public DataLoadingDialog(final JFrame frame, final MekHQ application, final @Nullable File campaignFile) { - this(frame, application, campaignFile, null); + this(frame, application, campaignFile, null, false); } public DataLoadingDialog(final JFrame frame, final MekHQ application, - final @Nullable File campaignFile, StoryArcStub stub) { + final @Nullable File campaignFile, StoryArcStub stub, final boolean isInAppNewCampaign) { super(frame, "DataLoadingDialog", "DataLoadingDialog.title"); this.application = application; this.campaignFile = campaignFile; this.storyArcStub = stub; + this.isInAppNewCampaign = isInAppNewCampaign; this.task = new Task(this); getTask().addPropertyChangeListener(this); initialize(); @@ -159,7 +172,7 @@ protected void finalizeInitialization() { setSize(getSplash().getPreferredSize()); pack(); fitAndCenter(); - getFrame().setVisible(false); + getFrame().setVisible(true); } // endregion Initialization @@ -295,6 +308,9 @@ public Campaign doInBackground() throws Exception { switch (presetSelectionDialog.getReturnState()) { case PRESET_SELECTION_CANCELLED -> { + if (isInAppNewCampaign) { + application.exit(false); + } return null; } case PRESET_SELECTION_SELECT -> { @@ -306,44 +322,26 @@ public Campaign doInBackground() throws Exception { + presetSelectionDialog.getReturnState()); } - // Date - final LocalDate date = ((preset == null) || (preset.getDate() == null)) - ? campaign.getLocalDate() - : preset.getDate(); - final DateChooser dc = new DateChooser(dialog, date); - dc.setLocationRelativeTo(getFrame()); - // user can either choose a date or cancel by closing - if (dc.showDateChooser() != DateChooser.OK_OPTION) { - return null; - } - + // MegaMek Options if ((preset != null) && (preset.getGameOptions() != null)) { campaign.setGameOptions(preset.getGameOptions()); } - // This must be after the date chooser to enable correct functionality. - setVisible(false); - // Campaign Options - if (isSelect && preset != null) { - preset.applyContinuousToCampaign(campaign); - - // This needs to be after we've applied the preset - campaign.setLocalDate(dc.getDate()); - campaign.getGameOptions().getOption(OptionsConstants.ALLOWED_YEAR).setValue(campaign.getGameYear()); - campaign.setStartingSystem(preset.getPlanet()); + // This needs to be before we trigger the customize preset dialog + campaign.setLocalDate(DEFAULT_START_DATE); + campaign.getGameOptions().getOption(OptionsConstants.ALLOWED_YEAR).setValue(campaign.getGameYear()); + campaign.setStartingSystem((preset == null) ? null : preset.getPlanet()); + + CampaignOptionsDialogMode mode = isSelect ? ABRIDGED : STARTUP; + CampaignOptionsDialog optionsDialog = + new CampaignOptionsDialog(getFrame(), campaign, preset, mode); + setVisible(false); // cede visibility to `optionsDialog` + optionsDialog.setVisible(true); + if (optionsDialog.wasCanceled()) { + return null; } else { - // This needs to be before we trigger the customize preset dialog - campaign.setLocalDate(dc.getDate()); - campaign.getGameOptions().getOption(OptionsConstants.ALLOWED_YEAR).setValue(campaign.getGameYear()); - campaign.setStartingSystem((preset == null) ? null : preset.getPlanet()); - - CampaignOptionsDialog optionsDialog = new CampaignOptionsDialog(dialog, getFrame(), campaign, true); - optionsDialog.setLocationRelativeTo(getFrame()); - optionsDialog.applyPreset(preset); - if (optionsDialog.showDialog().isCancelled()) { - return null; - } + setVisible(true); // restore loader visibility } // initialize reputation @@ -416,12 +414,60 @@ public Campaign doInBackground() throws Exception { reputationController.initializeReputation(campaign); campaign.setReputation(reputationController); } + + sellUnsupportedUnits(campaign); // endregion Progress 7 } campaign.setApp(getApplication()); return campaign; } + + + /** + * Sells unsupported units. + *

    + * This method checks all units in the specified campaign and determines if they are unsupported. + * Currently, only gun emplacements are identified as unsupported and are sold upon campaign load. + * If unsupported units are sold, a notification is displayed to the user detailing the sold units, + * including their names and IDs. Additionally, personnel assigned to these units are + * automatically unassigned. + *

    + * + * @param retVal The campaign being checked for unsupported units. This includes the unit list + * and the quartermaster who handles the selling process. + */ + private void sellUnsupportedUnits(Campaign retVal) { + List soldUnits = new ArrayList<>(); + for (Unit unit : retVal.getUnits()) { + Entity entity = unit.getEntity(); + + if (entity == null) { + continue; + } + + // We don't support Gun Emplacements in mhq. + // If the user has somehow acquired one, it is sold on load. + if (entity instanceof GunEmplacement) { + soldUnits.add(unit); + } + } + + if (!soldUnits.isEmpty()) { + StringBuilder message = new StringBuilder(resources.getString("unsupportedUnits.body")); + + for (Unit soldUnit : soldUnits) { + retVal.getQuartermaster().sellUnit(soldUnit); + message.append("- ").append(soldUnit.getName()).append("
    "); + } + + JOptionPane.showMessageDialog(null, + String.format("%s", message), + resources.getString("unsupportedUnits.title"), + JOptionPane.WARNING_MESSAGE); + } + } + /** * Executed in event dispatching thread */ diff --git a/MekHQ/src/mekhq/gui/dialog/DateChooser.java b/MekHQ/src/mekhq/gui/dialog/DateChooser.java index eb4cd937661..8db1e9eaa81 100644 --- a/MekHQ/src/mekhq/gui/dialog/DateChooser.java +++ b/MekHQ/src/mekhq/gui/dialog/DateChooser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2024-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -68,7 +68,7 @@ public class DateChooser extends JDialog implements ActionListener, FocusListene public static final int OK_OPTION = 1; public static final int CANCEL_OPTION = 2; - private static String RESOURCE_PACKAGE = "mekhq/resources/DateChooser"; + private static final String RESOURCE_PACKAGE = "mekhq/resources/DateChooser"; private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE, MekHQ.getMHQOptions().getLocale()); @@ -200,7 +200,7 @@ private void initialize(Component owner, LocalDate date) { contentPane.add(dayGrid, BorderLayout.CENTER); // Create the date label - JLabel dateLabel = new JLabel(resources.getString("dateField.text"), JLabel.CENTER); + JLabel dateLabel = new JLabel(String.format(resources.getString("dateField.text"))); // Set up the date input text field with the current campaign date. dateField = new JFormattedTextField(this.date); @@ -273,6 +273,7 @@ public String valueToString(Object value) { // center this dialog over the owner setLocationRelativeTo(owner); setUserPreferences(); + setAlwaysOnTop(true); } @Deprecated // These need to be migrated to the Suite Constants / Suite Options Setup @@ -565,14 +566,34 @@ public void keyReleased(KeyEvent event) {} * @return {@link true} if the update is successful, {@link false} otherwise. */ private boolean updateDateFromDateField() { + final LocalDate MINIMUM_VIABLE_DATE = LocalDate.of(2200, 1, 1); + final LocalDate MAXIMUM_VIABLE_DATE = LocalDate.of(4500, 1, 1); + LocalDate newDate = parseDate(dateField.getText()); - if (newDate == null) { - JOptionPane.showMessageDialog(this, - "Invalid Date Format\nTry: yyyy-MM-dd", "Date Format", - JOptionPane.WARNING_MESSAGE); + + String message = String.format(resources.getString("invalidDate.body"), + MINIMUM_VIABLE_DATE, MAXIMUM_VIABLE_DATE); + + if (newDate == null + || newDate.isBefore(MINIMUM_VIABLE_DATE) + || newDate.isAfter(MAXIMUM_VIABLE_DATE)) { + setVisible(false); + + // Creating JOptionPane as a message box + JOptionPane optionPane = new JOptionPane( + message, + JOptionPane.WARNING_MESSAGE, + JOptionPane.DEFAULT_OPTION + ); + + // Creating a JDialog from the optionPane and setting it always on top + JDialog dialog = optionPane.createDialog(resources.getString("invalidDate.title")); + dialog.setAlwaysOnTop(true); + dialog.setVisible(true); + + setVisible(true); return false; } - setDate(newDate); return true; } @@ -621,7 +642,11 @@ private JButton createEraButton(int era) { JButton button = new JButton(label); button.setToolTipText(wordWrap(resources.getString(reference + ".tooltip"))); button.setHorizontalAlignment(SwingConstants.CENTER); - button.addActionListener(e -> turningPointsDialog(era, reference)); + button.addActionListener(e -> { + setVisible(false); + turningPointsDialog(era, reference); + setVisible(true); + }); return button; } @@ -676,7 +701,8 @@ private void turningPointsDialog(int era, String reference) { turningPointsDialog.getContentPane().add(descriptionAndButtonsPanel, BorderLayout.CENTER); // set turningPointsDialog size and location, and make it visible - setResizable(false); + turningPointsDialog.setResizable(false); + turningPointsDialog.setAlwaysOnTop(true); turningPointsDialog.pack(); turningPointsDialog.setMinimumSize(turningPointsDialog.getSize()); turningPointsDialog.setLocationRelativeTo(null); @@ -732,7 +758,7 @@ private static TurningPoints getTurningPoints(int era) { eraLogo = new ImageIcon(LOGO_DIRECTORY + "era_sw" + LOGO_FILE_TYPE); } case 4 -> { - turningPoints = List.of("ThirdSuccessionWarEnds", "ForthSuccessionWar", "FRRFounded", + turningPoints = List.of("ThirdSuccessionWarEnds", "FourthSuccessionWar", "FRRFounded", "WarOf3039"); turningPointDates = List.of( LocalDate.of(3025, 1, 1), diff --git a/MekHQ/src/mekhq/gui/dialog/ForceTemplateAssignmentDialog.java b/MekHQ/src/mekhq/gui/dialog/ForceTemplateAssignmentDialog.java index 0f109889bef..91b57b2f1c8 100644 --- a/MekHQ/src/mekhq/gui/dialog/ForceTemplateAssignmentDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ForceTemplateAssignmentDialog.java @@ -192,7 +192,7 @@ private void assignUnitToTemplate() { currentScenario.addUnit(unit.getId(), templateList.getSelectedValue().getForceName()); unit.setScenarioId(currentScenario.getId()); MekHQ.triggerEvent(new DeploymentChangedEvent(unit, currentScenario)); - if (unit.hasTransportedUnits()) { + if (unit.hasShipTransportedUnits()) { // Prompt the player to also deploy any units transported by this one deployTransportedUnitsDialog(unit); } @@ -217,7 +217,7 @@ private void assignForceToTemplate() { u.setScenarioId(currentScenario.getId()); // If your force includes transports with units assigned, // prompt the player to also deploy any units transported by this one - if (u.hasTransportedUnits()) { + if (u.hasShipTransportedUnits()) { deployTransportedUnitsDialog(u); } } @@ -242,13 +242,13 @@ private void deployTransportedUnitsDialog(Unit unit) { } private void deployTransportedUnits(final Unit unit) { - for (final Unit cargo : unit.getTransportedUnits()) { + for (final Unit cargo : unit.getShipTransportedUnits()) { currentScenario.removeUnit(cargo.getId()); currentScenario.addUnit(cargo.getId(), templateList.getSelectedValue().getForceName()); cargo.setScenarioId(currentScenario.getId()); MekHQ.triggerEvent(new DeploymentChangedEvent(cargo, currentScenario)); - if (cargo.hasTransportedUnits()) { + if (cargo.hasShipTransportedUnits()) { deployTransportedUnits(cargo); } } diff --git a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java index e08903f31bf..d5b636128e2 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-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2013-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -43,7 +43,7 @@ import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Factions; import mekhq.gui.CampaignGUI; -import mekhq.gui.baseComponents.AbstractMHQDialog; +import mekhq.gui.baseComponents.AbstractMHQDialogBasic; import mekhq.gui.baseComponents.AbstractMHQScrollablePanel; import mekhq.gui.baseComponents.DefaultMHQScrollablePanel; import mekhq.gui.displayWrappers.ClanDisplay; @@ -64,7 +64,7 @@ import static mekhq.campaign.personnel.backgrounds.BackgroundsController.randomMercenaryCompanyNameGenerator; -public class GMToolsDialog extends AbstractMHQDialog { +public class GMToolsDialog extends AbstractMHQDialogBasic { private static final MMLogger logger = MMLogger.create(GMToolsDialog.class); // region Variable Declarations diff --git a/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java b/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java index be4e1940c19..0257d716d5d 100644 --- a/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java @@ -23,6 +23,7 @@ import megamek.client.ui.swing.UnitLoadingDialog; import megamek.client.ui.swing.dialog.AbstractUnitSelectorDialog; import megamek.common.*; +import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.unit.UnitOrder; @@ -32,8 +33,12 @@ import java.awt.*; import java.util.ArrayList; import java.util.List; +import java.util.ResourceBundle; import java.util.regex.PatternSyntaxException; +import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; + public class MekHQUnitSelectorDialog extends AbstractUnitSelectorDialog { //region Variable Declarations private Campaign campaign; @@ -125,6 +130,20 @@ protected JPanel createButtonsPanel() { @Override protected void select(boolean isGM) { if (getSelectedEntity() != null) { + // Block the purchase if the unit type is unsupported + if (selectedUnit.getEntity() instanceof GunEmplacement) { + final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.CampaignGUI", + MekHQ.getMHQOptions().getLocale()); + + campaign.addReport(String.format( + resources.getString("mekSelectorDialog.unsupported.gunEmplacement"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + + dispose(); + return; + } + if (isGM) { PartQuality quality = PartQuality.QUALITY_D; diff --git a/MekHQ/src/mekhq/gui/dialog/NewAtBContractDialog.java b/MekHQ/src/mekhq/gui/dialog/NewAtBContractDialog.java index d3e5fe1d394..e76d95789a0 100644 --- a/MekHQ/src/mekhq/gui/dialog/NewAtBContractDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/NewAtBContractDialog.java @@ -20,17 +20,6 @@ */ package mekhq.gui.dialog; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.util.HashSet; -import java.util.ResourceBundle; -import java.util.Set; - -import javax.swing.*; - import megamek.client.ui.baseComponents.MMComboBox; import megamek.client.ui.preferences.JWindowPreference; import megamek.client.ui.preferences.PreferencesNode; @@ -52,6 +41,15 @@ import mekhq.gui.utilities.JSuggestField; import mekhq.gui.utilities.MarkdownEditorPanel; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.HashSet; +import java.util.ResourceBundle; +import java.util.Set; + +import static mekhq.campaign.market.contractMarket.ContractAutomation.contractStartPrompt; + /** * @author Neoancient */ @@ -509,7 +507,7 @@ protected void updatePaymentMultiplier() { @Override protected void btnOKActionPerformed(ActionEvent evt) { - + if (!btnOK.equals(evt.getSource())) { return; } @@ -544,7 +542,7 @@ protected void btnOKActionPerformed(ActionEvent evt) { contract.setDesc(txtDesc.getText()); contract.setCommandRights(choiceCommand.getSelectedItem()); - contract.setRequiredLances(AtBContract.calculateRequiredLances(campaign)); + contract.setRequiredCombatTeams(AtBContract.calculateRequiredLances(campaign)); contract.setEnemyCode(getCurrentEnemyCode()); contract.setAllySkill(comboAllySkill.getSelectedItem()); @@ -570,6 +568,8 @@ protected void btnOKActionPerformed(ActionEvent evt) { } setVisible(false); + + contractStartPrompt(campaign, contract); } @Override diff --git a/MekHQ/src/mekhq/gui/dialog/NewCampaignConfirmationDialog.java b/MekHQ/src/mekhq/gui/dialog/NewCampaignConfirmationDialog.java index d5461072d2a..63216110478 100644 --- a/MekHQ/src/mekhq/gui/dialog/NewCampaignConfirmationDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/NewCampaignConfirmationDialog.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,12 +18,13 @@ */ package mekhq.gui.dialog; -import javax.swing.JOptionPane; +import javax.swing.*; public class NewCampaignConfirmationDialog { public int YesNoOption() { - return JOptionPane.showConfirmDialog(null, "Are you sure you want to start a new campaign?\n" + - "Do not save your current campaign if you cancel after this point!", "Start New Campaign?", + return JOptionPane.showConfirmDialog(null, + "Are you sure you want to start a new campaign?", + "Start New Campaign?", JOptionPane.YES_NO_OPTION); } -} \ No newline at end of file +} diff --git a/MekHQ/src/mekhq/gui/dialog/NewsDialog.java b/MekHQ/src/mekhq/gui/dialog/NewsDialog.java new file mode 100644 index 00000000000..9f37982ffb6 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/NewsDialog.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2025 - 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.gui.dialog; + +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; +import mekhq.campaign.Campaign; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.universe.NewsItem; +import mekhq.gui.baseComponents.MHQDialogImmersive; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; +import static mekhq.utilities.MHQInternationalization.getFormattedTextAt; + +/** + * NewsDialog is a dialog window for displaying news items within the context of a campaign. + * It includes information about a network, its image, headline, and other relevant details. + *

    + * This dialog is a part of MekHQ and displays immersive content in the game GUI. + *

    + */ +public class NewsDialog extends MHQDialogImmersive { + private static final String RESOURCE_BUNDLE = "mekhq.resources.NewsDialog"; + + private static final int OPERATION_KLONDIKE = 2822; + + private static final List NEWS_NETWORKS = getNewsNetworks(); + private static final String CHATTERWEB_NETWORK_NAME = "chatterweb"; + private static final String AFFILIATE_NETWORK_NAME = "affiliateNewsNetworks"; + + /** + * Constructs a new NewsDialog to display a given {@link NewsItem}. + * + * @param campaign The campaign instance containing relevant game details. + * @param news The {@link NewsItem} to be displayed in the dialog. + */ + public NewsDialog(Campaign campaign, NewsItem news) { + super(campaign, new Person(campaign), null, news.getFullDescription(), + createButtons(), null, UIUtil.scaleForGUI(400)); + } + + /** + * Creates the buttons for the dialog. + * + * @return A list of {@link ButtonLabelTooltipPair} containing buttons for the dialog. + */ + private static List createButtons() { + ButtonLabelTooltipPair btnClose = new ButtonLabelTooltipPair( + getFormattedTextAt(RESOURCE_BUNDLE, "newsReport.button"), null); + + return List.of(btnClose); + } + + @Override + protected void setTitle() { + setTitle(getFormattedTextAt(RESOURCE_BUNDLE, "incomingNews.title")); + } + + /** + * Builds the speaker panel that consists of the network's image and descriptive text. + * + * @param speaker The {@link Person} representing the speaker (may be {@code null}). + * @param campaign The {@link Campaign} object providing relevant game details. + * @return A {@link JPanel} containing the speaker's image and description. + */ + @Override + protected JPanel buildSpeakerPanel(@Nullable Person speaker, Campaign campaign) { + JPanel speakerBox = new JPanel(); + speakerBox.setLayout(new BoxLayout(speakerBox, BoxLayout.Y_AXIS)); + speakerBox.setAlignmentX(Component.CENTER_ALIGNMENT); + speakerBox.setMaximumSize(new Dimension(IMAGE_WIDTH, Integer.MAX_VALUE)); + + final NewsNetwork NETWORK = getNetwork(campaign); + + // Get Network image + String networkImage = NETWORK.imageAddress; + ImageIcon networkIcon = new ImageIcon(networkImage); + networkIcon = scaleImageIconToWidth(networkIcon, IMAGE_WIDTH); + + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(networkIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getNetworkDescription(campaign, NETWORK); + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + IMAGE_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the speakerBox + speakerBox.add(imageLabel); + speakerBox.add(leftDescription); + + return speakerBox; + } + + /** + * Generates a description string for the news network, including its code, name, and slogan. + * + * @param campaign The campaign data used to retrieve faction information. + * @param network The news network for which the description is generated. + * @return A {@link StringBuilder} containing the formatted network description. + */ + private static StringBuilder getNetworkDescription(Campaign campaign, NewsNetwork network) { + final String NETWORK_NAME = network.name; + String networkCode = getFormattedTextAt(RESOURCE_BUNDLE, NETWORK_NAME + ".network"); + String networkName = ""; + if (NETWORK_NAME.equals(CHATTERWEB_NETWORK_NAME)) { + networkName = campaign.getFaction().getFullName(campaign.getGameYear()); + } else if (!NETWORK_NAME.equals(AFFILIATE_NETWORK_NAME)) { + networkName = getFormattedTextAt(RESOURCE_BUNDLE, NETWORK_NAME + ".name"); + } + + String networkSlogan = ""; + if (!NETWORK_NAME.equals(AFFILIATE_NETWORK_NAME)) { + networkSlogan = getFormattedTextAt(RESOURCE_BUNDLE, NETWORK_NAME + ".slogan"); + } + + StringBuilder speakerDescription = new StringBuilder(); + + speakerDescription.append("").append(networkCode).append(""); + + if (!networkName.isEmpty()) { + speakerDescription.append("
    ").append(networkName); + } + + if (!networkSlogan.isEmpty()) { + speakerDescription.append("
    ").append(networkSlogan); + } + + return speakerDescription; + } + + /** + * Determines the most suitable news network for the campaign's current context. + * + * @param campaign The campaign context, including the current year and faction information. + * @return The appropriate {@link NewsNetwork} for the campaign. + */ + private NewsNetwork getNetwork(Campaign campaign) { + int currentYear = campaign.getGameYear(); + + if (campaign.getFaction().isClan() && currentYear >= OPERATION_KLONDIKE) { + // After Klondike Chatterweb comes along, and it makes sense for that to be used by the + // Clans moving forward + return NEWS_NETWORKS.get(NEWS_NETWORKS.size() - 2); // Chatterweb + } + + for (NewsNetwork network : NEWS_NETWORKS) { + int inception = network.inceptionYear; + int closure = network.closureYear; + + if (currentYear >= inception && currentYear <= closure) { + return network; + } + } + + return NEWS_NETWORKS.get(NEWS_NETWORKS.size() - 1); // Affiliated News Networks + } + + /** + * Initializes the list of all available news networks. + * + * @return A {@link List} of {@link NewsNetwork} objects, representing all predefined networks. + */ + private static List getNewsNetworks() { + // TODO Replace placeholder images + NewsNetwork terranNewsNetwork = new NewsNetwork( + "terranNewsNetwork", 0, 2314, + "data/images/force/Pieces/Logos/Inner Sphere/Terran Hegemony.png"); + NewsNetwork hegemonyNewsNetwork = new NewsNetwork( + "hegemonyNewsNetwork", 2315, 2767, + "data/images/force/Pieces/Logos/Inner Sphere/Terran Hegemony (Alternate, House Cameron).png"); + NewsNetwork starlightBroadcasting = new NewsNetwork( + "starlightBroadcasting", 2570, 2780, + "data/images/force/Pieces/Logos/Inner Sphere/Star League.png"); + NewsNetwork comStarNewsBureau = new NewsNetwork( + "comStarNewsBureau", 2826, 3061, + "data/images/universe/factions/logo_comstar.png"); + NewsNetwork interstellarNewsNetwork = new NewsNetwork( + "interstellarNewsNetwork", 3062, 3152, + "data/images/universe/factions/logo_solaris_VII.png"); + + // These two should always be last + NewsNetwork chatterweb = new NewsNetwork(CHATTERWEB_NETWORK_NAME, 0, 0, + "data/images/universe/factions/logo_star_league.png"); + NewsNetwork affiliateNewsNetworks = new NewsNetwork( + AFFILIATE_NETWORK_NAME, 0, 0, + "data/images/universe/factions/logo_mercenaries.png"); + + return List.of(terranNewsNetwork, hegemonyNewsNetwork, starlightBroadcasting, comStarNewsBureau, + interstellarNewsNetwork, chatterweb, affiliateNewsNetworks); + } + + /** + * Represents a news network with associated metadata such as its name, + * inception year, closure year, and the address for its associated image. + *

    + * This record is immutable and provides a compact way to store information about a news network. + *

    + * + * @param name The name of the news network. + * @param inceptionYear The year the news network was established or started broadcasting. + * @param closureYear The year the news network ceased operations. + * @param imageAddress The path or URL to an image representing the news network. + */ + private record NewsNetwork(String name, int inceptionYear, int closureYear, String imageAddress) {} +} diff --git a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java index b1cf0f1e5a3..6ad343d2dee 100644 --- a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java @@ -21,10 +21,12 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.event.ActionEvent; -import java.util.Collections; -import java.util.Optional; -import java.util.ResourceBundle; -import java.util.List; +import java.awt.Insets; +import java.util.*; +import java.util.Map.Entry; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import javax.swing.AbstractAction; import javax.swing.Action; @@ -32,6 +34,7 @@ import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; import static javax.swing.GroupLayout.Alignment; import javax.swing.JDialog; @@ -58,6 +61,8 @@ import mekhq.gui.sorter.FormattedNumberSorter; import mekhq.gui.sorter.TwoNumbersSorter; import mekhq.gui.utilities.JScrollPaneWithSpeed; +import mekhq.campaign.parts.AmmoStorage; +import mekhq.campaign.parts.Armor; /** * A dialog to show parts in use, ordered, in transit with actionable buttons for buying or adding more @@ -65,7 +70,8 @@ */ public class PartsReportDialog extends JDialog { - private JCheckBox ignoreMothballedCheck; + private JCheckBox ignoreMothballedCheck, topUpWeeklyCheck; + private JButton topUpButton, topUpGMButton; private JComboBox ignoreSparesUnderQualityCB; private JTable overviewPartsInUseTable; private PartsInUseTableModel overviewPartsModel; @@ -81,9 +87,16 @@ public PartsReportDialog(CampaignGUI gui, boolean modal) { this.gui = gui; this.campaign = gui.getCampaign(); initComponents(); - refreshOverviewPartsInUse(); + updateOverviewPartsInUse(); pack(); setLocationRelativeTo(gui.getFrame()); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + onClose(); // Call a custom method + } + }); } private void initComponents() { @@ -127,6 +140,7 @@ private void initComponents() { partsInUseSorter.setComparator(PartsInUseTableModel.COL_IN_USE, new FormattedNumberSorter()); partsInUseSorter.setComparator(PartsInUseTableModel.COL_STORED, new FormattedNumberSorter()); partsInUseSorter.setComparator(PartsInUseTableModel.COL_TONNAGE, new FormattedNumberSorter()); + partsInUseSorter.setComparator(PartsInUseTableModel.COL_REQUSTED_STOCK, new FormattedNumberSorter()); partsInUseSorter.setComparator(PartsInUseTableModel.COL_IN_TRANSFER, new TwoNumbersSorter()); partsInUseSorter.setComparator(PartsInUseTableModel.COL_COST, new FormattedNumberSorter()); // Default starting sort @@ -139,10 +153,10 @@ private void initComponents() { @Override public void actionPerformed(ActionEvent e) { int row = Integer.parseInt(e.getActionCommand()); - PartInUse piu = overviewPartsModel.getPartInUse(row); - IAcquisitionWork partToBuy = piu.getPartToBuy(); + PartInUse partInUse = overviewPartsModel.getPartInUse(row); + IAcquisitionWork partToBuy = partInUse.getPartToBuy(); campaign.getShoppingList().addShoppingItem(partToBuy, 1, campaign); - refreshOverviewSpecificPart(row, piu, partToBuy); + refreshOverviewSpecificPart(row, partInUse, partToBuy); } }; @@ -150,19 +164,19 @@ public void actionPerformed(ActionEvent e) { @Override public void actionPerformed(ActionEvent e) { int row = Integer.parseInt(e.getActionCommand()); - PartInUse piu = overviewPartsModel.getPartInUse(row); + PartInUse partInUse = overviewPartsModel.getPartInUse(row); int quantity = 1; PopupValueChoiceDialog pcd = new PopupValueChoiceDialog(gui.getFrame(), true, - "How Many " + piu.getPartToBuy().getAcquisitionName(), quantity, 1, + "How Many " + partInUse.getPartToBuy().getAcquisitionName(), quantity, 1, CampaignGUI.MAX_QUANTITY_SPINNER); pcd.setVisible(true); quantity = pcd.getValue(); if (quantity <= 0) { return; } - IAcquisitionWork partToBuy = piu.getPartToBuy(); + IAcquisitionWork partToBuy = partInUse.getPartToBuy(); campaign.getShoppingList().addShoppingItem(partToBuy, quantity, campaign); - refreshOverviewSpecificPart(row, piu, partToBuy); + refreshOverviewSpecificPart(row, partInUse, partToBuy); } }; @@ -224,29 +238,29 @@ public void actionPerformed(ActionEvent e) { @Override public void actionPerformed(ActionEvent e) { int row = Integer.parseInt(e.getActionCommand()); - PartInUse piu = overviewPartsModel.getPartInUse(row); - IAcquisitionWork partToBuy = piu.getPartToBuy(); + PartInUse partInUse = overviewPartsModel.getPartInUse(row); + IAcquisitionWork partToBuy = partInUse.getPartToBuy(); campaign.getQuartermaster().addPart((Part) partToBuy.getNewEquipment(), 0); - refreshOverviewSpecificPart(row, piu, partToBuy); + refreshOverviewSpecificPart(row, partInUse, partToBuy); } }; Action addInBulk = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { int row = Integer.parseInt(e.getActionCommand()); - PartInUse piu = overviewPartsModel.getPartInUse(row); + PartInUse partInUse = overviewPartsModel.getPartInUse(row); int quantity = 1; PopupValueChoiceDialog pcd = new PopupValueChoiceDialog(gui.getFrame(), true, - "How Many " + piu.getPartToBuy().getAcquisitionName(), quantity, 1, + "How Many " + partInUse.getPartToBuy().getAcquisitionName(), quantity, 1, CampaignGUI.MAX_QUANTITY_SPINNER); pcd.setVisible(true); quantity = pcd.getValue(); - IAcquisitionWork partToBuy = piu.getPartToBuy(); + IAcquisitionWork partToBuy = partInUse.getPartToBuy(); while (quantity > 0) { campaign.getQuartermaster().addPart((Part) partToBuy.getNewEquipment(), 0); --quantity; } - refreshOverviewSpecificPart(row, piu, partToBuy); + refreshOverviewSpecificPart(row, partInUse, partToBuy); } }; @@ -265,6 +279,31 @@ public void actionPerformed(ActionEvent e) { ignoreMothballedCheck = new JCheckBox(resourceMap.getString("chkIgnoreMothballed.text")); ignoreMothballedCheck.addActionListener(evt -> refreshOverviewPartsInUse()); + ignoreMothballedCheck.setSelected(campaign.getIgnoreMothballed()); + + + topUpWeeklyCheck = new JCheckBox(resourceMap.getString("chkTopUpWeekly.text")); + topUpWeeklyCheck.addActionListener(evt -> refreshOverviewPartsInUse()); + topUpWeeklyCheck.setSelected(campaign.getTopUpWeekly()); + + + topUpButton = new JButton(); + topUpButton.setText(resourceMap.getString("topUpBtn.text")); + topUpButton.setIcon(null); + topUpButton.setFocusPainted(false); + topUpButton.setEnabled(true); + topUpButton.setBorder(null); + topUpButton.setMargin(new Insets(10,20,10,20)); + topUpButton.addActionListener(evt -> topUp()); + + topUpGMButton = new JButton(); + topUpGMButton.setText(resourceMap.getString("topUpGMBtn.text")); + topUpGMButton.setIcon(null); + topUpGMButton.setFocusPainted(false); + topUpGMButton.setEnabled(true); + topUpGMButton.setBorder(null); + topUpGMButton.setMargin(new Insets(10,20,10,20)); + topUpGMButton.addActionListener(evt -> topUpGM()); boolean reverse = campaign.getCampaignOptions().isReverseQualityNames(); String[] qualities = { @@ -280,9 +319,18 @@ public void actionPerformed(ActionEvent e) { ignoreSparesUnderQualityCB.setMaximumSize(ignoreSparesUnderQualityCB.getPreferredSize()); ignoreSparesUnderQualityCB.addActionListener(evt -> refreshOverviewPartsInUse()); JLabel ignorePartsUnderLabel = new JLabel(resourceMap.getString("lblIgnoreSparesUnderQuality.text")); + if (campaign.getIgnoreSparesUnderQuality() != null) { + ignoreSparesUnderQualityCB.setSelectedItem(campaign.getIgnoreSparesUnderQuality()); + } else { + ignoreSparesUnderQualityCB.setSelectedItem(" "); + } + JButton btnClose = new JButton("Close"); - btnClose.addActionListener(evt -> setVisible(false)); + btnClose.addActionListener(evt -> { + setVisible(false); + onClose(); + }); layout.setHorizontalGroup(layout.createParallelGroup() .addComponent(tableScroll) @@ -292,6 +340,9 @@ public void actionPerformed(ActionEvent e) { .addComponent(ignorePartsUnderLabel) .addComponent(ignoreSparesUnderQualityCB) .addComponent(ignoreMothballedCheck) + .addComponent(topUpWeeklyCheck) + .addComponent(topUpButton) + .addComponent(topUpGMButton) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(btnClose)))); @@ -301,6 +352,9 @@ public void actionPerformed(ActionEvent e) { .addComponent(ignoreMothballedCheck) .addComponent(ignorePartsUnderLabel) .addComponent(ignoreSparesUnderQualityCB) + .addComponent(topUpWeeklyCheck) + .addComponent(topUpButton) + .addComponent(topUpGMButton) .addComponent(btnClose))); setPreferredSize(UIUtil.scaleForGUI(1400,1000)); @@ -313,6 +367,9 @@ public void actionPerformed(ActionEvent e) { * @return minimum internal quality level to use */ private PartQuality getMinimumQuality(String rating) { + if (rating == null) { + rating = " "; + } if (rating.equals(" ")) { // The blank spot always means "everything", so minimum = lowest return PartQuality.QUALITY_A; @@ -321,10 +378,11 @@ private PartQuality getMinimumQuality(String rating) { } } - private void refreshOverviewSpecificPart(int row, PartInUse piu, IAcquisitionWork newPart) { - if (piu.equals(new PartInUse((Part) newPart))) { + private void refreshOverviewSpecificPart(int row, PartInUse partInUse, IAcquisitionWork newPart) { + storePartInUseRequestedStock(partInUse); + if (partInUse.equals(new PartInUse((Part) newPart))) { // Simple update - campaign.updatePartInUse(piu, ignoreMothballedCheck.isSelected(), + campaign.updatePartInUse(partInUse, ignoreMothballedCheck.isSelected(), getMinimumQuality((String) ignoreSparesUnderQualityCB.getSelectedItem())); overviewPartsModel.fireTableRowsUpdated(row, row); } else { @@ -333,7 +391,7 @@ private void refreshOverviewSpecificPart(int row, PartInUse piu, IAcquisitionWor } } - private void refreshOverviewPartsInUse() { + private void updateOverviewPartsInUse() { overviewPartsModel.setData(campaign.getPartsInUse(ignoreMothballedCheck.isSelected(), getMinimumQuality((String) ignoreSparesUnderQualityCB.getSelectedItem()))); TableColumnModel tcm = overviewPartsInUseTable.getColumnModel(); @@ -344,6 +402,67 @@ private void refreshOverviewPartsInUse() { column = (PartsInUseTableModel.ButtonColumn) tcm.getColumn(PartsInUseTableModel.COL_BUTTON_GMADD_BULK) .getCellRenderer(); column.setEnabled(campaign.isGM()); + topUpGMButton.setEnabled(campaign.isGM()); + } + + private void refreshOverviewPartsInUse() { + storePartInUseRequestedStockMap(); + updateOverviewPartsInUse(); + } + + private Set getPartsInUseFromTable() { + Set partsInUse = new HashSet(); + for (int row = 0; row < overviewPartsInUseTable.getRowCount(); row++) { + partsInUse.add(overviewPartsModel.getPartInUse(row)); + } + return partsInUse; + } + + private void topUp() { + campaign.stockUpPartsInUse(getPartsInUseFromTable()); + storePartInUseRequestedStockMap(); // This is nescessary to prevent request stock values from resetting when topping up + refreshOverviewPartsInUse(); + } + + private void topUpGM() { + campaign.stockUpPartsInUseGM(getPartsInUseFromTable()); + storePartInUseRequestedStockMap(); // This is nescessary to prevent request stock values from resetting when topping up + refreshOverviewPartsInUse(); + } + + public void storePartInUseRequestedStockMap() { + campaign.setIgnoreMothballed(ignoreMothballedCheck.isSelected()); + campaign.setTopUpWeekly(topUpWeeklyCheck.isSelected()); + if (ignoreSparesUnderQualityCB == null) { + campaign.setIgnoreSparesUnderQuality(getMinimumQuality(" ")); + } else { + campaign.setIgnoreSparesUnderQuality(getMinimumQuality(ignoreSparesUnderQualityCB.getSelectedItem().toString())); + } + + Map stockMap = new LinkedHashMap<>(); + for (int row = 0; row < overviewPartsInUseTable.getRowCount(); row++) { + PartInUse partInUse = overviewPartsModel.getPartInUse(row); + stockMap.put(partInUse.getDescription(), partInUse.getRequestedStock()); + } + campaign.setPartsInUseRequestedStockMap(stockMap); + } + + private void storePartInUseRequestedStock(PartInUse partInUse) { + Map stockMap = campaign.getPartsInUseRequestedStockMap(); + stockMap.put(partInUse.getDescription(), partInUse.getRequestedStock()); + } + + private Map getCurrentRequestedStockMap() { + Map stockMap = new LinkedHashMap<>(); + for (int row = 0; row < overviewPartsInUseTable.getRowCount(); row++) { + PartInUse partInUse = overviewPartsModel.getPartInUse(row); + stockMap.put(partInUse.getDescription(), partInUse.getRequestedStock()); + } + + return stockMap; } + private void onClose() { + storePartInUseRequestedStockMap(); + } } diff --git a/MekHQ/src/mekhq/gui/dialog/PartsStoreDialog.java b/MekHQ/src/mekhq/gui/dialog/PartsStoreDialog.java index eeba1f1e6ec..1c95391431a 100644 --- a/MekHQ/src/mekhq/gui/dialog/PartsStoreDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PartsStoreDialog.java @@ -2,7 +2,7 @@ * PartsStoreDialog.java * * Copyright (c) 2009 Jay Lawson (jaylawson39 at yahoo.com). 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. * @@ -21,24 +21,6 @@ */ package mekhq.gui.dialog; -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.util.ArrayList; -import java.util.Objects; -import java.util.ResourceBundle; - -import javax.swing.*; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableRowSorter; - import megamek.client.ui.preferences.JComboBoxPreference; import megamek.client.ui.preferences.JTablePreference; import megamek.client.ui.preferences.JWindowPreference; @@ -60,6 +42,18 @@ import mekhq.gui.sorter.PartsDetailSorter; import mekhq.gui.utilities.JScrollPaneWithSpeed; +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableRowSorter; +import java.awt.*; +import java.util.ArrayList; +import java.util.Objects; +import java.util.ResourceBundle; + /** * @author Taharqa */ @@ -96,7 +90,6 @@ public class PartsStoreDialog extends JDialog { private JTextField txtFilter; private JComboBox choiceParts; private JCheckBox hideImpossible; - private JButton btnUseBonusPart; private final transient ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.PartsStoreDialog", MekHQ.getMHQOptions().getLocale()); @@ -246,13 +239,13 @@ public void removeUpdate(DocumentEvent e) { PartProxy partProxy = partsModel.getPartProxyAt(partsTable.convertRowIndexToModel(i)); int quantity = 1; PopupValueChoiceDialog pcd = new PopupValueChoiceDialog(campaignGUI.getFrame(), - true, "How Many " + partProxy.getName() + "?", quantity, + true, "How Many " + partProxy.getName() + '?', quantity, 1, CampaignGUI.MAX_QUANTITY_SPINNER); pcd.setVisible(true); quantity = pcd.getValue(); if (quantity > 0) { - addPart(true, false, partProxy.getPart(), quantity); + addPart(true, partProxy.getPart(), quantity); partProxy.updateTargetAndInventories(); partsModel.fireTableCellUpdated(partsTable.convertRowIndexToModel(i), PartsTableModel.COL_TARGET); @@ -269,38 +262,6 @@ public void removeUpdate(DocumentEvent e) { panButtons.add(btnBuyBulk, new GridBagConstraints()); // endregion Buy Bulk - // region Bonus Part - if (campaign.getCampaignOptions().isUseAtB() && campaign.hasActiveContract()) { - btnUseBonusPart = new JButton( - resourceMap.getString("useBonusPart.text") + " (" + campaign.totalBonusParts() + ")"); - btnUseBonusPart.addActionListener(evt -> { - if (partsTable.getSelectedRowCount() > 0) { - int[] selectedRow = partsTable.getSelectedRows(); - for (int i : selectedRow) { - PartProxy partProxy = partsModel.getPartProxyAt(partsTable.convertRowIndexToModel(i)); - addPart(true, campaign.totalBonusParts() > 0, partProxy.getPart(), 1); - partProxy.updateTargetAndInventories(); - partsModel.fireTableCellUpdated(partsTable.convertRowIndexToModel(i), - PartsTableModel.COL_TARGET); - partsModel.fireTableCellUpdated(partsTable.convertRowIndexToModel(i), - PartsTableModel.COL_TRANSIT); - partsModel.fireTableCellUpdated(partsTable.convertRowIndexToModel(i), - PartsTableModel.COL_SUPPLY); - partsModel.fireTableCellUpdated(partsTable.convertRowIndexToModel(i), - PartsTableModel.COL_QUEUE); - - btnUseBonusPart.setText(resourceMap.getString("useBonusPart.text") + " (" - + campaign.totalBonusParts() + ")"); - btnUseBonusPart.setVisible(campaign.totalBonusParts() > 0); - } - } - }); - btnUseBonusPart.setVisible(campaign.totalBonusParts() > 0); - - panButtons.add(btnUseBonusPart, new GridBagConstraints()); - } - // endregion Bonus Part - // region Add btnAdd = new JButton(resourceMap.getString("btnGMAdd.text")); btnAdd.addActionListener(evt -> { @@ -336,7 +297,7 @@ public void removeUpdate(DocumentEvent e) { int quantity = 1; PopupValueChoiceDialog pcd = new PopupValueChoiceDialog(campaignGUI.getFrame(), - true, "How Many " + partProxy.getName() + "?", quantity, + true, "How Many " + partProxy.getName() + '?', quantity, 1, CampaignGUI.MAX_QUANTITY_SPINNER); pcd.setVisible(true); quantity = pcd.getValue(); @@ -493,15 +454,8 @@ public boolean include(Entry entry }; partsSorter.setRowFilter(partsTypeFilter); } - private void addPart(boolean purchase, Part part, int quantity) { - addPart(purchase, false, part, quantity); - } - - private void addPart(boolean purchase, boolean bonus, Part part, int quantity) { - if (bonus) { - campaign.spendBonusPart(part.getAcquisitionWork()); - } else if (purchase) { + if (purchase) { campaign.getShoppingList().addShoppingItem(part.getAcquisitionWork(), quantity, campaign); } else { while (quantity > 0) { @@ -1050,8 +1004,8 @@ public String getTooltip(int row, int col) { return null; } - public PartsTableModel.Renderer getRenderer() { - return new PartsTableModel.Renderer(); + public Renderer getRenderer() { + return new Renderer(); } public class Renderer extends DefaultTableCellRenderer { diff --git a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java index cb4e0d68928..3108e295c53 100644 --- a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java @@ -22,48 +22,6 @@ package mekhq.gui.dialog; -import static mekhq.campaign.personnel.randomEvents.PersonalityController.writeDescription; - -import java.awt.Color; -import java.awt.FlowLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.event.KeyEvent; -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.Objects; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.UUID; - -import javax.swing.BorderFactory; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSlider; -import javax.swing.JTabbedPane; -import javax.swing.JTextArea; -import javax.swing.KeyStroke; -import javax.swing.ScrollPaneConstants; -import javax.swing.WindowConstants; -import javax.swing.SwingUtilities; - import megamek.client.ui.Messages; import megamek.client.ui.dialogs.EntityReadoutDialog; import megamek.client.ui.preferences.JWindowPreference; @@ -80,14 +38,18 @@ import mekhq.campaign.ResolveScenarioTracker.OppositionPersonnelStatus; import mekhq.campaign.ResolveScenarioTracker.PersonStatus; import mekhq.campaign.ResolveScenarioTracker.UnitStatus; +import mekhq.campaign.event.DeploymentChangedEvent; import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.Contract; import mekhq.campaign.mission.Loot; import mekhq.campaign.mission.ScenarioObjective; import mekhq.campaign.mission.ScenarioObjectiveProcessor; import mekhq.campaign.mission.enums.ScenarioStatus; import mekhq.campaign.personnel.Person; +import mekhq.campaign.stratcon.StratconCampaignState; import mekhq.campaign.stratcon.StratconRulesManager; import mekhq.campaign.unit.TestUnit; import mekhq.campaign.unit.Unit; @@ -97,6 +59,15 @@ import mekhq.gui.view.PersonViewPanel; import mekhq.utilities.ReportingUtilities; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.List; +import java.util.*; + +import static mekhq.campaign.mission.resupplyAndCaches.PerformResupply.RESUPPLY_LOOT_BOX_NAME; +import static mekhq.campaign.personnel.randomEvents.PersonalityController.writeDescription; + /** * @author Taharqa */ @@ -116,6 +87,7 @@ public class ResolveScenarioWizardDialog extends JDialog { OBJECTIVEPANEL, PREVIEWPANEL }; + private Campaign campaign; private final JFrame frame; private final ResolveScenarioTracker tracker; @@ -143,6 +115,8 @@ public class ResolveScenarioWizardDialog extends JDialog { private List btnsEditUnit; private List ustatuses; private List lblsUnitName; + private JButton btnSendReinforcements; + private boolean reinforcementsSent = false; // maps objectives to list of associated entity checkboxes private Map> objectiveCheckboxes; @@ -205,8 +179,9 @@ public class ResolveScenarioWizardDialog extends JDialog { private JTextArea txtMissingPilots; private JTextArea txtDeadPilots; private JTextArea txtSalvage; - private JTextArea txtRewards; + private JEditorPane txtRewards; //endregion Preview Panel components + private boolean aborted = true; private static final MMLogger logger = MMLogger.create(ResolveScenarioWizardDialog.class); @@ -214,8 +189,9 @@ public class ResolveScenarioWizardDialog extends JDialog { ResourceBundle.getBundle("mekhq.resources.ResolveScenarioWizardDialog", MekHQ.getMHQOptions().getLocale()); //endregion Variable Declarations - public ResolveScenarioWizardDialog(JFrame parent, boolean modal, ResolveScenarioTracker t) { + public ResolveScenarioWizardDialog(Campaign campaign, JFrame parent, boolean modal, ResolveScenarioTracker t) { super(parent, modal); + this.campaign = campaign; this.frame = parent; this.tracker = t; objectiveProcessor = new ScenarioObjectiveProcessor(); @@ -247,11 +223,9 @@ private void initComponents() { GridBagConstraints gridBagConstraints; setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - - // Adding Escape Mnemonic getRootPane().registerKeyboardAction(e -> dispose(), - KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), - JComponent.WHEN_IN_FOCUSED_WINDOW); + KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), + JComponent.WHEN_IN_FOCUSED_WINDOW); setName("Form"); @@ -367,7 +341,7 @@ private void initComponents() { } tabChanged(); // Make sure the right buttons are active. - + setMinimumSize(UIUtil.scaleForGUI(850,600)); setPreferredSize(UIUtil.scaleForGUI(850,1000)); } @@ -379,7 +353,7 @@ private void initComponents() { */ private JPanel makeUnitStatusPanel() { GridBagConstraints gridBagConstraints; - + JPanel pnlUnitStatus = new JPanel(new GridBagLayout()); gridBagConstraints = new GridBagConstraints(); @@ -388,19 +362,27 @@ private JPanel makeUnitStatusPanel() { gridBagConstraints.anchor = GridBagConstraints.CENTER; gridBagConstraints.insets = new Insets(5, 5, 0, 0); pnlUnitStatus.add(new JLabel(resourceMap.getString("totaled")), gridBagConstraints); - + chksTotaled = new ArrayList<>(); ustatuses = new ArrayList<>(); btnsEditUnit = new ArrayList<>(); lblsUnitName = new ArrayList<>(); - + JLabel nameLbl; JCheckBox chkTotaled; JButton btnViewUnit; JButton btnEditUnit; - + int gridy = 2; int unitIndex = 0; + + btnSendReinforcements = new JButton("Continue as Reinforcements"); + btnSendReinforcements.setEnabled(tracker.getScenario().getLinkedScenario() != 0); + btnSendReinforcements.setActionCommand("1"); + btnSendReinforcements.setName("Confirm Reinforcement"); + btnSendReinforcements.addActionListener(new ReinforcementListener()); + + for (Unit unit : tracker.getUnits()) { UnitStatus status = tracker.getUnitsStatus().get(unit.getId()); ustatuses.add(status); @@ -433,9 +415,7 @@ private JPanel makeUnitStatusPanel() { gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new Insets(5, 5, 0, 0); gridBagConstraints.weightx = 0.0; - if (unitIndex == tracker.getUnits().size() - 1) { - gridBagConstraints.weighty = 1.0; - } + pnlUnitStatus.add(nameLbl, gridBagConstraints); gridBagConstraints.gridx = 1; pnlUnitStatus.add(chkTotaled, gridBagConstraints); @@ -444,8 +424,15 @@ private JPanel makeUnitStatusPanel() { gridBagConstraints.gridx = 3; pnlUnitStatus.add(btnEditUnit, gridBagConstraints); gridy++; + if (unitIndex == tracker.getUnits().size() - 1) { + gridBagConstraints.weighty = 1.0; + gridBagConstraints.gridy= gridy; + gridBagConstraints.gridx = 1; + pnlUnitStatus.add(btnSendReinforcements, gridBagConstraints); + } unitIndex++; } + return pnlUnitStatus; } @@ -586,7 +573,7 @@ private JPanel makeSalvagePanel() { StringBuilder salvageUsed = new StringBuilder(); salvageUsed.append("") - .append((currentSalvagePct <= maxSalvagePct) ? "" : + .append((currentSalvagePct <= maxSalvagePct) ? "" : ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor())) .append(currentSalvagePct).append("%") .append((currentSalvagePct <= maxSalvagePct) ? "" : ReportingUtilities.CLOSING_SPAN_TAG) @@ -842,7 +829,7 @@ private JPanel makePrisonerStatusPanel() { writeDescription(status.getPerson()); prisonerIndex++; } - + return pnlPrisonerStatus; } @@ -957,7 +944,7 @@ private JPanel makeKillsPanel() { return pnlKills; } - + // region Make Rewards /** * Sub-function of initComponents. Makes the Rewards Panel. @@ -1064,7 +1051,7 @@ private JPanel makeObjectiveStatusPanel() { updateObjectiveDisplay(objective, lblObjective); } - // To push the objective list up to the top of the panel + // To push the objective list up to the top of the panel gbc.gridy++; gbc.weighty = 1.0; JLabel lblPlaceholder = new JLabel(" "); @@ -1088,7 +1075,7 @@ private JPanel makePreviewPanel() { txtMissingPilots = new JTextArea(); txtDeadPilots = new JTextArea(); txtSalvage = new JTextArea(); - txtRewards = new JTextArea(); + txtRewards = new JEditorPane(); JLabel lblStatus = new JLabel(); pnlPreview.setLayout(new GridBagLayout()); @@ -1121,9 +1108,8 @@ private JPanel makePreviewPanel() { pnlPreview.add(pnlStatus, gridBagConstraints); txtRewards.setText(resourceMap.getString("none")); + txtRewards.setContentType("text/html"); txtRewards.setEditable(false); - txtRewards.setLineWrap(true); - txtRewards.setWrapStyleWord(true); txtRewards.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder(resourceMap.getString("txtRewards.title")), BorderFactory.createEmptyBorder(5, 5, 5, 5))); @@ -1297,8 +1283,8 @@ private JPanel wrapWithInstructions(JPanel toWrap, JScrollPane scrPane, String i instructions.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder(resourceMap.getString("txtInstructions.title")), BorderFactory.createEmptyBorder(5, 5, 5, 5))); - - + + JPanel container = new JPanel(new GridBagLayout()); GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; @@ -1364,7 +1350,7 @@ private void recheckObjectives() { } /** - * Updates the final panel with information taken from the other ones. + * Updates the final panel with information taken from the other ones. */ private void updatePreviewPanel() { // set victory/defeat status based on scenario objectives @@ -1392,8 +1378,8 @@ private void updatePreviewPanel() { override = objectiveOverrideCheckboxes.get(objective).isSelected(); } - reportText.append(objectiveProcessor.processObjective( - objective, qualifyingUnitCount, override, tracker, true)); + reportText.append(objectiveProcessor.processObjective(campaign, objective, + qualifyingUnitCount, override, tracker, true)); reportText.append('\n'); } @@ -1506,16 +1492,12 @@ private void tabChanged() { btnBack.setEnabled(prevEnable); btnNext.setEnabled(nextEnable); - if (current == tabMain.getTabCount() - 1) { - btnFinish.setEnabled(true); - } else { - btnFinish.setEnabled(false); - } + btnFinish.setEnabled(current == tabMain.getTabCount() - 1); // Let's just call these all on tab change for safety for now updateFromUnitsTab(); updateFromSalvageTab(); // TODO: WeaverThree - This wipes out user selecitons on the objective panel, so we can't use it right now. - // recheckObjectives(); + // recheckObjectives(); updatePreviewPanel(); } @@ -1596,15 +1578,18 @@ private void finish() { tracker.assignKills(); - //now get loot - if (((ScenarioStatus) Objects.requireNonNull(choiceStatus.getSelectedItem())).isOverallVictory()) { + boolean isResupply = tracker.getScenario().getStratConScenarioType().isResupply(); + boolean isOverallVictory = ((ScenarioStatus) Objects.requireNonNull(choiceStatus.getSelectedItem())).isOverallVictory(); + if (isOverallVictory || isResupply) { for (int i = 0; i < lootBoxes.size(); i++) { JCheckBox box = lootBoxes.get(i); - if (box.isSelected()) { + if (box.isSelected() + && (!isResupply || loots.get(i).getName().equals(RESUPPLY_LOOT_BOX_NAME))) { tracker.addLoot(loots.get(i)); } } } + List forces = tracker.getScenario().getForceIDs(); //now process tracker.resolveScenario((ScenarioStatus) choiceStatus.getSelectedItem(), txtReport.getText()); @@ -1627,13 +1612,17 @@ private void finish() { override = objectiveOverrideCheckboxes.get(objective).isSelected(); } - objectiveProcessor.processObjective(objective, qualifyingUnitCount, override, tracker, false); + objectiveProcessor.processObjective(campaign, objective, qualifyingUnitCount, override, tracker, false); } } StratconRulesManager.processScenarioCompletion(tracker); - + if (reinforcementsSent && (tracker.getScenario().getStatus().isOverallVictory())) { + StratconRulesManager.linkedScenerioProcessing(tracker, forces); + } + aborted = false; this.setVisible(false); + } private void cancel() { @@ -1649,12 +1638,12 @@ private void cancel() { private void setEnabledTabs() { for( int i = 0; i < tabMain.getTabCount(); i++ ) { boolean enable = switch (tabMain.getTitleAt(i)) { - case UNITSPANEL -> !tracker.getUnitsStatus().keySet().isEmpty(); + case UNITSPANEL -> !tracker.getUnitsStatus().isEmpty(); case OBJECTIVEPANEL -> tracker.getScenario().hasObjectives(); - case PILOTPANEL -> !tracker.getPeopleStatus().keySet().isEmpty(); - case PRISONERPANEL -> !tracker.getOppositionPersonnel().keySet().isEmpty(); - case SALVAGEPANEL -> !tracker.getPotentialSalvage().isEmpty() - && (!(tracker.getMission() instanceof Contract) + case PILOTPANEL -> !tracker.getPeopleStatus().isEmpty(); + case PRISONERPANEL -> !tracker.getOppositionPersonnel().isEmpty(); + case SALVAGEPANEL -> !tracker.getPotentialSalvage().isEmpty() + && (!(tracker.getMission() instanceof Contract) || ((Contract) tracker.getMission()).canSalvage()); case KILLSPANEL -> !tracker.getKillCredits().isEmpty(); case REWARDPANEL -> !loots.isEmpty(); @@ -1824,14 +1813,13 @@ private void checkSalvageRights() { lblSalvageValueEmployer2.setText(salvageEmployer.toAmountAndSymbolString()); StringBuilder salvageUsed = new StringBuilder(); - salvageUsed.append("") - .append((currentSalvagePct <= maxSalvagePct) ? "" : - ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor())) - .append(currentSalvagePct).append("%") + .append((currentSalvagePct <= maxSalvagePct) ? "" + : ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor())) + .append(currentSalvagePct).append('%') .append((currentSalvagePct <= maxSalvagePct) ? "" : ReportingUtilities.CLOSING_SPAN_TAG) .append("(max ").append(maxSalvagePct).append("%)"); - + lblSalvagePct2.setText(salvageUsed.toString()); } @@ -1854,7 +1842,7 @@ private void showUnit(UUID id, boolean isSalvage) { } new EntityReadoutDialog(frame, true, ustatus.getEntity()).setVisible(true); } - + /** * Opens the unit dmaage editor for a given unit from the units or salvage panel @@ -1880,7 +1868,7 @@ private void editUnit(UUID id, int unitIndex, boolean isSalvage) { } } - + /** * Shows a person from the pilot or prisoner list in a dialog * @param status - the record to show @@ -1952,6 +1940,10 @@ private void updateObjectiveDisplay(ScenarioObjective objective, JLabel label) { ? Color.green.darker() : Color.RED); } + public boolean wasAborted() { + return aborted; + } + private class CheckTotalListener implements ItemListener { @Override public void itemStateChanged(ItemEvent evt) { @@ -2021,4 +2013,21 @@ public void actionPerformed(ActionEvent evt) { editUnit(id, idx, salvage); } } + + private class ReinforcementListener implements ActionListener { + + public ReinforcementListener() { + } + + @Override + public void actionPerformed(ActionEvent evt) { + reinforcementsSent = (!reinforcementsSent); + + if(reinforcementsSent){ + ((JButton)evt.getSource()).setBackground(new Color(6, 64, 43)); + }else{ + ((JButton) evt.getSource()).setBackground(UIManager.getColor("Button.background")); + } + } + } } diff --git a/MekHQ/src/mekhq/gui/dialog/VocationalExperienceAwardDialog.java b/MekHQ/src/mekhq/gui/dialog/VocationalExperienceAwardDialog.java new file mode 100644 index 00000000000..f3951185609 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/VocationalExperienceAwardDialog.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2025 - 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.gui.dialog; + +import megamek.common.annotations.Nullable; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.personnel.Person; +import mekhq.gui.CampaignGUI; +import mekhq.gui.baseComponents.MHQDialogImmersive; + +import java.util.List; +import java.util.UUID; + +import static mekhq.campaign.Campaign.AdministratorSpecialization.HR; +import static mekhq.utilities.MHQInternationalization.getFormattedTextAt; + +/** + * A dialog that displays a notification to the commander about personnel + * who have advanced via vocational experience points (XP). + * + *

    This dialog is primarily used to recognize individuals who have gained XP + * as part of the campaign's vocational experience system. It notifies the user, + * displays relevant information in character, and allows quick navigation to the + * personnel records via hyperlinks.

    + */ +public class VocationalExperienceAwardDialog extends MHQDialogImmersive { + private static final String RESOURCE_BUNDLE = "mekhq.resources.VocationalExperienceAwardDialog"; + + /** + * Constructs the {@link VocationalExperienceAwardDialog}. + * + *

    This dialog leverages the superclass {@link MHQDialogImmersive} to provide + * a visually immersive and interactive interface. It includes a left-side speaker and displays + * a message detailing personnel advancements.

    + * + * @param campaign the {@link Campaign} to which this dialog is tied + */ + public VocationalExperienceAwardDialog(Campaign campaign) { + super(campaign, getSpeaker(campaign), null, createInCharacterMessage(campaign), + createButtons(), createOutOfCharacterMessage(campaign), null); + + setModal(false); + setAlwaysOnTop(true); + } + + /** + * Handles the hyperlink click event in the dialog. + * + *

    This method parses the hyperlink reference to focus on the personnel record identified by + * the provided UUID in the campaign's graphical user interface.

    + * + * @param campaign the {@link Campaign} containing relevant personnel data + * @param hyperlinkReference the hyperlink reference containing the UUID of the selected character + */ + @Override + protected void handleHyperlinkClick(Campaign campaign, String hyperlinkReference) { + CampaignGUI campaignGUI = campaign.getApp().getCampaigngui(); + + final UUID id = UUID.fromString(hyperlinkReference.split(":")[1]); + campaignGUI.focusOnPerson(id); + } + + /** + * Creates the list of buttons to be displayed in the dialog. + * + *

    The dialog includes only a confirmation button for this purpose, allowing + * the user to acknowledge the information provided.

    + * + * @return a list of {@link ButtonLabelTooltipPair} representing the dialog's buttons + */ + private static List createButtons() { + ButtonLabelTooltipPair btnConfirm = new ButtonLabelTooltipPair( + getFormattedTextAt(RESOURCE_BUNDLE, "confirm.button"), null); + + return List.of(btnConfirm); + } + + /** + * Retrieves the left-side speaker for the dialog. + * + *

    The speaker is determined as the senior administrator personnel with the HR + * specialization within the campaign. If no such person exists, this method returns {@code null}.

    + * + * @param campaign the {@link Campaign} containing personnel data + * @return a {@link Person} representing the left speaker, or {@code null} if no suitable speaker is available + */ + private static @Nullable Person getSpeaker(Campaign campaign) { + return campaign.getSeniorAdminPerson(HR); + } + + /** + * Constructs the in-character message to be displayed in the dialog. + * + *

    This message addresses the commander and lists all personnel who have advanced + * in XP. The list of personnel is displayed in an HTML-styled table, where each person's + * name is hyperlinked to allow quick access to their record.

    + * + * @param campaign the {@link Campaign} containing the data for the personnel + * @return a string representing the in-character message in HTML format + */ + private static String createInCharacterMessage(Campaign campaign) { + List personnelWhoAdvanced = campaign.getPersonnelWhoAdvancedInXP(); + + String commanderAddress = campaign.getCommanderAddress(false); + + StringBuilder message = new StringBuilder(); + message.append(commanderAddress); + message.append(getFormattedTextAt(RESOURCE_BUNDLE, "dialog.message")); + + // Create a table to hold the personnel + message.append("
    "); + + for (int i = 0; i < personnelWhoAdvanced.size(); i++) { + if (i % 2 == 0) { + message.append(""); + } + + // Add the person in a column + Person person = personnelWhoAdvanced.get(i); + message.append(""); + + if ((i + 1) % 2 == 0 || i == personnelWhoAdvanced.size() - 1) { + message.append(""); + } + } + + message.append("
    - ").append(person.getHyperlinkedFullTitle()).append("
    "); + return message.toString(); + } + + /** + * Constructs an out-of-character (OOC) message to provide context for XP advancements + * based on the campaign's settings and current state. + * + *

    The generated message includes details about the idle XP gained, as determined by + * the campaign's configuration and active contracts. If the campaign has an active + * contract that is not a garrison type (when using AtB settings), or simply has an + * active contract otherwise, the default XP advancement rate is doubled.

    + * + *

    This method integrates campaign options such as:

    + *
      + *
    • The default vocational XP advancement rate ({@code VocationalXP})
    • + *
    • The status of whether the campaign is using the AtB (Against the Bot) + * system ({@code isUseAtB})
    • + *
    • The type of active employment contracts (e.g., garrison or non-garrison)
    • + *
    + * + *

    This information is formatted into a predefined message string using localized + * resource strings.

    + * + * @param campaign the {@link Campaign} containing the current campaign state, + * settings, and active contracts + * @return a string representing the out-of-character (OOC) message to be displayed + */ + private static String createOutOfCharacterMessage(Campaign campaign) { + final CampaignOptions campaignOptions = campaign.getCampaignOptions(); + + int advancement = campaignOptions.getVocationalXP(); + + if (campaign.hasActiveContract()) { + if (campaignOptions.isUseAtB()) { + for (AtBContract contract : campaign.getActiveAtBContracts()) { + if (!contract.getContractType().isGarrisonType()) { + advancement *= 2; + break; + } + } + } else { + advancement *= 2; + } + } + + return getFormattedTextAt(RESOURCE_BUNDLE, "dialog.ooc", advancement); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java b/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java index 72e19ffbc3f..55312211c47 100644 --- a/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java @@ -1,6 +1,7 @@ package mekhq.gui.dialog.helpDialogs; import megamek.client.ui.dialogs.helpDialogs.AbstractHelpDialog; +import megamek.common.internationalization.Internationalization; import mekhq.MekHQ; import javax.swing.*; @@ -9,18 +10,14 @@ public class AutoResolveBehaviorSettingsHelpDialog extends AbstractHelpDialog { - private static final ResourceBundle resourceMap = ResourceBundle.getBundle( - "mekhq.resources.AutoResolveBehaviorSettingsDialog", - MekHQ.getMHQOptions().getLocale()); - /** * Creates a new instance of AutoResolveBehaviorSettingsHelpDialog. * This screen opens a help dialog, using the megamek help dialog, which open an HTML file * @param frame parent frame */ public AutoResolveBehaviorSettingsHelpDialog(final JFrame frame) { - super(frame, resourceMap.getString("AutoResolveBehaviorSettingsDialog.title"), - resourceMap.getString("AutoResolveBehaviorSettingsDialog.autoResolveHelpPath")); + super(frame, Internationalization.getText("AutoResolveBehaviorSettingsDialog.title"), + Internationalization.getText("AutoResolveBehaviorSettingsDialog.autoResolveHelpPath")); setMinimumSize(new Dimension(400, 400)); setModalExclusionType(ModalExclusionType.TOOLKIT_EXCLUDE); diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/DeploymentShortfallNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/DeploymentShortfallNagDialog.java new file mode 100644 index 00000000000..cc51400a512 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/DeploymentShortfallNagDialog.java @@ -0,0 +1,78 @@ +/* + * 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.gui.dialog.nagDialogs; + +import mekhq.MHQConstants; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.gui.baseComponents.AbstractMHQNagDialog; + +import static mekhq.gui.dialog.nagDialogs.nagLogic.DeploymentShortfallNagLogic.hasDeploymentShortfall; + +/** + * A nag dialog that alerts the user if short deployments are detected in the campaign's active contracts. + * + *

    + * This dialog checks whether any active AtB (Against the Bot) contracts have a deployment deficit + * and alerts the player to address the issue. The check is performed weekly (on Sundays) and only + * when the campaign is currently located on a planet. If deployment requirements are not met, + * the dialog is displayed to prompt the user to correct the situation. + *

    + */ +public class DeploymentShortfallNagDialog extends AbstractMHQNagDialog { + /** + * Constructs the shortfall deployment nag dialog for a given campaign. + * + *

    + * This constructor initializes the dialog with the specified campaign and + * formats the resource message to display information about deployment shortfalls. + *

    + * + * @param campaign The {@link Campaign} object representing the current campaign. + */ + public DeploymentShortfallNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_SHORT_DEPLOYMENT); + + final String DIALOG_BODY = "DeploymentShortfallNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false))); + showDialog(); + } + + /** + * Checks if a nag dialog should be displayed for a campaign regarding deployment shortfalls. + * + *

    The method determines whether to show the nag dialog by verifying the following conditions:

    + *
      + *
    • If the campaign is using AtB rules.
    • + *
    • If the nag dialog for deployment shortfall has not been ignored as per the user options.
    • + *
    • If there is a deployment shortfall in the given campaign.
    • + *
    + * + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise + */ + static public boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_SHORT_DEPLOYMENT; + + return campaign.getCampaignOptions().isUseAtB() + && !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && hasDeploymentShortfall(campaign); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/EndContractNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/EndContractNagDialog.java index 9654f48da59..1958990a1ad 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/EndContractNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/EndContractNagDialog.java @@ -21,62 +21,63 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.mission.Contract; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; -import java.time.LocalDate; +import static mekhq.gui.dialog.nagDialogs.nagLogic.EndContractNagLogic.isContractEnded; /** - * This class represents a nag dialog displayed on the day a contract ends - * It extends the {@link AbstractMHQNagDialog} class. + * A dialog used to notify the user about the end date of a contract in the campaign. + * + *

    + * This nag dialog is triggered when a contract in the campaign is flagged as ending on the current date + * and the user has not opted to ignore such notifications. It shows relevant details about the situation + * and allows the user to take action or dismiss the dialog. + *

    + * + * Features: + *
      + *
    • Handles notifications for contract end dates in the campaign.
    • + *
    • Uses a localized message with context-specific details from the campaign.
    • + *
    • Extends the {@link AbstractMHQNagDialog} to reuse base nag dialog functionality.
    • + *
    */ public class EndContractNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "EndContractNagDialog"; - private static String DIALOG_TITLE = "EndContractNagDialog.title"; - private static String DIALOG_BODY = "EndContractNagDialog.text"; - /** - * Checks if a contract within a campaign has ended on the current date. + * Constructs an {@code EndContractNagDialog} for the given campaign. * - * @param campaign the {@link Campaign} containing the contracts to be checked - * @return {@code true} if a contract within the campaign has ended on the current date, - * {@code false} otherwise + *

    + * This dialog uses the localization key {@code "EndContractNagDialog.text"} to provide + * a message that includes additional information, such as the commander's address. + * It is specifically tailored to show when a contract is reaching its end date. + *

    + * + * @param campaign The {@link Campaign} that the nag dialog is tied to. */ - static boolean isContractEnded(Campaign campaign) { - LocalDate today = campaign.getLocalDate(); - - // we can't use 'is date y after x', as once the end date has been passed, - // the contract is removed from the list of active contracts - - // there is no reason to use a stream here, as there won't be enough iterations to warrant it - for (Contract contract : campaign.getActiveContracts()) { - if (contract.getEndingDate().equals(today)) { - return true; - } - } + public EndContractNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_CONTRACT_ENDED); - return false; + final String DIALOG_BODY = "EndContractNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false))); + showDialog(); } /** - * Creates a new instance of the {@link EndContractNagDialog} class. + * Checks if a nag dialog should be displayed for an ended contract within the given campaign. * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public EndContractNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_CONTRACT_ENDED); - } - - /** - * Checks if there is a nag message to display. + *

    The method evaluates the following conditions:

    + *
      + *
    • If the nag dialog for an ended contract has not been ignored in the user options.
    • + *
    • If the contract associated with the provided campaign has ended.
    • + *
    * - * @return {@code true} if there is a nag message to display, {@code false} otherwise + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && isContractEnded(getCampaign()); + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_CONTRACT_ENDED; + + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && isContractEnded(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialog.java index 488c37001a6..b22a0f94ca0 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialog.java @@ -21,82 +21,70 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.personnel.Person; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import static mekhq.gui.dialog.nagDialogs.nagLogic.InsufficientAstechTimeNagLogic.getAsTechTimeDeficit; +import static mekhq.gui.dialog.nagDialogs.nagLogic.InsufficientAstechTimeNagLogic.hasAsTechTimeDeficit; + /** - * This class represents a nag dialog displayed when a campaign's Astech time deficit is greater than 0. - * It extends the {@link AbstractMHQNagDialog} class. + * A dialog used to notify the user about insufficient available time for astechs to complete the + * required maintenance tasks. Not to be confused with {@link InsufficientAstechsNagDialog}. + * + *

    + * This nag dialog is triggered when the available work time for the astech pool is inadequate to meet + * the maintenance time requirements for the current campaign's hangar units. It provides a localized + * message detailing the time deficit and allows the user to take necessary action or dismiss the dialog. + *

    + * + * Features: + *
      + *
    • Calculates the time deficit for the astech pool based on hangar unit maintenance requirements.
    • + *
    • Notifies the user when there is inadequate time available to maintain all units.
    • + *
    • Extends {@link AbstractMHQNagDialog} for consistent nag dialog behavior.
    • + *
    */ public class InsufficientAstechTimeNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "InsufficientAstechTimeNagDialog"; - private static String DIALOG_TITLE = "InsufficientAstechTimeNagDialog.title"; - private static String DIALOG_BODY = "InsufficientAstechTimeNagDialog.text"; - /** - * Checks if the given campaign has a deficit in Astech time. + * Constructs an {@code InsufficientAstechTimeNagDialog} for the given campaign. * - * @param campaign the {@link Campaign} to check for an Astech time deficit - * @return {@code true} if the campaign has a deficit in Astech time, {@code false} otherwise - */ - static boolean checkAstechTimeDeficit(Campaign campaign) { - return getAstechTimeDeficit(campaign) > 0; - } - - /** - * Calculates the time deficit of Astechs needed for a given campaign. *

    - * The method calculates the Astech time deficit by determining the number of Astechs - * required to support maintenance for all valid units in the hangar of the campaign. + * This dialog calculates the astech time deficit and uses a localized message + * to notify the user about the shortage of available time. The message provides + * the commander's address, the time deficit, and a pluralized suffix for correctness. + *

    * - * @param campaign the {@link Campaign} for which to calculate the Astech time deficit - * @return the Astech time deficit, rounded up to the nearest whole number + * @param campaign The {@link Campaign} tied to this nag dialog. + * The campaign provides data about hangar units and astech availability. */ - static int getAstechTimeDeficit(Campaign campaign) { - // Units are only valid if they are maintained, present, and not self crewed (as the crew - // maintain it in that case). - // For each unit, this is valid for; we need six astechs to help the tech for the maintenance. - final int need = campaign.getHangar().getUnitsStream() - .filter(unit -> !unit.isUnmaintained() && unit.isPresent() && !unit.isSelfCrewed()) - .mapToInt(unit -> unit.getMaintenanceTime() * 6).sum(); + public InsufficientAstechTimeNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_INSUFFICIENT_ASTECH_TIME); - int available = campaign.getPossibleAstechPoolMinutes(); - if (campaign.isOvertimeAllowed()) { - available += campaign.getPossibleAstechPoolOvertime(); - } + int asTechsTimeDeficit = getAsTechTimeDeficit(campaign); - return (int) Math.ceil((need - available) / (double) Person.PRIMARY_ROLE_SUPPORT_TIME); - } + String pluralizer = (asTechsTimeDeficit > 1) ? "s" : ""; - //region Constructors - /** - * Creates a new instance of the {@link InsufficientAstechTimeNagDialog} class. - * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public InsufficientAstechTimeNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_INSUFFICIENT_ASTECH_TIME); + final String DIALOG_BODY = "InsufficientAstechTimeNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false), asTechsTimeDeficit, pluralizer)); + showDialog(); } - //endregion Constructors /** - * Checks if the Astech time deficit is greater than zero. - * If the count is greater than zero and the Nag dialog for the current key is not ignored, - * it sets the description using the specified format and returns {@code true}. - * Otherwise, it returns {@code false}. + * Checks if a nag dialog should be displayed for insufficient AsTech time in the given campaign. + * + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for insufficient AsTech time has not been ignored in the user options.
    • + *
    • If there is a deficit in the available AsTech time for the given campaign.
    • + *
    + * + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && checkAstechTimeDeficit(getCampaign())) { - setDescription(String.format( - resources.getString(DIALOG_BODY), - getAstechTimeDeficit(getCampaign()))); - return true; - } + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_INSUFFICIENT_ASTECH_TIME; - return false; + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && hasAsTechTimeDeficit(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialog.java index 9e50a061a21..fedaf64c731 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialog.java @@ -23,55 +23,65 @@ import mekhq.campaign.Campaign; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import static mekhq.gui.dialog.nagDialogs.nagLogic.InsufficientAstechsNagLogic.hasAsTechsNeeded; /** - * This class represents a nag dialog displayed when a campaign's Astech deficit is greater than 0. - * It extends the {@link AbstractMHQNagDialog} class. + * A dialog used to notify the user that their campaign has insufficient astechs. Not to be + * confused with {@link InsufficientAstechTimeNagDialog}. + * + *

    + * This nag dialog is triggered when the campaign does not have enough astechs to handle + * the current maintenance and repair workload. Users are notified via a localized + * message that provides relevant details about the issue. + *

    + * + * Features: + *
      + *
    • Notifies users about the shortage of astechs in the current campaign.
    • + *
    • Allows users to address the issue or dismiss the dialog while optionally ignoring future warnings.
    • + *
    • Extends {@link AbstractMHQNagDialog} for consistent functionality with other nag dialogs.
    • + *
    */ public class InsufficientAstechsNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "InsufficientAstechsNagDialog"; - private static String DIALOG_TITLE = "InsufficientAstechsNagDialog.title"; - private static String DIALOG_BODY = "InsufficientAstechsNagDialog.text"; - - /** - * Checks if the count of Astechs needed is greater than zero. - * If so, it sets the description using the specified format and returns {@code true}. - * Otherwise, it returns {@code false}. - */ - static boolean checkAstechsNeededCount(Campaign campaign) { - final int need = campaign.getAstechNeed(); - return need > 0; - } - - //region Constructors /** - * Creates a new instance of the {@link InsufficientAstechsNagDialog} class. + * Constructs an {@code InsufficientAstechsNagDialog} for the given campaign. + * + *

    + * This dialog uses a localized message identified by the key + * {@code "InsufficientAstechsNagDialog.text"} to inform the user of the insufficient + * astechs in their campaign. + *

    * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog + * @param campaign The {@link Campaign} associated with this nag dialog. */ - public InsufficientAstechsNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_INSUFFICIENT_ASTECHS); + public InsufficientAstechsNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_INSUFFICIENT_ASTECHS); + int asTechsNeeded = campaign.getAstechNeed(); + + String pluralizer = (asTechsNeeded > 1) ? "s" : ""; + + final String DIALOG_BODY = "InsufficientAstechsNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false), asTechsNeeded, pluralizer)); + showDialog(); } - //endregion Constructors /** - * Checks if the count of Astechs needed is greater than zero. - * If the count is greater than zero and the Nag dialog for the current key is not ignored, - * it sets the description using the specified format and returns {@code true}. - * Otherwise, it returns {@code false}. + * Checks if a nag dialog should be displayed for insufficient AsTechs in the given campaign. + * + *

    The method evaluates the following conditions:

    + *
      + *
    • If the nag dialog for insufficient AsTechs has not been ignored in the user options.
    • + *
    • If the campaign does not have the required number of AsTechs.
    • + *
    + * + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && checkAstechsNeededCount(getCampaign())) { - setDescription(String.format( - resources.getString(DIALOG_BODY), - getCampaign().getAstechNeed())); - return true; - } + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_INSUFFICIENT_ASTECHS; - return false; + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && hasAsTechsNeeded(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialog.java index 8e2836d6eda..f5db20d258f 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialog.java @@ -23,54 +23,67 @@ import mekhq.campaign.Campaign; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import static mekhq.gui.dialog.nagDialogs.nagLogic.InsufficientMedicsNagLogic.hasMedicsNeeded; /** - * This class represents a nag dialog displayed when a campaign's Medic deficit is greater than 0. - * It extends the {@link AbstractMHQNagDialog} class. + * A dialog used to notify the user about insufficient medics required to meet the medical needs of the campaign. + * + *

    + * This nag dialog is triggered when the count of available medics in the campaign falls short of + * the total number required for handling the current medical workload. It displays a localized + * message for the user with specifics about the deficit, and optionally allows the user to dismiss + * or ignore future warnings. + *

    + * + * Features: + *
      + *
    • Calculates the number of medics required for a campaign using {@link Campaign#getMedicsNeed()}.
    • + *
    • Displays a dialog to warn the user if the required number of medics exceeds the available count.
    • + *
    • Extends {@link AbstractMHQNagDialog} to provide consistent behavior with other nag dialogs.
    • + *
    */ public class InsufficientMedicsNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "InsufficientMedicsNagDialog"; - private static String DIALOG_TITLE = "InsufficientMedicsNagDialog.title"; - private static String DIALOG_BODY = "InsufficientMedicsNagDialog.text"; - - /** - * Checks if the count of Medics needed is greater than zero. - * If so, it sets the description using the specified format and returns {@code true}. - * Otherwise, it returns {@code false}. - */ - static boolean checkMedicsNeededCount(Campaign campaign) { - return campaign.getMedicsNeed() > 0; - } - - //region Constructors /** - * Creates a new instance of the {@link InsufficientAstechsNagDialog} class. + * Constructs an {@code InsufficientMedicsNagDialog} for the given campaign. * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog + *

    + * This dialog calculates the number of medics required and uses a localized + * message to notify the user about the shortage. The message includes the + * commander's address, the medic deficit, and a pluralized suffix based on the deficit count. + *

    + * + * @param campaign The {@link Campaign} associated with this nag dialog. + * The campaign provides the medical requirements for the calculation. */ - public InsufficientMedicsNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_INSUFFICIENT_MEDICS); + public InsufficientMedicsNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_INSUFFICIENT_MEDICS); + + int medicsRequired = campaign.getMedicsNeed(); + + String pluralizer = (medicsRequired > 1) ? "s" : ""; + + final String DIALOG_BODY = "InsufficientMedicsNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false), medicsRequired, pluralizer)); + showDialog(); } - //endregion Constructors /** - * Checks if the count of Medics needed is greater than zero. - * If the count is greater than zero and the Nag dialog for the current key is not ignored, - * it sets the description using the specified format and returns {@code true}. - * Otherwise, it returns {@code false}. + * Checks if a nag dialog should be displayed for insufficient medics in the given campaign. + * + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for insufficient medics has not been ignored in the user options.
    • + *
    • If the campaign does not have the required number of medics.
    • + *
    + * + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && checkMedicsNeededCount(getCampaign())) { - setDescription(String.format( - resources.getString(DIALOG_BODY), - getCampaign().getAstechNeed())); - return true; - } + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_INSUFFICIENT_MEDICS; - return false; + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && hasMedicsNeeded(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java index b9a853c89ba..8c0e18bb226 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java @@ -21,96 +21,64 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.universe.Faction; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; -import java.time.LocalDate; -import java.util.Objects; +import static mekhq.gui.dialog.nagDialogs.nagLogic.InvalidFactionNagLogic.isFactionInvalid; /** - * This class represents a nag dialog displayed when a campaign's faction has become extinct. - * It extends the {@link AbstractMHQNagDialog} class. + * A dialog used to notify the user about an invalid faction in the current campaign. + * + *

    + * This nag dialog is triggered when the campaign's selected faction is determined to be invalid + * for the current campaign date. It evaluates the validity of the faction based on the campaign + * date and displays a localized message warning the user about the issue. + *

    + * + * Features: + *
      + *
    • Checks whether the campaign's faction is valid based on the current in-game date.
    • + *
    • Displays a warning dialog to alert the user when an invalid faction is detected.
    • + *
    • Extends {@link AbstractMHQNagDialog} to ensure consistent behavior with other nag dialogs.
    • + *
    */ public class InvalidFactionNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "InvalidFactionNagDialog"; - private static String DIALOG_TITLE = "InvalidFactionNagDialog.title"; - private static String DIALOG_BODY = "InvalidFactionNagDialog.text"; - /** - * Checks if the given campaign's faction is valid. + * Constructs an {@code InvalidFactionNagDialog} for the given campaign. * - * @param campaign the campaign to check - * @return {@code true} if the campaign's faction is invalid, {@code false} otherwise - */ - static boolean isFactionInvalid(Campaign campaign) { - Faction campaignFaction = campaign.getFaction(); - - if (!campaign.getFaction().validIn(campaign.getLocalDate())) { - return true; - } - - // this is a special handler for FedSuns as they're the main culprit behind the issue of users having invalid factions. - // FS and LA campaigns won't trigger the above conditional, because those factions aren't technically ended when the FedSuns forms - // they just become dormant. - if (Objects.equals(campaignFaction.getShortName(), "LA")) { - return lyranAllianceSpecialHandler(campaign); - } - - // this is another special handler for FedSuns as they're the main culprit behind the issue of users having invalid factions. - if (Objects.equals(campaignFaction.getShortName(), "FS")) { - return federatedSunsSpecialHandler(campaign); - } - - return false; - } - - /** - * Checks if the given campaign falls within the inactive date range of the Federated Suns. + *

    + * This dialog initializes with the campaign information and sets a localized + * message to notify the user about the potential issue involving an invalid faction. + * The message includes the commander's address for better clarity. + *

    * - * @param campaign The current campaign. - * @return Returns {@code true} if the campaign falls within the active date range, otherwise {@code false}. + * @param campaign The {@link Campaign} associated with this nag dialog. + * The campaign provides the faction and other details for evaluation. */ - static boolean federatedSunsSpecialHandler(Campaign campaign) { - boolean isAfterActiveDate = campaign.getLocalDate().isAfter(LocalDate.of(3040, 1, 18)); - boolean isBeforeInactiveDate = campaign.getLocalDate().isBefore(LocalDate.of(3057, 9, 18)); + public InvalidFactionNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_INVALID_FACTION); - return isAfterActiveDate && isBeforeInactiveDate; + final String DIALOG_BODY = "InvalidFactionNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false))); + showDialog(); } /** - * Checks if the given campaign falls within the inactive date range of the Lyran Alliance. + * Checks if a nag dialog should be displayed for an invalid faction in the given campaign. * - * @param campaign The current campaign. - * @return Returns {@code true} if the campaign falls within the active date range, otherwise {@code false}. - */ - static boolean lyranAllianceSpecialHandler(Campaign campaign) { - // the dates picked are chosen as these are when mhq does the bulk of the faction ownership transfers - boolean isAfterActiveDate = campaign.getLocalDate().isAfter(LocalDate.of(3040, 1, 18)); - boolean isBeforeInactiveDate = campaign.getLocalDate().isBefore(LocalDate.of(3067, 4, 20)); - - return isAfterActiveDate && isBeforeInactiveDate; - } - - //region Constructors - /** - * Creates a new instance of the {@link InvalidFactionNagDialog} class. + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for an invalid faction has not been ignored in the user options.
    • + *
    • If the faction associated with the campaign is considered invalid.
    • + *
    * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - public InvalidFactionNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_INVALID_FACTION); - } - //endregion Constructors + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_INVALID_FACTION; - /** - * Checks if there is a nag message to display. - * - * @return {@code true} if there is a nag message to display, {@code false} otherwise - */ - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) && (isFactionInvalid(getCampaign())); + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && (isFactionInvalid(campaign)); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/NagController.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/NagController.java new file mode 100644 index 00000000000..440826c3850 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/NagController.java @@ -0,0 +1,175 @@ +package mekhq.gui.dialog.nagDialogs; + +import mekhq.campaign.Campaign; + +/** + * A controller class responsible for managing and triggering daily nag dialogs in the campaign. + * + *

    + * The purpose of this class is to sequentially check all predefined "nag" conditions + * to alert the player about issues that require their attention before advancing the day + * in the campaign. Each nag dialog is displayed based on specific conditions, and the daily + * nag process stops if the player cancels proceeding to the next day. + *

    + * + * Usage: + *

    + * This class is primarily called during the "Advance Day" process in MekHQ, to notify + * players of campaign-related issues spanning financial, personnel, and strategic concerns. + *

    + */ +public class NagController { + /** + * Triggers a sequence of daily nag dialogs to check and display issues in the campaign. + * + *

    + * This method iterates through all predefined nag dialogs, each associated with a specific + * condition or scenario within the campaign. Nags include, but are not limited to, the following: + *

      + *
    • Invalid faction settings.
    • + *
    • Missing commander.
    • + *
    • Untreated personnel requiring medical attention.
    • + *
    • Insufficient funds to cover daily expenses or upcoming costs.
    • + *
    • Unmaintained units in the hangar.
    • + *
    • Unresolved mission or contract scenarios.
    • + *
    + * If the player cancels any nag dialog, this method returns {@code true} and stops + * further processing. If all nag dialogs are successfully passed, it returns {@code false}, + * allowing the campaign to progress to the next day. + * + * @param campaign The {@link Campaign} object representing the current campaign. + * @return {@code true} if the player cancels any nag dialog, {@code false} otherwise. + */ + public static boolean triggerDailyNags(Campaign campaign) { + // Invalid Faction + if (InvalidFactionNagDialog.checkNag(campaign)) { + InvalidFactionNagDialog invalidFactionNagDialog = new InvalidFactionNagDialog(campaign); + if (invalidFactionNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // No Commander + if (NoCommanderNagDialog.checkNag(campaign)) { + NoCommanderNagDialog noCommanderNagDialog = new NoCommanderNagDialog(campaign); + if (noCommanderNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Untreated personnel + if (UntreatedPersonnelNagDialog.checkNag(campaign)) { + UntreatedPersonnelNagDialog untreatedPersonnelNagDialog = new UntreatedPersonnelNagDialog(campaign); + if (untreatedPersonnelNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Unable to afford expenses + if (UnableToAffordExpensesNagDialog.checkNag(campaign)) { + UnableToAffordExpensesNagDialog unableToAffordExpensesNagDialog = new UnableToAffordExpensesNagDialog(campaign); + if (unableToAffordExpensesNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Unable to afford next jump + if (UnableToAffordJumpNagDialog.checkNag(campaign)) { + UnableToAffordJumpNagDialog unableToAffordJumpNagDialog = new UnableToAffordJumpNagDialog(campaign); + if (unableToAffordJumpNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Unable to afford next loan payment + if (UnableToAffordLoanPaymentNagDialog.checkNag(campaign)) { + UnableToAffordLoanPaymentNagDialog unableToAffordLoanPaymentNagDialog = new UnableToAffordLoanPaymentNagDialog(campaign); + if (unableToAffordLoanPaymentNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Unmaintained Units + if (UnmaintainedUnitsNagDialog.checkNag(campaign)) { + UnmaintainedUnitsNagDialog unmaintainedUnitsNagDialog = new UnmaintainedUnitsNagDialog(campaign); + if (unmaintainedUnitsNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Insufficient Medics + if (InsufficientMedicsNagDialog.checkNag(campaign)) { + InsufficientMedicsNagDialog insufficientMedicsNagDialog = new InsufficientMedicsNagDialog(campaign); + if (insufficientMedicsNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Insufficient AsTechs + if (InsufficientAstechsNagDialog.checkNag(campaign)) { + InsufficientAstechsNagDialog insufficientAstechsNagDialog = new InsufficientAstechsNagDialog(campaign); + if (insufficientAstechsNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Insufficient AsTech Time + if (InsufficientAstechTimeNagDialog.checkNag(campaign)) { + InsufficientAstechTimeNagDialog insufficientAstechTimeNagDialog = new InsufficientAstechTimeNagDialog(campaign); + if (insufficientAstechTimeNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Unresolved StratCon AO Contacts + if (UnresolvedStratConContactsNagDialog.checkNag(campaign)) { + UnresolvedStratConContactsNagDialog unresolvedStratConContactsNagDialog = new UnresolvedStratConContactsNagDialog(campaign); + if (unresolvedStratConContactsNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Outstanding Scenarios + if (OutstandingScenariosNagDialog.checkNag(campaign)) { + OutstandingScenariosNagDialog outstandingScenariosNagDialog = new OutstandingScenariosNagDialog(campaign); + if (outstandingScenariosNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Deployment Shortfall + if (DeploymentShortfallNagDialog.checkNag(campaign)) { + DeploymentShortfallNagDialog deploymentShortfallNagDialog = new DeploymentShortfallNagDialog(campaign); + if (deploymentShortfallNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Prisoners of War + if (PrisonersNagDialog.checkNag(campaign)) { + PrisonersNagDialog prisonersNagDialog = new PrisonersNagDialog(campaign); + if (prisonersNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Pregnant Personnel Assigned to Active Force + if (PregnantCombatantNagDialog.checkNag(campaign)) { + PregnantCombatantNagDialog pregnantCombatantNagDialog = new PregnantCombatantNagDialog(campaign); + if (pregnantCombatantNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Contract Ended + if (EndContractNagDialog.checkNag(campaign)) { + EndContractNagDialog endContractNagDialog = new EndContractNagDialog(campaign); + if (endContractNagDialog.wasAdvanceDayCanceled()) { + return true; + } + } + + // Player did not cancel Advance Day at any point + return false; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialog.java index 61d8458bb4b..fd6fbe20c90 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialog.java @@ -23,46 +23,61 @@ import mekhq.campaign.Campaign; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import static mekhq.gui.dialog.nagDialogs.nagLogic.NoCommanderNagLogic.hasNoCommander; /** - * This class represents a nag dialog displayed when the campaign has no assigned commander - * It extends the {@link AbstractMHQNagDialog} class. + * A dialog used to notify the user that their campaign lacks a designated commander. + * + *

    + * This nag dialog is displayed when the campaign does not currently have a commander, + * and checks whether the user has opted to ignore such notifications in the future. + * If shown, the user has the option to dismiss the dialog or address the issue. + *

    + * + * Features: + *
      + *
    • Handles the "No Commander" notification for campaigns.
    • + *
    • Localized message fetched using resource bundles.
    • + *
    • Extends the {@link AbstractMHQNagDialog} for reusable dialog behavior.
    • + *
    + * + * @see AbstractMHQNagDialog */ public class NoCommanderNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "NoCommanderNagDialog"; - private static String DIALOG_TITLE = "NoCommanderNagDialog.title"; - private static String DIALOG_BODY = "NoCommanderNagDialog.text"; - - /** - * Checks if the given {@link Campaign} is missing a commander. + * Constructs a {@code NoCommanderNagDialog} for a campaign. * - * @param campaign the campaign to check for a missing commander - * @return {@code true} if the campaign is missing a commander, otherwise {@code false} - */ - static boolean isCommanderMissing (Campaign campaign) { - return (campaign.getFlaggedCommander() == null); - } - - /** - * Creates a new instance of the {@link EndContractNagDialog} class. + *

    + * This dialog uses the localization key {@code "NoCommanderNagDialog.text"} + * to display a message informing the user about the absence of a commander in their campaign. + *

    * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog + * @param campaign The {@link Campaign} for which the nag dialog is being triggered. */ - public NoCommanderNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_NO_COMMANDER); + public NoCommanderNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_NO_COMMANDER); + + final String DIALOG_BODY = "NoCommanderNagDialog.text"; + setRightDescriptionMessage(resources.getString(DIALOG_BODY)); + showDialog(); } /** - * Checks if there is a nag message to display. + * Checks if a nag dialog should be displayed for the absence of a commander in the given campaign. + * + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for the absence of a commander has not been ignored in the user options.
    • + *
    • If the campaign does not have a commander assigned.
    • + *
    * - * @return {@code true} if there is a nag message to display, {@code false} otherwise + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && isCommanderMissing(getCampaign()); + static public boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_NO_COMMANDER; + + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && hasNoCommander(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialog.java index 69a4f9deeaf..3b2ab617918 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialog.java @@ -21,68 +21,72 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.mission.AtBScenario; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; -import java.time.LocalDate; -import java.util.List; +import static mekhq.gui.dialog.nagDialogs.nagLogic.OutstandingScenariosNagLogic.getOutstandingScenarios; +import static mekhq.gui.dialog.nagDialogs.nagLogic.OutstandingScenariosNagLogic.hasOutStandingScenarios; /** - * This class represents a nag dialog displayed when the campaign one or more unresolved scenarios. - * It extends the {@link AbstractMHQNagDialog} class. + * Represents a nag dialog for displaying the list of outstanding scenarios in a campaign. + * + *

    + * This dialog checks for active scenarios within the campaign, categorizes them by + * their state (e.g., unresolved or requiring a track), and displays a list of these + * scenarios to the user. Scenarios are considered "outstanding" if they are unresolved or + * require attention on the current campaign date. + *

    + * + *

    + * The dialog includes logic to account for both AtB contracts and StratCon-enabled campaigns, + * formatting the outstanding scenarios with additional details when appropriate. + *

    */ public class OutstandingScenariosNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "OutstandingScenariosNagDialog"; - private static String DIALOG_TITLE = "OutstandingScenariosNagDialog.title"; - private static String DIALOG_BODY = "OutstandingScenariosNagDialog.text"; - /** - * Checks if there are any outstanding scenarios in the given campaign. - * An outstanding scenario is defined as a scenario whose date is the same as the current date. + * Constructs the OutstandingScenariosNagDialog for the given campaign. * - * @param campaign the campaign to check for outstanding scenarios - * @return {@code true} if there are outstanding scenarios, {@code false} otherwise + *

    + * Upon initialization, this dialog prepares a formatted string of outstanding + * scenarios (if any) and sets up the dialog UI for display. + *

    + * + * @param campaign The {@link Campaign} associated with this nag dialog. */ - static boolean checkForOutstandingScenarios(Campaign campaign) { - List activeContracts = campaign.getActiveAtBContracts(true); + public OutstandingScenariosNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_OUTSTANDING_SCENARIOS); - LocalDate today = campaign.getLocalDate(); + final String DIALOG_BODY = "OutstandingScenariosNagDialog.text"; - for (AtBContract contract : activeContracts) { - for (AtBScenario scenario : contract.getCurrentAtBScenarios()) { - LocalDate scenarioDate = scenario.getDate(); + String outstandingScenarios = getOutstandingScenarios(campaign); - if (scenarioDate.equals(today)) { - return true; - } - } + String addendum = ""; + if (campaign.getCampaignOptions().isUseStratCon()) { + addendum = resources.getString("OutstandingScenariosNagDialog.stratCon"); } - return false; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false), outstandingScenarios, addendum)); + showDialog(); } - //region Constructors /** - * Creates a new instance of the {@link OutstandingScenariosNagDialog} class. + * Checks if a nag dialog should be displayed for outstanding scenarios in the given campaign. * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public OutstandingScenariosNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_OUTSTANDING_SCENARIOS); - } - //endregion Constructors - - /** - * Checks if there is a nag message to display. + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the campaign is set to use AtB (Against the Bot) rules.
    • + *
    • If the nag dialog for outstanding scenarios has not been ignored in the user options.
    • + *
    • If there are outstanding scenarios in the campaign.
    • + *
    * - * @return {@code true} if there is a nag message to display, {@code false} otherwise + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && checkForOutstandingScenarios(getCampaign()); + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_OUTSTANDING_SCENARIOS; + + return campaign.getCampaignOptions().isUseAtB() + && !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && hasOutStandingScenarios(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialog.java index 89d7bfbffec..6b1635ebcb6 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialog.java @@ -21,72 +21,56 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.force.Force; -import mekhq.campaign.mission.Mission; -import mekhq.campaign.personnel.Person; -import mekhq.campaign.unit.Unit; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import static mekhq.gui.dialog.nagDialogs.nagLogic.PregnantCombatantNagLogic.hasActivePregnantCombatant; /** - * This class represents a nag dialog displayed when the campaign has an active mission and has one - * or more pregnant personnel assigned to the TOE - * It extends the {@link AbstractMHQNagDialog} class. + * A nag dialog that warns the user if there are pregnant personnel actively assigned to combat forces. + * + *

    + * This dialog checks the current campaign for any personnel who are actively pregnant and + * assigned to a combat unit that is part of a force. If such personnel are detected, and the + * dialog is not ignored in MekHQ options, the dialog is displayed to notify the user. + *

    */ public class PregnantCombatantNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "PregnantCombatantNagDialog"; - private static String DIALOG_TITLE = "PregnantCombatantNagDialog.title"; - private static String DIALOG_BODY = "PregnantCombatantNagDialog.text"; - /** - * Checks if there is a pregnant combatant in the provided campaign. Combatants are defined as - * personnel assigned to a {@link Unit} in the TO&E during an active {@link Mission} + * Constructs the pregnant combatant nag dialog for the given campaign. + * + *

    + * This constructor sets up the dialog to display a warning related to + * pregnant personnel actively assigned to combat forces. It also sets + * the appropriate resource message to be displayed as the dialog body. + *

    * - * @param campaign the campaign to check - * @return {@code true} if there is a pregnant combatant, {@code false} otherwise + * @param campaign The {@link Campaign} object representing the current campaign. */ - static boolean isPregnantCombatant(Campaign campaign) { - if (campaign.getActiveMissions(false).isEmpty()) { - return false; - } - - // there is no reason to use a stream here, as there won't be enough iterations to warrant it - for (Person person : campaign.getActivePersonnel()) { - if (person.isPregnant()) { - Unit unit = person.getUnit(); - - if (unit != null) { - if (unit.getForceId() != Force.FORCE_NONE) { - return true; - } - } - } - } + public PregnantCombatantNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_PREGNANT_COMBATANT); - return false; + final String DIALOG_BODY = "PregnantCombatantNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false))); + showDialog(); } - //region Constructors /** - * Creates a new instance of the {@link PregnantCombatantNagDialog} class. + * Checks if a nag dialog should be displayed for active pregnant combatants in the given campaign. * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public PregnantCombatantNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_PREGNANT_COMBATANT); - } - //endregion Constructors - - /** - * Checks if there is a nag message to display. + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for active pregnant combatants has not been ignored in the user options.
    • + *
    • If there are active pregnant combatants in the campaign.
    • + *
    * - * @return {@code true} if there is a nag message to display, {@code false} otherwise + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && isPregnantCombatant(getCampaign()); + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_PREGNANT_COMBATANT; + + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && (hasActivePregnantCombatant(campaign)); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/PrisonersNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/PrisonersNagDialog.java index 32616835b5e..cec9153e0f8 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/PrisonersNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/PrisonersNagDialog.java @@ -21,54 +21,55 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.mission.Mission; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import static mekhq.gui.dialog.nagDialogs.nagLogic.PrisonersNagLogic.hasPrisoners; /** - * This class represents a nag dialog displayed when the campaign has retained prisoners of war - * outside an active {@link Mission} - * It extends the {@link AbstractMHQNagDialog} class. + * A nag dialog that alerts the user about prisoners of war (POWs) in the campaign. + * + *

    + * This dialog checks whether there are prisoners of war in the campaign and displays a warning + * if the user attempts to advance the day without addressing them. The purpose is to ensure + * the player is aware of the prisoners and can take any necessary actions before proceeding. + *

    */ public class PrisonersNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "PrisonersNagDialog"; - private static String DIALOG_TITLE = "PrisonersNagDialog.title"; - private static String DIALOG_BODY = "PrisonersNagDialog.text"; - /** - * Checks if the given campaign has any prisoners. + * Constructs the prisoners nag dialog for the given campaign. * - * @param campaign the campaign to check for prisoners - * @return {@code true} if the campaign has prisoners, {@code false} otherwise + *

    + * This constructor initializes the dialog with the specified campaign and + * formats the resource message to display information about prisoners in the campaign. + *

    + * + * @param campaign The {@link Campaign} object representing the current campaign. */ - static boolean hasPrisoners (Campaign campaign) { - if (!campaign.hasActiveContract()) { - return !campaign.getCurrentPrisoners().isEmpty(); - } + public PrisonersNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_PRISONERS); - return false; + final String DIALOG_BODY = "PrisonersNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false))); + showDialog(); } - //region Constructors /** - * Creates a new instance of the {@link PrisonersNagDialog} class. + * Checks if a nag dialog should be displayed for prisoners in the given campaign. * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public PrisonersNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_PRISONERS); - } - //endregion Constructors - - /** - * Checks if there is a nag message to display. + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for prisoners has not been ignored in the user options.
    • + *
    • If the campaign has prisoners.
    • + *
    * - * @return {@code true} if there is a nag message to display, {@code false} otherwise + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) && hasPrisoners(getCampaign()); + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_PRISONERS; + + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && hasPrisoners(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialog.java deleted file mode 100644 index a008f9c424c..00000000000 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialog.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.gui.dialog.nagDialogs; - -import mekhq.MHQConstants; -import mekhq.MekHQ; -import mekhq.campaign.Campaign; -import mekhq.campaign.mission.AtBContract; -import mekhq.gui.baseComponents.AbstractMHQNagDialog; - -import javax.swing.*; -import java.time.DayOfWeek; - -/** - * This class represents a nag dialog displayed when the campaign does not meet the deployment - * levels required by their active {@link AtBContract} - * It extends the {@link AbstractMHQNagDialog} class. - */ -public class ShortDeploymentNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "ShortDeploymentNagDialog"; - private static String DIALOG_TITLE = "ShortDeploymentNagDialog.title"; - private static String DIALOG_BODY = "ShortDeploymentNagDialog.text"; - - /** - * Checks if the deployment requirements are met for a given campaign. - * - * @param campaign the campaign to check the deployment requirements for - * @return {@code true} if the deployment requirements are met, {@code false} otherwise - */ - static boolean checkDeploymentRequirementsMet(Campaign campaign) { - if (!campaign.getLocation().isOnPlanet()) { - return false; - } - - // this prevents the nag from spamming daily - if (campaign.getLocalDate().getDayOfWeek() != DayOfWeek.SUNDAY) { - return false; - } - - // There is no need to use a stream here, as the number of iterations doesn't warrant it. - for (AtBContract contract : campaign.getActiveAtBContracts()) { - if (campaign.getDeploymentDeficit(contract) > 0) { - return true; - } - } - - return false; - } - - //region Constructors - /** - * Creates a new instance of the {@link ShortDeploymentNagDialog} class. - * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public ShortDeploymentNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_SHORT_DEPLOYMENT); - } - //endregion Constructors - - /** - * Checks if there is a nag message to display. - * - * @return {@code true} if there is a nag message to display, {@code false} otherwise - */ - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && checkDeploymentRequirementsMet(getCampaign()); - } -} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialog.java index bf62fb31db7..edaafc9c0e6 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialog.java @@ -21,77 +21,66 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.finances.FinancialReport; import mekhq.campaign.finances.Money; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import java.time.temporal.TemporalAdjusters; + +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnableToAffordExpensesNagLogic.getMonthlyExpenses; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnableToAffordExpensesNagLogic.unableToAffordExpenses; /** - * This class represents a nag dialog displayed when the campaign does not have enough funds to - * cover monthly expenses - * It extends the {@link AbstractMHQNagDialog} class. + * A nag dialog that warns the user if the campaign's available funds are insufficient to cover monthly expenses. + * + *

    + * This dialog is designed to notify players when their campaign is at financial risk due to + * a shortage of funds relative to required monthly expenses. It calculates the total expenses + * for the current financial report and compares it with the campaign's available funds. If + * the funds are insufficient, the dialog is displayed to alert the player. + *

    */ public class UnableToAffordExpensesNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "UnableToAffordExpensesNagDialog"; - private static String DIALOG_TITLE = "UnableToAffordExpensesNagDialog.title"; - private static String DIALOG_BODY = "UnableToAffordExpensesNagDialog.text"; - /** - * Determines whether the given campaign is unable to afford its monthly expenses. + * Constructs the nag dialog for insufficient campaign funds. * - * @param campaign the ongoing campaign - * @return {@code true} if the campaign's funds are less than the total deficit, {@code false} otherwise - */ - static boolean isUnableToAffordExpenses (Campaign campaign) { - Money deficit = getMonthlyExpenses(campaign); - - // check if the campaign's funds are less than the total deficit - return campaign.getFunds().isLessThan(deficit); - } - - /** - * Calculates and returns the monthly expenses of a given campaign. + *

    + * This constructor initializes the dialog with the necessary campaign information + * and formats the displayed message to include the total monthly expenses and the + * commander's name or title. + *

    * - * @param campaign the campaign for which to calculate the monthly expenses - * @return the monthly expenses as a {@link Money} object + * @param campaign The {@link Campaign} object representing the current campaign. */ - static Money getMonthlyExpenses(Campaign campaign) { - // calculate a financial report which includes the monthly expenses - FinancialReport financialReport = FinancialReport.calculate(campaign); + public UnableToAffordExpensesNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_EXPENSES); - // get the total monthly expenses - return financialReport.getMonthlyExpenses(); - } + Money monthlyExpenses = getMonthlyExpenses(campaign); - //region Constructors - /** - * Creates a new instance of the {@link ShortDeploymentNagDialog} class. - * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public UnableToAffordExpensesNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_EXPENSES); + final String DIALOG_BODY = "UnableToAffordExpensesNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false), + monthlyExpenses.toAmountAndSymbolString())); + showDialog(); } - //endregion Constructors /** - * Checks if the campaign is able to afford its monthly expenses. - * If the campaign is unable to afford monthly expenses and the Nag dialog for the current key - * is not ignored, it sets the description using the specified format and returns {@code true}. - * Otherwise, it returns {@code false}. + * Checks if a nag dialog should be displayed for the inability to afford expenses in the given campaign. + * + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If it is the last day of the month in the campaign.
    • + *
    • If the nag dialog for the inability to afford expenses has not been ignored in the user options.
    • + *
    • If the campaign is unable to afford its expenses.
    • + *
    + * + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && isUnableToAffordExpenses(getCampaign())) { - setDescription(String.format( - resources.getString(DIALOG_BODY), - getMonthlyExpenses(getCampaign()).toAmountAndSymbolString())); - return true; - } + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_UNABLE_TO_AFFORD_EXPENSES; - return false; + return campaign.getLocalDate().equals(campaign.getLocalDate().with(TemporalAdjusters.lastDayOfMonth())) + && !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && unableToAffordExpenses(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java index 18a4ef9d30b..95bd919d641 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java @@ -21,95 +21,63 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.CurrentLocation; -import mekhq.campaign.JumpPath; import mekhq.campaign.finances.Money; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; -import java.util.Objects; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnableToAffordJumpNagLogic.getNextJumpCost; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnableToAffordJumpNagLogic.unableToAffordNextJump; /** - * This class represents a nag dialog displayed when the campaign can't afford its next jump - * It extends the {@link AbstractMHQNagDialog} class. + * A nag dialog that warns the user if the campaign's available funds are not enough to cover the + * next jump cost. + * + *

    + * This dialog calculates the cost of the next jump based on the campaign's current location and + * contract options. It alerts the user when the campaign's available funds are less than the + * calculated jump cost, ensuring players are notified of financial constraints before moving + * to another system. + *

    */ public class UnableToAffordJumpNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "UnableToAffordJumpNagDialog"; - private static String DIALOG_TITLE = "UnableToAffordJumpNagDialog.title"; - private static String DIALOG_BODY = "UnableToAffordJumpNagDialog.text"; - /** - * Checks if the campaign is unable to afford the cost of the next jump. + * Constructs the nag dialog for insufficient funds to afford a jump. * - * @param campaign The campaign for which to check the affordability of the next jump. - * @return {@code true} if the campaign is unable to afford the cost of the next jump, {@code false} otherwise. - */ - static boolean isUnableToAffordNextJump (Campaign campaign) { - Money currentFunds = campaign.getFunds(); - Money nextJumpCost = getNextJumpCost(campaign); - - if (nextJumpCost.isZero()) { - return false; - } else { - return currentFunds.isLessThan(nextJumpCost); - } - } - - /** - * Calculates the cost of the next jump for a given campaign. *

    - * This method determines the cost of the next jump by using the campaign's - * options and calculates the cost per jump based on whether the contract pays - * based on the value of the units in the campaign's TO&E. + * This constructor initializes the dialog with relevant campaign data. It formats the + * message to display the name or title of the commander and the calculated cost of the + * next jump, enabling the user to take appropriate financial action if necessary. + *

    * - * @param campaign the campaign for which to calculate the next jump cost - * @return the cost of the next jump for the campaign + * @param campaign The {@link Campaign} object representing the current campaign. */ - static Money getNextJumpCost(Campaign campaign) { - CurrentLocation location = campaign.getLocation(); - JumpPath jumpPath = location.getJumpPath(); + public UnableToAffordJumpNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_JUMP); - if (jumpPath == null) { - return Money.zero(); - } - - if (Objects.equals(jumpPath.getLastSystem(), location.getCurrentSystem())) { - return Money.zero(); - } - - boolean isContractPayBasedOnToeUnitsValue = campaign.getCampaignOptions().isEquipmentContractBase(); + Money nextJumpCost = getNextJumpCost(campaign); - return campaign.calculateCostPerJump(true, isContractPayBasedOnToeUnitsValue); + final String DIALOG_BODY = "UnableToAffordJumpNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false), + nextJumpCost.toAmountAndSymbolString())); + showDialog(); } - //region Constructors /** - * Creates a new instance of the {@link UnableToAffordJumpNagDialog} class. + * Checks if a nag dialog should be displayed for the inability to afford the next jump in the given campaign. * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public UnableToAffordJumpNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_JUMP); - } - //endregion Constructors - - /** - * Checks if the campaign is able to afford its next jump. - * If the campaign is unable to afford its next jump and the Nag dialog for the current key is - * not ignored, it sets the description using the specified format and returns {@code true}. - * Otherwise, it returns {@code false}. + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for the inability to afford the next jump has not been ignored in the user options.
    • + *
    • If the campaign is unable to afford the next jump.
    • + *
    + * + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && isUnableToAffordNextJump(getCampaign())) { - setDescription(String.format( - resources.getString(DIALOG_BODY), - getNextJumpCost(getCampaign()).toAmountAndSymbolString())); - return true; - } + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_UNABLE_TO_AFFORD_JUMP; - return false; + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && unableToAffordNextJump(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialog.java index 0b86ce9ae76..7fe4d9ef36a 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialog.java @@ -21,90 +21,62 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.finances.Loan; import mekhq.campaign.finances.Money; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; -import java.time.LocalDate; -import java.util.List; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnableToAffordLoanPaymentNag.getTotalPaymentsDue; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnableToAffordLoanPaymentNag.unableToAffordLoans; /** - * This class represents a nag dialog displayed when the campaign cannot afford its next loan payment - * It extends the {@link AbstractMHQNagDialog} class. + * A nag dialog that warns the user if the campaign's available funds are not enough to cover + * upcoming loan payments. + * + *

    + * This dialog calculates the total amount due for loan payments scheduled for the next day + * and compares it against the campaign's available funds. If the funds are not enough to cover + * the payments, the dialog is displayed to alert the user and prompt corrective action. + *

    */ public class UnableToAffordLoanPaymentNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "UnableToAffordLoanPaymentNagDialog"; - private static String DIALOG_TITLE = "UnableToAffordLoanPaymentNagDialog.title"; - private static String DIALOG_BODY = "UnableToAffordLoanPaymentNagDialog.text"; - /** - * Determines if the campaign is unable to afford its due loan payments. + * Constructs the nag dialog for insufficient funds to cover loan payments. * - * @param campaign the campaign for which the loan payment affordability needs to be checked - * @return {@code true} if the campaign is unable to afford the loan payment, {@code false} otherwise - */ - static boolean isUnableToAffordLoanPayment(Campaign campaign) { - Money totalPaymentsDue = getTotalPaymentsDue(campaign); - - // check if the campaign's funds are less than the total payments due tomorrow - return campaign.getFunds().isLessThan(totalPaymentsDue); - } - - /** - * Calculates the total payments due tomorrow, across all current loans + *

    + * This constructor initializes the dialog with relevant information about the campaign. + * The displayed message includes the commander's name or title and the total amount due + * for loans that must be paid the next day. + *

    * - * @param campaign the campaign for which to calculate the total payments due - * @return the total payments due as a {@link Money} object + * @param campaign The {@link Campaign} object representing the current campaign. */ - static Money getTotalPaymentsDue(Campaign campaign) { - // gets the list of the campaign's current loans - List loans = campaign.getFinances().getLoans(); + public UnableToAffordLoanPaymentNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_LOAN_PAYMENT); - // gets tomorrow's date - LocalDate tomorrow = campaign.getLocalDate().plusDays(1); - - // initialize the total loan payment due tomorrow as zero - Money totalPaymentsDue = Money.zero(); + Money totalPaymentsDue = getTotalPaymentsDue(campaign); - // iterate over all loans - for (Loan loan : loans) { - // if a loan payment is due tomorrow, add its payment amount to the total payments due - if (loan.getNextPayment().equals(tomorrow)) { - totalPaymentsDue = totalPaymentsDue.plus(loan.getPaymentAmount()); - } - } - return totalPaymentsDue; + final String DIALOG_BODY = "UnableToAffordLoanPaymentNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false), + totalPaymentsDue.toAmountAndSymbolString())); + showDialog(); } - //region Constructors /** - * Creates a new instance of the {@link UnableToAffordLoanPaymentNagDialog} class. + * Checks if a nag dialog should be displayed for the inability to afford loan payments in the given campaign. * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public UnableToAffordLoanPaymentNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_LOAN_PAYMENT); - } - //endregion Constructors - - /** - * Checks if the campaign is able to afford its next loan payment. - * If the campaign is unable to afford its next loan payment and the Nag dialog for the current - * key is not ignored, it sets the description using the specified format and returns {@code true}. - * Otherwise, it returns {@code false}. + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for the inability to afford loan payments has not been ignored in the user options.
    • + *
    • If the campaign is unable to afford its loan payments.
    • + *
    + * + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && isUnableToAffordLoanPayment(getCampaign())) { - setDescription(String.format( - resources.getString(DIALOG_BODY), - getTotalPaymentsDue(getCampaign()).toAmountAndSymbolString())); - return true; - } + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_UNABLE_TO_AFFORD_LOAN_PAYMENT; - return false; + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && unableToAffordLoans(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialog.java index 6ef957e9346..67e7aca625c 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialog.java @@ -21,55 +21,56 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.unit.Unit; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnmaintainedUnitsNagLogic.campaignHasUnmaintainedUnits; /** - * A dialog that displays a nag message if there are unmaintained units in the campaign's hangar. - * It extends the {@link AbstractMHQNagDialog} class. + * A nag dialog that alerts the user about unmaintained units in the campaign's hangar. + * + *

    + * This dialog identifies units that require maintenance but have not received it yet, + * excluding units marked as salvage. It provides a reminder to the player to keep + * active units in proper working order. + *

    */ public class UnmaintainedUnitsNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "UnmaintainedUnitsNagDialog"; - private static String DIALOG_TITLE = "UnmaintainedUnitsNagDialog.title"; - private static String DIALOG_BODY = "UnmaintainedUnitsNagDialog.text"; - /** - * Checks if there are any unmaintained units in the given campaign's hangar. + * Constructs the nag dialog for unmaintained units. * - * @param campaign the {@link Campaign} containing the hangar to check - * @return {@code true} if there are unmaintained units in the hangar, {@code false} otherwise - */ - static boolean checkHanger(Campaign campaign) { - for (Unit u : campaign.getHangar().getUnits()) { - if ((u.isUnmaintained()) && (!u.isSalvage())) { - return true; - } - } - return false; - } - - /** - * Creates a new instance of the {@link UnmaintainedUnitsNagDialog} class. + *

    + * This constructor initializes the dialog with relevant campaign details and + * formats the displayed message to include context for the commander. + *

    * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog + * @param campaign The {@link Campaign} object representing the current campaign. */ - //region Constructors - public UnmaintainedUnitsNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNMAINTAINED_UNITS); + public UnmaintainedUnitsNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_UNMAINTAINED_UNITS); + + final String DIALOG_BODY = "UnmaintainedUnitsNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false))); + showDialog(); } - //endregion Constructors /** - * Checks if there is a nag message to display. + * Checks if a nag dialog should be displayed for the inability to afford loan payments in the given campaign. * - * @return {@code true} if there is a nag message to display, {@code false} otherwise + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for the inability to afford loan payments has not been ignored in the user options.
    • + *
    • If the campaign is unable to afford its loan payments.
    • + *
    + * + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && checkHanger(getCampaign()); + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_UNMAINTAINED_UNITS; + + return campaign.getCampaignOptions().isCheckMaintenance() + && !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && campaignHasUnmaintainedUnits(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialog.java index b1483beb1a4..290601d48af 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialog.java @@ -21,99 +21,66 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.stratcon.StratconScenario; -import mekhq.campaign.stratcon.StratconScenario.ScenarioState; -import mekhq.campaign.stratcon.StratconTrackState; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnresolvedStratConContactsNagLogic.determineUnresolvedContacts; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnresolvedStratConContactsNagLogic.hasUnresolvedContacts; /** - * This class represents a nag dialog displayed when the campaign has outstanding StratCon contacts - * It extends the {@link AbstractMHQNagDialog} class. + * A nag dialog that warns the user about unresolved StratCon contacts within the campaign. + * + *

    + * This dialog identifies unresolved scenarios in StratCon tracks attached to active contracts + * where the player can deploy forces. It provides a detailed report of unresolved contacts to + * notify the player of critical actions required before advancing the campaign. + *

    */ public class UnresolvedStratConContactsNagDialog extends AbstractMHQNagDialog { - private static String DIALOG_NAME = "UnresolvedStratConContactsNagDialog"; - private static String DIALOG_TITLE = "UnresolvedStratConContactsNagDialog.title"; - private static String DIALOG_BODY = "UnresolvedStratConContactsNagDialog.text"; - /** - * Checks if the given campaign has unresolved contact nags. + * Constructs the nag dialog for unresolved StratCon contacts. * - * @param campaign the campaign to check for unresolved contacts - * @return a string indicating whether the campaign has unresolved contacts or not - */ - boolean hasUnresolvedContacts(Campaign campaign) { - String unresolvedContacts = nagUnresolvedContacts(campaign); - - if (unresolvedContacts.isEmpty()) { - return false; - } else { - setDescription(String.format(resources.getString(DIALOG_BODY), unresolvedContacts)); - return true; - } - } - - /** - * Determine whether the user should be nagged about unresolved scenarios on AtB - * StratCon tracks. + *

    + * The dialog is initialized with information about unresolved StratCon scenarios and the + * campaign's current state. The dynamic message is formatted to include the name or title + * of the commander, providing context for the player. + *

    * - * @param campaign Campaign to check. - * @return An informative string containing the reasons the user was nagged. + * @param campaign The {@link Campaign} object representing the current campaign. */ - static String nagUnresolvedContacts(Campaign campaign) { - if (!campaign.getCampaignOptions().isUseStratCon()) { - return ""; - } - - StringBuilder unresolvedContacts = new StringBuilder(); + public UnresolvedStratConContactsNagDialog(final Campaign campaign) { + super(campaign, MHQConstants.NAG_UNRESOLVED_STRATCON_CONTACTS); - // check every track attached to an active contract for unresolved scenarios - // to which the player must deploy forces today - for (AtBContract contract : campaign.getActiveAtBContracts()) { - if (contract.getStratconCampaignState() == null) { - continue; - } + String unresolvedContactsReport = determineUnresolvedContacts(campaign); - for (StratconTrackState track : contract.getStratconCampaignState().getTracks()) { - // "scenario name, track name" - for (StratconScenario scenario : track.getScenarios().values()) { - if ((scenario.getCurrentState() == ScenarioState.UNRESOLVED) - && (campaign.getLocalDate().equals(scenario.getDeploymentDate()))) { - String resolvedScenario = String.format("%s, %s\n", - scenario.getName(), - track.getDisplayableName()); - - unresolvedContacts.append(resolvedScenario); - } - } - } + String addendum = ""; + if (unresolvedContactsReport.isEmpty()) { + addendum = resources.getString("UnresolvedStratConContactsNagDialog.stratcon"); } - return unresolvedContacts.toString(); + final String DIALOG_BODY = "UnresolvedStratConContactsNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false), addendum)); + showDialog(); } - //region Constructors /** - * Creates a new instance of the {@link UnresolvedStratConContactsNagDialog} class. + * Checks if a nag dialog should be displayed for unresolved StratCon contacts in the given campaign. * - * @param frame the parent JFrame for the dialog - * @param campaign the {@link Campaign} associated with the dialog - */ - public UnresolvedStratConContactsNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNRESOLVED_STRATCON_CONTACTS); - } - //endregion Constructors - - /** - * Checks if there is a nag message to display. + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If StratCon is enabled in the campaign options.
    • + *
    • If the nag dialog for unresolved StratCon contacts has not been ignored in the user options.
    • + *
    • If the campaign has unresolved StratCon contacts.
    • + *
    * - * @return {@code true} if there is a nag message to display, {@code false} otherwise + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise */ - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && hasUnresolvedContacts(getCampaign()); + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_UNRESOLVED_STRATCON_CONTACTS; + + return campaign.getCampaignOptions().isUseStratCon() + && !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && hasUnresolvedContacts(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialog.java index d79e9a6d083..dd27c415ca1 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialog.java @@ -23,25 +23,53 @@ import mekhq.campaign.Campaign; import mekhq.gui.baseComponents.AbstractMHQNagDialog; -import javax.swing.*; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UntreatedPersonnelNagLogic.campaignHasUntreatedInjuries; +/** + * A nag dialog that alerts the user about untreated injuries within the campaign's personnel. + * + *

    + * This dialog checks for active, injured personnel who have not been assigned to a doctor, + * excluding those currently classified as prisoners. It provides a reminder to the player, ensuring + * that injured personnel receive immediate treatment. + *

    + */ public class UntreatedPersonnelNagDialog extends AbstractMHQNagDialog { - static boolean isUntreatedInjury(Campaign campaign) { - return campaign.getActivePersonnel().stream() - .filter(p -> (p.needsFixing()) && (p.getDoctorId() == null)) - .anyMatch(p -> !p.getPrisonerStatus().isCurrentPrisoner()); - } + /** + * Constructs the nag dialog for untreated personnel injuries. + * + *

    + * This constructor initializes the dialog with relevant campaign details + * and formats the displayed message to include context for the commander. + *

    + * + * @param campaign The {@link Campaign} object representing the current campaign. + */ + public UntreatedPersonnelNagDialog(Campaign campaign) { + super(campaign, MHQConstants.NAG_UNTREATED_PERSONNEL); - //region Constructors - public UntreatedPersonnelNagDialog(final JFrame frame, final Campaign campaign) { - super(frame, "UntreatedPersonnelNagDialog", "UntreatedPersonnelNagDialog.title", - "UntreatedPersonnelNagDialog.text", campaign, MHQConstants.NAG_UNTREATED_PERSONNEL); + final String DIALOG_BODY = "UntreatedPersonnelNagDialog.text"; + setRightDescriptionMessage(String.format(resources.getString(DIALOG_BODY), + campaign.getCommanderAddress(false))); + showDialog(); } - //endregion Constructors - @Override - protected boolean checkNag() { - return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) - && isUntreatedInjury(getCampaign()); + /** + * Checks if a nag dialog should be displayed for untreated personnel injuries in the given campaign. + * + *

    The method evaluates the following conditions to determine if the nag dialog should appear:

    + *
      + *
    • If the nag dialog for untreated personnel injuries has not been ignored in the user options.
    • + *
    • If the campaign has untreated injuries among its personnel.
    • + *
    + * + * @param campaign the {@link Campaign} to check for nagging conditions + * @return {@code true} if the nag dialog should be displayed, {@code false} otherwise + */ + public static boolean checkNag(Campaign campaign) { + final String NAG_KEY = MHQConstants.NAG_UNTREATED_PERSONNEL; + + return !MekHQ.getMHQOptions().getNagDialogIgnore(NAG_KEY) + && campaignHasUntreatedInjuries(campaign); } } diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/DeploymentShortfallNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/DeploymentShortfallNagLogic.java new file mode 100644 index 00000000000..9cd8973fa6f --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/DeploymentShortfallNagLogic.java @@ -0,0 +1,43 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.AtBContract; + +import java.time.DayOfWeek; + +public class DeploymentShortfallNagLogic { + /** + * Checks if the campaign's active contracts have deployment deficits that need to be addressed. + * + *

    + * The following conditions are evaluated to determine whether the requirements for short + * deployments are met: + *

      + *
    • The campaign must currently be located on a planet. If it is not, the dialog is skipped.
    • + *
    • The check is performed weekly, only on Sundays, to avoid spamming the user daily.
    • + *
    • If any active AtB contract has a deployment deficit, the method returns {@code true}.
    • + *
    + * If none of these conditions are met, the method returns {@code false}. + * + * @return {@code true} if there are unmet deployment requirements; otherwise, {@code false}. + */ + public static boolean hasDeploymentShortfall(Campaign campaign) { + if (!campaign.getLocation().isOnPlanet()) { + return false; + } + + // this prevents the nag from spamming daily + if (campaign.getLocalDate().getDayOfWeek() != DayOfWeek.SUNDAY) { + return false; + } + + // There is no need to use a stream here, as the number of iterations doesn't warrant it. + for (AtBContract contract : campaign.getActiveAtBContracts()) { + if (campaign.getDeploymentDeficit(contract) > 0) { + return true; + } + } + + return false; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/EndContractNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/EndContractNagLogic.java new file mode 100644 index 00000000000..91a591c907c --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/EndContractNagLogic.java @@ -0,0 +1,35 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.Contract; + +import java.time.LocalDate; + +public class EndContractNagLogic { + /** + * Checks if any contract in the current campaign has its end date set to today. + * + *

    + * This method is used to detect whether there are any active contracts + * ending on the campaign's current local date. It iterates over the active + * contracts for the campaign and compares each contract's ending date to today's date. + *

    + * + * @return {@code true} if a contract's end date matches today's date, otherwise {@code false}. + */ + public static boolean isContractEnded(Campaign campaign) { + LocalDate today = campaign.getLocalDate(); + + // we can't use 'is date y after x', as once the end date has been passed, + // the contract is removed from the list of active contracts + + // there is no reason to use a stream here, as there won't be enough iterations to warrant it + for (Contract contract : campaign.getActiveContracts()) { + if (contract.getEndingDate().equals(today)) { + return true; + } + } + + return false; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechTimeNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechTimeNagLogic.java new file mode 100644 index 00000000000..c492ab4f17e --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechTimeNagLogic.java @@ -0,0 +1,60 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.unit.Unit; + +public class InsufficientAstechTimeNagLogic { + /** + * Determines whether there is a deficit in available astech time for the campaign. + * + *

    + * This method calculates the astech time deficit for the given campaign and + * returns {@code true} if there is a positive deficit, indicating that the + * campaign's available astech time is insufficient. If the deficit is zero + * or negative, it returns {@code false}. + *

    + * + * @param campaign the campaign object to retrieve the astech time deficit information from + * @return {@code true} if there is a positive astech time deficit (deficit > 0), + * {@code false} otherwise + */ + public static boolean hasAsTechTimeDeficit(Campaign campaign) { + int asTechsTimeDeficit = getAsTechTimeDeficit(campaign); + return asTechsTimeDeficit > 0; + } + + /** + * Calculates the astech time deficit for the campaign. + * + *

    + * This method determines the total maintenance time required for all valid hangar units + * in the campaign and compares it to the available astech work time. Units are only considered + * valid if they meet the following conditions: + *

      + *
    • Not marked as unmaintained.
    • + *
    • Present in the hangar.
    • + *
    • Not self-crewed (units maintained by their own crew are excluded).
    • + *
    + * Each valid unit requires six astechs per unit of maintenance time. If overtime is allowed in + * the campaign, it is added to the available astech pool. The deficit is calculated, rounded up, + * and returned. + */ + public static int getAsTechTimeDeficit(Campaign campaign) { + // Calculate the total maintenance time needed using a traditional loop + int need = 0; + for (Unit unit : campaign.getHangar().getUnits()) { + if (unit.isMaintained() && unit.isPresent() && !unit.isSelfCrewed()) { + need += unit.getMaintenanceTime() * 6; + } + } + + int available = campaign.getPossibleAstechPoolMinutes(); + if (campaign.isOvertimeAllowed()) { + available += campaign.getPossibleAstechPoolOvertime(); + } + + // Ensure deficit is non-negative + return Math.max(0, (int) Math.ceil((need - available) / (double) Person.PRIMARY_ROLE_SUPPORT_TIME)); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechsNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechsNagLogic.java new file mode 100644 index 00000000000..cdb4982587d --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechsNagLogic.java @@ -0,0 +1,25 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; + +public class InsufficientAstechsNagLogic { + /** + * Determines whether the campaign has a need for astechs. + * + *

    + * This method checks the number of astechs needed in the given campaign + * and returns {@code true} if the number of needed astechs is greater than zero, + * indicating that there is a requirement for astechs. If the number is zero or negative, + * the method returns {@code false}. + *

    + * + * @param campaign the campaign object to retrieve the astech need from + * @return {@code true} if the campaign requires astechs (astech need > 0), + * {@code false} otherwise + */ + public static boolean hasAsTechsNeeded(Campaign campaign) { + int asTechsNeeded = campaign.getAstechNeed(); + + return asTechsNeeded > 0; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientMedicsNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientMedicsNagLogic.java new file mode 100644 index 00000000000..6de168f6f5d --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientMedicsNagLogic.java @@ -0,0 +1,23 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; + +public class InsufficientMedicsNagLogic { + /** + * Checks if there is a need for medics in the campaign. + * + *

    + * This method evaluates whether the number of required medics is greater than zero. + * If {@code medicsRequired} is greater than zero, it means that additional medics + * are needed to meet the campaign's requirements. + *

    + * + * @return {@code true} if the number of required medics ({@code medicsRequired}) is greater than zero; + * {@code false} otherwise. + */ + public static boolean hasMedicsNeeded(Campaign campaign) { + int medicsRequired = campaign.getMedicsNeed(); + + return medicsRequired > 0; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InvalidFactionNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InvalidFactionNagLogic.java new file mode 100644 index 00000000000..6696e546cbc --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/InvalidFactionNagLogic.java @@ -0,0 +1,27 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.universe.Faction; + +import java.time.LocalDate; + +public class InvalidFactionNagLogic { + /** + * Checks whether the campaign's faction is invalid for the current in-game date. + * + *

    + * This method retrieves the campaign's faction using {@link Campaign#getFaction()} and evaluates + * its validity for the current date using {@link Faction#validIn(LocalDate)}. A faction is considered + * invalid if it is not valid for the campaign's local date. + *

    + * + * @return {@code true} if the faction is invalid for the campaign's current date; otherwise, + * {@code false}. + */ + public static boolean isFactionInvalid(Campaign campaign) { + Faction campaignFaction = campaign.getFaction(); + LocalDate today = campaign.getLocalDate(); + + return !campaignFaction.validIn(today); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/NoCommanderNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/NoCommanderNagLogic.java new file mode 100644 index 00000000000..5753c05aafe --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/NoCommanderNagLogic.java @@ -0,0 +1,21 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; + +public class NoCommanderNagLogic { + /** + * Checks if the campaign has no assigned commander. + * + *

    + * This method determines whether the campaign has a flagged commander assigned or not. + * If {@code campaign.getFlaggedCommander()} returns {@code null}, it indicates + * that no commander has been assigned. + *

    + * + * @return {@code true} if the campaign has no flagged commander + * ({@code campaign.getFlaggedCommander()} is {@code null}); {@code false} otherwise. + */ + public static boolean hasNoCommander(Campaign campaign) { + return campaign.getFlaggedCommander() == null; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/OutstandingScenariosNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/OutstandingScenariosNagLogic.java new file mode 100644 index 00000000000..2cb5215a771 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/OutstandingScenariosNagLogic.java @@ -0,0 +1,101 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.AtBScenario; +import mekhq.campaign.stratcon.StratconScenario; +import mekhq.campaign.stratcon.StratconTrackState; + +import java.time.LocalDate; +import java.util.List; + +import static mekhq.campaign.stratcon.StratconCampaignState.getStratconScenarioFromAtBScenario; +import static mekhq.campaign.stratcon.StratconScenario.ScenarioState.UNRESOLVED; + +public class OutstandingScenariosNagLogic { + /** + * Checks if there are any outstanding scenarios in the campaign. + * + *

    + * This method evaluates whether the {@code outstandingScenarios} string is blank or not. + * If the string is not blank, it indicates that there are outstanding scenarios + * that need to be addressed. + *

    + * + * @return {@code true} if {@code outstandingScenarios} is not blank, indicating there are + * outstanding scenarios; {@code false} otherwise. + */ + public static boolean hasOutStandingScenarios(Campaign campaign) { + String outstandingScenarios = getOutstandingScenarios(campaign); + return !outstandingScenarios.isBlank(); + } + + /** + * Retrieves and processes the list of outstanding scenarios for the current campaign. + * + *

    + * This method iterates through all active contracts and their associated AtB scenarios, + * identifying scenarios that are outstanding based on the following conditions: + *

      + *
    • Whether the scenario's date matches the current campaign date.
    • + *
    • If the scenario is part of StratCon and is unresolved or critical.
    • + *
    • If it's associated with a track and includes detailed information about that track.
    • + *
    + * Scenarios are categorized into "critical" scenarios (e.g., required StratCon scenarios) + * and others, with additional formatting for StratCon-specific scenarios where applicable. + */ + public static String getOutstandingScenarios(Campaign campaign) { + List activeContracts = campaign.getActiveAtBContracts(true); + LocalDate today = campaign.getLocalDate(); + StringBuilder activeScenarios = new StringBuilder(); + + for (AtBContract contract : activeContracts) { + for (AtBScenario scenario : contract.getCurrentAtBScenarios()) { + LocalDate scenarioDate = scenario.getDate(); + + if (scenario.getDate() == null) { + continue; + } + + // Skip scenarios not matching today's date + if (!scenarioDate.equals(today)) { + continue; + } + + if (scenario.getHasTrack()) { + StratconScenario stratconScenario = getStratconScenarioFromAtBScenario(campaign, scenario); + + if (stratconScenario != null) { + // Skip if the scenario is unresolved + if (stratconScenario.getCurrentState() == UNRESOLVED) { + continue; + } + + StratconTrackState track = stratconScenario.getTrackForScenario(campaign, null); + + if (track != null) { + activeScenarios.append("
    - ") + .append(scenario.getName()) + .append(", ").append(contract.getName()) + .append(", ").append(track.getDisplayableName()) + .append('-').append(stratconScenario.getCoords().toBTString()); + + if (stratconScenario.isTurningPoint()) { + activeScenarios.append(" (Turning Point)"); + } + + continue; + } + } + } + + // Add non-track scenarios + activeScenarios.append("
    - ") + .append(scenario.getName()) + .append(", ").append(contract.getName()); + } + } + + return activeScenarios.toString(); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/PregnantCombatantNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/PregnantCombatantNagLogic.java new file mode 100644 index 00000000000..7a9ee63d1a8 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/PregnantCombatantNagLogic.java @@ -0,0 +1,48 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.unit.Unit; + +public class PregnantCombatantNagLogic { + /** + * Checks if the current campaign contains any personnel who are pregnant and actively assigned + * to a force. + * + *

    + * This method iterates through all active personnel in the campaign to determine if any + * are pregnant. If a pregnant person is assigned to a unit that belongs to a combat force + * (i.e., a force with an ID other than {@link Force#FORCE_NONE}), this method returns {@code true}. + * Otherwise, it returns {@code false}. + *

    + * + *

    + * If there are no active missions in the campaign, the method short-circuits and immediately + * returns {@code false}. + *

    + * + * @return {@code true} if there are pregnant personnel actively assigned to a combat force, + * {@code false} otherwise. + */ + public static boolean hasActivePregnantCombatant(Campaign campaign) { + if (campaign.getActiveMissions(false).isEmpty()) { + return false; + } + + // there is no reason to use a stream here, as there won't be enough iterations to warrant it + for (Person person : campaign.getActivePersonnel()) { + if (person.isPregnant()) { + Unit unit = person.getUnit(); + + if (unit != null) { + if (unit.getForceId() != Force.FORCE_NONE) { + return true; + } + } + } + } + + return false; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/PrisonersNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/PrisonersNagLogic.java new file mode 100644 index 00000000000..e22ec83b420 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/PrisonersNagLogic.java @@ -0,0 +1,24 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; + +public class PrisonersNagLogic { + /** + * Checks if the current campaign has prisoners of war (POWs). + * + *

    + * This method evaluates the state of the campaign to determine if there are prisoners present. + * If the campaign does not have an active contract, the method checks the campaign's list of + * current prisoners. If the list is not empty, the method returns {@code true}. + *

    + * + * @return {@code true} if there are prisoners in the campaign; otherwise, {@code false}. + */ + public static boolean hasPrisoners(Campaign campaign) { + if (!campaign.hasActiveContract()) { + return !campaign.getCurrentPrisoners().isEmpty(); + } + + return false; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordExpensesNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordExpensesNagLogic.java new file mode 100644 index 00000000000..8413078a909 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordExpensesNagLogic.java @@ -0,0 +1,42 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.finances.FinancialReport; +import mekhq.campaign.finances.Money; + +public class UnableToAffordExpensesNagLogic { + /** + * Determines whether the campaign's current funds are insufficient to cover + * the monthly expenses. + * + *

    + * This method compares the campaign's available funds with the {@code monthlyExpenses} + * amount. If the available funds are less than the monthly expenses, it returns {@code true}, + * indicating that the campaign cannot afford its expenses; otherwise, it returns {@code false}. + *

    + * + * @return {@code true} if the campaign's funds are less than the monthly expenses; + * {@code false} otherwise. + */ + public static boolean unableToAffordExpenses(Campaign campaign) { + Money monthlyExpenses = getMonthlyExpenses(campaign); + return campaign.getFunds().isLessThan(monthlyExpenses); + } + + /** + * Retrieves and calculates the campaign's total monthly expenses. + * + *

    + * This method generates a {@link FinancialReport} for the campaign to compute the + * total monthly expenses, which are then stored in the {@code monthlyExpenses} field. + * The expenses include operational costs, unit upkeep, payroll, and other recurring items. + *

    + */ + public static Money getMonthlyExpenses(Campaign campaign) { + // calculate a financial report which includes the monthly expenses + FinancialReport financialReport = FinancialReport.calculate(campaign); + + // get the total monthly expenses + return financialReport.getMonthlyExpenses(); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordJumpNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordJumpNagLogic.java new file mode 100644 index 00000000000..a44d2835af7 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordJumpNagLogic.java @@ -0,0 +1,56 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.CurrentLocation; +import mekhq.campaign.JumpPath; +import mekhq.campaign.finances.Money; + +import java.util.Objects; + +public class UnableToAffordJumpNagLogic { + /** + * Determines whether the campaign's current funds are insufficient to cover the cost + * of the next jump. + * + *

    + * This method compares the campaign's available funds with the calculated cost + * of the next jump stored in the {@code nextJumpCost} field. If the funds are less than + * the jump cost, it returns {@code true}, indicating that the jump cannot be afforded; + * otherwise, it returns {@code false}. + *

    + * + * @return {@code true} if the campaign's funds are less than the cost of the next jump; + * {@code false} otherwise. + */ + public static boolean unableToAffordNextJump(Campaign campaign) { + Money nextJumpCost = getNextJumpCost(campaign); + return campaign.getFunds().isLessThan(nextJumpCost); + } + + /** + * Calculates the cost of the next jump based on the campaign's location and financial settings. + * + *

    + * This method retrieves the {@link JumpPath} for the campaign's current location and only + * calculates the jump cost if the next system on the path differs from the current system. + * The actual jump cost is determined by the campaign's settings, particularly whether + * contracts base their costs on the value of units in the player's TOE (Table of Equipment). + *

    + */ + public static Money getNextJumpCost(Campaign campaign) { + CurrentLocation location = campaign.getLocation(); + JumpPath jumpPath = location.getJumpPath(); + + if (jumpPath == null) { + return Money.zero(); + } + + if (Objects.equals(jumpPath.getLastSystem(), location.getCurrentSystem())) { + return Money.zero(); + } + + boolean isContractPayBasedOnToeUnitsValue = campaign.getCampaignOptions().isEquipmentContractBase(); + + return campaign.calculateCostPerJump(true, isContractPayBasedOnToeUnitsValue); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordLoanPaymentNag.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordLoanPaymentNag.java new file mode 100644 index 00000000000..1434c44a5ac --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordLoanPaymentNag.java @@ -0,0 +1,57 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.finances.Loan; +import mekhq.campaign.finances.Money; + +import java.time.LocalDate; +import java.util.List; + +public class UnableToAffordLoanPaymentNag { + /** + * Determines whether the campaign's current funds are insufficient to cover + * the total loan payments due. + * + *

    + * This method compares the campaign's available funds with the {@code totalPaymentsDue} + * amount. If the available funds are less than the total loan payments due, it returns {@code true}, + * indicating that the campaign cannot afford the loan payments; otherwise, it returns {@code false}. + *

    + * + * @return {@code true} if the campaign's funds are less than the total loan payments due; + * {@code false} otherwise. + */ + public static boolean unableToAffordLoans(Campaign campaign) { + Money totalPaymentsDue = getTotalPaymentsDue(campaign); + return campaign.getFunds().isLessThan(totalPaymentsDue); + } + + /** + * Calculates the total loan payments due for tomorrow. + * + *

    + * This method retrieves the list of loans associated with the campaign and checks if their + * next payment date matches tomorrow's date. If a payment is due, the amount is added to the + * cumulative total, which is stored in the {@code totalPaymentsDue} field. + *

    + */ + public static Money getTotalPaymentsDue(Campaign campaign) { + Money totalPaymentsDue = Money.zero(); + + // gets the list of the campaign's current loans + List loans = campaign.getFinances().getLoans(); + + // gets tomorrow's date + LocalDate tomorrow = campaign.getLocalDate().plusDays(1); + + // iterate over all loans + for (Loan loan : loans) { + // if a loan payment is due tomorrow, add its payment amount to the total payments due + if (loan.getNextPayment().equals(tomorrow)) { + totalPaymentsDue = totalPaymentsDue.plus(loan.getPaymentAmount()); + } + } + + return totalPaymentsDue; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnmaintainedUnitsNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnmaintainedUnitsNagLogic.java new file mode 100644 index 00000000000..6aed2702897 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnmaintainedUnitsNagLogic.java @@ -0,0 +1,29 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.unit.Unit; + +public class UnmaintainedUnitsNagLogic { + /** + * Checks whether the campaign has any unmaintained units in the hangar. + * + *

    + * This method iterates over the units in the campaign's hangar and identifies units + * that meet the following criteria: + *

      + *
    • The unit is classified as unmaintained ({@link Unit#isUnmaintained()}).
    • + *
    • The unit is not marked as salvage ({@link Unit#isSalvage()}).
    • + *
    + * If any units match these conditions, the method returns {@code true}. + * + * @return {@code true} if unmaintained units are found, otherwise {@code false}. + */ + public static boolean campaignHasUnmaintainedUnits(Campaign campaign) { + for (Unit unit : campaign.getHangar().getUnits()) { + if ((unit.isUnmaintained()) && (!unit.isSalvage())) { + return true; + } + } + return false; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnresolvedStratConContactsNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnresolvedStratConContactsNagLogic.java new file mode 100644 index 00000000000..c1b3bfabe15 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UnresolvedStratConContactsNagLogic.java @@ -0,0 +1,65 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.stratcon.StratconScenario; +import mekhq.campaign.stratcon.StratconScenario.ScenarioState; +import mekhq.campaign.stratcon.StratconTrackState; + +public class UnresolvedStratConContactsNagLogic { + /** + * Checks if there are any unresolved contacts in the current report. + * + *

    + * This method inspects the {@code unresolvedContactsReport} and determines whether + * it contains any unresolved contacts. If the report is not empty, it indicates + * that there are unresolved contacts, and the method returns {@code true}; + * otherwise, it returns {@code false}. + *

    + * + * @return {@code true} if there are unresolved contacts in the report; + * {@code false} otherwise. + */ + public static boolean hasUnresolvedContacts(Campaign campaign) { + String unresolvedContactsReport = determineUnresolvedContacts(campaign); + return !unresolvedContactsReport.isEmpty(); + } + + /** + * Determines unresolved StratCon contacts for the campaign and generates a report. + * + *

    + * This method checks all active AtB contracts in the campaign and iterates over their + * StratCon tracks to find unresolved scenarios. Scenarios are considered unresolved if: + *

      + *
    • Their current state is {@link ScenarioState#UNRESOLVED}.
    • + *
    • Their deployment date matches the current campaign date.
    • + *
    + * A formatted report is created, summarizing all unresolved scenarios and marking critical ones. + */ + public static String determineUnresolvedContacts(Campaign campaign) { + StringBuilder unresolvedContacts = new StringBuilder(); + + // check every track attached to an active contract for unresolved scenarios + // to which the player can deploy forces + for (AtBContract contract : campaign.getActiveAtBContracts()) { + if (contract.getStratconCampaignState() == null) { + continue; + } + + for (StratconTrackState track : contract.getStratconCampaignState().getTracks()) { + for (StratconScenario scenario : track.getScenarios().values()) { + if ((scenario.getCurrentState() == ScenarioState.UNRESOLVED) + && (campaign.getLocalDate().equals(scenario.getDeploymentDate()))) { + unresolvedContacts.append(String.format("
    - %s, %s, %s-%s %s", + scenario.getName(), contract.getName(), + track.getDisplayableName(), scenario.getCoords().toBTString(), + scenario.isTurningPoint() ? " (Turning Point)" : "")); + } + } + } + } + + return unresolvedContacts.toString(); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UntreatedPersonnelNagLogic.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UntreatedPersonnelNagLogic.java new file mode 100644 index 00000000000..1c4754ae6d3 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/nagLogic/UntreatedPersonnelNagLogic.java @@ -0,0 +1,32 @@ +package mekhq.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.personnel.Person; + +public class UntreatedPersonnelNagLogic { + /** + * Checks whether the campaign has any untreated personnel with injuries. + * + *

    + * This method iterates over the campaign's active personnel and identifies individuals + * who meet the following criteria: + *

      + *
    • The individual requires treatment ({@link Person#needsFixing()}).
    • + *
    • The individual has not been assigned to a doctor.
    • + *
    • The individual is not currently classified as a prisoner.
    • + *
    + * If any personnel match these conditions, the method returns {@code true}. + * + * @return {@code true} if untreated injuries are present, otherwise {@code false}. + */ + public static boolean campaignHasUntreatedInjuries(Campaign campaign) { + for (Person person : campaign.getActivePersonnel()) { + if (!person.getPrisonerStatus().isCurrentPrisoner() + && person.needsFixing() + && person.getDoctorId() == null) { + return true; + } + } + return false; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/reportDialogs/AbstractReportDialog.java b/MekHQ/src/mekhq/gui/dialog/reportDialogs/AbstractReportDialog.java index 42205a8a909..90d561c8c72 100644 --- a/MekHQ/src/mekhq/gui/dialog/reportDialogs/AbstractReportDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/reportDialogs/AbstractReportDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,7 +18,7 @@ */ package mekhq.gui.dialog.reportDialogs; -import mekhq.gui.baseComponents.AbstractMHQDialog; +import mekhq.gui.baseComponents.AbstractMHQDialogBasic; import mekhq.gui.utilities.JScrollPaneWithSpeed; import javax.swing.*; @@ -30,7 +30,7 @@ * * Inheriting classes must call initialize() in their constructors and override getText. */ -public abstract class AbstractReportDialog extends AbstractMHQDialog { +public abstract class AbstractReportDialog extends AbstractMHQDialogBasic { //region Constructors protected AbstractReportDialog(final JFrame frame, final String name, final String title) { super(frame, name, title); diff --git a/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogAbandonedConvoy.java b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogAbandonedConvoy.java new file mode 100644 index 00000000000..e714cec0e13 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogAbandonedConvoy.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024-2025 - 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.gui.dialog.resupplyAndCaches; + +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; +import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.personnel.Person; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; +import java.util.UUID; + +import static megamek.common.Compute.randomInt; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerDescription; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +/** + * This class provides a utility method to display a custom dialog related to abandoned convoys + * in the MekHQ game. The dialog includes detailed information and visuals, like the convoy + * commander or speaker, a status update message, and employer details. + */ +public class DialogAbandonedConvoy extends JDialog { + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + public DialogAbandonedConvoy(Campaign campaign, AtBContract contract, @Nullable Force targetConvoy) { + setTitle(resources.getString("incomingTransmission.title")); + + final int INSERT_SIZE = UIUtil.scaleForGUI(10); + + // Main Panel to hold both boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + // Left box for speaker details + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get speaker details + Person speaker = null; + if (targetConvoy != null) { + UUID speakerId = targetConvoy.getForceCommanderID(); + speaker = campaign.getPerson(speakerId); + } + + String speakerName; + if (speaker != null) { + speakerName = speaker.getFullTitle(); + } else { + if (targetConvoy == null) { + speakerName = String.format(resources.getString("dialogBorderConvoySpeakerDefault.text"), + contract.getEmployerName(campaign.getGameYear())); + } else { + speakerName = campaign.getName(); + } + } + + // Add speaker image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, speaker, speakerName); + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, INSERT_SIZE))); + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + mainPanel.add(leftBox, constraints); + + // Right box: Just a message + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + + String message = String.format( + resources.getString("statusUpdateAbandoned" + randomInt(20) + ".text"), + campaign.getCommanderAddress(false)); + + JLabel rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, message)); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + constraints.gridx = 1; + constraints.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, constraints); + + add(mainPanel, BorderLayout.CENTER); + + // Create a container panel to hold both the button panel and the new panel + JPanel containerPanel = new JPanel(); + containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS)); // Stack vertically + + // Buttons panel + JPanel buttonPanel = new JPanel(); + JButton confirmButton = new JButton(resources.getString("logisticsDestroyed.text")); + confirmButton.addActionListener(e -> dispose()); + buttonPanel.add(confirmButton); + + // Add the button panel to the container + containerPanel.add(buttonPanel); + + // New panel (to be added below the button panel) + JPanel infoPanel = new JPanel(); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + infoPanel.setLayout(new BorderLayout()); + + JLabel newPanelLabel = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH + RIGHT_WIDTH, resources.getString("documentation.prompt"))); + infoPanel.add(newPanelLabel, BorderLayout.CENTER); + + // Add the new panel to the container (below the button panel) + containerPanel.add(infoPanel); + + // Add the container panel to the dialog (at the bottom of the layout) + add(containerPanel, BorderLayout.SOUTH); + + // Dialog settings + pack(); + setModal(true); + setLocationRelativeTo(null); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogContractStart.java b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogContractStart.java new file mode 100644 index 00000000000..b8f50045dda --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogContractStart.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2024-2025 - 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.gui.dialog.resupplyAndCaches; + +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.Entity; +import mekhq.campaign.Campaign; +import mekhq.campaign.Campaign.AdministratorSpecialization; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.unit.Unit; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; +import java.util.UUID; + +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.isProhibitedUnitType; +import static mekhq.campaign.mission.resupplyAndCaches.ResupplyUtilities.estimateCargoRequirements; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerDescription; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +/** + * This class provides utility methods to display dialogs related to the beginning of a contract. + * It generates user-friendly messages summarizing cargo requirements, player convoy capabilities, + * and mission details. + */ +public class DialogContractStart extends JDialog { + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + final int INSERT_SIZE = UIUtil.scaleForGUI(10); + + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + /** + * Displays a dialog at the start of a contract, providing summarized details about the mission + * and player convoy capabilities. The content is dynamically generated based on the given + * {@link Campaign} and {@link AtBContract}. + *

    + * This method: + * - Generates a message summarizing the player's convoy capabilities and cargo capacity. + * - Fetches localized text from the resource bundle based on the contract type and command rights. + * - Displays a dialog with visuals (e.g., faction icon) and a confirmation button to proceed. + * + * @param campaign the current {@link Campaign}. + * @param contract the active contract. + */ + public DialogContractStart(Campaign campaign, AtBContract contract) { + setTitle(resources.getString("incomingTransmission.title")); + + // Main Panel to hold both boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + // Left box for speaker details + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get speaker details + Person speaker = campaign.getSeniorAdminPerson(AdministratorSpecialization.LOGISTICS); + + String speakerName; + if (speaker != null) { + speakerName = speaker.getFullTitle(); + } else { + speakerName = campaign.getName(); + } + + // Add speaker image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, speaker, speakerName); + JLabel leftDescription = new JLabel( + String.format("

    %s
    ", + LEFT_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, INSERT_SIZE))); + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + mainPanel.add(leftBox, constraints); + + // Right box: Just a message + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + + String message = generateContractStartMessage(campaign, contract); + + JLabel rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, message)); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + constraints.gridx = 1; + constraints.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, constraints); + + add(mainPanel, BorderLayout.CENTER); + + // Create a container panel to hold both the button panel and the new panel + JPanel containerPanel = new JPanel(); + containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS)); // Stack vertically + + // Buttons panel + JPanel buttonPanel = new JPanel(); + JButton confirmButton = new JButton(resources.getString("convoyConfirm.text")); + confirmButton.addActionListener(e -> dispose()); + buttonPanel.add(confirmButton); + + // Add the button panel to the container + containerPanel.add(buttonPanel); + + // New panel (to be added below the button panel) + JPanel infoPanel = new JPanel(new BorderLayout()); + JLabel lblInfo = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH + LEFT_WIDTH, + String.format(resources.getString("documentation.prompt")))); + lblInfo.setHorizontalAlignment(SwingConstants.CENTER); + infoPanel.add(lblInfo, BorderLayout.CENTER); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + + // Add the new panel to the container (below the button panel) + containerPanel.add(infoPanel); + + // Add the container panel to the dialog (at the bottom of the layout) + add(containerPanel, BorderLayout.SOUTH); + + // Dialog settings + pack(); + setModal(true); + setLocationRelativeTo(null); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + } + + /** + * Generates an HTML-formatted message to display in the start-of-contract dialog. + * The message includes details such as + * - Total player convoy cargo capacity. + * - Number of operational player convoys. + * - Cargo requirements for the contract. + *

    + * The message format adapts based on the contract type (e.g., guerrilla warfare vs. general contract) + * and the player's command rights (e.g., independent command). + *

    + * This method: + * - Iterates through all player forces to calculate total convoy cargo capacity. + * - Checks for convoy readiness, excluding units that are damaged, uncrewed, or prohibited. + * - Formats the message using localized templates from the resource bundle. + * + * @param campaign the current {@link Campaign}. + * @param contract the current {@link AtBContract}. + * @return an HTML-formatted string message summarizing the player's readiness and convoy details + * in the context of the contract. + */ + private static String generateContractStartMessage(Campaign campaign, AtBContract contract) { + int playerConvoys = 0; + double totalPlayerCargoCapacity = 0; + + for (Force force : campaign.getAllForces()) { + if (!force.isConvoyForce()) { + continue; + } + + double cargoCapacitySubTotal = 0; + if (force.isConvoyForce()) { + boolean hasCargo = false; + for (UUID unitId : force.getAllUnits(false)) { + try { + Unit unit = campaign.getUnit(unitId); + Entity entity = unit.getEntity(); + + if (unit.isDamaged() + || !unit.isFullyCrewed() + || isProhibitedUnitType(entity, true)) { + continue; + } + + double individualCargo = unit.getCargoCapacity(); + + if (individualCargo > 0) { + hasCargo = true; + } + + cargoCapacitySubTotal += individualCargo; + } catch (Exception ignored) { + // If we run into an exception, it's because we failed to get Unit or Entity. + // In either case, we just ignore that unit. + } + } + + if (hasCargo) { + if (cargoCapacitySubTotal > 0) { + totalPlayerCargoCapacity += cargoCapacitySubTotal; + playerConvoys++; + } + } + } + } + + String convoyMessage; + String commanderTitle = campaign.getCommanderAddress(false); + + if (contract.getContractType().isGuerrillaWarfare()) { + String convoyMessageTemplate = resources.getString("contractStartMessageGuerrilla.text"); + convoyMessage = String.format(convoyMessageTemplate, commanderTitle); + } else { + String convoyMessageTemplate = resources.getString("contractStartMessageGeneric.text"); + if (contract.getCommandRights().isIndependent()) { + convoyMessageTemplate = resources.getString("contractStartMessageIndependent.text"); + } + + convoyMessage = String.format(convoyMessageTemplate, commanderTitle, + estimateCargoRequirements(campaign, contract), totalPlayerCargoCapacity, + playerConvoys, playerConvoys != 1 ? "s" : ""); + } + + int width = UIUtil.scaleForGUI(500); + return String.format("

    %s
    ", + width, convoyMessage); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogInterception.java b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogInterception.java new file mode 100644 index 00000000000..9085d306a8e --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogInterception.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2024-2025 - 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.gui.dialog.resupplyAndCaches; + +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; +import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; +import mekhq.campaign.personnel.Person; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; +import java.util.UUID; + +import static megamek.common.Compute.randomInt; +import static mekhq.campaign.mission.resupplyAndCaches.ResupplyUtilities.forceContainsOnlyAerialForces; +import static mekhq.campaign.mission.resupplyAndCaches.ResupplyUtilities.forceContainsOnlyVTOLForces; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerDescription; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +/** + * The {@code DialogInterception} class is responsible for displaying a UI dialog when an + * interception scenario occurs during a supply or convoy mission in MekHQ. The dialog uses + * localized resources to generate the content dynamically and includes relevant visual elements + * like the speaker's icon, title, and mission details. + */ +public class DialogInterception extends JDialog{ + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + final int INSERT_SIZE = UIUtil.scaleForGUI(10); + + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + /** + * Displays a dialog for an interception event that occurs during a resupply operation. + * + *

    This method performs the following steps:

    + * 1. Constructs a {@link JDialog} with a title from the localized {@link ResourceBundle}. + * 2. Determines the speaker's name and icon. This involves: + * - Retrieving the force commander from the convoy (if available). + * - Using fallback values for the speaker name and icon if no target convoy was provided. + * 3. Creates a message describing the interception event, using dynamic text + * generated from the resources. + * 4. Builds a GUI with Swing components: + * - A description panel containing a localized, HTML-styled message. + * - An image panel displaying the speaker's icon (sized to a width of 100px). + * 5. Adds confirmation buttons to the dialog, allowing users to close it. + * 6. Displays the dialog as modal to block further user interaction until dismissed. + * + * @param resupply the {@link Resupply} instance containing the current campaign + * and contract details. Used to access mission context, player commander + * information, and employer details. + * @param targetConvoy the optional {@link Force} representing the convoy involved + * in the interception. If {@code null}, the dialog will use default values + * for the speaker and faction visuals. + */ + public DialogInterception(Resupply resupply, @Nullable Force targetConvoy) { + final Campaign campaign = resupply.getCampaign(); + final AtBContract contract = resupply.getContract(); + + setTitle(resources.getString("incomingTransmission.title")); + + // Main Panel to hold both boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + // Left box for speaker details + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get speaker details + Person speaker = null; + if (targetConvoy != null) { + UUID speakerId = targetConvoy.getForceCommanderID(); + speaker = campaign.getPerson(speakerId); + } + + String speakerName; + if (speaker != null) { + speakerName = speaker.getFullTitle(); + } else { + if (targetConvoy == null) { + speakerName = String.format(resources.getString("dialogBorderConvoySpeakerDefault.text"), + contract.getEmployerName(campaign.getGameYear())); + } else { + speakerName = campaign.getName(); + } + } + + // Add speaker image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, speaker, speakerName); + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, INSERT_SIZE))); + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + mainPanel.add(leftBox, constraints); + + // Right box: Just a message + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + + String message = ""; + + if (targetConvoy != null) { + if (forceContainsOnlyVTOLForces(campaign, targetConvoy) + || forceContainsOnlyAerialForces(campaign, targetConvoy)) { + message = String.format( + resources.getString("statusUpdateIntercepted.boilerplate"), + campaign.getCommanderAddress(false), + resources.getString("interceptionInstructions.text")); + } + } + + if (message.isBlank()) { + message = String.format( + resources.getString("statusUpdateIntercepted" + randomInt(20) + ".text"), + campaign.getCommanderAddress(false), + resources.getString("interceptionInstructions.text")); + } + + JLabel rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, message)); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + constraints.gridx = 1; + constraints.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, constraints); + + add(mainPanel, BorderLayout.CENTER); + + // Create a container panel to hold both the button panel and the new panel + JPanel containerPanel = new JPanel(); + containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS)); // Stack vertically + + // Buttons panel + JPanel buttonPanel = new JPanel(); + JButton confirmButton = new JButton(resources.getString("logisticsReceived.text")); + confirmButton.addActionListener(e -> dispose()); + buttonPanel.add(confirmButton); + + // Add the button panel to the container + containerPanel.add(buttonPanel); + + // New panel (to be added below the button panel) + JPanel infoPanel = new JPanel(new BorderLayout()); + JLabel lblInfo = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH + LEFT_WIDTH, + String.format(resources.getString("documentation.prompt")))); + lblInfo.setHorizontalAlignment(SwingConstants.CENTER); + infoPanel.add(lblInfo, BorderLayout.CENTER); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + + // Add the new panel to the container (below the button panel) + containerPanel.add(infoPanel); + + // Add the container panel to the dialog (at the bottom of the layout) + add(containerPanel, BorderLayout.SOUTH); + + // Dialog settings + pack(); + setModal(true); + setLocationRelativeTo(null); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogItinerary.java b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogItinerary.java new file mode 100644 index 00000000000..d0b7c4aa851 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogItinerary.java @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2024-2025 - 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.gui.dialog.resupplyAndCaches; + +import megamek.client.ui.swing.util.UIUtil; +import mekhq.campaign.Campaign; +import mekhq.campaign.Campaign.AdministratorSpecialization; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.enums.AtBMoraleLevel; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; +import mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType; +import mekhq.campaign.parts.Part; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.enums.PersonnelRole; + +import javax.swing.*; +import java.awt.*; +import java.util.List; +import java.util.ResourceBundle; + +import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE; +import static megamek.common.Compute.randomInt; +import static mekhq.campaign.finances.enums.TransactionType.EQUIPMENT_PURCHASE; +import static mekhq.campaign.mission.resupplyAndCaches.PerformResupply.loadPlayerConvoys; +import static mekhq.campaign.mission.resupplyAndCaches.PerformResupply.makeDelivery; +import static mekhq.campaign.mission.resupplyAndCaches.PerformResupply.makeSmugglerDelivery; +import static mekhq.campaign.mission.resupplyAndCaches.PerformResupply.processConvoy; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType.RESUPPLY_CONTRACT_END; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType.RESUPPLY_LOOT; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.ResupplyType.RESUPPLY_SMUGGLER; +import static mekhq.campaign.universe.Factions.getFactionLogo; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.gui.dialog.resupplyAndCaches.ResupplyDialogUtilities.createPartsReport; +import static mekhq.gui.dialog.resupplyAndCaches.ResupplyDialogUtilities.formatColumnData; +import static mekhq.gui.dialog.resupplyAndCaches.ResupplyDialogUtilities.getEnemyFactionReference; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +/** + * The {@code DialogItinerary} class generates and displays dialogs related to resupply operations. + * These include normal resupply, looting, contract-ending resupply, and smuggler-related resupplies. + */ +public class DialogItinerary { + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + /** + * Displays a detailed itinerary dialog based on the type of resupply operation. The dialog + * provides information such as convoy contents, supply values, roleplay items, speaker + * details, and visual assets. It also includes appropriate action buttons to handle + * confirmation, refusal, or delivery of supplies, depending on the operation type. + * + *

    This method performs the following tasks:

    + *
      + *
    1. Retrieves localized text and speaker information based on the resupply type.
    2. + *
    3. Generates a dynamic description message, including a formatted table of convoy contents.
    4. + *
    5. Builds a GUI using Swing, including visual assets like speaker icons and HTML-formatted text for details.
    6. + *
    7. Provides buttons with action listeners for confirmation, refusal, or receipt acknowledgment.
    8. + *
    9. Executes specific follow-up logic, such as resupply delivery or updating campaign finances, based on the + * user's choice and the resupply type.
    10. + *
    + * + * @param resupply the {@link Resupply} instance, which contains details about the resupply + * operation, including the campaign context, contract, convoy details, and + * resupply type. + */ + public static void itineraryDialog(Resupply resupply) { + final Campaign campaign = resupply.getCampaign(); + final AtBContract contract = resupply.getContract(); + final ResupplyType resupplyType = resupply.getResupplyType(); + + final int DIALOG_WIDTH = UIUtil.scaleForGUI(700); + + // Retrieves the title from the resources + String title = resources.getString("dialog.title"); + + // Create a custom dialog + JDialog dialog = new JDialog(); + dialog.setTitle(title); + dialog.setLayout(new BorderLayout()); + dialog.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + + // Establish the speaker + Person speaker; + String speakerName; + ImageIcon speakerIcon; + + if (resupplyType.equals(RESUPPLY_LOOT) || resupplyType.equals(RESUPPLY_CONTRACT_END)) { + speaker = campaign.getSeniorAdminPerson(AdministratorSpecialization.LOGISTICS); + + if (speaker != null) { + speakerName = speaker.getFullTitle(); + } else { + speakerName = campaign.getName(); + } + + speakerIcon = getSpeakerIcon(campaign, speaker); + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + } else if (resupplyType.equals(RESUPPLY_SMUGGLER)) { + speakerName = resources.getString("guerrillaSpeaker.text"); + + speakerIcon = getFactionLogo(campaign, "PIR", true); + speakerIcon = scaleImageIconToWidth(speakerIcon, 200); + } else { + speakerName = contract.getEmployerName(campaign.getGameYear()); + + speakerIcon = getFactionLogo(campaign, contract.getEmployerCode(), true); + speakerIcon = scaleImageIconToWidth(speakerIcon, 200); + } + + StringBuilder message = new StringBuilder(getInitialDescription(resupply)); + + List partsReport = createPartsReport(resupply); + if (!partsReport.isEmpty()) { + if (!resupplyType.equals(RESUPPLY_LOOT) && !resupplyType.equals(RESUPPLY_CONTRACT_END)) { + generateRoleplayItems(campaign, partsReport); + } + } + + String[] columns = formatColumnData(partsReport); + + message.append("") + .append("") + .append("") + .append("") + .append("
    ").append(columns[0]).append("").append(columns[1]).append("").append(columns[2]).append("
    "); + + // Create a panel to display the icon and the message + JLabel description = new JLabel( + String.format("
    %s
    ", + UIUtil.scaleForGUI(DIALOG_WIDTH), message)); + description.setHorizontalAlignment(JLabel.CENTER); + + JPanel descriptionPanel = new JPanel(); + descriptionPanel.setBorder(BorderFactory.createTitledBorder( + String.format(resources.getString("dialogBorderTitle.text"), speakerName))); + descriptionPanel.add(description); + + // Create the main panel to hold the description and image + JPanel panel = new JPanel(new BorderLayout()); + JLabel imageLabel = new JLabel(speakerIcon); + panel.add(imageLabel, BorderLayout.CENTER); + panel.add(descriptionPanel, BorderLayout.SOUTH); + + // Wrap the main content panel (panel) in a JScrollPane for scrolling + JScrollPane scrollPane = new JScrollPane(panel); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + + // Create the buttons and add their action listeners + JButton confirmButton = new JButton(resources.getString("confirmAccept.text")); + confirmButton.addActionListener(e -> { + dialog.dispose(); + campaign.getFinances().debit(EQUIPMENT_PURCHASE, campaign.getLocalDate(), + resupply.getConvoyContentsValueCalculated(), resources.getString("smugglerFee.text")); + + if (resupplyType.equals(RESUPPLY_SMUGGLER)) { + makeSmugglerDelivery(resupply); + } else { + if (resupply.getUsePlayerConvoy()) { + loadPlayerConvoys(resupply); + + final List convoyContents = resupply.getConvoyContents(); + if (!convoyContents.isEmpty()) { + campaign.addReport(String.format(resources.getString("convoyInsufficientSize.text"))); + + for (Part part : convoyContents) { + campaign.addReport("- " + part.getName()); + } + } + } else { + processConvoy(resupply, resupply.getConvoyContents(), null); + } + } + }); + + JButton refuseButton = new JButton(resources.getString("confirmRefuse.text")); + refuseButton.addActionListener(evt -> dialog.dispose()); + + JButton okButton = new JButton(resources.getString("confirmReceipt.text")); + okButton.addActionListener(evt -> { + dialog.dispose(); + makeDelivery(resupply, null); + }); + + // Create a panel for buttons and add buttons to it + JPanel buttonPanel = new JPanel(); + + switch (resupplyType) { + case RESUPPLY_NORMAL, RESUPPLY_SMUGGLER, RESUPPLY_CONTRACT_END -> { + buttonPanel.add(confirmButton); + buttonPanel.add(refuseButton); + } + case RESUPPLY_LOOT -> buttonPanel.add(okButton); + } + + // Create a new panel to show additional information below the button panel + JPanel infoPanel = new JPanel(); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + JLabel lblInfo = new JLabel( + String.format("
    %s
    %s
    ", + DIALOG_WIDTH, + String.format(resources.getString("roleplayItems.prompt")), + String.format(resources.getString("documentation.prompt")))); + infoPanel.add(lblInfo); + + // Create a container panel to hold both buttonPanel and infoPanel + JPanel southPanel = new JPanel(); + southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS)); // Stack them vertically + southPanel.add(buttonPanel); + southPanel.add(infoPanel); + + // Add the scroll pane for content and south panel to the dialog + dialog.add(scrollPane, BorderLayout.CENTER); + dialog.add(southPanel, BorderLayout.SOUTH); + + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + /** + * Generates roleplay item descriptions based on active personnel and their roles in the campaign. + * The roleplay items include items such as ration packs and medical supplies tailored to the personnel's roles. + * Additionally, randomized content is added to increase immersion and variety. + * + *

    This method processes:

    + *
      + *
    • Combat personnel to determine the number of ration packs required.
    • + *
    • Medical personnel to determine the need for medical supplies.
    • + *
    • Randomized flavor text for additional roleplay items with no tangible in-game effect.
    • + *
    + * + * @param campaign the {@link Campaign} to retrieve active personnel and their roles. + * @param partsReport the list of strings to which new descriptive content (roleplay items) is appended. + */ + private static void generateRoleplayItems(Campaign campaign, List partsReport) { + int rationPacks = 0; + int medicalSupplies = 0; + + for (Person person : campaign.getActivePersonnel()) { + PersonnelRole primaryRole = person.getPrimaryRole(); + PersonnelRole secondaryRole = person.getSecondaryRole(); + + if (primaryRole.isCombat() || secondaryRole.isCombat()) { + rationPacks++; + } + + if (primaryRole.isDoctor() || secondaryRole.isDoctor()) { + medicalSupplies++; + } + } + + rationPacks *= (int) Math.ceil((double) campaign.getLocalDate().lengthOfMonth() / 4); + + // These are all roleplay items that have no tangible benefit + if (rationPacks > 0) { + partsReport.add("" + resources.getString("resourcesRations.text") + + " x" + rationPacks + ""); + } + + if (medicalSupplies > 0) { + partsReport.add("" + resources.getString("resourcesMedical.text") + + " x" + medicalSupplies + ""); + } + + partsReport.add("" + resources.getString("resourcesRoleplay" + randomInt(50) + + ".text") + " x" + (randomInt((int) Math.ceil((double) rationPacks / 5)) + 1) + ""); + } + + /** + * Constructs the initial description of the resupply, tailored to the type of resupply event. + * + *

    This method:

    + *
      + *
    • Uses switch expressions to customize the output for different resupply types: + *
        + *
      • RESUPPLY_NORMAL: Includes morale-based flavor text and full supply cost details.
      • + *
      • RESUPPLY_LOOT: Generates text regarding salvaged supplies and their value.
      • + *
      • RESUPPLY_CONTRACT_END: Details the loot acquired at the end of the contract.
      • + *
      • RESUPPLY_SMUGGLER: Includes guerrilla flavor text, enemy faction info, + * and adjusted supply costs.
      • + *
      + *
    • + *
    + * + * @param resupply the {@link Resupply} object containing context such as resupply type, convoy contents, + * and related mission details. + * @return a string containing an HTML-formatted description that includes supply costs, salvage details, or + * guerrilla interactions, depending on the {@link ResupplyType}. + */ + private static String getInitialDescription(Resupply resupply) { + final Campaign campaign = resupply.getCampaign(); + final ResupplyType resupplyType = resupply.getResupplyType(); + + return switch (resupplyType) { + case RESUPPLY_NORMAL -> { + AtBContract contract = resupply.getContract(); + AtBMoraleLevel morale = contract.getMoraleLevel(); + + String message = resources.getString(morale.toString().toLowerCase() + "Supplies" + + randomInt(20) + ".text"); + + String value = String.format(resources.getString("supplyCostFull.text"), + resupply.getConvoyContentsValueCalculated().toAmountAndSymbolString(), + resupply.getConvoyContentsValueBase().toAmountAndSymbolString()); + + yield String.format(message, value); + } + case RESUPPLY_LOOT -> { + String message = resources.getString("salvaged" + randomInt(10) + ".text"); + + String value = String.format(resources.getString("supplyCostAbridged.text"), + resupply.getConvoyContentsValueBase().toAmountAndSymbolString()); + + yield String.format(message, value); + } + case RESUPPLY_CONTRACT_END -> { + String message = resources.getString("looted" + randomInt(10) + ".text"); + + String value = String.format(resources.getString("supplyCostAbridged.text"), + resupply.getConvoyContentsValueBase().toAmountAndSymbolString()); + + yield String.format(message, value); + } + case RESUPPLY_SMUGGLER -> { + String value = String.format(resources.getString("supplyCostFull.text"), + resupply.getConvoyContentsValueCalculated().toAmountAndSymbolString(), + resupply.getConvoyContentsValueBase().toAmountAndSymbolString()); + + yield String.format( + resources.getString("guerrillaSupplies" + randomInt(25) + ".text"), + campaign.getCommanderAddress(true), getEnemyFactionReference(resupply), + resupply.getConvoyContentsValueCalculated().toAmountAndSymbolString(), value); + } + }; + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogPlayerConvoyOption.java b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogPlayerConvoyOption.java new file mode 100644 index 00000000000..efa53c36f93 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogPlayerConvoyOption.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2024-2025 - 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.gui.dialog.resupplyAndCaches; + +import megamek.client.ui.swing.util.UIUtil; +import mekhq.campaign.Campaign; +import mekhq.campaign.Campaign.AdministratorSpecialization; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; +import mekhq.campaign.personnel.Person; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowListener; +import java.util.ResourceBundle; + +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerDescription; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +/** + * The {@code DialogPlayerConvoyOption} class provides functionality to display a dialog + * that prompts the player to choose whether to use their own convoy to transport cargo. + * The dialog dynamically generates content, localized messages, and speaker visuals to + * guide the player's decision. + */ +public class DialogPlayerConvoyOption extends JDialog { + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + final int INSERT_SIZE = UIUtil.scaleForGUI(10); + + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + /** + * Displays a dialog that allows the player to decide if they want to use their own convoy for resupply. + * The dialog presents key information such as required cargo tonnage, available convoy capacity, and + * the number of operational player convoys. When convoy usage is forced, the dialog reflects that with + * a customized message and altered behavior. + * + *

    This method performs the following tasks:

    + *
      + *
    • Builds a {@link JDialog} with a title and localized content fetched from a {@link ResourceBundle}.
    • + *
    • Determines and displays the speaker (e.g., logistics representative or faction representative) + * with an appropriate icon, scaled to 100px wide.
    • + *
    • Calculates relevant convoy data: + *
        + *
      • Required cargo tonnage for the operation
      • + *
      • Total player convoy capacity
      • + *
      • Number of operational convoys, with proper pluralization for the display text
      • + *
      + *
    • + *
    • Generates a decision-related message tailored to either optional or forced use of a convoy.
    • + *
    • Constructs the GUI with: + *
        + *
      • An icon and HTML-formatted description message panel.
      • + *
      • Action buttons—Accept (choose to use player convoy) and Refuse (reject)—with appropriate behavior.
      • + *
      + *
    • + *
    • Attaches a {@link WindowListener} to handle dialog closure and set default behavior + * (refuse convoy) if the dialog is closed without a selection.
    • + *
    • Applies modal blocking to ensure the dialog is interacted with before proceeding.
    • + *
    + * + *

    The dialog dynamically disables the "Accept" button if no operational convoys are available.

    + * + * @param resupply the {@link Resupply} instance providing campaign context, cargo details, + * and available player convoys. + * @param forcedUseOfPlayerConvoy a boolean flag indicating whether the use of the player's convoy + * is mandatory. If true, the optional decision is bypassed, and the + * dialog displays a notice reflecting the forced choice. + */ + public DialogPlayerConvoyOption(Resupply resupply, boolean forcedUseOfPlayerConvoy) { + final Campaign campaign = resupply.getCampaign(); + + setTitle(resources.getString("incomingTransmission.title")); + + // Main Panel to hold both boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + // Left box for speaker details + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get speaker details + Person speaker = campaign.getSeniorAdminPerson(AdministratorSpecialization.LOGISTICS); + + String speakerName; + if (speaker != null) { + speakerName = speaker.getFullTitle(); + } else { + speakerName = campaign.getName(); + } + + // Add speaker image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, speaker, speakerName); + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, INSERT_SIZE))); + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + mainPanel.add(leftBox, constraints); + + // Right box: Just a message + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + + int playerConvoyCount = resupply.getPlayerConvoys().size(); + String pluralizer = playerConvoyCount != 1 ? "s" : ""; + String messageResource; + + String message; + if (forcedUseOfPlayerConvoy) { + messageResource = resources.getString("usePlayerConvoyForced.text"); + + message = String.format(messageResource, campaign.getCommanderAddress(false), + resupply.getTargetCargoTonnagePlayerConvoy(), resupply.getTotalPlayerCargoCapacity(), + playerConvoyCount, pluralizer, pluralizer); + } else { + messageResource = resources.getString("usePlayerConvoyOptional.text"); + + message = String.format(messageResource, campaign.getCommanderAddress(false), + resupply.getTargetCargoTonnagePlayerConvoy(), resupply.getTotalPlayerCargoCapacity(), + playerConvoyCount, pluralizer, resupply.getTargetCargoTonnage(), pluralizer); + } + + JLabel rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, message)); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + constraints.gridx = 1; + constraints.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, constraints); + + add(mainPanel, BorderLayout.CENTER); + + // Create a container panel to hold both the button panel and the new panel + JPanel containerPanel = new JPanel(); + containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS)); // Stack vertically + + // Buttons panel + JPanel buttonPanel = new JPanel(); + + // Create the buttons and add their action listener. + JButton acceptButton = new JButton(resources.getString("confirmAccept.text")); + acceptButton.addActionListener(e -> { + dispose(); + resupply.setUsePlayerConvoy(true); + }); + acceptButton.setEnabled(playerConvoyCount > 0); + buttonPanel.add(acceptButton); + + JButton refuseButton = new JButton(resources.getString("confirmRefuse.text")); + refuseButton.addActionListener(e -> { + dispose(); + resupply.setUsePlayerConvoy(false); + }); + buttonPanel.add(refuseButton); + + // Add the button panel to the container + containerPanel.add(buttonPanel); + + // New panel (to be added below the button panel) + JPanel infoPanel = new JPanel(new BorderLayout()); + JLabel lblInfo = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH + LEFT_WIDTH, + String.format(resources.getString("documentation.prompt")))); + lblInfo.setHorizontalAlignment(SwingConstants.CENTER); + infoPanel.add(lblInfo, BorderLayout.CENTER); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + + // Add the new panel to the container (below the button panel) + containerPanel.add(infoPanel); + + // Add the container panel to the dialog (at the bottom of the layout) + add(containerPanel, BorderLayout.SOUTH); + + // Dialog settings + pack(); + setModal(true); + setLocationRelativeTo(null); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogResupplyFocus.java b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogResupplyFocus.java new file mode 100644 index 00000000000..9c6d881ede7 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogResupplyFocus.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2024-2025 - 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.gui.dialog.resupplyAndCaches; + +import megamek.client.ui.swing.util.UIUtil; +import mekhq.campaign.Campaign; +import mekhq.campaign.Campaign.AdministratorSpecialization; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; +import mekhq.campaign.personnel.Person; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; + +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerDescription; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +/** + * The {@code DialogResupplyFocus} class is responsible for displaying a dialog that allows + * the player to select their focus preference during a resupply operation. The player can + * choose between a balanced approach, prioritizing armor, or prioritizing ammunition. + * The dialog includes a speaker icon, a dynamically generated message, and actionable options. + */ +public class DialogResupplyFocus extends JDialog { + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + final int INSERT_SIZE = UIUtil.scaleForGUI(10); + + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + /** + * Displays a dialog to let the player select a resupply focus. The available options include: + *
      + *
    • Balanced: A default option with no specific prioritization.
    • + *
    • Armor Focus: Prioritize replenishing armor.
    • + *
    • Ammo Focus: Prioritize ammunition replenishment.
    • + *
    + * The player’s choice will dynamically adjust the resupply focus allocation in the {@link Resupply} object. + * + *

    This method performs the following tasks:

    + *
      + *
    1. Builds a {@link JDialog} with a localized title, speaker representation, and decision buttons.
    2. + *
    3. Generates a message describing the resupply decision options.
    4. + *
    5. Presents a dynamically chosen speaker from campaign logistics personnel, or a suitable fallback.
    6. + *
    7. Displays an icon representing the speaker, scaled to 100px wide for consistent presentation.
    8. + *
    9. Defines and attaches behaviors associated with each selection: + *
        + *
      • Balanced: Applies the default allocation (i.e., no adjustable focus).
      • + *
      • Armor: Sets 75% prioritization to armor resources, disabling focus on ammo and parts.
      • + *
      • Ammo: Sets 75% prioritization to ammunition, disabling focus on armor and parts.
      • + *
      + *
    10. + *
    11. Fixes restrictions for game balance purposes by disallowing direct prioritization of parts.
    12. + *
    13. Enforces modal behavior to ensure that the player interacts with the dialog before proceeding.
    14. + *
    + * + *

    The resupply focus is stored in the {@link Resupply} object after the dialog is completed.

    + * + * @param resupply the {@link Resupply} instance containing campaign and logistical context. + * The resupply focus preferences will be set within this object based on + * the player's selection. + */ + public DialogResupplyFocus(Resupply resupply) { + final Campaign campaign = resupply.getCampaign(); + + setTitle(resources.getString("incomingTransmission.title")); + + // Main Panel to hold both boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + // Left box for speaker details + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get speaker details + Person speaker = campaign.getSeniorAdminPerson(AdministratorSpecialization.LOGISTICS); + + String speakerName; + if (speaker != null) { + speakerName = speaker.getFullTitle(); + } else { + speakerName = campaign.getName(); + } + + // Add speaker image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, speaker, speakerName); + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, INSERT_SIZE))); + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + mainPanel.add(leftBox, constraints); + + // Right box: Just a message + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + + String message = String.format(resources.getString("focusDescription.text"), + campaign.getCommanderAddress(false)); + + JLabel rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, message)); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + constraints.gridx = 1; + constraints.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, constraints); + + add(mainPanel, BorderLayout.CENTER); + + // Create a container panel to hold both the button panel and the new panel + JPanel containerPanel = new JPanel(); + containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS)); // Stack vertically + + // Buttons panel + JPanel buttonPanel = new JPanel(); + JButton optionBalanced = new JButton(resources.getString("optionBalanced.text")); + optionBalanced.setToolTipText(resources.getString("optionBalanced.tooltip")); + optionBalanced.addActionListener(e -> { + dispose(); + // The Resupply class initialization assumes a balanced approach + }); + buttonPanel.add(optionBalanced); + + // The player should not be able to focus on parts for game balance reasons. + // If the player could pick parts, the optimum choice would be to always pick parts. + + JButton optionArmor = new JButton(resources.getString("optionArmor.text")); + optionArmor.setToolTipText(resources.getString("optionArmor.tooltip")); + optionArmor.addActionListener(e -> { + dispose(); + resupply.setFocusAmmo(0); + resupply.setFocusArmor(0.75); + resupply.setFocusParts(0); + }); + buttonPanel.add(optionArmor); + + JButton optionAmmo = new JButton(resources.getString("optionAmmo.text")); + optionAmmo.setToolTipText(resources.getString("optionAmmo.tooltip")); + optionAmmo.addActionListener(e -> { + dispose(); + resupply.setFocusAmmo(0.75); + resupply.setFocusArmor(0); + resupply.setFocusParts(0); + }); + buttonPanel.add(optionAmmo); + + // Add the button panel to the container + containerPanel.add(buttonPanel); + + // New panel (to be added below the button panel) + JPanel infoPanel = new JPanel(new BorderLayout()); + JLabel lblInfo = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH + LEFT_WIDTH, + String.format(resources.getString("documentation.prompt")))); + lblInfo.setHorizontalAlignment(SwingConstants.CENTER); + infoPanel.add(lblInfo, BorderLayout.CENTER); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + + // Add the new panel to the container (below the button panel) + containerPanel.add(infoPanel); + + // Add the container panel to the dialog (at the bottom of the layout) + add(containerPanel, BorderLayout.SOUTH); + + // Dialog settings + pack(); + setModal(true); + setLocationRelativeTo(null); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogRoleplayEvent.java b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogRoleplayEvent.java new file mode 100644 index 00000000000..3aed64a44ca --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogRoleplayEvent.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2024-2025 - 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.gui.dialog.resupplyAndCaches; + +import megamek.client.ui.swing.util.UIUtil; +import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; +import mekhq.campaign.personnel.Person; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; +import java.util.UUID; + +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerDescription; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +/** + * The {@code DialogRoleplayEvent} class handles the creation and display of roleplay event dialogs + * for convoy missions in MekHQ. These dialogs provide narrative elements to enhance the immersion + * of convoy missions by using dynamic content, player convoy details, and localized text. + */ +public class DialogRoleplayEvent extends JDialog { + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + final int INSERT_SIZE = UIUtil.scaleForGUI(10); + + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + /** + * Displays a roleplay event dialog for a player convoy. The dialog is used to present messages related + * to narrative events that occur during convoy missions, accompanied by speaker details for immersion. + * + *

    This method performs the following steps:

    + *
      + *
    1. Constructs a {@link JDialog} with a localized title from the resource bundle.
    2. + *
    3. Determines the speaker: + *
        + *
      • Attempts to retrieve the force commander from the convoy.
      • + *
      • If no commander is found, defaults to displaying the convoy name.
      • + *
      + *
    4. + *
    5. Fetches the speaker's faction or individual icon, scales it to 100 pixels wide for consistency, + * and displays it as part of the dialog.
    6. + *
    7. Constructs and displays a narrative message that includes game-specific dynamic details + * such as the commander address derived from the campaign.
    8. + *
    9. Organizes the GUI into panels: + *
        + *
      • An icon panel for the speaker's visualization.
      • + *
      • A description panel containing an HTML-formatted, localized message displayed in a + * centered, styled layout.
      • + *
      + *
    10. + *
    11. Includes a confirmation button that allows the user to dismiss the dialog, concluding the roleplay event.
    12. + *
    + * + *

    The dialog is modal to ensure the player engages with it before resuming other actions.

    + * + * @param campaign the {@link Campaign} instance, providing context for accessing relevant personnel + * and dynamic game data like the player commander address. + * @param playerConvoy the {@link Force} instance representing the player's convoy. This is used to retrieve + * the force commander and the convoy's name if no commander is available. + * @param eventText the narrative text describing the roleplay event. This string may include formatting + * placeholders ({@code %s}) to dynamically incorporate campaign-specific details. + */ + public DialogRoleplayEvent(Campaign campaign, Force playerConvoy, String eventText) { + setTitle(resources.getString("incomingTransmission.title")); + + // Main Panel to hold both boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + // Left box for speaker details + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get speaker details + UUID speakerId = playerConvoy.getForceCommanderID(); + Person speaker = campaign.getPerson(speakerId); + + String speakerName; + if (speaker != null) { + speakerName = speaker.getFullTitle(); + } else { + speakerName = campaign.getName(); + } + + // Add speaker image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, speaker); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, speaker, speakerName); + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, INSERT_SIZE))); + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + mainPanel.add(leftBox, constraints); + + // Right box: Just a message + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + + String message = String.format(eventText, campaign.getCommanderAddress(false)); + + JLabel rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, message)); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + constraints.gridx = 1; + constraints.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, constraints); + + add(mainPanel, BorderLayout.CENTER); + + // Create a container panel to hold both the button panel and the new panel + JPanel containerPanel = new JPanel(); + containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS)); // Stack vertically + + // Buttons panel + JPanel buttonPanel = new JPanel(); + JButton confirmButton = new JButton(resources.getString("convoyConfirm.text")); + confirmButton.addActionListener(e -> dispose()); + buttonPanel.add(confirmButton); + + // Add the button panel to the container + containerPanel.add(buttonPanel); + + // New panel (to be added below the button panel) + JPanel infoPanel = new JPanel(new BorderLayout()); + JLabel lblInfo = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH + LEFT_WIDTH, + String.format(resources.getString("documentation.prompt")))); + lblInfo.setHorizontalAlignment(SwingConstants.CENTER); + infoPanel.add(lblInfo, BorderLayout.CENTER); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + + // Add the new panel to the container (below the button panel) + containerPanel.add(infoPanel); + + // Add the container panel to the dialog (at the bottom of the layout) + add(containerPanel, BorderLayout.SOUTH); + + // Dialog settings + pack(); + setModal(true); + setLocationRelativeTo(null); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogSwindled.java b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogSwindled.java new file mode 100644 index 00000000000..682d854854d --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/DialogSwindled.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 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.gui.dialog.resupplyAndCaches; + +import megamek.client.generator.RandomCallsignGenerator; +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.Compute; +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; + +import static mekhq.campaign.universe.Factions.getFactionLogo; +import static mekhq.gui.dialog.resupplyAndCaches.ResupplyDialogUtilities.getEnemyFactionReference; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; + +/** + * The {@code DialogSwindled} class provides functionality to display a dialog related to swindling events + * during guerrilla contract missions. This dialog presents localized narrative content with + * dynamic elements such as faction logos and contextual details about enemy factions. + */ +public class DialogSwindled extends JDialog { + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + final int INSERT_SIZE = UIUtil.scaleForGUI(10); + + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + /** + * Displays a dialog notifying the player that they have been swindled during a resupply. + * The dialog includes details about the incident, a visual representation via a faction logo, and a confirmation + * button to allow the player to dismiss the dialog after reading it. + * + *

    This method performs the following tasks:

    + *
      + *
    1. Sets up a {@link JDialog} with a localized title and manages dialog layout.
    2. + *
    3. Retrieves and displays a faction representative's logo: + *
        + *
      • Uses a default faction logo (PIR for generic pirate factions).
      • + *
      • Scales the logo to 100 pixels for consistent sizing and presentation.
      • + *
      + *
    4. + *
    5. Generates a randomized, localized narrative message describing the swindling event: + *
        + *
      • Fetches the player's commander address from the campaign.
      • + *
      • Includes a reference to the enemy faction involved in the event, dynamically resolved from the resupply data.
      • + *
      • Applies randomization to select from predefined narrative templates for replay variety.
      • + *
      + *
    6. + *
    7. Organizes the dialog layout into: + *
        + *
      • An icon panel displaying the faction logo.
      • + *
      • A description panel containing the dynamically generated narrative message.
      • + *
      + *
    8. + *
    9. Adds a confirmation button that dismisses the dialog when pressed.
    10. + *
    11. Enforces modal behavior to ensure the player acknowledges the dialog before continuing.
    12. + *
    + * + *

    This dialog enhances the narrative immersion of being swindled in missions by presenting + * visually and contextually rich content relevant to the player's situation.

    + * + * @param resupply the {@link Resupply} instance containing details about the current campaign, contract, + * and mission context. This object is used to retrieve dynamic elements such as the enemy + * faction and the player's information. + */ + public DialogSwindled(Resupply resupply) { + final Campaign campaign = resupply.getCampaign(); + + setTitle(resources.getString("incomingTransmission.title")); + + // Main Panel to hold both boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(INSERT_SIZE, INSERT_SIZE, INSERT_SIZE, INSERT_SIZE); + constraints.fill = GridBagConstraints.BOTH; + constraints.weighty = 1; + + // Left box for speaker details + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get speaker details + final RandomCallsignGenerator callsignGenerator = RandomCallsignGenerator.getInstance(); + String smugglerCallSign = callsignGenerator.generate(); + String smugglerTitle = resources.getString("guerrillaSpeaker.text"); + String speakerName = String.format("'%s'
    %s", smugglerCallSign, smugglerTitle); + + ImageIcon speakerIcon = getFactionLogo(campaign, "PIR", true); + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH, speakerName)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add the image and description to the leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, INSERT_SIZE))); + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + mainPanel.add(leftBox, constraints); + + // Right box: Just a message + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + + String enemyFactionReference = getEnemyFactionReference(resupply); + String message = String.format( + resources.getString("guerrillaSwindled" + Compute.randomInt(25) + ".text"), + campaign.getCommanderAddress(true), + enemyFactionReference); + + JLabel rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, message)); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + constraints.gridx = 1; + constraints.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, constraints); + + add(mainPanel, BorderLayout.CENTER); + + // Create a container panel to hold both the button panel and the new panel + JPanel containerPanel = new JPanel(); + containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS)); // Stack vertically + + // Buttons panel + JPanel buttonPanel = new JPanel(); + JButton confirmButton = new JButton(resources.getString("logisticsDestroyed.text")); + confirmButton.addActionListener(e -> dispose()); + buttonPanel.add(confirmButton); + + // Add the button panel to the container + containerPanel.add(buttonPanel); + + // New panel (to be added below the button panel) + JPanel infoPanel = new JPanel(new BorderLayout()); + JLabel lblInfo = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH + LEFT_WIDTH, + String.format(resources.getString("documentation.prompt")))); + lblInfo.setHorizontalAlignment(SwingConstants.CENTER); + infoPanel.add(lblInfo, BorderLayout.CENTER); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + + // Add the new panel to the container (below the button panel) + containerPanel.add(infoPanel); + + // Add the container panel to the dialog (at the bottom of the layout) + add(containerPanel, BorderLayout.SOUTH); + + // Dialog settings + pack(); + setModal(true); + setLocationRelativeTo(null); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + } +} diff --git a/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/ResupplyDialogUtilities.java b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/ResupplyDialogUtilities.java new file mode 100644 index 00000000000..75aa53c08be --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/resupplyAndCaches/ResupplyDialogUtilities.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024-2025 - 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.gui.dialog.resupplyAndCaches; + +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; +import mekhq.campaign.parts.Armor; +import mekhq.campaign.parts.MekActuator; +import mekhq.campaign.parts.MekLocation; +import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.equipment.AmmoBin; +import mekhq.campaign.universe.Faction; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static mekhq.campaign.market.procurement.Procurement.getFactionTechCode; + +/** + * Utility class for managing and facilitating dialog-related operations + * in resupply missions within MekHQ campaigns. + * + *

    This class includes methods for:

    + *
      + *
    • Selecting appropriate logistics representatives based on rank and skill.
    • + *
    • Fetching icons for dialog speakers or factions.
    • + *
    • Creating detailed parts reports from resupply convoy contents.
    • + *
    • Formatting data for column-based UI display.
    • + *
    • Generating references to enemy factions in the campaign.
    • + *
    + * + *

    Primarily used for managing UI-related elements in the resupply dialogs, including + * presentation and interaction contexts tied to resupply scenarios.

    + */ +public class ResupplyDialogUtilities { + /** + * Generates a detailed report of the parts available in the convoy contents of the given resupply mission. + * + *

    The report lists parts with their names, qualities, and additional properties such as technology type + * (Clan or Mixed), potential extinction status, tonnage, and ammunition shot counts.

    + * + * @param resupply the {@link Resupply} instance defining the convoy's context. + * @return a {@link List} of formatted strings representing the parts report, sorted alphabetically. + */ + static List createPartsReport(Resupply resupply) { + final Campaign campaign = resupply.getCampaign(); + Faction originFaction = campaign.getFaction(); + int year = campaign.getGameYear(); + + final List convoyContents = resupply.getConvoyContents(); + + Map entries = convoyContents.stream().collect(Collectors.toMap( + part -> { + String name = part.getName(); + String quality = part.getQualityName(); + + String append = part.isClan() ? " (Clan)" : ""; + append = part.isMixedTech() ? " (Mixed)" : append; + append += " (" + quality + ')'; + append += part.isExtinct(year, originFaction.isClan(), getFactionTechCode(originFaction)) ? + " (EXTINCT!)" : ""; + + if (part instanceof AmmoBin) { + return ((AmmoBin) part).getType().getName() + append; + } else if (part instanceof MekLocation || part instanceof MekActuator) { + return name + " (" + part.getUnitTonnage() + "t)" + append; + } else { + return name + append; + } + }, + part -> { + if (part instanceof AmmoBin) { + return ((AmmoBin) part).getFullShots() * 5; + } else if (part instanceof Armor) { + return (int) Math.ceil(((Armor) part).getArmorPointsPerTon() * 5); + } else { + return 1; + } + }, + Integer::sum)); + + return entries.keySet().stream() + .map(item -> item + " x" + entries.get(item)) + .sorted() + .collect(Collectors.toList()); + } + + /** + * Formats a list of part reports into an array of three columns for visual representation, + * ensuring that the parts are distributed across the columns evenly. + * + *

    Each column entry is prepended with a bullet point and presented as an HTML string for rendering.

    + * + * @param partsReport the {@link List} of part report entries to be formatted. + * @return a {@link String} array containing three HTML-formatted columns. + */ + public static String[] formatColumnData(List partsReport) { + String[] columns = new String[3]; + Arrays.fill(columns, ""); + + int i = 0; + for (String entry : partsReport) { + columns[i % 3] += "
    - " + entry; + i++; + } + + return columns; + } + + /** + * Retrieves a formatted string referencing the enemy faction in the context of the current resupply mission. + * + *

    If the faction is not a Clan, it adds the prefix "the" to the faction name. The full faction + * name is resolved for the campaign's current game year.

    + * + * @param resupply the {@link Resupply} instance defining the mission's context. + * @return a {@link String} containing the enemy faction reference. + */ + public static String getEnemyFactionReference(Resupply resupply) { + final AtBContract contract = resupply.getContract(); + + String enemyFactionReference = contract.getEnemyBotName(); + + if (!enemyFactionReference.contains("Clan")) { + enemyFactionReference = "the " + enemyFactionReference; + } + + return enemyFactionReference; + } +} diff --git a/MekHQ/src/mekhq/gui/menus/AssignForceToShipTransportMenu.java b/MekHQ/src/mekhq/gui/menus/AssignForceToShipTransportMenu.java new file mode 100644 index 00000000000..4ce95a27529 --- /dev/null +++ b/MekHQ/src/mekhq/gui/menus/AssignForceToShipTransportMenu.java @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2025-2025 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.gui.menus; + +import mekhq.campaign.unit.enums.TransporterType; +import mekhq.campaign.utilities.CampaignTransportUtilities; +import mekhq.utilities.MHQInternationalization; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.event.UnitChangedEvent; +import mekhq.campaign.unit.Unit; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.util.HashSet; +import java.util.Set; + +import static mekhq.campaign.enums.CampaignTransportType.SHIP_TRANSPORT; + +/** + * Menu for assigning a unit to a specific Ship Transport + * @see CampaignTransportType#SHIP_TRANSPORT + * @see mekhq.campaign.unit.ShipTransportedUnitsSummary + * @see mekhq.campaign.unit.TransportShipAssignment + */ +public class AssignForceToShipTransportMenu extends AssignForceToTransportMenu { + + /** + * Constructor for a new ship transport Menu + * @param campaign current campaign + * @param units selected units to try and assign + * @see CampaignTransportType#SHIP_TRANSPORT + */ + public AssignForceToShipTransportMenu(Campaign campaign, Set units) { + super(SHIP_TRANSPORT, campaign, units); + } + + /** + * Returns a Set of Transporters that the provided units could all be loaded into + * for Ship Transport. + * @param units filter the transporter list based on what these units could use + * @return Transporters suitable for long-term or space travel. + */ + @Override + protected Set filterTransporterTypeMenus(final Set units) { + Set transporterTypes = new HashSet<>(campaign.getTransports(campaignTransportType).keySet()); + + for (Unit unit : units) { + Set unitTransporterTypes = CampaignTransportUtilities.mapEntityToTransporters(SHIP_TRANSPORT, unit.getEntity()); + if (!unitTransporterTypes.isEmpty()) { + transporterTypes.retainAll(unitTransporterTypes); + } else { + return new HashSet<>(); + } + } + if (transporterTypes.isEmpty()) { + return new HashSet<>(); + } + + return transporterTypes; + } + + /** + * Assigns the units to the Ship Transport. + * @param evt ActionEvent from the selection happening + * @param transporterType transporter type selected in an earlier menu + * @param transport transport (Unit) that will load these units + * @param units units being assigned to the transport + */ + @Override + protected void transportMenuAction(ActionEvent evt, TransporterType transporterType, Unit transport, Set units) { + for (Unit unit : units) { + if (!transport.getEntity().canLoad(unit.getEntity(), false)) { + JOptionPane.showMessageDialog(null,MHQInternationalization.getFormattedTextAt( + "mekhq.resources.AssignForceToTransport", "AssignForceToTransportMenu.warningCouldNotLoadUnit.text", + unit.getName(), transport.getName()), "Warning", JOptionPane.WARNING_MESSAGE); + return; + } + + } + Set oldTransports = transport.loadShipTransport(transporterType, units); + if (!oldTransports.isEmpty()) { + oldTransports.forEach(oldTransport -> campaign.updateTransportInTransports(campaignTransportType, oldTransport)); + oldTransports.forEach(oldTransport -> MekHQ.triggerEvent(new UnitChangedEvent(transport))); + } + for (Unit unit : units) { + MekHQ.triggerEvent(new UnitChangedEvent(unit)); + } + campaign.updateTransportInTransports(campaignTransportType, transport); + MekHQ.triggerEvent(new UnitChangedEvent(transport)); + } + +} diff --git a/MekHQ/src/mekhq/gui/menus/AssignForceToTacticalTransportMenu.java b/MekHQ/src/mekhq/gui/menus/AssignForceToTacticalTransportMenu.java new file mode 100644 index 00000000000..d70497ea649 --- /dev/null +++ b/MekHQ/src/mekhq/gui/menus/AssignForceToTacticalTransportMenu.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2025-2025 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.gui.menus; + +import mekhq.campaign.unit.enums.TransporterType; +import mekhq.campaign.utilities.CampaignTransportUtilities; +import mekhq.utilities.MHQInternationalization; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.event.UnitChangedEvent; +import mekhq.campaign.unit.Unit; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.util.HashSet; +import java.util.Set; + +import static mekhq.campaign.enums.CampaignTransportType.TACTICAL_TRANSPORT; + +/** + * Menu for assigning a force to a specific Tactical transport + * @see CampaignTransportType#TACTICAL_TRANSPORT + * @see mekhq.campaign.unit.TacticalTransportedUnitsSummary + * @see mekhq.campaign.unit.TransportAssignment + */ +public class AssignForceToTacticalTransportMenu extends AssignForceToTransportMenu { + + /** + * Constructor for a new tactical transport Menu + * @param campaign current campaign + * @param units selected units to try and assign + * @see CampaignTransportType#TACTICAL_TRANSPORT + */ + public AssignForceToTacticalTransportMenu(Campaign campaign, Set units) { + super(TACTICAL_TRANSPORT, campaign, units); + } + + /** + * Returns a Set of Transporters that the provided units could all be loaded into + * for Tactical Transport. + * @param units filter the Transporter list based on what these units could use + * @return most Transporter types except cargo and hitches + */ + @Override + protected Set filterTransporterTypeMenus(final Set units) { + Set transporterTypes = new HashSet<>(campaign.getTransports(TACTICAL_TRANSPORT).keySet()); + + for (Unit unit : units) { + Set unitTransporterTypes = CampaignTransportUtilities.mapEntityToTransporters(TACTICAL_TRANSPORT, unit.getEntity()); + if (!unitTransporterTypes.isEmpty()) { + transporterTypes.retainAll(unitTransporterTypes); + } else { + return new HashSet<>(); + } + } + if (transporterTypes.isEmpty()) { + return new HashSet<>(); + } + + return transporterTypes; + } + + /** + * Assign a unit to a Tactical Transport. + * @param evt ActionEvent from the selection happening + * @param transporterType transporter type selected in an earlier menu + * @param transport transport (Unit) that will load these units + * @param units units being assigned to the transport + */ + @Override + protected void transportMenuAction(ActionEvent evt, TransporterType transporterType, Unit transport, Set units) { + for (Unit unit : units) { + if (!transport.getEntity().canLoad(unit.getEntity(), false)) { + JOptionPane.showMessageDialog(null,MHQInternationalization.getFormattedTextAt( + "mekhq.resources.AssignForceToTransport", "AssignForceToTransportMenu.warningCouldNotLoadUnit.text", + unit.getName(), transport.getName() ), "Warning", JOptionPane.WARNING_MESSAGE); + return; + } + + } + Set oldTransports = transport.loadTacticalTransport(transporterType, units); + if (!oldTransports.isEmpty()) { + oldTransports.forEach(oldTransport -> campaign.updateTransportInTransports(TACTICAL_TRANSPORT, oldTransport)); + oldTransports.forEach(oldTransport -> MekHQ.triggerEvent(new UnitChangedEvent(transport))); + } + for (Unit unit : units) { + MekHQ.triggerEvent(new UnitChangedEvent(unit)); + } + campaign.updateTransportInTransports(TACTICAL_TRANSPORT, transport); + MekHQ.triggerEvent(new UnitChangedEvent(transport)); + } +} diff --git a/MekHQ/src/mekhq/gui/menus/AssignForceToTransportMenu.java b/MekHQ/src/mekhq/gui/menus/AssignForceToTransportMenu.java new file mode 100644 index 00000000000..c65b813e071 --- /dev/null +++ b/MekHQ/src/mekhq/gui/menus/AssignForceToTransportMenu.java @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2025-2025 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.gui.menus; + +import mekhq.campaign.Campaign; +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.force.Force; +import mekhq.campaign.unit.Unit; +import mekhq.campaign.unit.enums.TransporterType; +import mekhq.campaign.utilities.CampaignTransportUtilities; +import mekhq.gui.baseComponents.JScrollableMenu; +import mekhq.utilities.MHQInternationalization; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.util.HashSet; +import java.util.Set; + +/** + * Generic menu for displaying transports for the units in the force selected (or an individual unit). + * + * @see CampaignTransportType + * @see mekhq.campaign.unit.AbstractTransportedUnitsSummary + * @see mekhq.campaign.unit.ITransportAssignment + */ +public abstract class AssignForceToTransportMenu extends JScrollableMenu { + + final Campaign campaign; + final CampaignTransportType campaignTransportType; + + // region Constructors + + /** + * Constructor for a new Transport Menu + * @param campaignTransportType type (Enum) of transport type for this menu + * @param campaign current campaign + * @param units selected units to try and assign + * @see CampaignTransportType + */ + public AssignForceToTransportMenu(CampaignTransportType campaignTransportType, final Campaign campaign, final Set units) { + super(campaignTransportType.name()); + this.campaign = campaign; + this.campaignTransportType = campaignTransportType; + initialize(units); + } + // endregion Constructors + + private void initialize(final Set units) { + /* + * Immediate Return for Illegal Assignments: + * 1) No units to assign + * 2) Any units are currently unavailable + * 3) No transports available + */ + if ((units.isEmpty() || (units.stream().anyMatch(unit -> !unit.isAvailable())) + || (!campaign.hasTransports(campaignTransportType)))) { + return; + } + + Set transporterTypeMenus = createTransporterTypeMenus(units); + if(transporterTypeMenus.isEmpty()) { + return; + } + + //Assign Unit to {campaignTransportTypeName} + setText(MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", "AssignForceToTransportMenu." + campaignTransportType.name() + ".text")); + for (JScrollableMenu transporterTypeMenu : transporterTypeMenus) { + add(transporterTypeMenu); + } + } + + + /** + * Create the menus for selecting a transporter type + * to try and load these units into + * @param units units being assigned a transport + * @return menu of transporter types + */ + protected Set createTransporterTypeMenus(final Set units) { + Set transporterTypeMenus = new HashSet<>(); + /* Let's get the transport types our campaign has + and remove the ones that can't be used by these units. + While we're at it, let's get the total capacity we'll + need to transport all these units. If they use different + calculation methods (an infantry and tank were both selected) + then they shouldn't have any compatible transport types + */ + for (TransporterType transporterType : filterTransporterTypeMenus(units)) { + double requiredTransportCapacity = 0.0; + for (Unit unit : units) { + requiredTransportCapacity += CampaignTransportUtilities.transportCapacityUsage(transporterType, unit.getEntity()); + } + + Set transports = campaign.getTransportsByType(campaignTransportType, transporterType, requiredTransportCapacity); + transports.removeIf(transport -> transport.getForceId() == Force.FORCE_NONE); + + if (!transports.isEmpty()) { + JScrollableMenu transporterTypeMenu = new JScrollableMenu(transporterType.toString(), transporterType.toString()); + Set transportMenus = createTransportMenus(transporterType, transports, units); + for (JMenuItem transportMenu : transportMenus) { + transporterTypeMenu.add(transportMenu); + } + + // {name of the bay} + transporterTypeMenu.setText(MHQInternationalization.getTextAt("mekhq.resources.AssignForceToTransport", + "AssignForceToTransportMenu." + transporterType + ".text")); + + transporterTypeMenus.add(transporterTypeMenu); + } + } + + return transporterTypeMenus; + } + + private Set createTransportMenus(TransporterType transporterType, Set transports, Set units) { + Set transportMenus = new HashSet<>(); + for (Unit transport : transports) { + JMenuItem transportMenu = new JMenuItem(transport.getId().toString()); + + // {Transport Name} | Space Remaining: {Current Transport Capacity} + transportMenu.setText(MHQInternationalization.getFormattedTextAt("mekhq.resources.AssignForceToTransport", + "AssignForceToTransportMenu.transportSpaceRemaining.text", + transport.getName(), transport.getCurrentTransportCapacity(campaignTransportType, transporterType))); + + transportMenu.addActionListener(evt -> transportMenuAction(evt, transporterType, transport, units)); + transportMenus.add(transportMenu); + } + return transportMenus; + } + + /** + * Different transporter type menus return different transporters + * @param units filter the transporter list based on what these units could use + * @return transporters that can be used by all these units + * @see CampaignTransportType + */ + protected abstract Set filterTransporterTypeMenus(final Set units); + + /** + * Different transporter type menus do different things when selected + * @param evt ActionEvent from the selection happening + * @param transporterType transporter type selected in an earlier menu + * @param transport transport (Unit) that will load these units + * @param units units being assigned to the transport + */ + protected abstract void transportMenuAction(ActionEvent evt, TransporterType transporterType, Unit transport, Set units); + +} diff --git a/MekHQ/src/mekhq/gui/menus/AssignPersonToUnitMenu.java b/MekHQ/src/mekhq/gui/menus/AssignPersonToUnitMenu.java index 89692da20f5..368c6881d10 100644 --- a/MekHQ/src/mekhq/gui/menus/AssignPersonToUnitMenu.java +++ b/MekHQ/src/mekhq/gui/menus/AssignPersonToUnitMenu.java @@ -225,7 +225,7 @@ private void initialize(final Campaign campaign, final Person... people) { // Pilot Menu if (unit.canTakeMoreDrivers()) { // Pilot Menu - Solo Pilot and VTOL Pilot Assignment - if (singlePerson && (unit.usesSoloPilot() || (entity instanceof VTOL) || entity.isSuperHeavy() || entity.isTripodMek())) { + if (singlePerson && (unit.usesSoloPilot() || (entity instanceof VTOL) || entity.isSuperHeavy() || entity.isTripodMek() || entity.isQuadMek())) { final boolean valid; if (entity instanceof Mek) { valid = areAllBattleMekPilots; @@ -320,7 +320,7 @@ private void initialize(final Campaign campaign, final Person... people) { } else if ((entity instanceof SmallCraft) || (entity instanceof Jumpship)) { valid = areAllVesselGunners; - } else if (entity.isTripodMek() || entity.isSuperHeavy()) { + } else if (entity.isTripodMek() || entity.isSuperHeavy() || entity.isQuadMek()) { valid = areAllBattleMekPilots; } else { valid = false; diff --git a/MekHQ/src/mekhq/gui/menus/AssignUnitToPersonMenu.java b/MekHQ/src/mekhq/gui/menus/AssignUnitToPersonMenu.java index e758b2108f5..1847b272c7d 100644 --- a/MekHQ/src/mekhq/gui/menus/AssignUnitToPersonMenu.java +++ b/MekHQ/src/mekhq/gui/menus/AssignUnitToPersonMenu.java @@ -109,6 +109,7 @@ private void createPersonAssignmentMenus(final Campaign campaign, final Unit... final boolean isVTOL = entity instanceof VTOL; final boolean isMek = entity instanceof Mek; final boolean isTripod = entity instanceof TripodMek; + final boolean isQuadMek = entity instanceof QuadMek; final boolean isSuperHeavyMek = isMek && entity.isSuperHeavy(); final boolean isProtoMek = entity instanceof ProtoMek; final boolean isConventionalAircraft = entity instanceof ConvFighter; @@ -214,7 +215,7 @@ private void createPersonAssignmentMenus(final Campaign campaign, final Unit... // Pilot Menu if (canTakeMoreDrivers) { // Pilot Menu - if (usesSoloPilot || isVTOL || isSmallCraftOrJumpShip || isSuperHeavyMek || isTripod) { + if (usesSoloPilot || isVTOL || isSmallCraftOrJumpShip || isSuperHeavyMek || isTripod || isQuadMek) { if (isMek) { filteredPersonnel = personnel.stream() .filter(person -> person.getPrimaryRole().isMekWarriorGrouping() @@ -418,11 +419,11 @@ private void createPersonAssignmentMenus(final Campaign campaign, final Unit... } // Gunners Menu - if (canTakeMoreGunners && (isTank || isSmallCraftOrJumpShip || isSuperHeavyMek || isTripod)) { + if (canTakeMoreGunners && (isTank || isSmallCraftOrJumpShip || isSuperHeavyMek || isTripod || isQuadMek)) { filteredPersonnel = personnel.stream() .filter(person -> (isSmallCraftOrJumpShip && person.hasRole(PersonnelRole.VESSEL_GUNNER)) || (isTank && person.hasRole(PersonnelRole.VEHICLE_GUNNER)) || - ((isSuperHeavyMek || isTripod) && person.getPrimaryRole().isMekWarriorGrouping() + ((isSuperHeavyMek || isTripod || isQuadMek) && person.getPrimaryRole().isMekWarriorGrouping() || person.getSecondaryRole().isMekWarriorGrouping())) .collect(Collectors.toList()); if (!filteredPersonnel.isEmpty()) { @@ -447,7 +448,7 @@ private void createPersonAssignmentMenus(final Campaign campaign, final Unit... skillLevel = person.getSkillLevel(campaign, !person.getPrimaryRole().isVesselGunner()); } else if (isTank) { skillLevel = person.getSkillLevel(campaign, !person.getPrimaryRole().isVehicleGunner()); - } else if (isSuperHeavyMek || isTripod) { + } else if (isSuperHeavyMek || isTripod || isQuadMek) { skillLevel = person.getSkillLevel(campaign, !person.getPrimaryRole().isMekWarriorGrouping()); } diff --git a/MekHQ/src/mekhq/gui/model/AutoAwardsTableModel.java b/MekHQ/src/mekhq/gui/model/AutoAwardsTableModel.java index 14f78dd5012..2f537f7c65e 100644 --- a/MekHQ/src/mekhq/gui/model/AutoAwardsTableModel.java +++ b/MekHQ/src/mekhq/gui/model/AutoAwardsTableModel.java @@ -61,11 +61,11 @@ public void setData(Map> map) { if (map.isEmpty()) { logger.error("AutoAwardsDialog failed to pass 'data' into AutoAwardsTableModel"); } else { - logger.info("AutoAwardsDialog passed 'data' into AutoAwardsTableModel: {}", map); + logger.debug("AutoAwardsDialog passed 'data' into AutoAwardsTableModel: {}", map); } data = map; - logger.info("Translated data: {}", data); + logger.debug("Translated data: {}", data); } @Override diff --git a/MekHQ/src/mekhq/gui/model/FinanceTableModel.java b/MekHQ/src/mekhq/gui/model/FinanceTableModel.java index 62291d33742..01f360271ee 100644 --- a/MekHQ/src/mekhq/gui/model/FinanceTableModel.java +++ b/MekHQ/src/mekhq/gui/model/FinanceTableModel.java @@ -75,6 +75,9 @@ public String getColumnName(int column) { @Override public Object getValueAt(int row, int col) { + if (row < 0 || row >= getRowCount() || col < 0 || col >= getColumnCount()) { + return ""; + } Transaction transaction = getTransaction(row); Money amount = transaction.getAmount(); Money balance = Money.zero(); diff --git a/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java b/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java index 3bd36fb7222..aee6ec569b0 100644 --- a/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java +++ b/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java @@ -39,6 +39,7 @@ import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumnModel; +import javax.swing.event.*; import mekhq.MekHQ; import mekhq.campaign.parts.PartInUse; @@ -55,14 +56,15 @@ public class PartsInUseTableModel extends DataTableModel { public final static int COL_IN_USE = 1; public final static int COL_STORED = 2; public final static int COL_TONNAGE = 3; - public final static int COL_IN_TRANSFER = 4; - public final static int COL_COST = 5; - public final static int COL_BUTTON_BUY = 6; - public final static int COL_BUTTON_BUY_BULK = 7; - public final static int COL_BUTTON_SELL = 8; - public final static int COL_BUTTON_SELL_BULK = 9; - public final static int COL_BUTTON_GMADD = 10; - public final static int COL_BUTTON_GMADD_BULK = 11; + public final static int COL_REQUSTED_STOCK = 4; + public final static int COL_IN_TRANSFER = 5; + public final static int COL_COST = 6; + public final static int COL_BUTTON_BUY = 7; + public final static int COL_BUTTON_BUY_BULK = 8; + public final static int COL_BUTTON_SELL = 9; + public final static int COL_BUTTON_SELL_BULK = 10; + public final static int COL_BUTTON_GMADD = 11; + public final static int COL_BUTTON_GMADD_BULK = 12; private final transient ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.PartsInUseTableModel", MekHQ.getMHQOptions().getLocale()); @@ -96,6 +98,8 @@ public String getColumnName(int column) { return resourceMap.getString("ordered.heading"); case COL_COST: return resourceMap.getString("cost.heading"); + case COL_REQUSTED_STOCK: + return resourceMap.getString("requestedStock.heading"); default: return EMPTY_CELL; } @@ -103,27 +107,27 @@ public String getColumnName(int column) { @Override public Object getValueAt(int row, int column) { - PartInUse piu = getPartInUse(row); + PartInUse partInUse = getPartInUse(row); switch (column) { case COL_PART: - return piu.getDescription(); + return partInUse.getDescription(); case COL_IN_USE: - return FORMATTER.format(piu.getUseCount()); + return FORMATTER.format(partInUse.getUseCount()); case COL_STORED: - return (piu.getStoreCount() > 0) ? FORMATTER.format(piu.getStoreCount()) : EMPTY_CELL; + return (partInUse.getStoreCount() > 0) ? FORMATTER.format(partInUse.getStoreCount()) : EMPTY_CELL; case COL_TONNAGE: - return (piu.getStoreTonnage() > 0) ? FORMATTER.format(piu.getStoreTonnage()) : EMPTY_CELL; + return (partInUse.getStoreTonnage() > 0) ? FORMATTER.format(partInUse.getStoreTonnage()) : EMPTY_CELL; case COL_IN_TRANSFER: - if (piu.getTransferCount() > 0 && piu.getPlannedCount() <= 0) { - return FORMATTER.format(piu.getTransferCount()); - } else if (piu.getPlannedCount() > 0) { + if (partInUse.getTransferCount() > 0 && partInUse.getPlannedCount() <= 0) { + return FORMATTER.format(partInUse.getTransferCount()); + } else if (partInUse.getPlannedCount() > 0) { return String.format("%s [+%s]", - FORMATTER.format(piu.getTransferCount()), FORMATTER.format(piu.getPlannedCount())); + FORMATTER.format(partInUse.getTransferCount()), FORMATTER.format(partInUse.getPlannedCount())); } else { return EMPTY_CELL; } case COL_COST: - return piu.getCost().toAmountAndSymbolString(); + return partInUse.getCost().toAmountAndSymbolString(); case COL_BUTTON_BUY: return resourceMap.getString("buy.text"); case COL_BUTTON_BUY_BULK: @@ -136,6 +140,8 @@ public Object getValueAt(int row, int column) { return resourceMap.getString("add.text"); case COL_BUTTON_GMADD_BULK: return resourceMap.getString("addInBulk.text"); + case COL_REQUSTED_STOCK: + return partInUse.getRequestedStock() + "%"; default: return EMPTY_CELL; } @@ -155,6 +161,7 @@ public boolean isCellEditable(int row, int col) { case COL_BUTTON_SELL_BULK: case COL_BUTTON_GMADD: case COL_BUTTON_GMADD_BULK: + case COL_REQUSTED_STOCK: return true; default: return false; @@ -166,8 +173,8 @@ public void setData(Set data) { } @SuppressWarnings("unchecked") - public void updateRow(int row, PartInUse piu) { - ((ArrayList) data).set(row, piu); + public void updateRow(int row, PartInUse partInUse) { + ((ArrayList) data).set(row, partInUse); fireTableRowsUpdated(row, row); } @@ -192,6 +199,7 @@ public int getAlignment(int column) { case COL_TONNAGE: case COL_IN_TRANSFER: case COL_COST: + case COL_REQUSTED_STOCK: return SwingConstants.RIGHT; default: return SwingConstants.CENTER; @@ -206,6 +214,7 @@ public int getPreferredWidth(int column) { case COL_BUTTON_BUY, COL_BUTTON_SELL -> 25; case COL_BUTTON_GMADD -> 65; case COL_BUTTON_BUY_BULK, COL_BUTTON_SELL_BULK -> 65; + case COL_REQUSTED_STOCK -> 45; default -> 100; }; } @@ -400,4 +409,24 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole return renderButton; } } + + @Override + public void setValueAt(Object value, int rowIndex, int columnIndex) { + if (columnIndex == COL_REQUSTED_STOCK) { + try { + //Quick String parsing here, we ignore anything that isn't a number or a . so that a user can input a % symbol or not, it's added regardless + double newVal = Double.parseDouble(value.toString().replaceAll("[^0-9.]", "")); + PartInUse partInUse = getPartInUse(rowIndex); + if (partInUse != null) { + partInUse.setRequestedStock(newVal); + fireTableCellUpdated(rowIndex, columnIndex); + } + } catch (NumberFormatException e) { + + } + } else { + super.setValueAt(value, rowIndex, columnIndex); + } + } + } diff --git a/MekHQ/src/mekhq/gui/model/ScenarioTableModel.java b/MekHQ/src/mekhq/gui/model/ScenarioTableModel.java index 207e3490f85..d0141b2be74 100644 --- a/MekHQ/src/mekhq/gui/model/ScenarioTableModel.java +++ b/MekHQ/src/mekhq/gui/model/ScenarioTableModel.java @@ -25,6 +25,7 @@ import mekhq.campaign.mission.Scenario; import mekhq.campaign.mission.enums.ScenarioStatus; import mekhq.campaign.stratcon.StratconCampaignState; +import mekhq.campaign.stratcon.StratconCoords; import mekhq.campaign.stratcon.StratconScenario; import mekhq.campaign.stratcon.StratconTrackState; import mekhq.gui.utilities.MekHqTableCellRenderer; @@ -35,6 +36,10 @@ import java.util.Objects; import java.util.ResourceBundle; +import static mekhq.campaign.mission.enums.ScenarioStatus.CURRENT; +import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; + /** * A table model for displaying scenarios */ @@ -108,6 +113,30 @@ public Object getValueAt(int row, int col) { } if (col == COL_NAME) { + if (campaign.getCampaignOptions().isUseStratCon()) { + if (scenario instanceof AtBScenario) { + if (scenario.getStatus() != CURRENT) { + return scenario.getName(); + } + + StratconScenario stratconScenario = ((AtBScenario) scenario).getStratconScenario(campaign); + + if (stratconScenario != null) { + boolean isTurningPoint = stratconScenario.isTurningPoint(); + String openingSpan = isTurningPoint + ? spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()) + : ""; + + String colorblindHelper = isTurningPoint ? " \u26A0" : ""; + + String closingSpan = isTurningPoint ? CLOSING_SPAN_TAG : ""; + + return String.format("%s%s%s%s { - final CreateCampaignPresetDialog dialog = new CreateCampaignPresetDialog( + final CreateCampaignPreset dialog = new CreateCampaignPreset( getFrame(), getCampaign(), getPreset()); if (dialog.showDialog().isConfirmed()) { updateFromPreset(getPreset()); diff --git a/MekHQ/src/mekhq/gui/panels/StartupScreenPanel.java b/MekHQ/src/mekhq/gui/panels/StartupScreenPanel.java index 1bf14dbcc06..4b9930881e1 100644 --- a/MekHQ/src/mekhq/gui/panels/StartupScreenPanel.java +++ b/MekHQ/src/mekhq/gui/panels/StartupScreenPanel.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. * @@ -18,25 +18,6 @@ */ package mekhq.gui.panels; -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FilenameFilter; -import java.util.Arrays; -import java.util.List; - -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.UIManager; - import megamek.client.ui.swing.util.UIUtil; import megamek.client.ui.swing.widget.MegaMekButton; import megamek.client.ui.swing.widget.SkinSpecification; @@ -57,6 +38,16 @@ import mekhq.gui.dialog.DataLoadingDialog; import mekhq.gui.dialog.StoryArcSelectionDialog; +import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FilenameFilter; +import java.util.Arrays; +import java.util.List; + public class StartupScreenPanel extends AbstractMHQPanel { private static final MMLogger logger = MMLogger.create(StartupScreenPanel.class); @@ -71,8 +62,7 @@ public class StartupScreenPanel extends AbstractMHQPanel { public boolean accept(File dir, String name) { // Allow any .xml, .cpnx, and .cpnx.gz file that is not in the list of excluded // files - List toReject = Arrays.asList( - PreferenceManager.DEFAULT_CFG_FILE_NAME.toLowerCase()); + List toReject = List.of(PreferenceManager.DEFAULT_CFG_FILE_NAME.toLowerCase()); return (((name.toLowerCase().endsWith(".cpnx") || name.toLowerCase().endsWith(".xml")) || name.toLowerCase().endsWith(".cpnx.gz")) && !toReject.contains(name.toLowerCase())); } @@ -126,29 +116,58 @@ protected void initialize() { MegaMekButton btnNewCampaign = new MegaMekButton(resources.getString("btnNewCampaign.text"), UIComponents.MainMenuButton.getComp(), true); - btnNewCampaign.addActionListener(evt -> startCampaign(null)); + btnNewCampaign.addActionListener(evt -> { + btnNewCampaign.setEnabled(false); + + SwingUtilities.invokeLater(() -> { + try { + startCampaign(null); + } finally { + btnNewCampaign.setEnabled(true); + } + }); + }); MegaMekButton btnLoadCampaign = new MegaMekButton(resources.getString("btnLoadCampaign.text"), UIComponents.MainMenuButton.getComp(), true); btnLoadCampaign.addActionListener(evt -> { - final File file = selectCampaignFile(); - if (file != null) { - startCampaign(file); - } + btnLoadCampaign.setEnabled(false); + + SwingUtilities.invokeLater(() -> { + try { + final File file = selectCampaignFile(); + if (file != null) { + startCampaign(file); + } + } finally { + btnLoadCampaign.setEnabled(true); + } + }); }); MegaMekButton btnLoadLastCampaign = new MegaMekButton(resources.getString("btnLoadLastCampaign.text"), UIComponents.MainMenuButton.getComp(), true); btnLoadLastCampaign.setEnabled(lastSaveFile != null); - btnLoadLastCampaign.addActionListener(evt -> startCampaign(lastSaveFile)); + btnLoadLastCampaign.addActionListener(evt -> { + btnLoadLastCampaign.setEnabled(false); + startCampaign(lastSaveFile); + }); MegaMekButton btnLoadStoryArc = new MegaMekButton(resources.getString("btnLoadStoryArc.text"), UIComponents.MainMenuButton.getComp(), true); btnLoadStoryArc.addActionListener(evt -> { - StoryArcStub storyArcStub = selectStoryArc(); - if ((null != storyArcStub) && (null != storyArcStub.getInitCampaignFile())) { - startCampaign(storyArcStub.getInitCampaignFile(), storyArcStub); - } + btnLoadStoryArc.setEnabled(false); + + SwingUtilities.invokeLater(() -> { + try { + StoryArcStub storyArcStub = selectStoryArc(); + if ((null != storyArcStub) && (null != storyArcStub.getInitCampaignFile())) { + startCampaign(storyArcStub.getInitCampaignFile(), storyArcStub); + } + } finally { + btnLoadStoryArc.setEnabled(true); + } + }); }); MegaMekButton btnQuit = new MegaMekButton(resources.getString("Quit.text"), UIComponents.MainMenuButton.getComp(), true); @@ -240,7 +259,7 @@ private void startCampaign(final @Nullable File file) { } private void startCampaign(final @Nullable File file, @Nullable StoryArcStub storyArcStub) { - new DataLoadingDialog(getFrame(), app, file, storyArcStub).setVisible(true); + new DataLoadingDialog(getFrame(), app, file, storyArcStub, false).setVisible(true); } private @Nullable File selectCampaignFile() { diff --git a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java index 780b5d88746..e69de29bb2d 100644 --- a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java +++ b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java @@ -1,10121 +0,0 @@ -/* - * 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 2 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.gui.panes; - -import megamek.client.generator.RandomGenderGenerator; -import megamek.client.generator.RandomNameGenerator; -import megamek.client.ui.baseComponents.JDisableablePanel; -import megamek.client.ui.baseComponents.MMButton; -import megamek.client.ui.baseComponents.MMComboBox; -import megamek.client.ui.dialogs.CamoChooserDialog; -import megamek.client.ui.enums.ValidationState; -import megamek.client.ui.swing.util.PlayerColour; -import megamek.codeUtilities.StringUtility; -import megamek.common.EquipmentType; -import megamek.common.ITechnology; -import megamek.common.annotations.Nullable; -import megamek.common.enums.Gender; -import megamek.common.enums.SkillLevel; -import megamek.common.icons.Camouflage; -import megamek.common.options.IOption; -import megamek.common.options.IOptionGroup; -import megamek.common.options.OptionsConstants; -import megamek.common.util.sorter.NaturalOrderComparator; -import megamek.logging.MMLogger; -import mekhq.MekHQ; -import mekhq.campaign.Campaign; -import mekhq.campaign.CampaignOptions; -import mekhq.campaign.CampaignPreset; -import mekhq.campaign.RandomSkillPreferences; -import mekhq.campaign.enums.PlanetaryAcquisitionFactionLimit; -import mekhq.campaign.event.OptionsChangedEvent; -import mekhq.campaign.finances.enums.FinancialYearDuration; -import mekhq.campaign.icons.StandardForceIcon; -import mekhq.campaign.market.PersonnelMarketDylan; -import mekhq.campaign.market.PersonnelMarketRandom; -import mekhq.campaign.market.enums.ContractMarketMethod; -import mekhq.campaign.market.enums.UnitMarketMethod; -import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.mission.enums.AtBLanceRole; -import mekhq.campaign.parts.enums.PartQuality; -import mekhq.campaign.personnel.PersonnelOptions; -import mekhq.campaign.personnel.SkillType; -import mekhq.campaign.personnel.Skills; -import mekhq.campaign.personnel.SpecialAbility; -import mekhq.campaign.personnel.backgrounds.BackgroundsController; -import mekhq.campaign.personnel.enums.*; -import mekhq.campaign.rating.UnitRatingMethod; -import mekhq.campaign.universe.Factions; -import mekhq.campaign.universe.RATManager; -import mekhq.gui.SpecialAbilityPanel; -import mekhq.gui.baseComponents.AbstractMHQScrollablePanel; -import mekhq.gui.baseComponents.AbstractMHQTabbedPane; -import mekhq.gui.baseComponents.DefaultMHQScrollablePanel; -import mekhq.gui.dialog.DateChooser; -import mekhq.gui.dialog.SelectUnusedAbilityDialog; -import mekhq.gui.dialog.iconDialogs.UnitIconDialog; -import mekhq.gui.displayWrappers.FactionDisplay; -import mekhq.gui.panels.RandomOriginOptionsPanel; -import mekhq.gui.utilities.JScrollPaneWithSpeed; -import mekhq.module.PersonnelMarketServiceManager; -import mekhq.module.api.PersonnelMarketMethod; - -import javax.swing.*; -import javax.swing.GroupLayout.Alignment; -import javax.swing.JSpinner.DefaultEditor; -import javax.swing.JSpinner.NumberEditor; -import javax.swing.LayoutStyle.ComponentPlacement; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.JTableHeader; -import javax.swing.table.TableColumn; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.time.LocalDate; -import java.util.List; -import java.util.*; -import java.util.Map.Entry; -import java.util.stream.IntStream; - -import static megamek.client.ui.WrapLayout.wordWrap; -import static mekhq.campaign.force.StrategicFormation.recalculateStrategicFormations; - -/** - * @author Justin 'Windchild' Bowen - */ -public class CampaignOptionsPane extends AbstractMHQTabbedPane { - private static final MMLogger logger = MMLogger.create(CampaignOptionsPane.class); - - // region Variable Declarations - // region General Variables (ones not relating to a specific tab) - private final Campaign campaign; - private final boolean startup; - private CampaignOptions options; - private RandomSkillPreferences rSkillPrefs; - private LocalDate date; - private Camouflage camouflage; - private final PlayerColour colour; - private StandardForceIcon unitIcon; - private final Hashtable hashSkillTargets; - private final Hashtable hashGreenSkill; - private final Hashtable hashRegSkill; - private final Hashtable hashVetSkill; - private final Hashtable hashEliteSkill; - // endregion General Variables (ones not relating to a specific tab) - - // region General Tab - private JTextField txtName; - private MMComboBox comboFaction; - private MMComboBox unitRatingMethodCombo; - private JSpinner manualUnitRatingModifier; - private JButton btnDate; - private JButton btnCamo; - private JButton btnIcon; - // endregion General Tab - - // region Repair and Maintenance Tab - // Repair - private JCheckBox useEraModsCheckBox; - private JCheckBox assignedTechFirstCheckBox; - private JCheckBox resetToFirstTechCheckBox; - private JCheckBox useQuirksBox; - private JCheckBox useAeroSystemHitsBox; - private JCheckBox useDamageMargin; - private JSpinner spnDamageMargin; - private JSpinner spnDestroyPartTarget; - - // Maintenance - private JCheckBox checkMaintenance; - private JSpinner spnMaintenanceDays; - private JSpinner spnMaintenanceBonus; - private JCheckBox useQualityMaintenance; - private JCheckBox reverseQualityNames; - private JCheckBox chkUseRandomUnitQualities; - private JCheckBox chkUsePlanetaryModifiers; - private JCheckBox useUnofficialMaintenance; - private JCheckBox logMaintenance; - private JSpinner spnDefaultMaintenanceTime; - // endregion Repair and Maintenance Tab - - // region Supplies and Acquisitions Tab - // Acquisition - private JSpinner spnAcquireWaitingPeriod; - private MMComboBox choiceAcquireSkill; - private JCheckBox chkSupportStaffOnly; - private JSpinner spnAcquireClanPenalty; - private JSpinner spnAcquireIsPenalty; - private JSpinner spnMaxAcquisitions; - - // Delivery - private JSpinner spnNDiceTransitTime; - private JSpinner spnConstantTransitTime; - private MMComboBox choiceTransitTimeUnits; - private JSpinner spnAcquireMinimum; - private MMComboBox choiceAcquireMinimumUnit; - private JSpinner spnAcquireMosBonus; - private MMComboBox choiceAcquireMosUnits; - - // Planetary Acquisitions - private JCheckBox usePlanetaryAcquisitions; - private JSpinner spnMaxJumpPlanetaryAcquisitions; - private MMComboBox comboPlanetaryAcquisitionsFactionLimits; - private JCheckBox disallowPlanetaryAcquisitionClanCrossover; - private JCheckBox usePlanetaryAcquisitionsVerbose; - private JSpinner[] spnPlanetAcquireTechBonus; - private JSpinner[] spnPlanetAcquireIndustryBonus; - private JSpinner[] spnPlanetAcquireOutputBonus; - private JCheckBox disallowClanPartsFromIS; - private JSpinner spnPenaltyClanPartsFromIS; - // endregion Supplies and Acquisitions Tab - - // region Tech Limits Tab - private JCheckBox limitByYearBox; - private JCheckBox disallowExtinctStuffBox; - private JCheckBox allowClanPurchasesBox; - private JCheckBox allowISPurchasesBox; - private JCheckBox allowCanonOnlyBox; - private JCheckBox allowCanonRefitOnlyBox; - private MMComboBox choiceTechLevel; - private JCheckBox variableTechLevelBox; - private JCheckBox factionIntroDateBox; - private JCheckBox useAmmoByTypeBox; - // endregion Tech Limits Tab - - // region Personnel Tab - // General Personnel - private JCheckBox chkUseTactics; - private JCheckBox chkUseInitiativeBonus; - private JCheckBox chkUseToughness; - private JCheckBox chkUseRandomToughness; - private JCheckBox chkUseArtillery; - private JCheckBox chkUseAbilities; - private JCheckBox chkUseEdge; - private JCheckBox chkUseSupportEdge; - private JCheckBox chkUseImplants; - private JCheckBox chkUseAlternativeQualityAveraging; - private JCheckBox chkUseTransfers; - private JCheckBox chkUseExtendedTOEForceName; - private JCheckBox chkPersonnelLogSkillGain; - private JCheckBox chkPersonnelLogAbilityGain; - private JCheckBox chkPersonnelLogEdgeGain; - private JCheckBox chkDisplayPersonnelLog; - private JCheckBox chkDisplayScenarioLog; - private JCheckBox chkDisplayKillRecord; - - // Expanded Personnel - private JCheckBox chkUseTimeInService; - private MMComboBox comboTimeInServiceDisplayFormat; - private JCheckBox chkUseTimeInRank; - private MMComboBox comboTimeInRankDisplayFormat; - private JCheckBox chkTrackTotalEarnings; - private JCheckBox chkTrackTotalXPEarnings; - private JCheckBox chkShowOriginFaction; - - // Admin - private final JPanel administratorsPanel = new JPanel(); - private JCheckBox chkAdminsHaveNegotiation; - private JCheckBox chkAdminsHaveScrounge; - private JCheckBox chkAdminExperienceLevelIncludeNegotiation; - private JCheckBox chkAdminExperienceLevelIncludeScrounge; - - // Medical - private JCheckBox chkUseAdvancedMedical; - private JSpinner spnHealWaitingPeriod; - private JSpinner spnNaturalHealWaitingPeriod; - private JSpinner spnMinimumHitsForVehicles; - private JCheckBox chkUseRandomHitsForVehicles; - private JCheckBox chkUseTougherHealing; - private JSpinner spnMaximumPatients; - - // Prisoners - private MMComboBox comboPrisonerCaptureStyle; - private MMComboBox comboPrisonerStatus; - private JCheckBox chkPrisonerBabyStatus; - private JCheckBox chkAtBPrisonerDefection; - private JCheckBox chkAtBPrisonerRansom; - - // Dependent - private JCheckBox chkUseRandomDependentAddition; - private JCheckBox chkUseRandomDependentRemoval; - - // Personnel Removal - private JPanel personnelRemovalSubPanel = new JPanel(); - private JCheckBox chkUsePersonnelRemoval; - private JCheckBox chkUseRemovalExemptCemetery; - private JCheckBox chkUseRemovalExemptRetirees; - - // Salary - private JCheckBox chkDisableSecondaryRoleSalary; - private JSpinner spnAntiMekSalary; - private JSpinner spnSpecialistInfantrySalary; - private Map spnSalaryExperienceMultipliers; - private JSpinner[] spnBaseSalary; - // endregion Personnel Tab - - // region Turnover and Retention Tab - // Header Options - private JCheckBox chkUseRandomRetirement; - - // Settings - private final JPanel turnoverAndRetentionSettingsPanel = new JPanel(); - private JLabel lblTurnoverFixedTargetNumber; - private JSpinner spnTurnoverFixedTargetNumber; - private MMComboBox comboTurnoverFrequency; - private JCheckBox chkUseContractCompletionRandomRetirement; - private JCheckBox chkUseRandomFounderTurnover; - private JCheckBox chkUseFounderRetirement; - private JCheckBox chkAeroRecruitsHaveUnits; - private JCheckBox chkTrackOriginalUnit; - private JCheckBox chkUseSubContractSoldiers; - private JSpinner spnServiceContractDuration; - private JSpinner spnServiceContractModifier; - private JCheckBox chkPayBonusDefault; - private JLabel lblPayBonusDefaultThreshold; - private JSpinner spnPayBonusDefaultThreshold; - - // Modifiers - private final JPanel turnoverAndRetentionModifiersPanel = new JPanel(); - private JCheckBox chkUseCustomRetirementModifiers; - private JCheckBox chkUseFatigueModifiers; - private JCheckBox chkUseSkillModifiers; - private JCheckBox chkUseAgeModifiers; - private JCheckBox chkUseUnitRatingModifiers; - private JCheckBox chkUseFactionModifiers; - private JCheckBox chkUseMissionStatusModifiers; - private JCheckBox chkUseHostileTerritoryModifiers; - private JCheckBox chkUseFamilyModifiers; - private JCheckBox chkUseLoyaltyModifiers; - - private final JPanel loyaltySubPanel = new JPanel(); - private JCheckBox chkUseHideLoyalty; - - // Payout - private final JPanel turnoverAndRetentionPayoutPanel = new JPanel(); - private JSpinner spnPayoutRateOfficer; - private JSpinner spnPayoutRateEnlisted; - private JSpinner spnPayoutRetirementMultiplier; - private JCheckBox chkUsePayoutServiceBonus; - - private final JPanel payoutServiceBonusSubPanel = new JPanel(); - private JLabel lblPayoutServiceBonusRate; - private JSpinner spnPayoutServiceBonusRate; - - // Unit Cohesion - private final JPanel turnoverAndRetentionUnitCohesionPanel = new JPanel(); - private JCheckBox chkUseAdministrativeStrain; - private JCheckBox chkUseManagementSkill; - - private final JPanel administrativeStrainSubPanel = new JPanel(); - private JLabel lblAdministrativeCapacity; - private JSpinner spnAdministrativeCapacity; - private JLabel lblMultiCrewStrainDivider; - private JSpinner spnMultiCrewStrainDivider; - - private final JPanel managementSkillSubPanel = new JPanel(); - private JCheckBox chkUseCommanderLeadershipOnly; - private JLabel lblManagementSkillPenalty; - private JSpinner spnManagementSkillPenalty; - - // Fatigue - private final JPanel turnoverAndRetentionFatiguePanel = new JPanel(); - private JCheckBox chkUseFatigue; - - private final JPanel fatigueSubPanel = new JPanel(); - private JSpinner spnFatigueRate; - private JCheckBox chkUseInjuryFatigue; - private JSpinner spnFieldKitchenCapacity; - private JCheckBox chkFieldKitchenIgnoreNonCombatants; - private JSpinner spnFatigueLeaveThreshold; - // endregion Turnover and Retention Tab - - // 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 spnCheckMutualAncestorsDepth; - private JSpinner spnNoInterestInMarriageDiceSize; - private JCheckBox chkLogMarriageNameChanges; - private Map spnMarriageSurnameWeights; - private MMComboBox comboRandomMarriageMethod; - private JCheckBox chkUseRandomClanPersonnelMarriages; - private JCheckBox chkUseRandomPrisonerMarriages; - private JSpinner spnRandomMarriageAgeRange; - private JSpinner spnRandomMarriageDiceSize; - private JSpinner spnRandomSameSexMarriageDiceSize; - private JSpinner spnRandomNewDependentMarriage; - - // Divorce - private JCheckBox chkUseManualDivorce; - private JCheckBox chkUseClanPersonnelDivorce; - private JCheckBox chkUsePrisonerDivorce; - private Map spnDivorceSurnameWeights; - private MMComboBox comboRandomDivorceMethod; - private JCheckBox chkUseRandomOppositeSexDivorce; - private JCheckBox chkUseRandomSameSexDivorce; - private JCheckBox chkUseRandomClanPersonnelDivorce; - private JCheckBox chkUseRandomPrisonerDivorce; - private JLabel lblRandomDivorceDiceSize; - private JSpinner spnRandomDivorceDiceSize; - - // Procreation - private JCheckBox chkUseManualProcreation; - private JCheckBox chkUseClanPersonnelProcreation; - private JCheckBox chkUsePrisonerProcreation; - private JSpinner spnMultiplePregnancyOccurrences; - private MMComboBox comboBabySurnameStyle; - private JCheckBox chkAssignNonPrisonerBabiesFounderTag; - 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 spnRandomProcreationRelationshipDiceSize; - private JLabel lblRandomProcreationRelationshiplessDiceSize; - private JSpinner spnRandomProcreationRelationshiplessDiceSize; - - // Awards - private MMComboBox comboAwardBonusStyle; - private JSpinner spnAwardTierSize; - private JCheckBox chkEnableAutoAwards; - private JCheckBox chkIssuePosthumousAwards; - private JCheckBox chkIssueBestAwardOnly; - private JCheckBox chkIgnoreStandardSet; - private JCheckBox chkEnableContractAwards; - private JCheckBox chkEnableFactionHunterAwards; - private JCheckBox chkEnableInjuryAwards; - private JCheckBox chkEnableIndividualKillAwards; - private JCheckBox chkEnableFormationKillAwards; - private JCheckBox chkEnableRankAwards; - private JCheckBox chkEnableScenarioAwards; - private JCheckBox chkEnableSkillAwards; - private JCheckBox chkEnableTheatreOfWarAwards; - private JCheckBox chkEnableTimeAwards; - private JCheckBox chkEnableTrainingAwards; - private JCheckBox chkEnableMiscAwards; - private JLabel lblAwardSetFilterList; - private JTextArea txtAwardSetFilterList; - - // Death - private JCheckBox chkKeepMarriedNameUponSpouseDeath; - private MMComboBox comboRandomDeathMethod; - private Map chkEnabledRandomDeathAgeGroups; - private JCheckBox chkUseRandomClanPersonnelDeath; - private JCheckBox chkUseRandomPrisonerDeath; - private JCheckBox chkUseRandomDeathSuicideCause; - private JSpinner spnPercentageRandomDeathChance; - private JSpinner[] spnExponentialRandomDeathMaleValues; - private JSpinner[] spnExponentialRandomDeathFemaleValues; - private Map spnAgeRangeRandomDeathMaleValues; - private Map spnAgeRangeRandomDeathFemaleValues; - - // Family - private MMComboBox comboFamilyDisplayLevel; - - // Anniversaries - private final JPanel anniversaryPanel = new JPanel(); - private JCheckBox chkAnnounceBirthdays; - private JCheckBox chkAnnounceRecruitmentAnniversaries; - private JCheckBox chkAnnounceOfficersOnly; - private JCheckBox chkAnnounceChildBirthdays; - - // Education - private JCheckBox chkUseEducationModule; - private JLabel lblCurriculumXpRate; - private JSpinner spnCurriculumXpRate; - private JLabel lblMaximumJumpCount; - private JSpinner spnMaximumJumpCount; - private JCheckBox chkUseReeducationCamps; - private JCheckBox chkEnableLocalAcademies; - private JCheckBox chkEnablePrestigiousAcademies; - private JCheckBox chkEnableUnitEducation; - private JCheckBox chkShowIneligibleAcademies; - private JCheckBox chkEnableOverrideRequirements; - private JLabel lblEntranceExamBaseTargetNumber; - private JSpinner spnEntranceExamBaseTargetNumber; - private JCheckBox chkEnableBonuses; - private JLabel lblFacultyXpMultiplier; - private JSpinner spnFacultyXpMultiplier; - private JLabel lblAdultDropoutChance; - private JSpinner spnAdultDropoutChance; - private JLabel lblChildrenDropoutChance; - private JSpinner spnChildrenDropoutChance; - private JCheckBox chkAllAges; - private JLabel lblMilitaryAcademyAccidents; - private JSpinner spnMilitaryAcademyAccidents; - // endregion Life Paths Tab - - // region Finances Tab - private JCheckBox payForPartsBox; - private JCheckBox payForRepairsBox; - private JCheckBox payForUnitsBox; - private JCheckBox payForSalariesBox; - private JCheckBox payForOverheadBox; - private JCheckBox payForMaintainBox; - private JCheckBox payForTransportBox; - private JCheckBox sellUnitsBox; - private JCheckBox sellPartsBox; - private JCheckBox payForRecruitmentBox; - private JCheckBox useLoanLimitsBox; - private JCheckBox usePercentageMaintenanceBox; - private JCheckBox useInfantryDoseNotCountBox; - private JCheckBox usePeacetimeCostBox; - private JCheckBox useExtendedPartsModifierBox; - private JCheckBox showPeacetimeCostBox; - private JCheckBox newFinancialYearFinancesToCSVExportBox; - private MMComboBox comboFinancialYearDuration; - - // Price Multipliers - private JSpinner spnCommonPartPriceMultiplier; - private JSpinner spnInnerSphereUnitPriceMultiplier; - private JSpinner spnInnerSpherePartPriceMultiplier; - private JSpinner spnClanUnitPriceMultiplier; - private JSpinner spnClanPartPriceMultiplier; - private JSpinner spnMixedTechUnitPriceMultiplier; - private JSpinner[] spnUsedPartPriceMultipliers; - private JSpinner spnDamagedPartsValueMultiplier; - private JSpinner spnUnrepairablePartsValueMultiplier; - private JSpinner spnCancelledOrderRefundMultiplier; - - // Taxes - private JCheckBox chkUseTaxes; - private JPanel taxesSubPanel = new JPanel(); - private JSpinner spnTaxesPercentage; - - private JCheckBox chkUseShareSystem; - private JPanel sharesSubPanel; - private JCheckBox chkSharesForAll; - // endregion Finances Tab - - // region Mercenary Tab - private JRadioButton btnContractEquipment; - private JSpinner spnEquipPercent; - private JSpinner spnDropShipPercent; - private JSpinner spnJumpShipPercent; - private JSpinner spnWarShipPercent; - private JCheckBox chkEquipContractSaleValue; - private JRadioButton btnContractPersonnel; - private JCheckBox chkBLCSaleValue; - private JCheckBox chkOverageRepaymentInFinalPayment; - // endregion Mercenary Tab - - // region Experience Tab - private JSpinner spnXpCostMultiplier; - private JSpinner spnScenarioXP; - private JSpinner spnKillXP; - private JSpinner spnKills; - private JSpinner spnTaskXP; - private JSpinner spnNTasksXP; - private JSpinner spnSuccessXP; - private JSpinner spnMistakeXP; - private JSpinner spnIdleXP; - private JSpinner spnMonthsIdleXP; - private JSpinner spnTargetIdleXP; - private JSpinner spnContractNegotiationXP; - private JSpinner spnAdminWeeklyXP; - private JSpinner spnAdminWeeklyXPPeriod; - private JSpinner spnMissionXpFail; - private JSpinner spnMissionXpSuccess; - private JSpinner spnMissionXpOutstandingSuccess; - - private JSpinner spnEdgeCost; - private JTable tableXP; - private static final String[] TABLE_XP_COLUMN_NAMES = { "+0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", "+8", - "+9", - "+10" }; - // endregion Experience Tab - - // region Skills Tab - // endregion Skills Tab - - // region Special Abilities Tab - private AbstractMHQScrollablePanel panSpecialAbilities; - private Map tempSPA; - private JButton btnAddSPA; - // endregion Special Abilities Tab - - // region Skill Randomization Tab - private JCheckBox chkExtraRandom; - private JSpinner[] phenotypeSpinners; - private JSpinner spnProbAntiMek; - private JSpinner spnOverallRecruitBonus; - private JSpinner[] spnTypeRecruitBonus; - private JSpinner spnArtyProb; - private JSpinner spnArtyBonus; - private JSpinner spnTacticsGreen; - private JSpinner spnTacticsReg; - private JSpinner spnTacticsVet; - private JSpinner spnTacticsElite; - private JSpinner spnCombatSA; - private JSpinner spnSupportSA; - private JSpinner spnSecondProb; - private JSpinner spnSecondBonus; - private JSpinner spnAbilityGreen; - private JSpinner spnAbilityReg; - private JSpinner spnAbilityVet; - private JSpinner spnAbilityElite; - // endregion Skill Randomization Tab - - // region Rank System Tab - private RankSystemsPane rankSystemsPane; - // endregion Rank System Tab - - // region Name and Portrait Generation Tab - private JCheckBox chkUseOriginFactionForNames; - private MMComboBox comboFactionNames; - private JSlider sldGender; - private JCheckBox[] chkUsePortrait; - private JCheckBox allPortraitsBox; - private JCheckBox noPortraitsBox; - private JCheckBox chkAssignPortraitOnRoleChange; - // endregion Name and Portrait Generation Tab - - // region Markets Tab - // Personnel Market - private MMComboBox comboPersonnelMarketType; - private JCheckBox chkPersonnelMarketReportRefresh; - private Map spnPersonnelMarketRandomRemovalTargets; - private JSpinner spnPersonnelMarketDylansWeight; - private JCheckBox chkUsePersonnelHireHiringHallOnly; - - // Unit Market - private MMComboBox comboUnitMarketMethod; - private JCheckBox chkUnitMarketRegionalMekVariations; - private JLabel lblUnitMarketSpecialUnitChance; - private JSpinner spnUnitMarketSpecialUnitChance; - private JLabel lblUnitMarketRarityModifier; - private JSpinner spnUnitMarketRarityModifier; - private JCheckBox chkInstantUnitMarketDelivery; - private JCheckBox chkUnitMarketReportRefresh; - - // Contract Market - private JPanel contractMarketPanel; - private MMComboBox comboContractMarketMethod; - private JSpinner spnContractSearchRadius; - private JCheckBox chkVariableContractLength; - private JCheckBox chkContractMarketReportRefresh; - private JSpinner spnContractMaxSalvagePercentage; - private JSpinner spnDropShipBonusPercentage; - // endregion Markets Tab - - // region RATs Tab - private JRadioButton btnUseRATGenerator; - private JRadioButton btnUseStaticRATs; - private DefaultListModel chosenRATModel; - private DefaultListModel availableRATModel; - private JCheckBox chkIgnoreRATEra; - // endregion RATs Tab - - // region Against the Bot Tab - private AbstractMHQScrollablePanel panAtB; - private JCheckBox chkUseAtB; - private JCheckBox chkUseStratCon; - private MMComboBox comboSkillLevel; - - // unit administration - private JCheckBox chkUseAero; - private JCheckBox chkUseVehicles; - private JCheckBox chkClanVehicles; - private JCheckBox chkAutoConfigMunitions; - - // contract operations - private JCheckBox chkMercSizeLimited; - private JCheckBox chkRestrictPartsByMission; - private JSpinner spnBonusPartExchangeValue; - private JSpinner spnBonusPartMaxExchangeCount; - private JCheckBox chkLimitLanceWeight; - private JCheckBox chkLimitLanceNumUnits; - private JCheckBox chkUseStrategy; - private JSpinner spnBaseStrategyDeployment; - private JSpinner spnAdditionalStrategyDeployment; - private JCheckBox chkAdjustPaymentForStrategy; - private JSpinner spnAtBBattleIntensity; - private JSpinner[] spnAtBBattleChance; - private JButton btnIntensityUpdate; - private JCheckBox chkGenerateChases; - - // scenarios - private JCheckBox chkUseGenericBattleValue; - private JCheckBox chkUseVerboseBidding; - private JCheckBox chkDoubleVehicles; - private JSpinner spnOpForLanceTypeMeks; - private JSpinner spnOpForLanceTypeMixed; - private JSpinner spnOpForLanceTypeVehicles; - private JCheckBox chkOpForUsesVTOLs; - private JCheckBox chkOpForUsesAero; - private JSpinner spnOpForAeroChance; - private JCheckBox chkOpForUsesLocalForces; - private JSpinner spnOpForLocalForceChance; - private JSpinner spnSPAUpgradeIntensity; - private JSpinner spnFixedMapChance; - private JCheckBox chkAdjustPlayerVehicles; - private JCheckBox chkRegionalMekVariations; - private JCheckBox chkAttachedPlayerCamouflage; - private JCheckBox chkPlayerControlsAttachedUnits; - private JCheckBox chkUseDropShips; - private JCheckBox chkUseWeatherConditions; - private JCheckBox chkUseLightConditions; - private JCheckBox chkUsePlanetaryConditions; - private JSpinner spnScenarioModMax; - private JSpinner spnScenarioModChance; - private JSpinner spnScenarioModBV; - // endregion Against the Bot Tab - // endregion Variable Declarations - - // region Constructors - public CampaignOptionsPane(final JFrame frame, final Campaign campaign, final boolean startup) { - super(frame, ResourceBundle.getBundle("mekhq.resources.CampaignOptionsDialog", - MekHQ.getMHQOptions().getLocale()), - "CampaignOptionsPane"); - this.campaign = campaign; - this.startup = startup; - this.date = campaign.getLocalDate(); - this.camouflage = campaign.getCamouflage(); - this.colour = campaign.getColour(); - this.unitIcon = campaign.getUnitIcon(); - hashSkillTargets = new Hashtable<>(); - hashGreenSkill = new Hashtable<>(); - hashRegSkill = new Hashtable<>(); - hashVetSkill = new Hashtable<>(); - hashEliteSkill = new Hashtable<>(); - - initialize(); - setOptions(campaign.getCampaignOptions(), campaign.getRandomSkillPreferences()); - btnCamo.setIcon(camouflage.getImageIcon()); - btnIcon.setIcon(unitIcon.getImageIcon(75)); - } - // endregion Constructors - - // region Getters/Setters - public Campaign getCampaign() { - return campaign; - } - - public boolean isStartup() { - return startup; - } - // endregion Getters/Setters - - // region Initialization - @Override - protected void initialize() { - addTab(resources.getString("generalPanel.title"), createGeneralTab()); - addTab(resources.getString("repairAndMaintenancePanel.title"), createRepairAndMaintenanceTab()); - addTab(resources.getString("suppliesAndAcquisitionsPanel.title"), createSuppliesAndAcquisitionsTab()); - addTab(resources.getString("techLimitsPanel.title"), createTechLimitsTab()); - addTab(resources.getString("personnelPanel.title"), createPersonnelTab()); - addTab(resources.getString("lifePathsPanel.title"), createLifePathsPanel()); - addTab(resources.getString("turnoverAndRetentionPanel.title"), createTurnoverAndRetentionTab()); - addTab(resources.getString("financesPanel.title"), - createFinancesTab(campaign.getCampaignOptions().isReverseQualityNames())); - addTab(resources.getString("mercenaryPanel.title"), createMercenaryTab()); - addTab(resources.getString("experiencePanel.title"), createExperienceTab()); - addTab(resources.getString("skillsPanel.title"), createSkillsTab()); - addTab(resources.getString("specialAbilitiesPanel.title"), createSpecialAbilitiesTab()); - addTab(resources.getString("skillRandomizationPanel.title"), createSkillRandomizationTab()); - addTab(resources.getString("rankSystemsPanel.title"), createRankSystemsTab()); - addTab(resources.getString("nameAndPortraitGenerationPanel.title"), - createNameAndPortraitGenerationTab()); - addTab(resources.getString("marketsPanel.title"), createMarketsTab()); - addTab(resources.getString("ratPanel.title"), createRATTab()); - addTab(resources.getString("againstTheBotPanel.title"), createAgainstTheBotTab()); - - setPreferences(); - } - - // region Legacy Initialization - // Define what is legacy and not and remove in Milestone after 0.49.19 - - private JScrollPane createGeneralTab() { - int gridy = 0; - int gridx = 0; - - AbstractMHQScrollablePanel panGeneral = new DefaultMHQScrollablePanel(getFrame(), "generalPanel", - new GridBagLayout()); - - JLabel lblName = new JLabel(resources.getString("lblName.text")); - lblName.setName("lblName"); - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = gridx++; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.anchor = GridBagConstraints.WEST; - panGeneral.add(lblName, gridBagConstraints); - - txtName = new JTextField(campaign.getName()); - txtName.setName("txtName"); - txtName.setMinimumSize(new Dimension(500, 30)); - txtName.setPreferredSize(new Dimension(500, 30)); - gridBagConstraints.gridx = gridx--; - panGeneral.add(txtName, gridBagConstraints); - - JButton lblNameGenerator = new JButton(resources.getString("lblNameGenerator.text")); - lblNameGenerator.setName("lblNameGenerator"); - lblNameGenerator.addActionListener(e -> txtName - .setText(BackgroundsController - .randomMercenaryCompanyNameGenerator(campaign.getFlaggedCommander()))); - gridBagConstraints = new GridBagConstraints(); - panGeneral.add(lblNameGenerator, gridBagConstraints); - - JLabel lblFaction = new JLabel(resources.getString("lblFaction.text")); - lblFaction.setName("lblFaction"); - gridBagConstraints.gridx = gridx++; - gridBagConstraints.gridy = gridy++; - panGeneral.add(lblFaction, gridBagConstraints); - - final DefaultComboBoxModel factionModel = new DefaultComboBoxModel<>(); - factionModel.addAll(FactionDisplay - .getSortedValidFactionDisplays(Factions.getInstance().getChoosableFactions(), date)); - comboFaction = new MMComboBox<>("comboFaction", factionModel); - comboFaction.setSelectedItem(new FactionDisplay(campaign.getFaction(), date)); - comboFaction.setMinimumSize(new Dimension(400, 30)); - comboFaction.setPreferredSize(new Dimension(400, 30)); - gridBagConstraints.gridx = gridx--; - panGeneral.add(comboFaction, gridBagConstraints); - - JPanel unitRatingPanel = new JPanel(new GridBagLayout()); - - JLabel unitRatingMethodLabel = new JLabel(resources.getString("unitRatingMethodLabel.text")); - unitRatingMethodLabel.setName("unitRatingMethodLabel"); - gridBagConstraints.gridx = 0; - unitRatingPanel.add(unitRatingMethodLabel, gridBagConstraints); - - unitRatingMethodCombo = new MMComboBox<>("unitRatingMethodCombo", UnitRatingMethod.values()); - gridBagConstraints.gridx = 1; - unitRatingPanel.add(unitRatingMethodCombo, gridBagConstraints); - - JLabel manualUnitRatingModifierLabel = new JLabel( - resources.getString("manualUnitRatingModifierLabel.text")); - gridBagConstraints.gridx = 2; - unitRatingPanel.add(manualUnitRatingModifierLabel, gridBagConstraints); - - manualUnitRatingModifier = new JSpinner(new SpinnerNumberModel(0, -100, 100, 1)); - gridBagConstraints.gridx = 3; - unitRatingPanel.add(manualUnitRatingModifier, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = gridx; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panGeneral.add(unitRatingPanel, gridBagConstraints); - - JLabel lblDate = new JLabel(resources.getString("lblDate.text")); - lblDate.setName("lblDate"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = gridx++; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.anchor = GridBagConstraints.WEST; - panGeneral.add(lblDate, gridBagConstraints); - - btnDate = new JButton(MekHQ.getMHQOptions().getDisplayFormattedDate(date)); - btnDate.setName("btnDate"); - btnDate.setMinimumSize(new Dimension(400, 30)); - btnDate.setPreferredSize(new Dimension(400, 30)); - btnDate.addActionListener(this::btnDateActionPerformed); - gridBagConstraints.gridx = gridx--; - panGeneral.add(btnDate, gridBagConstraints); - - JLabel lblCamo = new JLabel(resources.getString("lblCamo.text")); - lblCamo.setName("lblCamo"); - gridBagConstraints.gridx = gridx++; - gridBagConstraints.gridy = gridy++; - panGeneral.add(lblCamo, gridBagConstraints); - - btnCamo = new JButton(); - btnCamo.setName("btnCamo"); - btnCamo.setMinimumSize(new Dimension(84, 72)); - btnCamo.setPreferredSize(new Dimension(84, 72)); - btnCamo.setMaximumSize(new Dimension(84, 72)); - btnCamo.addActionListener(this::btnCamoActionPerformed); - gridBagConstraints.gridx = gridx--; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panGeneral.add(btnCamo, gridBagConstraints); - - JLabel lblIcon = new JLabel(resources.getString("lblIcon.text")); - gridBagConstraints.gridx = gridx++; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.anchor = GridBagConstraints.WEST; - panGeneral.add(lblIcon, gridBagConstraints); - - btnIcon = new JButton(); - btnIcon.setMinimumSize(new Dimension(84, 72)); - btnIcon.setPreferredSize(new Dimension(84, 72)); - btnIcon.setMaximumSize(new Dimension(84, 72)); - btnIcon.addActionListener(evt -> { - final UnitIconDialog unitIconDialog = new UnitIconDialog(getFrame(), unitIcon); - if (unitIconDialog.showDialog().isConfirmed() && (unitIconDialog.getSelectedItem() != null)) { - unitIcon = unitIconDialog.getSelectedItem(); - btnIcon.setIcon(unitIcon.getImageIcon(75)); - } - }); - gridBagConstraints.gridx = gridx--; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panGeneral.add(btnIcon, gridBagConstraints); - - return new JScrollPaneWithSpeed(panGeneral); - } - - private JScrollPane createRepairAndMaintenanceTab() { - final AbstractMHQScrollablePanel panRepair = new DefaultMHQScrollablePanel(getFrame(), - "repairAndMaintenancePanel", new GridBagLayout()); - - JPanel panSubRepair = new JPanel(new GridBagLayout()); - JPanel panSubMaintenance = new JPanel(new GridBagLayout()); - - panSubRepair.setBorder(BorderFactory.createTitledBorder(resources.getString("lblSubRepair.text"))); - panSubMaintenance.setBorder( - BorderFactory.createTitledBorder(resources.getString("lblSubMaintenance.text"))); - - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = 0.5; - gridBagConstraints.gridwidth = 1; - gridBagConstraints.fill = GridBagConstraints.BOTH; - panRepair.add(panSubRepair, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridwidth = 1; - gridBagConstraints.fill = GridBagConstraints.BOTH; - panRepair.add(panSubMaintenance, gridBagConstraints); - - useEraModsCheckBox = new JCheckBox(resources.getString("useEraModsCheckBox.text")); - useEraModsCheckBox.setToolTipText(resources.getString("useEraModsCheckBox.toolTipText")); - useEraModsCheckBox.setName("useEraModsCheckBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubRepair.add(useEraModsCheckBox, gridBagConstraints); - - assignedTechFirstCheckBox = new JCheckBox(resources.getString("assignedTechFirstCheckBox.text")); - assignedTechFirstCheckBox.setToolTipText(resources.getString("assignedTechFirstCheckBox.toolTipText")); - assignedTechFirstCheckBox.setName("assignedTechFirstCheckBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubRepair.add(assignedTechFirstCheckBox, gridBagConstraints); - - resetToFirstTechCheckBox = new JCheckBox(resources.getString("resetToFirstTechCheckBox.text")); - resetToFirstTechCheckBox.setToolTipText(resources.getString("resetToFirstTechCheckBox.toolTipText")); - resetToFirstTechCheckBox.setName("resetToFirstTechCheckBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubRepair.add(resetToFirstTechCheckBox, gridBagConstraints); - - useQuirksBox = new JCheckBox(resources.getString("useQuirksBox.text")); - useQuirksBox.setToolTipText(resources.getString("useQuirksBox.toolTipText")); - useQuirksBox.setName("useQuirksBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubRepair.add(useQuirksBox, gridBagConstraints); - - useAeroSystemHitsBox = new JCheckBox(resources.getString("useAeroSystemHits.text")); - useAeroSystemHitsBox.setToolTipText(resources.getString("useAeroSystemHits.toolTipText")); - useAeroSystemHitsBox.setName("useAeroSystemHits"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubRepair.add(useAeroSystemHitsBox, gridBagConstraints); - - useDamageMargin = new JCheckBox(resources.getString("useDamageMargin.text")); - useDamageMargin.setToolTipText(resources.getString("useDamageMargin.toolTipText")); - useDamageMargin.addActionListener(evt -> spnDamageMargin.setEnabled(useDamageMargin.isSelected())); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 5; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubRepair.add(useDamageMargin, gridBagConstraints); - - spnDamageMargin = new JSpinner(new SpinnerNumberModel(1, 1, 20, 1)); - ((DefaultEditor) spnDamageMargin.getEditor()).getTextField().setEditable(false); - JPanel pnlDamageMargin = new JPanel(); - pnlDamageMargin.add(new JLabel(resources.getString("lblDamageMargin.text"))); - pnlDamageMargin.add(spnDamageMargin); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 6; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubRepair.add(pnlDamageMargin, gridBagConstraints); - - spnDestroyPartTarget = new JSpinner(new SpinnerNumberModel(2, 2, 13, 1)); - ((DefaultEditor) spnDestroyPartTarget.getEditor()).getTextField().setEditable(false); - - JPanel pnlDestroyPartTarget = new JPanel(); - pnlDestroyPartTarget.add(new JLabel(resources.getString("lblDestroyPartTarget.text"))); - pnlDestroyPartTarget.add(spnDestroyPartTarget); - pnlDestroyPartTarget.add(new JLabel(resources.getString("lblDestroyPartTargetSuffix.text"))); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 7; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubRepair.add(pnlDestroyPartTarget, gridBagConstraints); - - checkMaintenance = new JCheckBox(resources.getString("checkMaintenance.text")); - checkMaintenance.setToolTipText(resources.getString("checkMaintenance.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubMaintenance.add(checkMaintenance, gridBagConstraints); - - checkMaintenance.addActionListener(evt -> { - if (checkMaintenance.isSelected()) { - spnMaintenanceDays.setEnabled(true); - useQualityMaintenance.setEnabled(true); - useUnofficialMaintenance.setEnabled(true); - reverseQualityNames.setEnabled(true); - chkUseRandomUnitQualities.setEnabled(true); - chkUsePlanetaryModifiers.setEnabled(true); - spnMaintenanceBonus.setEnabled(true); - logMaintenance.setEnabled(true); - spnDefaultMaintenanceTime.setEnabled(true); - } else { - spnMaintenanceDays.setEnabled(false); - useQualityMaintenance.setEnabled(false); - useUnofficialMaintenance.setEnabled(false); - reverseQualityNames.setEnabled(false); - chkUseRandomUnitQualities.setEnabled(false); - chkUsePlanetaryModifiers.setEnabled(false); - spnMaintenanceBonus.setEnabled(false); - logMaintenance.setEnabled(false); - spnDefaultMaintenanceTime.setEnabled(false); - } - }); - - spnMaintenanceDays = new JSpinner(new SpinnerNumberModel(30, 1, 365, 1)); - ((DefaultEditor) spnMaintenanceDays.getEditor()).getTextField().setEditable(false); - JPanel pnlMaintenanceDays = new JPanel(); - pnlMaintenanceDays.add(spnMaintenanceDays); - pnlMaintenanceDays.add(new JLabel(resources.getString("lblMaintenanceDays.text"))); - pnlMaintenanceDays.setToolTipText(wordWrap(resources.getString("lblMaintenanceDays.toolTipText"))); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubMaintenance.add(pnlMaintenanceDays, gridBagConstraints); - - spnMaintenanceBonus = new JSpinner(new SpinnerNumberModel(0, -13, 13, 1)); - ((DefaultEditor) spnMaintenanceBonus.getEditor()).getTextField().setEditable(false); - spnMaintenanceBonus.setToolTipText(resources.getString("spnMaintenanceBonus.toolTipText")); - - JPanel pnlMaintenanceBonus = new JPanel(); - pnlMaintenanceBonus.add(spnMaintenanceBonus); - pnlMaintenanceBonus.add(new JLabel(resources.getString("lblMaintenanceBonus.text"))); - - spnDefaultMaintenanceTime = new JSpinner(new SpinnerNumberModel(4, 1, 4, 1)); - ((DefaultEditor) spnDefaultMaintenanceTime.getEditor()).getTextField().setEditable(false); - spnDefaultMaintenanceTime.setToolTipText(resources.getString("lblDefaultMaintenanceTime.toolTipText")); - - JPanel pnlDefaultMaintenanceTime = new JPanel(); - pnlDefaultMaintenanceTime.add(spnDefaultMaintenanceTime); - pnlDefaultMaintenanceTime.add(new JLabel(resources.getString("lblDefaultMaintenanceTime.text"))); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubMaintenance.add(pnlDefaultMaintenanceTime, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubMaintenance.add(pnlMaintenanceBonus, gridBagConstraints); - - useQualityMaintenance = new JCheckBox(resources.getString("useQualityMaintenance.text")); - useQualityMaintenance.setToolTipText(resources.getString("useQualityMaintenance.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubMaintenance.add(useQualityMaintenance, gridBagConstraints); - - reverseQualityNames = new JCheckBox(resources.getString("reverseQualityNames.text")); - reverseQualityNames.setToolTipText(resources.getString("reverseQualityNames.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 5; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubMaintenance.add(reverseQualityNames, gridBagConstraints); - - reverseQualityNames.addActionListener(evt -> recreateFinancesPanel(reverseQualityNames.isSelected())); - - chkUseRandomUnitQualities = new JCheckBox(resources.getString("chkUseRandomUnitQualities.text")); - chkUseRandomUnitQualities.setToolTipText(resources.getString("chkUseRandomUnitQualities.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 6; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubMaintenance.add(chkUseRandomUnitQualities, gridBagConstraints); - - chkUsePlanetaryModifiers = new JCheckBox(resources.getString("chkUsePlanetaryModifiers.text")); - chkUsePlanetaryModifiers.setToolTipText(resources.getString("chkUsePlanetaryModifiers.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 7; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubMaintenance.add(chkUsePlanetaryModifiers, gridBagConstraints); - - useUnofficialMaintenance = new JCheckBox(resources.getString("useUnofficialMaintenance.text")); - useUnofficialMaintenance.setToolTipText(resources.getString("useUnofficialMaintenance.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 8; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubMaintenance.add(useUnofficialMaintenance, gridBagConstraints); - - logMaintenance = new JCheckBox(resources.getString("logMaintenance.text")); - logMaintenance.setToolTipText(resources.getString("logMaintenance.toolTipText")); - logMaintenance.setName("logMaintenance"); - gridBagConstraints.gridy = 9; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - panSubMaintenance.add(logMaintenance, gridBagConstraints); - - return new JScrollPaneWithSpeed(panRepair); - } - - private JScrollPane createSuppliesAndAcquisitionsTab() { - final AbstractMHQScrollablePanel panSupplies = new DefaultMHQScrollablePanel(getFrame(), - "suppliesAndAcquisitionsPanel", new GridBagLayout()); - - JPanel panSubAcquire = new JPanel(new GridBagLayout()); - JPanel panSubDelivery = new JPanel(new GridBagLayout()); - JPanel panSubPlanetAcquire = new JPanel(new GridBagLayout()); - - panSubAcquire.setBorder(BorderFactory.createTitledBorder(resources.getString("lblSubAcquire.text"))); - panSubDelivery.setBorder(BorderFactory.createTitledBorder(resources.getString("lblSubDelivery.text"))); - panSubPlanetAcquire - .setBorder(BorderFactory - .createTitledBorder(resources.getString("lblSubPlanetAcquire.text"))); - - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = .5; - gridBagConstraints.gridwidth = 1; - gridBagConstraints.fill = GridBagConstraints.BOTH; - panSupplies.add(panSubAcquire, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = .5; - gridBagConstraints.gridwidth = 1; - gridBagConstraints.fill = GridBagConstraints.BOTH; - panSupplies.add(panSubDelivery, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.BOTH; - panSupplies.add(panSubPlanetAcquire, gridBagConstraints); - - spnAcquireWaitingPeriod = new JSpinner(new SpinnerNumberModel(1, 1, 365, 1)); - ((DefaultEditor) spnAcquireWaitingPeriod.getEditor()).getTextField().setEditable(false); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.WEST; - panSubAcquire.add(new JLabel(resources.getString("lblAcquireSkill.text")), gridBagConstraints); - - DefaultComboBoxModel acquireSkillModel = new DefaultComboBoxModel<>(); - acquireSkillModel.addElement(CampaignOptions.S_TECH); - acquireSkillModel.addElement(SkillType.S_ADMIN); - acquireSkillModel.addElement(SkillType.S_SCROUNGE); - acquireSkillModel.addElement(SkillType.S_NEG); - acquireSkillModel.addElement(CampaignOptions.S_AUTO); - choiceAcquireSkill = new MMComboBox<>("choiceAcquireSkill", acquireSkillModel); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.WEST; - panSubAcquire.add(choiceAcquireSkill, gridBagConstraints); - - chkSupportStaffOnly = new JCheckBox(resources.getString("lblSupportStaffOnly.text")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAcquire.add(chkSupportStaffOnly, gridBagConstraints); - - spnAcquireClanPenalty = new JSpinner(new SpinnerNumberModel(0, 0, 13, 1)); - ((DefaultEditor) spnAcquireClanPenalty.getEditor()).getTextField().setEditable(false); - - JPanel pnlClanPenalty = new JPanel(); - pnlClanPenalty.add(spnAcquireClanPenalty); - pnlClanPenalty.add(new JLabel(resources.getString("lblAcquireClanPenalty.text"))); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAcquire.add(pnlClanPenalty, gridBagConstraints); - - spnAcquireIsPenalty = new JSpinner(new SpinnerNumberModel(0, 0, 13, 1)); - ((DefaultEditor) spnAcquireIsPenalty.getEditor()).getTextField().setEditable(false); - - JPanel pnlIsPenalty = new JPanel(); - pnlIsPenalty.add(spnAcquireIsPenalty); - pnlIsPenalty.add(new JLabel(resources.getString("lblAcquireIsPenalty.text"))); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAcquire.add(pnlIsPenalty, gridBagConstraints); - - JPanel pnlWaitingPeriod = new JPanel(); - pnlWaitingPeriod.add(spnAcquireWaitingPeriod); - pnlWaitingPeriod.add(new JLabel(resources.getString("lblWaitingPeriod.text"))); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 5; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.gridwidth = 5; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - - panSubAcquire.add(pnlWaitingPeriod, gridBagConstraints); - spnMaxAcquisitions = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1)); - spnMaxAcquisitions.setName("spnMaxAcquisitions"); - spnMaxAcquisitions.setToolTipText(resources.getString("spnMaxAcquisitions.toolTipText")); - - JPanel pnlMaxAcquisitions = new JPanel(); - pnlMaxAcquisitions.add(spnMaxAcquisitions); - pnlMaxAcquisitions.add(new JLabel(resources.getString("lblMaxAcquisitions.text"))); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 6; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAcquire.add(pnlMaxAcquisitions, gridBagConstraints); - - spnNDiceTransitTime = new JSpinner(new SpinnerNumberModel(0, 0, 365, 1)); - ((DefaultEditor) spnNDiceTransitTime.getEditor()).getTextField().setEditable(false); - - spnConstantTransitTime = new JSpinner(new SpinnerNumberModel(0, 0, 365, 1)); - ((DefaultEditor) spnConstantTransitTime.getEditor()).getTextField().setEditable(false); - - spnAcquireMosBonus = new JSpinner(new SpinnerNumberModel(0, 0, 365, 1)); - ((DefaultEditor) spnAcquireMosBonus.getEditor()).getTextField().setEditable(false); - - spnAcquireMinimum = new JSpinner(new SpinnerNumberModel(0, 0, 365, 1)); - ((DefaultEditor) spnAcquireMinimum.getEditor()).getTextField().setEditable(false); - - DefaultComboBoxModel transitUnitModel = new DefaultComboBoxModel<>(); - - for (int i = 0; i < CampaignOptions.TRANSIT_UNIT_NUM; i++) { - transitUnitModel.addElement(CampaignOptions.getTransitUnitName(i)); - } - - choiceTransitTimeUnits = new MMComboBox<>("choiceTransitTimeUnits", transitUnitModel); - - DefaultComboBoxModel transitMosUnitModel = new DefaultComboBoxModel<>(); - - for (int i = 0; i < CampaignOptions.TRANSIT_UNIT_NUM; i++) { - transitMosUnitModel.addElement(CampaignOptions.getTransitUnitName(i)); - } - - choiceAcquireMosUnits = new MMComboBox<>("choiceAcquireMosUnits", transitMosUnitModel); - - DefaultComboBoxModel transitMinUnitModel = new DefaultComboBoxModel<>(); - - for (int i = 0; i < CampaignOptions.TRANSIT_UNIT_NUM; i++) { - transitMinUnitModel.addElement(CampaignOptions.getTransitUnitName(i)); - } - - choiceAcquireMinimumUnit = new MMComboBox<>("choiceAcquireMinimumUnit", transitMinUnitModel); - - JPanel pnlTransitTime = new JPanel(); - pnlTransitTime.add(new JLabel(resources.getString("lblTransitTime.text"))); - pnlTransitTime.add(spnNDiceTransitTime); - pnlTransitTime.add(new JLabel("d6 + ")); - pnlTransitTime.add(spnConstantTransitTime); - pnlTransitTime.add(choiceTransitTimeUnits); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubDelivery.add(pnlTransitTime, gridBagConstraints); - - JPanel pnlMinTransit = new JPanel(); - pnlMinTransit.add(new JLabel(resources.getString("lblMinTransit.text"))); - pnlMinTransit.add(spnAcquireMinimum); - pnlMinTransit.add(choiceAcquireMinimumUnit); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubDelivery.add(pnlMinTransit, gridBagConstraints); - - JPanel pnlMosBonus = new JPanel(); - pnlMosBonus.add(new JLabel(resources.getString("lblMosBonus.text"))); - pnlMosBonus.add(spnAcquireMosBonus); - pnlMosBonus.add(choiceAcquireMosUnits); - pnlMosBonus.add(new JLabel(resources.getString("lblMosBonusSuffix.text"))); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubDelivery.add(pnlMosBonus, gridBagConstraints); - - usePlanetaryAcquisitions = new JCheckBox(resources.getString("usePlanetaryAcquisitions.text")); - usePlanetaryAcquisitions.setToolTipText(resources.getString("usePlanetaryAcquisitions.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubPlanetAcquire.add(usePlanetaryAcquisitions, gridBagConstraints); - - spnMaxJumpPlanetaryAcquisitions = new JSpinner(new SpinnerNumberModel(2, 0, 5, 1)); - JPanel panMaxJump = new JPanel(); - panMaxJump.add(spnMaxJumpPlanetaryAcquisitions); - panMaxJump.add(new JLabel(resources.getString("lblMaxJumpPlanetaryAcquisitions.text"))); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubPlanetAcquire.add(panMaxJump, gridBagConstraints); - - comboPlanetaryAcquisitionsFactionLimits = new MMComboBox<>("comboPlanetaryAcquisitionsFactionLimits", - PlanetaryAcquisitionFactionLimit.values()); - JPanel panFactionLimit = new JPanel(); - panFactionLimit.add(new JLabel(resources.getString("lblPlanetaryAcquisitionsFactionLimits.text"))); - panFactionLimit.add(comboPlanetaryAcquisitionsFactionLimits); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubPlanetAcquire.add(panFactionLimit, gridBagConstraints); - - disallowPlanetaryAcquisitionClanCrossover = new JCheckBox( - resources.getString("disallowPlanetaryAcquisitionClanCrossover.text")); - disallowPlanetaryAcquisitionClanCrossover - .setToolTipText(resources - .getString("disallowPlanetaryAcquisitionClanCrossover.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubPlanetAcquire.add(disallowPlanetaryAcquisitionClanCrossover, gridBagConstraints); - - disallowClanPartsFromIS = new JCheckBox(resources.getString("disallowClanPartsFromIS.text")); - disallowClanPartsFromIS.setToolTipText(resources.getString("disallowClanPartsFromIS.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubPlanetAcquire.add(disallowClanPartsFromIS, gridBagConstraints); - - spnPenaltyClanPartsFromIS = new JSpinner(new SpinnerNumberModel(0, 0, 12, 1)); - JPanel panPenaltyClanPartsFromIS = new JPanel(); - panPenaltyClanPartsFromIS.add(spnPenaltyClanPartsFromIS); - JLabel lblPenaltyClanPartsFromIS = new JLabel(resources.getString("spnPenaltyClanPartsFromIS.text")); - lblPenaltyClanPartsFromIS.setToolTipText(resources.getString("spnPenaltyClanPartsFromIS.toolTipText")); - panPenaltyClanPartsFromIS.add(lblPenaltyClanPartsFromIS); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 5; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubPlanetAcquire.add(panPenaltyClanPartsFromIS, gridBagConstraints); - - usePlanetaryAcquisitionsVerbose = new JCheckBox( - resources.getString("usePlanetaryAcquisitionsVerbose.text")); - usePlanetaryAcquisitionsVerbose - .setToolTipText(resources.getString("usePlanetaryAcquisitionsVerbose.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 6; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubPlanetAcquire.add(usePlanetaryAcquisitionsVerbose, gridBagConstraints); - - JPanel panSocioIndustrialBonus = new JPanel(); - panSocioIndustrialBonus.setLayout(new BoxLayout(panSocioIndustrialBonus, BoxLayout.LINE_AXIS)); - panSocioIndustrialBonus - .setBorder(BorderFactory.createTitledBorder( - resources.getString("lblSocioIndustrialBonusPanel.text"))); - - JPanel panTechBonus = new JPanel(new GridBagLayout()); - JPanel panIndustryBonus = new JPanel(new GridBagLayout()); - JPanel panOutputBonus = new JPanel(new GridBagLayout()); - - spnPlanetAcquireTechBonus = new JSpinner[6]; - spnPlanetAcquireIndustryBonus = new JSpinner[6]; - spnPlanetAcquireOutputBonus = new JSpinner[6]; - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 0; - gridBagConstraints.gridx = 0; - gridBagConstraints.gridwidth = 2; - panTechBonus.add(new JLabel(resources.getString("lblTechBonus.text")), gridBagConstraints); - panIndustryBonus.add(new JLabel(resources.getString("lblIndustryBonus.text")), gridBagConstraints); - panOutputBonus.add(new JLabel(resources.getString("lblOutputBonus.text")), gridBagConstraints); - for (int i = EquipmentType.RATING_A; i <= EquipmentType.RATING_F; i++) { - gridBagConstraints.gridwidth = 1; - gridBagConstraints.gridy++; - gridBagConstraints.gridx = 0; - gridBagConstraints.insets = new Insets(0, 20, 0, 0); - panTechBonus.add(new JLabel(ITechnology.getRatingName(i) + " Level"), gridBagConstraints); - panIndustryBonus.add(new JLabel(ITechnology.getRatingName(i) + " Level"), gridBagConstraints); - panOutputBonus.add(new JLabel(ITechnology.getRatingName(i) + " Level"), gridBagConstraints); - gridBagConstraints.gridx = 1; - gridBagConstraints.insets = new Insets(0, 10, 0, 0); - spnPlanetAcquireTechBonus[i] = new JSpinner(new SpinnerNumberModel(0, -12, 12, 1)); - spnPlanetAcquireIndustryBonus[i] = new JSpinner(new SpinnerNumberModel(0, -12, 12, 1)); - spnPlanetAcquireOutputBonus[i] = new JSpinner(new SpinnerNumberModel(0, -12, 12, 1)); - ((DefaultEditor) spnPlanetAcquireTechBonus[i].getEditor()).getTextField().setEditable(false); - ((DefaultEditor) spnPlanetAcquireIndustryBonus[i].getEditor()).getTextField() - .setEditable(false); - ((DefaultEditor) spnPlanetAcquireOutputBonus[i].getEditor()).getTextField().setEditable(false); - - panTechBonus.add(spnPlanetAcquireTechBonus[i], gridBagConstraints); - panOutputBonus.add(spnPlanetAcquireOutputBonus[i], gridBagConstraints); - panIndustryBonus.add(spnPlanetAcquireIndustryBonus[i], gridBagConstraints); - } - - panSocioIndustrialBonus.add(panTechBonus); - panSocioIndustrialBonus.add(panIndustryBonus); - panSocioIndustrialBonus.add(panOutputBonus); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = 0.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.gridheight = 7; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubPlanetAcquire.add(panSocioIndustrialBonus, gridBagConstraints); - - return new JScrollPaneWithSpeed(panSupplies); - } - - private JScrollPane createTechLimitsTab() { - int gridy = 0; - - final AbstractMHQScrollablePanel panTech = new DefaultMHQScrollablePanel(getFrame(), "techLimitsPanel", - new GridBagLayout()); - - limitByYearBox = new JCheckBox(resources.getString("limitByYearBox.text")); - limitByYearBox.setToolTipText(resources.getString("limitByYearBox.toolTipText")); - limitByYearBox.setName("limitByYearBox"); - limitByYearBox.addActionListener(e -> variableTechLevelBox.setEnabled(limitByYearBox.isSelected())); - - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(limitByYearBox, gridBagConstraints); - - disallowExtinctStuffBox = new JCheckBox(resources.getString("disallowExtinctStuffBox.text")); - disallowExtinctStuffBox.setToolTipText(resources.getString("disallowExtinctStuffBox.toolTipText")); - disallowExtinctStuffBox.setName("disallowExtinctStuffBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(disallowExtinctStuffBox, gridBagConstraints); - - allowClanPurchasesBox = new JCheckBox(resources.getString("allowClanPurchasesBox.text")); - allowClanPurchasesBox.setToolTipText(resources.getString("allowClanPurchasesBox.toolTipText")); - allowClanPurchasesBox.setName("allowClanPurchasesBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(allowClanPurchasesBox, gridBagConstraints); - - allowISPurchasesBox = new JCheckBox(resources.getString("allowISPurchasesBox.text")); - allowISPurchasesBox.setToolTipText(resources.getString("allowISPurchasesBox.toolTipText")); - allowISPurchasesBox.setName("allowISPurchasesBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(allowISPurchasesBox, gridBagConstraints); - - allowCanonOnlyBox = new JCheckBox(resources.getString("allowCanonOnlyBox.text")); - allowCanonOnlyBox.setToolTipText(resources.getString("allowCanonOnlyBox.toolTipText")); - allowCanonOnlyBox.setName("allowCanonOnlyBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(allowCanonOnlyBox, gridBagConstraints); - - allowCanonRefitOnlyBox = new JCheckBox(resources.getString("allowCanonRefitOnlyBox.text")); - allowCanonRefitOnlyBox.setToolTipText(resources.getString("allowCanonRefitOnlyBox.toolTipText")); - allowCanonRefitOnlyBox.setName("allowCanonRefitOnlyBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(allowCanonRefitOnlyBox, gridBagConstraints); - - JLabel lblTechLevel = new JLabel(resources.getString("lblTechLevel.text")); - lblTechLevel.setName("lblTechLevel"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.WEST; - panTech.add(lblTechLevel, gridBagConstraints); - - DefaultComboBoxModel techLevelComboBoxModel = new DefaultComboBoxModel<>(); - techLevelComboBoxModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_INTRO)); - techLevelComboBoxModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_STANDARD)); - techLevelComboBoxModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_ADVANCED)); - techLevelComboBoxModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_EXPERIMENTAL)); - techLevelComboBoxModel.addElement(CampaignOptions.getTechLevelName(CampaignOptions.TECH_UNOFFICIAL)); - choiceTechLevel = new MMComboBox<>("choiceTechLevel", techLevelComboBoxModel); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(choiceTechLevel, gridBagConstraints); - - variableTechLevelBox = new JCheckBox(resources.getString("variableTechLevelBox.text")); - variableTechLevelBox.setToolTipText(resources.getString("variableTechLevelBox.toolTipText")); - variableTechLevelBox.setName("variableTechLevelBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(variableTechLevelBox, gridBagConstraints); - - factionIntroDateBox = new JCheckBox(resources.getString("factionIntroDateBox.text")); - factionIntroDateBox.setToolTipText(resources.getString("factionIntroDateBox.toolTipText")); - factionIntroDateBox.setName("factionIntroDateBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(factionIntroDateBox, gridBagConstraints); - - useAmmoByTypeBox = new JCheckBox(resources.getString("useAmmoByTypeBox.text")); - useAmmoByTypeBox.setToolTipText(resources.getString("useAmmoByTypeBox.toolTipText")); - useAmmoByTypeBox.setName("useAmmoByTypeBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panTech.add(useAmmoByTypeBox, gridBagConstraints); - - return new JScrollPaneWithSpeed(panTech); - } - - private JScrollPane createFinancesTab(boolean reverseQualities) { - int gridy = 0; - - AbstractMHQScrollablePanel panFinances = new DefaultMHQScrollablePanel(getFrame(), - "financesPanel", new GridBagLayout()); - - payForPartsBox = new JCheckBox(resources.getString("payForPartsBox.text")); - payForPartsBox.setToolTipText(resources.getString("payForPartsBox.toolTipText")); - payForPartsBox.setName("payForPartsBox"); - - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(payForPartsBox, gridBagConstraints); - - payForRepairsBox = new JCheckBox(resources.getString("payForRepairsBox.text")); - payForRepairsBox.setToolTipText(resources.getString("payForRepairsBox.toolTipText")); - payForRepairsBox.setName("payForRepairsBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(payForRepairsBox, gridBagConstraints); - - payForUnitsBox = new JCheckBox(resources.getString("payForUnitsBox.text")); - payForUnitsBox.setToolTipText(resources.getString("payForUnitsBox.toolTipText")); - payForUnitsBox.setName("payForUnitsBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(payForUnitsBox, gridBagConstraints); - - payForSalariesBox = new JCheckBox(resources.getString("payForSalariesBox.text")); - payForSalariesBox.setToolTipText(resources.getString("payForSalariesBox.toolTipText")); - payForSalariesBox.setName("payForSalariesBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(payForSalariesBox, gridBagConstraints); - - payForOverheadBox = new JCheckBox(resources.getString("payForOverheadBox.text")); - payForOverheadBox.setToolTipText(resources.getString("payForOverheadBox.toolTipText")); - payForOverheadBox.setName("payForOverheadBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(payForOverheadBox, gridBagConstraints); - - payForMaintainBox = new JCheckBox(resources.getString("payForMaintainBox.text")); - payForMaintainBox.setToolTipText(resources.getString("payForMaintainBox.toolTipText")); - payForMaintainBox.setName("payForMaintainBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(payForMaintainBox, gridBagConstraints); - - payForTransportBox = new JCheckBox(resources.getString("payForTransportBox.text")); - payForTransportBox.setToolTipText(resources.getString("payForTransportBox.toolTipText")); - payForTransportBox.setName("payForTransportBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(payForTransportBox, gridBagConstraints); - - sellUnitsBox = new JCheckBox(resources.getString("sellUnitsBox.text")); - sellUnitsBox.setToolTipText(resources.getString("sellUnitsBox.toolTipText")); - sellUnitsBox.setName("sellUnitsBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(sellUnitsBox, gridBagConstraints); - - sellPartsBox = new JCheckBox(resources.getString("sellPartsBox.text")); - sellPartsBox.setToolTipText(resources.getString("sellPartsBox.toolTipText")); - sellPartsBox.setName("sellPartsBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(sellPartsBox, gridBagConstraints); - - payForRecruitmentBox = new JCheckBox(resources.getString("payForRecruitmentBox.text")); - payForRecruitmentBox.setToolTipText(resources.getString("payForRecruitmentBox.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(payForRecruitmentBox, gridBagConstraints); - - useLoanLimitsBox = new JCheckBox(resources.getString("useLoanLimitsBox.text")); - useLoanLimitsBox.setToolTipText(resources.getString("useLoanLimitsBox.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(useLoanLimitsBox, gridBagConstraints); - - // Unofficial maintenance costs - usePercentageMaintenanceBox = new JCheckBox(resources.getString("usePercentageMaintenanceBox.text")); - usePercentageMaintenanceBox - .setToolTipText(resources.getString("usePercentageMaintenanceBox.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(usePercentageMaintenanceBox, gridBagConstraints); - - // Unofficial infantry doesn't count for contract pay - useInfantryDoseNotCountBox = new JCheckBox(resources.getString("infantryDontCount.text")); - useInfantryDoseNotCountBox.setToolTipText(resources.getString("infantryDontCount.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(useInfantryDoseNotCountBox, gridBagConstraints); - - // Campaign Operations Peacetime operating costs - usePeacetimeCostBox = new JCheckBox(resources.getString("usePeacetimeCostBox.text")); - usePeacetimeCostBox.setToolTipText(resources.getString("usePeacetimeCostBox.toolTipText")); - usePeacetimeCostBox.setName("usePeacetimeCostBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(usePeacetimeCostBox, gridBagConstraints); - - useExtendedPartsModifierBox = new JCheckBox(resources.getString("useExtendedPartsModifierBox.text")); - useExtendedPartsModifierBox.setName("useExtendedPartsModifierBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(useExtendedPartsModifierBox, gridBagConstraints); - - showPeacetimeCostBox = new JCheckBox(resources.getString("showPeacetimeCostBox.text")); - showPeacetimeCostBox.setToolTipText(resources.getString("showPeacetimeCostBox.toolTipText")); - showPeacetimeCostBox.setName("showPeacetimeCostBox"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panFinances.add(showPeacetimeCostBox, gridBagConstraints); - - DefaultComboBoxModel financialYearDurationModel = new DefaultComboBoxModel<>( - FinancialYearDuration.values()); - comboFinancialYearDuration = new MMComboBox<>("comboFinancialYearDuration", financialYearDurationModel); - comboFinancialYearDuration.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, - boolean isSelected, - boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof FinancialYearDuration) { - list.setToolTipText(((FinancialYearDuration) value).getToolTipText()); - } - return this; - } - }); - JPanel pnlFinancialYearDuration = new JPanel(); - pnlFinancialYearDuration.add(new JLabel(resources.getString("financialYearDuration.text"))); - pnlFinancialYearDuration.setToolTipText(resources.getString("financialYearDuration.toolTipText")); - pnlFinancialYearDuration.add(comboFinancialYearDuration); - gridBagConstraints.gridy = gridy++; - panFinances.add(pnlFinancialYearDuration, gridBagConstraints); - - newFinancialYearFinancesToCSVExportBox = new JCheckBox( - resources.getString("newFinancialYearFinancesToCSVExportBox.text")); - newFinancialYearFinancesToCSVExportBox - .setToolTipText(resources - .getString("newFinancialYearFinancesToCSVExportBox.toolTipText")); - newFinancialYearFinancesToCSVExportBox.setName("newFinancialYearFinancesToCSVExportBox"); - gridBagConstraints.gridy = gridy++; - panFinances.add(newFinancialYearFinancesToCSVExportBox, gridBagConstraints); - - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridheight = 20; - panFinances.add(createPriceModifiersPanel(reverseQualities), gridBagConstraints); - - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy++; - panFinances.add(createTaxesPanel(), gridBagConstraints); - - gridBagConstraints.gridx = 4; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridheight = 20; - panFinances.add(createSharesPanel(), gridBagConstraints); - - return new JScrollPaneWithSpeed(panFinances); - } - - private JScrollPane createMercenaryTab() { - final AbstractMHQScrollablePanel panMercenary = new DefaultMHQScrollablePanel(getFrame(), - "mercenaryPanel", new GridBagLayout()); - - btnContractEquipment = new JRadioButton(resources.getString("panMercenary.IntOpsPayment.title")); - btnContractEquipment.setToolTipText(resources.getString("panMercenary.IntOpsPayment.tooltip")); - - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(btnContractEquipment, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 30, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(new JLabel(resources.getString("lblEquipPercent.text")), gridBagConstraints); - - spnEquipPercent = new JSpinner( - new SpinnerNumberModel(0.1, 0.1, CampaignOptions.MAXIMUM_COMBAT_EQUIPMENT_PERCENT, - 0.1)); - spnEquipPercent.setEditor(new NumberEditor(spnEquipPercent, "0.0")); - ((DefaultEditor) spnEquipPercent.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(spnEquipPercent, gridBagConstraints); - - chkEquipContractSaleValue = new JCheckBox(resources.getString("chkEquipContractSaleValue.text")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(chkEquipContractSaleValue, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 30, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(new JLabel(resources.getString("lblDropShipPercent.text")), gridBagConstraints); - - spnDropShipPercent = new JSpinner( - new SpinnerNumberModel(0.1, 0.0, CampaignOptions.MAXIMUM_DROPSHIP_EQUIPMENT_PERCENT, - 0.1)); - spnDropShipPercent.setEditor(new NumberEditor(spnDropShipPercent, "0.0")); - ((NumberEditor) spnDropShipPercent.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(spnDropShipPercent, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 30, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(new JLabel(resources.getString("lblJumpShipPercent.text")), gridBagConstraints); - - spnJumpShipPercent = new JSpinner( - new SpinnerNumberModel(0.1, 0.0, CampaignOptions.MAXIMUM_JUMPSHIP_EQUIPMENT_PERCENT, - 0.1)); - spnJumpShipPercent.setEditor(new NumberEditor(spnJumpShipPercent, "0.0")); - ((DefaultEditor) spnJumpShipPercent.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(spnJumpShipPercent, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 30, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(new JLabel(resources.getString("lblWarShipPercent.text")), gridBagConstraints); - - spnWarShipPercent = new JSpinner( - new SpinnerNumberModel(0.1, 0.0, CampaignOptions.MAXIMUM_WARSHIP_EQUIPMENT_PERCENT, - 0.1)); - spnWarShipPercent.setEditor(new NumberEditor(spnWarShipPercent, "0.0")); - ((DefaultEditor) spnWarShipPercent.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 4; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(spnWarShipPercent, gridBagConstraints); - - btnContractPersonnel = new JRadioButton(resources.getString("panMercenary.FMMRPayment.title")); - btnContractPersonnel.setToolTipText(resources.getString("panMercenary.FMMRPayment.tooltip")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 5; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panMercenary.add(btnContractPersonnel, gridBagConstraints); - - chkBLCSaleValue = new JCheckBox(resources.getString("lblBLCSaleValue.text")); - gridBagConstraints.gridy = 6; - panMercenary.add(chkBLCSaleValue, gridBagConstraints); - - chkOverageRepaymentInFinalPayment = new JCheckBox( - resources.getString("chkOverageRepaymentInFinalPayment.text")); - chkOverageRepaymentInFinalPayment - .setToolTipText(resources.getString("chkOverageRepaymentInFinalPayment.toolTipText")); - gridBagConstraints.gridy = 7; - panMercenary.add(chkOverageRepaymentInFinalPayment, gridBagConstraints); - - ButtonGroup groupContract = new ButtonGroup(); - groupContract.add(btnContractEquipment); - groupContract.add(btnContractPersonnel); - - return new JScrollPaneWithSpeed(panMercenary); - } - - private JScrollPane createExperienceTab() { - final AbstractMHQScrollablePanel panXP = new DefaultMHQScrollablePanel(getFrame(), - "experiencePanel", new GridBagLayout()); - - JLabel lblXpCostMultiplier = new JLabel(resources.getString("lblXpCostMultiplier.text")); - lblXpCostMultiplier.setToolTipText(resources.getString("lblXpCostMultiplier.tooltip")); - spnXpCostMultiplier = new JSpinner(new SpinnerNumberModel(1, 0, 5, 0.05)); - ((DefaultEditor) spnXpCostMultiplier.getEditor()).getTextField().setEditable(false); - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnXpCostMultiplier, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(lblXpCostMultiplier, gridBagConstraints); - - JLabel lblScenarioXP = new JLabel(resources.getString("lblScenarioXP.text")); - spnScenarioXP = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnScenarioXP.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnScenarioXP, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 1; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(lblScenarioXP, gridBagConstraints); - - JLabel lblKillXP = new JLabel(resources.getString("lblXPForEvery.text")); - spnKillXP = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnKillXP.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnKillXP, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(lblKillXP, gridBagConstraints); - - JLabel lblKills = new JLabel(resources.getString("lblKills.text")); - spnKills = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnKills.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnKills, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(lblKills, gridBagConstraints); - - JLabel lblTaskXP = new JLabel(resources.getString("lblXPForEvery.text")); - spnTaskXP = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnTaskXP.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnTaskXP, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(lblTaskXP, gridBagConstraints); - - JLabel lblTasks = new JLabel(resources.getString("lblTasks.text")); - spnNTasksXP = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnNTasksXP.getEditor()).getTextField().setEditable(false); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnNTasksXP, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(lblTasks, gridBagConstraints); - - JLabel lblSuccessXp = new JLabel(resources.getString("lblSuccessXP.text")); - spnSuccessXP = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnSuccessXP.getEditor()).getTextField().setEditable(false); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnSuccessXP, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 4; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(lblSuccessXp, gridBagConstraints); - - JLabel lblMistakeXP = new JLabel(resources.getString("lblMistakeXP.text")); - spnMistakeXP = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnMistakeXP.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 5; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnMistakeXP, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 5; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(lblMistakeXP, gridBagConstraints); - - spnIdleXP = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnIdleXP.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 6; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnIdleXP, gridBagConstraints); - - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 6; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(new JLabel(resources.getString("lblXPForEvery.text")), gridBagConstraints); - - spnMonthsIdleXP = new JSpinner(new SpinnerNumberModel(0, 0, 36, 1)); - ((DefaultEditor) spnMonthsIdleXP.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 6; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnMonthsIdleXP, gridBagConstraints); - - gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 6; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(new JLabel(resources.getString("lblTargetIdleXP.text")), gridBagConstraints); - - spnTargetIdleXP = new JSpinner(new SpinnerNumberModel(2, 2, 13, 1)); - ((DefaultEditor) spnTargetIdleXP.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 4; - gridBagConstraints.gridy = 6; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnTargetIdleXP, gridBagConstraints); - - spnContractNegotiationXP = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnContractNegotiationXP.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 7; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnContractNegotiationXP, gridBagConstraints); - - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 7; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(new JLabel(resources.getString("lblContractNegotiationXP.text")), gridBagConstraints); - - spnAdminWeeklyXP = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnAdminWeeklyXP.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 8; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnAdminWeeklyXP, gridBagConstraints); - - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 8; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(new JLabel(resources.getString("lblAdminWeeklyXP.text")), gridBagConstraints); - - spnAdminWeeklyXPPeriod = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1)); - ((DefaultEditor) spnAdminWeeklyXPPeriod.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 8; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnAdminWeeklyXPPeriod, gridBagConstraints); - - gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 8; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(new JLabel(resources.getString("lblAdminWeeklyXPPeriod.text")), gridBagConstraints); - - spnMissionXpFail = new JSpinner(new SpinnerNumberModel(1, 0, 10, 1)); - ((DefaultEditor) spnMissionXpFail.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 9; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnMissionXpFail, gridBagConstraints); - - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 9; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(new JLabel(resources.getString("spnMissionXpFail.text")), gridBagConstraints); - - spnMissionXpSuccess = new JSpinner(new SpinnerNumberModel(1, 0, 10, 1)); - ((DefaultEditor) spnMissionXpSuccess.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 10; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnMissionXpSuccess, gridBagConstraints); - - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 10; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(new JLabel(resources.getString("spnMissionXpSuccess.text")), gridBagConstraints); - - spnMissionXpOutstandingSuccess = new JSpinner(new SpinnerNumberModel(1, 0, 10, 1)); - ((DefaultEditor) spnMissionXpOutstandingSuccess.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 11; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnMissionXpOutstandingSuccess, gridBagConstraints); - - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 11; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(new JLabel(resources.getString("spnMissionXpOutstandingSuccess.text")), gridBagConstraints); - - spnEdgeCost = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); - ((DefaultEditor) spnEdgeCost.getEditor()).getTextField().setEditable(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 12; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(spnEdgeCost, gridBagConstraints); - - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 12; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(new JLabel(resources.getString("lblEdgeCost.text")), gridBagConstraints); - - final JTextArea txtInstructionsXP = new JTextArea(resources.getString("txtInstructionsXP.text")); - txtInstructionsXP.setName("txtInstructions"); - txtInstructionsXP.setEditable(false); - txtInstructionsXP.setEditable(false); - txtInstructionsXP.setLineWrap(true); - txtInstructionsXP.setWrapStyleWord(true); - txtInstructionsXP.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder(resources.getString("txtInstructionsXP.title")), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - txtInstructionsXP.setOpaque(false); - txtInstructionsXP.setMinimumSize(new Dimension(550, 120)); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 13; - gridBagConstraints.gridwidth = 6; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 0.0; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(txtInstructionsXP, gridBagConstraints); - - tableXP = new JTable(getSkillCostsArray(SkillType.getSkillHash()), TABLE_XP_COLUMN_NAMES); - tableXP.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - tableXP.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); - tableXP.setRowHeight(25); - tableXP.setRowSelectionAllowed(false); - tableXP.setColumnSelectionAllowed(false); - tableXP.setCellSelectionEnabled(true); - tableXP.setShowGrid(true); - final JScrollPane scrXP = new JScrollPaneWithSpeed(tableXP); - scrXP.setMinimumSize(new Dimension(500, ((tableXP.getRowCount() * 25) + 50))); - scrXP.setPreferredSize(new Dimension(500, ((tableXP.getRowCount() * 25) + 50))); - JTable rowTable = new RowNamesTable(tableXP); - rowTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); - rowTable.setShowGrid(true); - rowTable.setRowHeight(25); - scrXP.setRowHeaderView(rowTable); - scrXP.setCorner(JScrollPane.UPPER_LEFT_CORNER, rowTable.getTableHeader()); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 14; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.gridwidth = 5; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panXP.add(scrXP, gridBagConstraints); - - return new JScrollPaneWithSpeed(panXP); - } - - private JScrollPane createSkillsTab() { - final AbstractMHQScrollablePanel panSkill = new DefaultMHQScrollablePanel(getFrame(), "skillsPanel", - new GridBagLayout()); - - JPanel skPanel; - - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - - GridBagConstraints c; - JSpinner spnTarget; - JSpinner spnGreen; - JSpinner spnReg; - JSpinner spnVet; - JSpinner spnElite; - SkillType type; - JLabel lblSkill; - for (final String skillName : SkillType.getSkillList()) { - type = SkillType.getType(skillName); - skPanel = new JPanel(); - c = new GridBagConstraints(); - c.gridx = 0; - c.gridy = 0; - c.weightx = 1.0; - c.weighty = 1.0; - c.fill = GridBagConstraints.BOTH; - c.anchor = GridBagConstraints.WEST; - c.insets = new Insets(5, 5, 5, 5); - lblSkill = new JLabel(resources.getString("lblSkillTarget.text")); - skPanel.add(lblSkill, c); - c.gridx++; - spnTarget = new JSpinner(new SpinnerNumberModel(type.getTarget(), 0, 12, 1)); - ((DefaultEditor) spnTarget.getEditor()).getTextField().setEditable(false); - hashSkillTargets.put(skillName, spnTarget); - skPanel.add(spnTarget, c); - c.gridx++; - lblSkill = new JLabel(resources.getString("lblSkillGreen.text")); - skPanel.add(lblSkill, c); - c.gridx++; - spnGreen = new JSpinner(new SpinnerNumberModel(type.getGreenLevel(), 0, 10, 1)); - ((DefaultEditor) spnGreen.getEditor()).getTextField().setEditable(false); - hashGreenSkill.put(skillName, spnGreen); - skPanel.add(spnGreen, c); - c.gridx++; - lblSkill = new JLabel(resources.getString("lblSkillRegular.text")); - skPanel.add(lblSkill, c); - c.gridx++; - spnReg = new JSpinner(new SpinnerNumberModel(type.getRegularLevel(), 0, 10, 1)); - ((DefaultEditor) spnReg.getEditor()).getTextField().setEditable(false); - hashRegSkill.put(skillName, spnReg); - skPanel.add(spnReg, c); - c.gridx++; - lblSkill = new JLabel(resources.getString("lblSkillVeteran.text")); - skPanel.add(lblSkill, c); - c.gridx++; - spnVet = new JSpinner(new SpinnerNumberModel(type.getVeteranLevel(), 0, 10, 1)); - ((DefaultEditor) spnVet.getEditor()).getTextField().setEditable(false); - hashVetSkill.put(skillName, spnVet); - skPanel.add(spnVet, c); - c.gridx++; - lblSkill = new JLabel(resources.getString("lblSkillElite.text")); - skPanel.add(lblSkill, c); - c.gridx++; - spnElite = new JSpinner(new SpinnerNumberModel(type.getEliteLevel(), 0, 10, 1)); - ((DefaultEditor) spnElite.getEditor()).getTextField().setEditable(false); - hashEliteSkill.put(skillName, spnElite); - skPanel.add(spnElite, c); - c.gridx++; - - skPanel.setBorder(BorderFactory.createTitledBorder(skillName)); - panSkill.add(skPanel, gridBagConstraints); - gridBagConstraints.gridy++; - } - - final JScrollPane scrSkill = new JScrollPaneWithSpeed(panSkill); - scrSkill.setPreferredSize(new Dimension(500, 400)); - return scrSkill; - } - - private JScrollPane createSpecialAbilitiesTab() { - panSpecialAbilities = new DefaultMHQScrollablePanel(getFrame(), "specialAbilitiesPanel", - new GridBagLayout()); - - Set spaNames = SpecialAbility.getSpecialAbilities().keySet(); - // We need to create a temporary hash of special abilities that we can modify - // without - // changing the underlying one in case the user cancels the changes - tempSPA = new Hashtable<>(); - for (final String name : spaNames) { - getCurrentSPA().put(name, SpecialAbility.getAbility(name).clone()); - } - - btnAddSPA = new JButton("Add Another Special Ability"); - btnAddSPA.addActionListener(evt -> btnAddSPA()); - - recreateSPAPanel(!getUnusedSPA().isEmpty()); - - JScrollPane scrSPA = new JScrollPaneWithSpeed(panSpecialAbilities); - scrSPA.setPreferredSize(new Dimension(500, 400)); - return scrSPA; - } - - private JScrollPane createSkillRandomizationTab() { - GridBagConstraints gridBagConstraints; - - final AbstractMHQScrollablePanel panRandomSkill = new DefaultMHQScrollablePanel(getFrame(), - "skillRandomizationPanel", new GridBagLayout()); - - JPanel panRollTable = new JPanel(new GridLayout(6, 3, 5, 0)); - panRollTable.add(new JLabel("Value")); - panRollTable.add(new JLabel("Level")); - panRollTable.add(new JLabel("# Random SPAs")); - panRollTable.add(new JLabel("less than 2")); - JLabel lblUltraGreen = new JLabel(SkillLevel.ULTRA_GREEN.toString()); - lblUltraGreen.setToolTipText(resources.getString("lblUltraGreen.toolTipText")); - panRollTable.add(lblUltraGreen); - panRollTable.add(new JLabel("0")); - panRollTable.add(new JLabel("2-5")); - panRollTable.add(new JLabel(SkillLevel.GREEN.toString())); - panRollTable.add(new JLabel("0")); - panRollTable.add(new JLabel("6-9")); - panRollTable.add(new JLabel(SkillLevel.REGULAR.toString())); - panRollTable.add(new JLabel("0")); - panRollTable.add(new JLabel("10-11")); - panRollTable.add(new JLabel(SkillLevel.VETERAN.toString())); - panRollTable.add(new JLabel("1")); - panRollTable.add(new JLabel("12 or more")); - panRollTable.add(new JLabel(SkillLevel.ELITE.toString())); - panRollTable.add(new JLabel("2")); - panRollTable.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder("2d6 + Bonus"), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - - JLabel lblOverallRecruitBonus = new JLabel(resources.getString("lblOverallRecruitBonus.text")); - chkExtraRandom = new JCheckBox(resources.getString("chkExtraRandom.text")); - chkExtraRandom.setToolTipText(resources.getString("chkExtraRandom.toolTipText")); - JLabel lblProbAntiMek = new JLabel(resources.getString("lblProbAntiMek.text")); - spnProbAntiMek = new JSpinner(new SpinnerNumberModel(0, 0, 100, 5)); - ((DefaultEditor) spnProbAntiMek.getEditor()).getTextField().setEditable(false); - spnOverallRecruitBonus = new JSpinner(new SpinnerNumberModel(0, -12, 12, 1)); - ((DefaultEditor) spnOverallRecruitBonus.getEditor()).getTextField().setEditable(false); - spnOverallRecruitBonus.setToolTipText(resources.getString("spnOverallRecruitBonus.toolTipText")); - - final PersonnelRole[] personnelRoles = PersonnelRole.values(); - spnTypeRecruitBonus = new JSpinner[personnelRoles.length]; - int nRow = (int) Math.ceil(personnelRoles.length / 4.0); - JPanel panTypeRecruitBonus = new JPanel(new GridLayout(nRow, 4)); - JSpinner spin; - JPanel panRecruit; - for (final PersonnelRole role : personnelRoles) { - panRecruit = new JPanel(new GridBagLayout()); - - spin = new JSpinner(new SpinnerNumberModel(0, -12, 12, 1)); - ((DefaultEditor) spin.getEditor()).getTextField().setEditable(false); - spnTypeRecruitBonus[role.ordinal()] = spin; - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new Insets(2, 5, 0, 0); - panRecruit.add(spin, gridBagConstraints); - - gridBagConstraints.gridx = 1; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.weightx = 1.0; - panRecruit.add(new JLabel(role.toString()), gridBagConstraints); - - panTypeRecruitBonus.add(panRecruit); - } - - panTypeRecruitBonus.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder(resources.getString("panTypeRecruitBonus.title")), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panRandomSkill.add(chkExtraRandom, gridBagConstraints); - - // Phenotype Percentage Generation - List phenotypes = Phenotype.getExternalPhenotypes(); - phenotypeSpinners = new JSpinner[phenotypes.size()]; - - JPanel phenotypesPanel = new JPanel(new GridLayout((int) Math.ceil(phenotypes.size() / 2.0), 2)); - phenotypesPanel.setBorder( - BorderFactory.createTitledBorder(resources.getString("lblPhenotypesPanel.text"))); - - for (int i = 0; i < phenotypes.size(); i++) { - JSpinner phenotypeSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1)); - phenotypeSpinners[i] = phenotypeSpinner; - JPanel phenotypePanel = new JPanel(); - phenotypePanel.add(phenotypeSpinner); - phenotypePanel.add(new JLabel(phenotypes.get(i).getName())); - phenotypePanel.setToolTipText(phenotypes.get(i).getToolTipText()); - phenotypesPanel.add(phenotypePanel); - } - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panRandomSkill.add(phenotypesPanel, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridheight = 3; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panRandomSkill.add(panRollTable, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panRandomSkill.add(spnProbAntiMek, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panRandomSkill.add(lblProbAntiMek, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panRandomSkill.add(spnOverallRecruitBonus, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panRandomSkill.add(lblOverallRecruitBonus, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panRandomSkill.add(panTypeRecruitBonus, gridBagConstraints); - - JPanel panArtillery = new JPanel(); - panArtillery.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder(resources.getString("lblArtillerySkill.text")), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - spnArtyProb = new JSpinner(new SpinnerNumberModel(0, 0, 100, 5)); - ((DefaultEditor) spnArtyProb.getEditor()).getTextField().setEditable(false); - spnArtyProb.setToolTipText(resources.getString("spnArtyProb.toolTipText")); - panArtillery.add(spnArtyProb); - panArtillery.add(new JLabel("Probability")); - spnArtyBonus = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnArtyBonus.getEditor()).getTextField().setEditable(false); - panArtillery.add(spnArtyBonus); - panArtillery.add(new JLabel("Bonus")); - JPanel panSecondary = new JPanel(); - spnSecondProb = new JSpinner(new SpinnerNumberModel(0, 0, 100, 5)); - ((DefaultEditor) spnSecondProb.getEditor()).getTextField().setEditable(false); - spnSecondProb.setToolTipText(resources.getString("spnSecondProb.toolTipText")); - panSecondary.add(spnSecondProb); - panSecondary.add(new JLabel("Probability")); - spnSecondBonus = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnSecondBonus.getEditor()).getTextField().setEditable(false); - panSecondary.add(spnSecondBonus); - panSecondary.add(new JLabel("Bonus")); - panSecondary.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder(resources.getString("lblSecondarySkills.text")), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - JPanel panTactics = new JPanel(); - spnTacticsGreen = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnTacticsGreen.getEditor()).getTextField().setEditable(false); - spnTacticsGreen.setToolTipText(resources.getString("spnTacticsGreen.toolTipText")); - spnTacticsReg = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnTacticsReg.getEditor()).getTextField().setEditable(false); - spnTacticsReg.setToolTipText(resources.getString("spnTacticsReg.toolTipText")); - spnTacticsVet = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnTacticsVet.getEditor()).getTextField().setEditable(false); - spnTacticsVet.setToolTipText(resources.getString("spnTacticsVet.toolTipText")); - spnTacticsElite = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnTacticsElite.getEditor()).getTextField().setEditable(false); - spnTacticsElite.setToolTipText(resources.getString("spnTacticsElite.toolTipText")); - panTactics.add(spnTacticsGreen); - panTactics.add(new JLabel("Green")); - panTactics.add(spnTacticsReg); - panTactics.add(new JLabel("Reg")); - panTactics.add(spnTacticsVet); - panTactics.add(new JLabel("Vet")); - panTactics.add(spnTacticsElite); - panTactics.add(new JLabel("Elite")); - panTactics.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder(resources.getString("lblTacticsSkill.text")), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - JPanel panSmallArms = new JPanel(); - spnCombatSA = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnCombatSA.getEditor()).getTextField().setEditable(false); - spnCombatSA.setToolTipText(resources.getString("spnCombatSA.toolTipText")); - spnSupportSA = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnSupportSA.getEditor()).getTextField().setEditable(false); - spnSupportSA.setToolTipText(resources.getString("spnSupportSA.toolTipText")); - panSmallArms.add(spnCombatSA); - panSmallArms.add(new JLabel(resources.getString("lblCombatPersonnel.text"))); - panSmallArms.add(spnSupportSA); - panSmallArms.add(new JLabel(resources.getString("lblSupportPersonnel.text"))); - panSmallArms.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder(resources.getString("lblSmallArmsSkill.text")), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - JPanel panAbilities = new JPanel(); - spnAbilityGreen = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnAbilityGreen.getEditor()).getTextField().setEditable(false); - spnAbilityGreen.setToolTipText(resources.getString("spnAbilityGreen.toolTipText")); - spnAbilityReg = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnAbilityReg.getEditor()).getTextField().setEditable(false); - spnAbilityReg.setToolTipText(resources.getString("spnAbilityReg.toolTipText")); - spnAbilityVet = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnAbilityVet.getEditor()).getTextField().setEditable(false); - spnAbilityVet.setToolTipText(resources.getString("spnAbilityVet.toolTipText")); - spnAbilityElite = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - ((DefaultEditor) spnAbilityElite.getEditor()).getTextField().setEditable(false); - spnAbilityElite.setToolTipText(resources.getString("spnAbilityElite.toolTipText")); - panAbilities.add(spnAbilityGreen); - panAbilities.add(new JLabel("Green")); - panAbilities.add(spnAbilityReg); - panAbilities.add(new JLabel("Reg")); - panAbilities.add(spnAbilityVet); - panAbilities.add(new JLabel("Vet")); - panAbilities.add(spnAbilityElite); - panAbilities.add(new JLabel("Elite")); - panAbilities.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder(resources.getString("lblSpecialAbilities.text")), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - - JPanel panOtherBonuses = new JPanel(new GridLayout(3, 2)); - panOtherBonuses.add(panArtillery); - panOtherBonuses.add(panSecondary); - panOtherBonuses.add(panTactics); - panOtherBonuses.add(panAbilities); - panOtherBonuses.add(panSmallArms); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 5; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - panRandomSkill.add(panOtherBonuses, gridBagConstraints); - - JScrollPane scrRandomSkill = new JScrollPaneWithSpeed(panRandomSkill); - scrRandomSkill.setPreferredSize(new Dimension(500, 400)); - return scrRandomSkill; - } - - private JScrollPane createNameAndPortraitGenerationTab() { - int gridy = 0; - - final AbstractMHQScrollablePanel panNameGen = new DefaultMHQScrollablePanel(getFrame(), - "nameAndPortraitGenerationPanel", new GridBagLayout()); - - chkUseOriginFactionForNames = new JCheckBox(resources.getString("chkUseOriginFactionForNames.text")); - chkUseOriginFactionForNames - .setToolTipText(resources.getString("chkUseOriginFactionForNames.toolTipText")); - chkUseOriginFactionForNames.setName("chkUseOriginFactionForNames"); - chkUseOriginFactionForNames.addActionListener( - evt -> comboFactionNames.setEnabled(!chkUseOriginFactionForNames.isSelected())); - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = gridy; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panNameGen.add(chkUseOriginFactionForNames, gridBagConstraints); - - JLabel lblFactionNames = new JLabel(resources.getString("lblFactionNames.text")); - lblFactionNames.setName("lblFactionNames"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = ++gridy; - gridBagConstraints.anchor = GridBagConstraints.WEST; - panNameGen.add(lblFactionNames, gridBagConstraints); - - DefaultComboBoxModel factionNamesModel = new DefaultComboBoxModel<>(); - for (final String faction : RandomNameGenerator.getInstance().getFactions()) { - factionNamesModel.addElement(faction); - } - factionNamesModel.setSelectedItem(RandomNameGenerator.getInstance().getChosenFaction()); - comboFactionNames = new MMComboBox<>("comboFactionNames", factionNamesModel); - comboFactionNames.setMinimumSize(new Dimension(400, 30)); - comboFactionNames.setPreferredSize(new Dimension(400, 30)); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = gridy; - gridBagConstraints.anchor = GridBagConstraints.WEST; - panNameGen.add(comboFactionNames, gridBagConstraints); - - JLabel lblGender = new JLabel(resources.getString("lblGender.text")); - lblGender.setName("lblGender"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = ++gridy; - gridBagConstraints.insets = new Insets(10, 0, 0, 0); - gridBagConstraints.anchor = GridBagConstraints.WEST; - panNameGen.add(lblGender, gridBagConstraints); - - sldGender = new JSlider(SwingConstants.HORIZONTAL, 0, 100, RandomGenderGenerator.getPercentFemale()); - sldGender.setMajorTickSpacing(25); - sldGender.setPaintTicks(true); - sldGender.setPaintLabels(true); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = gridy; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new Insets(10, 0, 0, 0); - panNameGen.add(sldGender, gridBagConstraints); - - final JPanel panRandomPortrait = new JPanel(); - panRandomPortrait.setName("panRandomPortrait"); - panRandomPortrait.setLayout(new BorderLayout()); - - // The math below is used to determine how to split the personnel role options - // for portraits, - // which it does into 4 columns with rows equal to the number of roles plus two, - // with the - // additional two being the all role and no role options. - final PersonnelRole[] personnelRoles = PersonnelRole.values(); - JPanel panUsePortrait = new JPanel( - new GridLayout((int) Math.ceil((personnelRoles.length + 2) / 4.0), 4)); - chkUsePortrait = new JCheckBox[personnelRoles.length]; - allPortraitsBox = new JCheckBox(resources.getString("panUsePortrait.all.text")); - noPortraitsBox = new JCheckBox(resources.getString("panUsePortrait.no.text")); - allPortraitsBox.addActionListener(evt -> { - final boolean selected = allPortraitsBox.isSelected(); - for (final JCheckBox box : chkUsePortrait) { - if (selected) { - box.setSelected(true); - } - box.setEnabled(!selected); - } - if (selected) { - noPortraitsBox.setSelected(false); - } - }); - noPortraitsBox.addActionListener(evt -> { - final boolean selected = noPortraitsBox.isSelected(); - for (final JCheckBox box : chkUsePortrait) { - if (selected) { - box.setSelected(false); - } - box.setEnabled(!selected); - } - if (selected) { - allPortraitsBox.setSelected(false); - } - }); - panUsePortrait.add(allPortraitsBox); - panUsePortrait.add(noPortraitsBox); - - JCheckBox box; - for (final PersonnelRole role : PersonnelRole.values()) { - box = new JCheckBox(role.toString()); - panUsePortrait.add(box); - chkUsePortrait[role.ordinal()] = box; - } - - panRandomPortrait.add(panUsePortrait, BorderLayout.CENTER); - JTextArea txtPortraitInst = new JTextArea(resources.getString("txtPortraitInst.text")); - txtPortraitInst.setPreferredSize(new Dimension(728, 60)); - txtPortraitInst.setEditable(false); - txtPortraitInst.setLineWrap(true); - txtPortraitInst.setWrapStyleWord(true); - txtPortraitInst.setOpaque(false); - panRandomPortrait.add(txtPortraitInst, BorderLayout.PAGE_START); - - panRandomPortrait.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder(resources.getString("panRandomPortrait.title")), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = ++gridy; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.insets = new Insets(10, 0, 0, 0); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panNameGen.add(panRandomPortrait, gridBagConstraints); - - chkAssignPortraitOnRoleChange = new JCheckBox( - resources.getString("chkAssignPortraitOnRoleChange.text")); - chkAssignPortraitOnRoleChange - .setToolTipText(resources.getString("chkAssignPortraitOnRoleChange.toolTipText")); - chkAssignPortraitOnRoleChange.setName("chkAssignPortraitOnRoleChange"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = ++gridy; - gridBagConstraints.anchor = GridBagConstraints.WEST; - panNameGen.add(chkAssignPortraitOnRoleChange, gridBagConstraints); - - return new JScrollPaneWithSpeed(panNameGen); - } - - private JScrollPane createAgainstTheBotTab() { - panAtB = new DefaultMHQScrollablePanel(getFrame(), "atbPanel", new GridBagLayout()); - - JPanel panSubAtBAdmin = new JPanel(new GridBagLayout()); - JPanel panSubAtBContract = new JPanel(new GridBagLayout()); - JPanel panSubAtBScenario = new JPanel(new GridBagLayout()); - panSubAtBAdmin.setBorder(BorderFactory.createTitledBorder(resources.getString("lblSubAtbAdmin.text"))); - panSubAtBContract.setBorder( - BorderFactory.createTitledBorder(resources.getString("lblSubAtBContract.text"))); - panSubAtBScenario.setBorder( - BorderFactory.createTitledBorder(resources.getString("lblSubAtBScenario.text"))); - - chkUseAtB = new JCheckBox(resources.getString("chkUseAtB.text")); - chkUseAtB.setToolTipText(resources.getString("chkUseAtB.toolTipText")); - chkUseAtB.setSelected(true); - chkUseAtB.addActionListener(evt -> { - final boolean enabled = chkUseAtB.isSelected(); - enableAtBComponents(panAtB, enabled); - - // TODO : AbstractContractMarket : Delink more from AtB -// if (contractMarketPanel.isEnabled() != enabled) { -// comboContractMarketMethod.setSelectedItem(enabled -// ? ContractMarketMethod.ATB_MONTHLY -// : ContractMarketMethod.NONE); -// contractMarketPanel.setEnabled(enabled); -// comboContractMarketMethod.setEnabled(true); -// } - }); - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(10, 10, 10, 10); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panAtB.add(chkUseAtB, gridBagConstraints); - - JLabel lblSkillLevel = new JLabel(resources.getString("lblSkillLevel.text")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.gridwidth = 1; - panAtB.add(lblSkillLevel, gridBagConstraints); - - final DefaultComboBoxModel skillLevelModel = new DefaultComboBoxModel<>( - Skills.SKILL_LEVELS); - skillLevelModel.removeElement(SkillLevel.NONE); // we don't want this as a standard use case for the - // skill level - comboSkillLevel = new MMComboBox<>("comboSkillLevel", skillLevelModel); - comboSkillLevel.setToolTipText(resources.getString("lblSkillLevel.toolTipText")); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 1; - panAtB.add(comboSkillLevel, gridBagConstraints); - - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.gridwidth = 2; - panAtB.add(panSubAtBAdmin, gridBagConstraints); - - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 3; - panAtB.add(panSubAtBScenario, gridBagConstraints); - - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - panAtB.add(panSubAtBContract, gridBagConstraints); - - chkUseStratCon = new JCheckBox(resources.getString("chkUseStratCon.text")); - chkUseStratCon.setToolTipText(resources.getString("chkUseStratCon.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.gridwidth = 2; - panAtB.add(chkUseStratCon, gridBagConstraints); - - // AtB options: "Unit Administration" frame controls - chkUseAero = new JCheckBox(resources.getString("chkUseAero.text")); - chkUseAero.setToolTipText(resources.getString("chkUseAero.toolTipText")); - gridBagConstraints.gridy = 0; - panSubAtBAdmin.add(chkUseAero, gridBagConstraints); - - chkUseVehicles = new JCheckBox(resources.getString("chkUseVehicles.text")); - chkUseVehicles.setToolTipText(resources.getString("chkUseVehicles.toolTipText")); - gridBagConstraints.gridy++; - panSubAtBAdmin.add(chkUseVehicles, gridBagConstraints); - - chkClanVehicles = new JCheckBox(resources.getString("chkClanVehicles.text")); - chkClanVehicles.setToolTipText(resources.getString("chkClanVehicles.toolTipText")); - gridBagConstraints.gridy++; - panSubAtBAdmin.add(chkClanVehicles, gridBagConstraints); - - chkAutoConfigMunitions = new JCheckBox(resources.getString("chkAutoConfigMunitions.text")); - chkAutoConfigMunitions.setToolTipText(resources.getString("chkAutoConfigMunitions.toolTipText")); - gridBagConstraints.gridy++; - panSubAtBAdmin.add(chkAutoConfigMunitions, gridBagConstraints); - - chkMercSizeLimited = new JCheckBox(resources.getString("chkMercSizeLimited.text")); - chkMercSizeLimited.setToolTipText(resources.getString("chkMercSizeLimited.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.gridwidth = 2; - panSubAtBContract.add(chkMercSizeLimited, gridBagConstraints); - - chkRestrictPartsByMission = new JCheckBox(resources.getString("chkRestrictPartsByMission.text")); - chkRestrictPartsByMission.setToolTipText(resources.getString("chkRestrictPartsByMission.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.gridwidth = 2; - panSubAtBContract.add(chkRestrictPartsByMission, gridBagConstraints); - - JLabel lblBonusPartExchangeValue = new JLabel(resources.getString("lblBonusPartExchangeValue.text")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.gridwidth = 1; - panSubAtBContract.add(lblBonusPartExchangeValue, gridBagConstraints); - - spnBonusPartExchangeValue = new JSpinner(new SpinnerNumberModel(500000, 0, 1000000, 1)); - spnBonusPartExchangeValue.setToolTipText(resources.getString("lblBonusPartExchangeValue.toolTipText")); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 4; - panSubAtBContract.add(spnBonusPartExchangeValue, gridBagConstraints); - - JLabel lblBonusPartMaxExchangeCount = new JLabel(resources.getString("lblBonusPartMaxExchangeCount.text")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 5; - gridBagConstraints.gridwidth = 1; - panSubAtBContract.add(lblBonusPartMaxExchangeCount, gridBagConstraints); - - spnBonusPartMaxExchangeCount = new JSpinner(new SpinnerNumberModel(10, 0, 100, 1)); - spnBonusPartMaxExchangeCount - .setToolTipText(resources.getString("lblBonusPartMaxExchangeCount.toolTipText")); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 5; - panSubAtBContract.add(spnBonusPartMaxExchangeCount, gridBagConstraints); - - chkLimitLanceWeight = new JCheckBox(resources.getString("chkLimitLanceWeight.text")); - chkLimitLanceWeight.setToolTipText(resources.getString("chkLimitLanceWeight.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 6; - gridBagConstraints.gridwidth = 2; - panSubAtBContract.add(chkLimitLanceWeight, gridBagConstraints); - - chkLimitLanceNumUnits = new JCheckBox(resources.getString("chkLimitLanceNumUnits.text")); - chkLimitLanceNumUnits.setToolTipText(resources.getString("chkLimitLanceNumUnits.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 7; - gridBagConstraints.gridwidth = 2; - panSubAtBContract.add(chkLimitLanceNumUnits, gridBagConstraints); - - JLabel lblLanceStructure = new JLabel(resources.getString("lblLanceStructure.text")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 8; - gridBagConstraints.gridwidth = 1; - panSubAtBContract.add(lblLanceStructure, gridBagConstraints); - - chkUseStrategy = new JCheckBox(resources.getString("chkUseStrategy.text")); - chkUseStrategy.setToolTipText(resources.getString("chkUseStrategy.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 9; - gridBagConstraints.gridwidth = 2; - panSubAtBContract.add(chkUseStrategy, gridBagConstraints); - - JLabel lblBaseStrategyDeployment = new JLabel(resources.getString("lblBaseStrategyDeployment.text")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 10; - gridBagConstraints.gridwidth = 1; - panSubAtBContract.add(lblBaseStrategyDeployment, gridBagConstraints); - - spnBaseStrategyDeployment = new JSpinner(new SpinnerNumberModel(0, 0, 10, 1)); - spnBaseStrategyDeployment.setToolTipText(resources.getString("spnBaseStrategyDeployment.toolTipText")); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 10; - panSubAtBContract.add(spnBaseStrategyDeployment, gridBagConstraints); - - JLabel lblAdditionalStrategyDeployment = new JLabel( - resources.getString("lblAdditionalStrategyDeployment.text")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 11; - gridBagConstraints.gridwidth = 1; - panSubAtBContract.add(lblAdditionalStrategyDeployment, gridBagConstraints); - - spnAdditionalStrategyDeployment = new JSpinner(new SpinnerNumberModel(0, 0, 10, 1)); - spnAdditionalStrategyDeployment - .setToolTipText(resources.getString("spnAdditionalStrategyDeployment.toolTipText")); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 11; - panSubAtBContract.add(spnAdditionalStrategyDeployment, gridBagConstraints); - - chkAdjustPaymentForStrategy = new JCheckBox(resources.getString("chkAdjustPaymentForStrategy.text")); - chkAdjustPaymentForStrategy.setName("chkAdjustPaymentForStrategy"); - chkAdjustPaymentForStrategy - .setToolTipText(resources.getString("chkAdjustPaymentForStrategy.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 12; - gridBagConstraints.gridwidth = 2; - panSubAtBContract.add(chkAdjustPaymentForStrategy, gridBagConstraints); - - JLabel lblIntensity = new JLabel(resources.getString("lblIntensity.text")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 13; - panSubAtBContract.add(lblIntensity, gridBagConstraints); - - // Note that spnAtBBattleIntensity is located here visibly, however must be - // initialized - // following the chance of battle by role - - JLabel lblBattleFrequency = new JLabel(resources.getString("lblBattleFrequency.text")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 14; - gridBagConstraints.gridwidth = 2; - panSubAtBContract.add(lblBattleFrequency, gridBagConstraints); - - spnAtBBattleChance = new JSpinner[AtBLanceRole.values().length - 1]; - - JLabel lblFightChance = new JLabel(AtBLanceRole.FIGHTING + ":"); - gridBagConstraints.gridy = 15; - gridBagConstraints.gridwidth = 1; - panSubAtBContract.add(lblFightChance, gridBagConstraints); - - JSpinner atbBattleChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1)); - spnAtBBattleChance[AtBLanceRole.FIGHTING.ordinal()] = atbBattleChance; - gridBagConstraints.gridx = 1; - panSubAtBContract.add(atbBattleChance, gridBagConstraints); - - JLabel lblDefendChance = new JLabel(AtBLanceRole.DEFENCE + ":"); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 16; - panSubAtBContract.add(lblDefendChance, gridBagConstraints); - - atbBattleChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1)); - spnAtBBattleChance[AtBLanceRole.DEFENCE.ordinal()] = atbBattleChance; - gridBagConstraints.gridx = 1; - panSubAtBContract.add(atbBattleChance, gridBagConstraints); - - JLabel lblScoutChance = new JLabel(AtBLanceRole.SCOUTING + ":"); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 17; - panSubAtBContract.add(lblScoutChance, gridBagConstraints); - - atbBattleChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1)); - spnAtBBattleChance[AtBLanceRole.SCOUTING.ordinal()] = atbBattleChance; - gridBagConstraints.gridx = 1; - panSubAtBContract.add(atbBattleChance, gridBagConstraints); - - JLabel lblTrainingChance = new JLabel(AtBLanceRole.TRAINING + ":"); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 18; - panSubAtBContract.add(lblTrainingChance, gridBagConstraints); - - atbBattleChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1)); - spnAtBBattleChance[AtBLanceRole.TRAINING.ordinal()] = atbBattleChance; - gridBagConstraints.gridx = 1; - panSubAtBContract.add(atbBattleChance, gridBagConstraints); - - btnIntensityUpdate = new JButton(resources.getString("btnIntensityUpdate.text")); - AtBBattleIntensityChangeListener atBBattleIntensityChangeListener = new AtBBattleIntensityChangeListener(); - btnIntensityUpdate.addChangeListener(evt -> { - spnAtBBattleIntensity.removeChangeListener(atBBattleIntensityChangeListener); - spnAtBBattleIntensity.setValue(determineAtBBattleIntensity()); - spnAtBBattleIntensity.addChangeListener(atBBattleIntensityChangeListener); - }); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 19; - gridBagConstraints.gridwidth = 2; - panSubAtBContract.add(btnIntensityUpdate, gridBagConstraints); - - // Note that this must be after the chance by role because it requires the - // chance by role - // for the initial value to be calculated - spnAtBBattleIntensity = new JSpinner(new SpinnerNumberModel(0.0, 0.0, 100.0, 0.1)); - spnAtBBattleIntensity.setToolTipText(resources.getString("spnIntensity.toolTipText")); - spnAtBBattleIntensity.addChangeListener(atBBattleIntensityChangeListener); - spnAtBBattleIntensity.setMinimumSize(new Dimension(60, 25)); - spnAtBBattleIntensity.setPreferredSize(new Dimension(60, 25)); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 13; - gridBagConstraints.gridwidth = 1; - panSubAtBContract.add(spnAtBBattleIntensity, gridBagConstraints); - - chkGenerateChases = new JCheckBox(resources.getString("chkGenerateChases.text")); - chkGenerateChases.setName("chkGenerateChases"); - chkGenerateChases.setToolTipText(resources.getString("chkGenerateChases.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 20; - panSubAtBContract.add(chkGenerateChases, gridBagConstraints); - - int yTablePosition = 0; - chkUseGenericBattleValue = new JCheckBox(resources.getString("chkUseGenericBattleValue.text")); - chkUseGenericBattleValue.setToolTipText(wordWrap(resources.getString("chkUseGenericBattleValue.toolTipText"))); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(chkUseGenericBattleValue, gridBagConstraints); - - chkUseVerboseBidding = new JCheckBox(resources.getString("chkUseVerboseBidding.text")); - chkUseVerboseBidding.setToolTipText(wordWrap(resources.getString("chkUseVerboseBidding.toolTipText"))); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(chkUseVerboseBidding, gridBagConstraints); - - chkDoubleVehicles = new JCheckBox(resources.getString("chkDoubleVehicles.text")); - chkDoubleVehicles.setToolTipText(resources.getString("chkDoubleVehicles.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(chkDoubleVehicles, gridBagConstraints); - - JLabel lblOpForLanceType = new JLabel(resources.getString("lblOpForLanceType.text")); - lblOpForLanceType.setToolTipText(resources.getString("lblOpForLanceType.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(lblOpForLanceType, gridBagConstraints); - - spnOpForLanceTypeMeks = new JSpinner(new SpinnerNumberModel(0, 0, 10, 1)); - spnOpForLanceTypeMeks.setToolTipText(resources.getString("lblOpForLanceType.toolTipText")); - spnOpForLanceTypeMixed = new JSpinner(new SpinnerNumberModel(0, 0, 10, 1)); - spnOpForLanceTypeMixed.setToolTipText(resources.getString("lblOpForLanceType.toolTipText")); - spnOpForLanceTypeVehicles = new JSpinner(new SpinnerNumberModel(0, 0, 10, 1)); - spnOpForLanceTypeVehicles.setToolTipText(resources.getString("lblOpForLanceType.toolTipText")); - JPanel panOpForLanceType = new JPanel(); - panOpForLanceType.add(new JLabel(resources.getString("lblOpForLanceTypeMek.text"))); - panOpForLanceType.add(spnOpForLanceTypeMeks); - panOpForLanceType.add(new JLabel(resources.getString("lblOpForLanceTypeMixed.text"))); - panOpForLanceType.add(spnOpForLanceTypeMixed); - panOpForLanceType.add(new JLabel(resources.getString("lblOpForLanceTypeVehicle.text"))); - panOpForLanceType.add(spnOpForLanceTypeVehicles); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(panOpForLanceType, gridBagConstraints); - - chkOpForUsesVTOLs = new JCheckBox(resources.getString("chkOpForUsesVTOLs.text")); - chkOpForUsesVTOLs.setToolTipText(resources.getString("chkOpForUsesVTOLs.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(chkOpForUsesVTOLs, gridBagConstraints); - - JPanel panOpForAero = new JPanel(); - chkOpForUsesAero = new JCheckBox(resources.getString("chkOpForUsesAero.text")); - chkOpForUsesAero.setToolTipText(resources.getString("chkOpForUsesAero.toolTipText")); - JLabel lblOpForAeroChance = new JLabel(resources.getString("lblOpForAeroLikelihood.text")); - lblOpForAeroChance.setToolTipText(resources.getString("lblOpForAeroLikelihood.toolTipText")); - spnOpForAeroChance = new JSpinner(new SpinnerNumberModel(0, 0, 6, 1)); - panOpForAero.add(chkOpForUsesAero); - panOpForAero.add(spnOpForAeroChance); - panOpForAero.add(lblOpForAeroChance); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(panOpForAero, gridBagConstraints); - - JPanel panOpForLocal = new JPanel(); - chkOpForUsesLocalForces = new JCheckBox(resources.getString("chkOpForUsesLocalForces.text")); - chkOpForUsesLocalForces.setToolTipText(resources.getString("chkOpForUsesLocalForces.toolTipText")); - JLabel lblOpForLocalForceChance = new JLabel(resources.getString("lblOpForLocalForceLikelihood.text")); - lblOpForLocalForceChance - .setToolTipText(resources.getString("lblOpForLocalForceLikelihood.toolTipText")); - spnOpForLocalForceChance = new JSpinner(new SpinnerNumberModel(0, 0, 6, 1)); - panOpForLocal.add(chkOpForUsesLocalForces); - panOpForLocal.add(spnOpForLocalForceChance); - panOpForLocal.add(lblOpForLocalForceChance); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(0, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(panOpForLocal, gridBagConstraints); - - chkAdjustPlayerVehicles = new JCheckBox(resources.getString("chkAdjustPlayerVehicles.text")); - chkAdjustPlayerVehicles.setToolTipText(resources.getString("chkAdjustPlayerVehicles.toolTipText")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(chkAdjustPlayerVehicles, gridBagConstraints); - - chkRegionalMekVariations = new JCheckBox(resources.getString("chkRegionalMekVariations.text")); - chkRegionalMekVariations.setToolTipText(resources.getString("chkRegionalMekVariations.toolTipText")); - gridBagConstraints.gridy = yTablePosition++; - panSubAtBScenario.add(chkRegionalMekVariations, gridBagConstraints); - - chkAttachedPlayerCamouflage = new JCheckBox(resources.getString("chkAttachedPlayerCamouflage.text")); - gridBagConstraints.gridy = yTablePosition++; - panSubAtBScenario.add(chkAttachedPlayerCamouflage, gridBagConstraints); - - chkPlayerControlsAttachedUnits = new JCheckBox( - resources.getString("chkPlayerControlsAttachedUnits.text")); - gridBagConstraints.gridy = yTablePosition++; - panSubAtBScenario.add(chkPlayerControlsAttachedUnits, gridBagConstraints); - - chkUseDropShips = new JCheckBox(resources.getString("chkUseDropShips.text")); - chkUseDropShips.setToolTipText(resources.getString("chkUseDropShips.toolTipText")); - gridBagConstraints.gridy = yTablePosition++; - panSubAtBScenario.add(chkUseDropShips, gridBagConstraints); - - chkUseWeatherConditions = new JCheckBox(resources.getString("chkUseWeatherConditions.text")); - chkUseWeatherConditions.setToolTipText(resources.getString("chkUseWeatherConditions.toolTipText")); - gridBagConstraints.gridy = yTablePosition++; - panSubAtBScenario.add(chkUseWeatherConditions, gridBagConstraints); - - chkUseLightConditions = new JCheckBox(resources.getString("chkUseLightConditions.text")); - chkUseLightConditions.setToolTipText(resources.getString("chkUseLightConditions.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(chkUseLightConditions, gridBagConstraints); - - chkUsePlanetaryConditions = new JCheckBox(resources.getString("chkUsePlanetaryConditions.text")); - chkUsePlanetaryConditions.setToolTipText(resources.getString("chkUsePlanetaryConditions.toolTipText")); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(chkUsePlanetaryConditions, gridBagConstraints); - - JPanel panFixedMapChance = new JPanel(); - JLabel lblFixedMapChance = new JLabel(resources.getString("lblFixedMapChance.text")); - lblFixedMapChance.setToolTipText(resources.getString("lblFixedMapChance.toolTipText")); - spnFixedMapChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1)); - panFixedMapChance.add(lblFixedMapChance); - panFixedMapChance.add(spnFixedMapChance); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(0, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(panFixedMapChance, gridBagConstraints); - - JPanel panSPAUpgradeIntensity = new JPanel(); - JLabel lblSPAUpgradeIntensity = new JLabel(resources.getString("lblSPAUpgradeIntensity.text")); - lblSPAUpgradeIntensity.setToolTipText(resources.getString("lblSPAUpgradeIntensity.toolTipText")); - spnSPAUpgradeIntensity = new JSpinner(new SpinnerNumberModel(0, -1, 3, 1)); - panSPAUpgradeIntensity.add(lblSPAUpgradeIntensity); - panSPAUpgradeIntensity.add(spnSPAUpgradeIntensity); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(0, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(panSPAUpgradeIntensity, gridBagConstraints); - - JPanel panScenarioMod = new JPanel(); - JLabel lblScenarioMod = new JLabel(resources.getString("lblScenarioMod.text")); - panScenarioMod.add(lblScenarioMod); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(0, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(panScenarioMod, gridBagConstraints); - - JLabel lblScenarioModMax = new JLabel(resources.getString("lblScenarioModMax.text")); - lblScenarioModMax.setToolTipText(resources.getString("lblScenarioModMax.toolTipText")); - spnScenarioModMax = new JSpinner(new SpinnerNumberModel(3, 0, 10, 1)); - panScenarioMod.add(lblScenarioModMax); - panScenarioMod.add(spnScenarioModMax); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(0, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(panScenarioMod, gridBagConstraints); - - JLabel lblScenarioModChance = new JLabel(resources.getString("lblScenarioModChance.text")); - lblScenarioModChance.setToolTipText(resources.getString("lblScenarioModChance.toolTipText")); - spnScenarioModChance = new JSpinner(new SpinnerNumberModel(25, 5, 100, 5)); - panScenarioMod.add(lblScenarioModChance); - panScenarioMod.add(spnScenarioModChance); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(0, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(panScenarioMod, gridBagConstraints); - - JLabel lblScenarioModBV = new JLabel(resources.getString("lblScenarioModBV.text")); - lblScenarioModBV.setToolTipText(resources.getString("lblScenarioModBV.toolTipText")); - spnScenarioModBV = new JSpinner(new SpinnerNumberModel(50, 5, 100, 5)); - panScenarioMod.add(lblScenarioModBV); - panScenarioMod.add(spnScenarioModBV); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = yTablePosition++; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.insets = new Insets(0, 5, 5, 5); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panSubAtBScenario.add(panScenarioMod, gridBagConstraints); - - JScrollPane scrAtB = new JScrollPaneWithSpeed(panAtB); - scrAtB.setPreferredSize(new Dimension(500, 410)); - return scrAtB; - } - // endregion Legacy Initialization - - // region Modern Initialization - // region Personnel Tab - private JScrollPane createPersonnelTab() { - final AbstractMHQScrollablePanel personnelPanel = new DefaultMHQScrollablePanel(getFrame(), - "personnelPanel", new GridBagLayout()); - personnelPanel.setTracksViewportWidth(false); - - final GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.anchor = GridBagConstraints.CENTER; - gbc.fill = GridBagConstraints.HORIZONTAL; - personnelPanel.add(createGeneralPersonnelPanel(), gbc); - - gbc.gridx++; - personnelPanel.add(createExpandedPersonnelInformationPanel(), gbc); - - // - - gbc.gridx = 0; - gbc.gridy++; - personnelPanel.add(createLogPanel(), gbc); - - gbc.gridx++; - personnelPanel.add(createAdministratorsPanel(), gbc); - - // - - gbc.gridx = 0; - gbc.gridy++; - personnelPanel.add(createAwardsPanel(), gbc); - - gbc.gridx++; - personnelPanel.add(createMedicalPanel(), gbc); - - // - - gbc.gridx = 0; - gbc.gridy++; - personnelPanel.add(createPrisonerPanel(), gbc); - - gbc.gridx++; - personnelPanel.add(createDependentPanel(), gbc); - - // - - gbc.gridx = 0; - gbc.gridy++; - gbc.gridwidth = 2; - personnelPanel.add(createPersonnelRemovalPanel(), gbc); - - // - - gbc.gridx = 0; - gbc.gridy++; - gbc.gridwidth = 2; - personnelPanel.add(createSalaryPanel(), gbc); - - final JScrollPane scrollPersonnel = new JScrollPaneWithSpeed(personnelPanel); - scrollPersonnel.setPreferredSize(new Dimension(500, 400)); - - return scrollPersonnel; - } - - // region Turnover and Retention Tab - private JScrollPane createTurnoverAndRetentionTab() { - final AbstractMHQScrollablePanel turnoverAndRetentionPanel = new DefaultMHQScrollablePanel(getFrame(), - "turnoverAndRetentionPanel", new GridBagLayout()); - turnoverAndRetentionPanel.setTracksViewportWidth(false); - - final GridBagConstraints gbc = new GridBagConstraints(); - - gbc.gridx = 0; - gbc.gridy = 0; - gbc.anchor = GridBagConstraints.CENTER; - gbc.fill = GridBagConstraints.HORIZONTAL; - turnoverAndRetentionPanel.add(createTurnoverAndRetentionHeaderPanel(), gbc); - - gbc.gridy++; - turnoverAndRetentionPanel.add(createTurnoverAndRetentionSettingsPanel(), gbc); - - gbc.gridx++; - turnoverAndRetentionPanel.add(createTurnoverAndRetentionModifiersPanel(), gbc); - - gbc.gridx++; - turnoverAndRetentionPanel.add(createTurnoverAndRetentionPayoutPanel(), gbc); - - gbc.gridx = 0; - gbc.gridy++; - turnoverAndRetentionPanel.add(createTurnoverAndRetentionFatiguePanel(), gbc); - - gbc.gridx++; - turnoverAndRetentionPanel.add(createTurnoverAndRetentionUnitCohesionPanel(), gbc); - - final JScrollPane scrollPersonnel = new JScrollPaneWithSpeed(turnoverAndRetentionPanel); - scrollPersonnel.setPreferredSize(new Dimension(500, 400)); - - return scrollPersonnel; - } - - // region Life Paths Tab - private JScrollPane createLifePathsPanel() { - final AbstractMHQScrollablePanel lifePathsPanel = new DefaultMHQScrollablePanel(getFrame(), - "lifePathsPanel", new GridBagLayout()); - lifePathsPanel.setTracksViewportWidth(false); - - final GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.fill = GridBagConstraints.HORIZONTAL; - lifePathsPanel.add(createPersonnelRandomizationPanel(), gbc); - - gbc.gridx++; - lifePathsPanel.add(createRandomHistoriesPanel(), gbc); - - // - - gbc.gridx = 0; - gbc.gridy++; - lifePathsPanel.add(createFamilyPanel(), gbc); - - gbc.gridx++; - lifePathsPanel.add(createAnniversaryPanel(), gbc); - - // - - gbc.gridx = 0; - gbc.gridy++; - gbc.gridwidth = 1; - lifePathsPanel.add(createMarriagePanel(), gbc); - - gbc.gridx++; - lifePathsPanel.add(createDivorcePanel(), gbc); - - // - - gbc.gridx = 0; - gbc.gridy++; - lifePathsPanel.add(createEducationPanel(), gbc); - - gbc.gridx++; - lifePathsPanel.add(createProcreationPanel(), gbc); - - // - - gbc.gridx = 0; - gbc.gridy++; - gbc.gridwidth = 2; - lifePathsPanel.add(createDeathPanel(), gbc); - - final JScrollPane scrollLifePaths = new JScrollPaneWithSpeed(lifePathsPanel); - scrollLifePaths.setPreferredSize(new Dimension(400, 400)); - - return scrollLifePaths; - } - - private JPanel createGeneralPersonnelPanel() { - // Create Panel Components - chkUseTactics = new JCheckBox(resources.getString("chkUseTactics.text")); - chkUseTactics.setToolTipText(resources.getString("chkUseTactics.toolTipText")); - chkUseTactics.setName("chkUseTactics"); - - chkUseInitiativeBonus = new JCheckBox(resources.getString("chkUseInitiativeBonus.text")); - chkUseInitiativeBonus.setToolTipText(resources.getString("chkUseInitiativeBonus.toolTipText")); - chkUseInitiativeBonus.setName("chkUseInitiativeBonus"); - - chkUseToughness = new JCheckBox(resources.getString("chkUseToughness.text")); - chkUseToughness.setToolTipText(resources.getString("chkUseToughness.toolTipText")); - chkUseToughness.setName("chkUseToughness"); - - chkUseRandomToughness = new JCheckBox(resources.getString("chkUseRandomToughness.text")); - chkUseRandomToughness.setToolTipText(resources.getString("chkUseRandomToughness.toolTipText")); - chkUseRandomToughness.setName("chkUseRandomToughness"); - - chkUseArtillery = new JCheckBox(resources.getString("chkUseArtillery.text")); - chkUseArtillery.setToolTipText(resources.getString("chkUseArtillery.toolTipText")); - chkUseArtillery.setName("chkUseArtillery"); - - chkUseAbilities = new JCheckBox(resources.getString("chkUseAbilities.text")); - chkUseAbilities.setToolTipText(resources.getString("chkUseAbilities.toolTipText")); - chkUseAbilities.setName("chkUseAbilities"); - - chkUseEdge = new JCheckBox(resources.getString("chkUseEdge.text")); - chkUseEdge.setToolTipText(resources.getString("chkUseEdge.toolTipText")); - chkUseEdge.setName("chkUseEdge"); - chkUseEdge.addActionListener(evt -> chkUseSupportEdge.setEnabled(chkUseEdge.isSelected())); - - chkUseSupportEdge = new JCheckBox(resources.getString("chkUseSupportEdge.text")); - chkUseSupportEdge.setToolTipText(resources.getString("chkUseSupportEdge.toolTipText")); - chkUseSupportEdge.setName("chkUseSupportEdge"); - - chkUseImplants = new JCheckBox(resources.getString("chkUseImplants.text")); - chkUseImplants.setToolTipText(resources.getString("chkUseImplants.toolTipText")); - chkUseImplants.setName("chkUseImplants"); - - chkUseAlternativeQualityAveraging = new JCheckBox( - resources.getString("chkUseAlternativeQualityAveraging.text")); - chkUseAlternativeQualityAveraging - .setToolTipText(resources.getString("chkUseAlternativeQualityAveraging.toolTipText")); - chkUseAlternativeQualityAveraging.setName("chkUseAlternativeQualityAveraging"); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder("")); - panel.setName("generalPersonnelPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseTactics) - .addComponent(chkUseInitiativeBonus) - .addComponent(chkUseToughness) - .addComponent(chkUseRandomToughness) - .addComponent(chkUseArtillery) - .addComponent(chkUseAbilities) - .addComponent(chkUseEdge) - .addComponent(chkUseSupportEdge) - .addComponent(chkUseImplants) - .addComponent(chkUseAlternativeQualityAveraging)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseTactics) - .addComponent(chkUseInitiativeBonus) - .addComponent(chkUseToughness) - .addComponent(chkUseRandomToughness) - .addComponent(chkUseArtillery) - .addComponent(chkUseAbilities) - .addComponent(chkUseEdge) - .addComponent(chkUseSupportEdge) - .addComponent(chkUseImplants) - .addComponent(chkUseAlternativeQualityAveraging)); - - return panel; - } - - private JPanel createLogPanel() { - chkUseTransfers = new JCheckBox(resources.getString("chkUseTransfers.text")); - chkUseTransfers.setToolTipText(resources.getString("chkUseTransfers.toolTipText")); - chkUseTransfers.setName("chkUseTransfers"); - - chkUseExtendedTOEForceName = new JCheckBox(resources.getString("chkUseExtendedTOEForceName.text")); - chkUseExtendedTOEForceName - .setToolTipText(resources.getString("chkUseExtendedTOEForceName.toolTipText")); - chkUseExtendedTOEForceName.setName("chkUseExtendedTOEForceName"); - - chkPersonnelLogSkillGain = new JCheckBox(resources.getString("chkPersonnelLogSkillGain.text")); - chkPersonnelLogSkillGain.setToolTipText(resources.getString("chkPersonnelLogSkillGain.toolTipText")); - chkPersonnelLogSkillGain.setName("chkPersonnelLogSkillGain"); - - chkPersonnelLogAbilityGain = new JCheckBox(resources.getString("chkPersonnelLogAbilityGain.text")); - chkPersonnelLogAbilityGain - .setToolTipText(resources.getString("chkPersonnelLogAbilityGain.toolTipText")); - chkPersonnelLogAbilityGain.setName("chkPersonnelLogAbilityGain"); - - chkPersonnelLogEdgeGain = new JCheckBox(resources.getString("chkPersonnelLogEdgeGain.text")); - chkPersonnelLogEdgeGain.setToolTipText(resources.getString("chkPersonnelLogEdgeGain.toolTipText")); - chkPersonnelLogEdgeGain.setName("chkPersonnelLogEdgeGain"); - - chkDisplayPersonnelLog = new JCheckBox(resources.getString("chkDisplayPersonnelLog.text")); - chkDisplayPersonnelLog.setToolTipText(resources.getString("chkDisplayPersonnelLog.toolTipText")); - chkDisplayPersonnelLog.setName("chkDisplayPersonnelLog"); - - chkDisplayScenarioLog = new JCheckBox(resources.getString("chkDisplayScenarioLog.text")); - chkDisplayScenarioLog.setToolTipText(resources.getString("chkDisplayScenarioLog.toolTipText")); - chkDisplayScenarioLog.setName("chkDisplayScenarioLog"); - - chkDisplayKillRecord = new JCheckBox(resources.getString("chkDisplayKillRecord.text")); - chkDisplayKillRecord.setToolTipText(resources.getString("chkDisplayKillRecord.toolTipText")); - chkDisplayKillRecord.setName("chkDisplayKillRecord"); - - final JPanel logPanel = new JPanel(); - logPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("logPanel.title"))); - logPanel.setName("logPanel"); - - final GroupLayout layout = new GroupLayout(logPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - logPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseTransfers) - .addComponent(chkUseExtendedTOEForceName) - .addComponent(chkPersonnelLogSkillGain) - .addComponent(chkPersonnelLogAbilityGain) - .addComponent(chkPersonnelLogEdgeGain) - .addComponent(chkDisplayPersonnelLog) - .addComponent(chkDisplayScenarioLog) - .addComponent(chkDisplayKillRecord)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseTransfers) - .addComponent(chkUseExtendedTOEForceName) - .addComponent(chkPersonnelLogSkillGain) - .addComponent(chkPersonnelLogAbilityGain) - .addComponent(chkPersonnelLogEdgeGain) - .addComponent(chkDisplayPersonnelLog) - .addComponent(chkDisplayScenarioLog) - .addComponent(chkDisplayKillRecord)); - - return logPanel; - } - - private void createFatigueSubPanel() { - JLabel lblFatigueWarning = new JLabel(resources.getString("lblFatigueWarning.text")); - lblFatigueWarning.setName("lblFatigueWarning"); - lblFatigueWarning.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - JLabel lblFatigueRate = new JLabel(resources.getString("lblFatigueRate.text")); - lblFatigueRate.setToolTipText(resources.getString("lblFatigueRate.toolTipText")); - lblFatigueRate.setName("lblFatigueRate"); - lblFatigueRate.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - spnFatigueRate = new JSpinner(new SpinnerNumberModel(1, 1, 10, 1)); - spnFatigueRate.setToolTipText(resources.getString("lblFatigueRate.toolTipText")); - spnFatigueRate.setName("spnFatigueRate"); - spnFatigueRate.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - chkUseInjuryFatigue = new JCheckBox(resources.getString("chkUseInjuryFatigue.text")); - chkUseInjuryFatigue.setToolTipText(resources.getString("chkUseInjuryFatigue.toolTipText")); - chkUseInjuryFatigue.setName("chkUseInjuryFatigue"); - chkUseInjuryFatigue.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - JLabel lblFieldKitchenCapacity = new JLabel(resources.getString("lblFieldKitchenCapacity.text")); - lblFieldKitchenCapacity.setToolTipText(resources.getString("lblFieldKitchenCapacity.toolTipText")); - lblFieldKitchenCapacity.setName("lblFieldKitchenCapacity"); - lblFieldKitchenCapacity.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - spnFieldKitchenCapacity = new JSpinner(new SpinnerNumberModel(150, 0, 450, 1)); - spnFieldKitchenCapacity.setToolTipText(resources.getString("lblFieldKitchenCapacity.toolTipText")); - spnFieldKitchenCapacity.setName("spnFieldKitchenCapacity"); - spnFieldKitchenCapacity.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - chkFieldKitchenIgnoreNonCombatants = new JCheckBox( - resources.getString("chkFieldKitchenIgnoreNonCombatants.text")); - chkFieldKitchenIgnoreNonCombatants - .setToolTipText(resources.getString("chkFieldKitchenIgnoreNonCombatants.toolTipText")); - chkFieldKitchenIgnoreNonCombatants.setName("chkFieldKitchenIgnoreNonCombatants"); - chkFieldKitchenIgnoreNonCombatants.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - JLabel lblFatigueLeaveThreshold = new JLabel(resources.getString("lblFatigueLeaveThreshold.text")); - lblFatigueLeaveThreshold.setToolTipText(resources.getString("lblFatigueLeaveThreshold.toolTipText")); - lblFatigueLeaveThreshold.setName("lblFatigueLeaveThreshold"); - lblFatigueLeaveThreshold.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - spnFatigueLeaveThreshold = new JSpinner(new SpinnerNumberModel(13, 0, 17, 1)); - spnFatigueLeaveThreshold.setToolTipText(resources.getString("lblFatigueLeaveThreshold.toolTipText")); - spnFatigueLeaveThreshold.setName("spnFatigueLeaveThreshold"); - spnFatigueLeaveThreshold.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - fatigueSubPanel.setBorder(BorderFactory.createTitledBorder("")); - fatigueSubPanel.setName("fatigueSubPanel"); - fatigueSubPanel.setEnabled(campaign.getCampaignOptions().isUseFatigue()); - - final GroupLayout layout = new GroupLayout(fatigueSubPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - fatigueSubPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(lblFatigueWarning) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblFatigueRate) - .addComponent(spnFatigueRate, Alignment.LEADING)) - .addComponent(chkUseInjuryFatigue) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblFieldKitchenCapacity) - .addComponent(spnFieldKitchenCapacity, - Alignment.LEADING)) - .addComponent(chkFieldKitchenIgnoreNonCombatants) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblFatigueLeaveThreshold) - .addComponent(spnFatigueLeaveThreshold, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(lblFatigueWarning) - .addGroup(layout.createSequentialGroup() - .addComponent(lblFatigueRate) - .addComponent(spnFatigueRate)) - .addComponent(chkUseInjuryFatigue) - .addGroup(layout.createSequentialGroup() - .addComponent(lblFieldKitchenCapacity) - .addComponent(spnFieldKitchenCapacity)) - .addComponent(chkFieldKitchenIgnoreNonCombatants) - .addGroup(layout.createSequentialGroup() - .addComponent(lblFatigueLeaveThreshold) - .addComponent(spnFatigueLeaveThreshold))); - } - - private JPanel createExpandedPersonnelInformationPanel() { - // Initialize Labels Used in ActionListeners - final JLabel lblTimeInServiceDisplayFormat = new JLabel(); - final JLabel lblTimeInRankDisplayFormat = new JLabel(); - - // Create Panel Components - chkUseTimeInService = new JCheckBox(resources.getString("chkUseTimeInService.text")); - chkUseTimeInService.setToolTipText(resources.getString("chkUseTimeInService.toolTipText")); - chkUseTimeInService.setName("chkUseTimeInService"); - chkUseTimeInService.addActionListener(evt -> { - lblTimeInServiceDisplayFormat.setEnabled(chkUseTimeInService.isSelected()); - comboTimeInServiceDisplayFormat.setEnabled(chkUseTimeInService.isSelected()); - }); - - lblTimeInServiceDisplayFormat.setText(resources.getString("lblTimeInServiceDisplayFormat.text")); - lblTimeInServiceDisplayFormat - .setToolTipText(resources.getString("lblTimeInServiceDisplayFormat.toolTipText")); - lblTimeInServiceDisplayFormat.setName("lblTimeInServiceDisplayFormat"); - - comboTimeInServiceDisplayFormat = new MMComboBox<>("comboTimeInServiceDisplayFormat", - TimeInDisplayFormat.values()); - comboTimeInServiceDisplayFormat - .setToolTipText(resources.getString("lblTimeInServiceDisplayFormat.toolTipText")); - - chkUseTimeInRank = new JCheckBox(resources.getString("chkUseTimeInRank.text")); - chkUseTimeInRank.setToolTipText(resources.getString("chkUseTimeInRank.toolTipText")); - chkUseTimeInRank.setName("chkUseTimeInRank"); - chkUseTimeInRank.addActionListener(evt -> { - lblTimeInRankDisplayFormat.setEnabled(chkUseTimeInRank.isSelected()); - comboTimeInRankDisplayFormat.setEnabled(chkUseTimeInRank.isSelected()); - }); - - lblTimeInRankDisplayFormat.setText(resources.getString("lblTimeInRankDisplayFormat.text")); - lblTimeInRankDisplayFormat - .setToolTipText(resources.getString("lblTimeInRankDisplayFormat.toolTipText")); - lblTimeInRankDisplayFormat.setName("lblTimeInRankDisplayFormat"); - - comboTimeInRankDisplayFormat = new MMComboBox<>("comboTimeInRankDisplayFormat", - TimeInDisplayFormat.values()); - comboTimeInRankDisplayFormat - .setToolTipText(resources.getString("lblTimeInRankDisplayFormat.toolTipText")); - - chkTrackTotalEarnings = new JCheckBox(resources.getString("chkTrackTotalEarnings.text")); - chkTrackTotalEarnings.setToolTipText(resources.getString("chkTrackTotalEarnings.toolTipText")); - chkTrackTotalEarnings.setName("chkTrackTotalEarnings"); - - chkTrackTotalXPEarnings = new JCheckBox(resources.getString("chkTrackTotalXPEarnings.text")); - chkTrackTotalXPEarnings.setToolTipText(resources.getString("chkTrackTotalXPEarnings.toolTipText")); - chkTrackTotalXPEarnings.setName("chkTrackTotalXPEarnings"); - - chkShowOriginFaction = new JCheckBox(resources.getString("chkShowOriginFaction.text")); - chkShowOriginFaction.setToolTipText(resources.getString("chkShowOriginFaction.toolTipText")); - chkShowOriginFaction.setName("chkShowOriginFaction"); - - // Programmatically Assign Accessibility Labels - lblTimeInServiceDisplayFormat.setLabelFor(comboTimeInServiceDisplayFormat); - lblTimeInRankDisplayFormat.setLabelFor(comboTimeInRankDisplayFormat); - - // Disable Panel Portions by Default - chkUseTimeInService.setSelected(true); - chkUseTimeInService.doClick(); - chkUseTimeInRank.setSelected(true); - chkUseTimeInRank.doClick(); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder( - BorderFactory.createTitledBorder( - resources.getString("expandedPersonnelInformationPanel.title"))); - panel.setName("expandedPersonnelInformationPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseTimeInService) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblTimeInServiceDisplayFormat) - .addComponent(comboTimeInServiceDisplayFormat, - Alignment.LEADING)) - .addComponent(chkUseTimeInRank) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblTimeInRankDisplayFormat) - .addComponent(comboTimeInRankDisplayFormat, - Alignment.LEADING)) - .addComponent(chkTrackTotalEarnings) - .addComponent(chkTrackTotalXPEarnings) - .addComponent(chkShowOriginFaction)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseTimeInService) - .addGroup(layout.createSequentialGroup() - .addComponent(lblTimeInServiceDisplayFormat) - .addComponent(comboTimeInServiceDisplayFormat)) - .addComponent(chkUseTimeInRank) - .addGroup(layout.createSequentialGroup() - .addComponent(lblTimeInRankDisplayFormat) - .addComponent(comboTimeInRankDisplayFormat)) - .addComponent(chkTrackTotalEarnings) - .addComponent(chkTrackTotalXPEarnings) - .addComponent(chkShowOriginFaction)); - - return panel; - } - - private JPanel createAwardsPanel() { - // Create Panel Components - final JPanel autoAwardsPanel = createAutoAwardsPanel(); - - final JLabel lblAwardTierSize = new JLabel(resources.getString("lblAwardTierSize.text")); - lblAwardTierSize.setToolTipText(resources.getString("lblAwardTierSize.toolTipText")); - lblAwardTierSize.setName("lblAwardTierSize"); - spnAwardTierSize = new JSpinner(new SpinnerNumberModel(5, 1, 100, 1)); - spnAwardTierSize.setMaximumSize(new Dimension(60, 25)); - - chkIssuePosthumousAwards = new JCheckBox(resources.getString("chkIssuePosthumousAwards.text")); - chkIssuePosthumousAwards.setToolTipText(resources.getString("chkIssuePosthumousAwards.toolTipText")); - chkIssuePosthumousAwards.setName("chkIssuePosthumousAwards"); - - chkIssueBestAwardOnly = new JCheckBox(resources.getString("chkIssueBestAwardOnly.text")); - chkIssueBestAwardOnly.setToolTipText(resources.getString("chkIssueBestAwardOnly.toolTipText")); - chkIssueBestAwardOnly.setName("chkIssueBestAwardOnly"); - - chkIgnoreStandardSet = new JCheckBox(resources.getString("chkIgnoreStandardSet.text")); - chkIgnoreStandardSet.setToolTipText(resources.getString("chkIgnoreStandardSet.toolTipText")); - chkIgnoreStandardSet.setName("chkIgnoreStandardSet"); - - final JLabel lblAwardBonusStyle = new JLabel(resources.getString("lblAwardBonusStyle.text")); - lblAwardBonusStyle.setToolTipText(resources.getString("lblAwardBonusStyle.toolTipText")); - lblAwardBonusStyle.setName("lblAwardBonusStyle"); - - comboAwardBonusStyle = new MMComboBox<>("comboAwardBonusStyle", AwardBonus.values()); - comboAwardBonusStyle.setToolTipText(resources.getString("lblAwardBonusStyle.toolTipText")); - comboAwardBonusStyle.setMaximumSize(new Dimension(80, 25)); - comboAwardBonusStyle.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof AwardBonus) { - list.setToolTipText(((AwardBonus) value).getToolTipText()); - } - return this; - } - }); - - lblAwardSetFilterList = new JLabel(resources.getString("lblAwardSetFilterList.text")); - lblAwardSetFilterList.setToolTipText( - wordWrap(resources.getString("lblAwardSetFilterList.toolTipText"), 100)); - lblAwardSetFilterList.setName("lblAwardSetFilterList"); - - txtAwardSetFilterList = new JTextArea(5, 20); - txtAwardSetFilterList.setLineWrap(true); - txtAwardSetFilterList.setWrapStyleWord(true); - txtAwardSetFilterList.setToolTipText( - wordWrap(resources.getString("lblAwardSetFilterList.toolTipText"), 100)); - txtAwardSetFilterList.setName("txtAwardSetFilterList"); - txtAwardSetFilterList.setText(""); - - JScrollPane scrollAwardSetFilterList = new JScrollPaneWithSpeed(txtAwardSetFilterList); - scrollAwardSetFilterList.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollAwardSetFilterList.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); - - chkEnableAutoAwards = new JCheckBox(resources.getString("chkEnableAutoAwards.text")); - chkEnableAutoAwards.setToolTipText(resources.getString("chkEnableAutoAwards.toolTipText")); - chkEnableAutoAwards.setName("chkEnableAutoAwards"); - chkEnableAutoAwards.addActionListener(evt -> { - final boolean isEnabled = chkEnableAutoAwards.isSelected(); - - chkIssuePosthumousAwards.setEnabled(isEnabled); - chkIssueBestAwardOnly.setEnabled(isEnabled); - chkIgnoreStandardSet.setEnabled(isEnabled); - autoAwardsPanel.setEnabled(isEnabled); - chkEnableContractAwards.setEnabled(isEnabled); - chkEnableFactionHunterAwards.setEnabled(isEnabled); - chkEnableInjuryAwards.setEnabled(isEnabled); - chkEnableIndividualKillAwards.setEnabled(isEnabled); - chkEnableFormationKillAwards.setEnabled(isEnabled); - chkEnableRankAwards.setEnabled(isEnabled); - chkEnableScenarioAwards.setEnabled(isEnabled); - chkEnableSkillAwards.setEnabled(isEnabled); - chkEnableTheatreOfWarAwards.setEnabled(isEnabled); - chkEnableTimeAwards.setEnabled(isEnabled); - chkEnableTrainingAwards.setEnabled(isEnabled); - chkEnableMiscAwards.setEnabled(isEnabled); - lblAwardSetFilterList.setEnabled(isEnabled); - txtAwardSetFilterList.setEnabled(isEnabled); - }); - - // this prevents a really annoying bug where disabled options don't stay - // disabled when - // reloading Campaign Options - if (!campaign.getCampaignOptions().isEnableAutoAwards()) { - chkIssuePosthumousAwards.setEnabled(false); - chkIssueBestAwardOnly.setEnabled(false); - chkIgnoreStandardSet.setEnabled(false); - autoAwardsPanel.setEnabled(false); - chkEnableContractAwards.setEnabled(false); - chkEnableFactionHunterAwards.setEnabled(false); - chkEnableInjuryAwards.setEnabled(false); - chkEnableIndividualKillAwards.setEnabled(false); - chkEnableFormationKillAwards.setEnabled(false); - chkEnableRankAwards.setEnabled(false); - chkEnableScenarioAwards.setEnabled(false); - chkEnableSkillAwards.setEnabled(false); - chkEnableTheatreOfWarAwards.setEnabled(false); - chkEnableTimeAwards.setEnabled(false); - chkEnableTrainingAwards.setEnabled(false); - chkEnableMiscAwards.setEnabled(false); - lblAwardSetFilterList.setEnabled(false); - txtAwardSetFilterList.setEnabled(false); - } - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("awardsPanel.title"))); - panel.setName("awardsPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblAwardBonusStyle) - .addComponent(comboAwardBonusStyle)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblAwardTierSize) - .addComponent(spnAwardTierSize)) - .addComponent(chkEnableAutoAwards) - .addComponent(chkIssuePosthumousAwards) - .addComponent(chkIssueBestAwardOnly) - .addComponent(chkIgnoreStandardSet) - .addComponent(autoAwardsPanel) - .addComponent(lblAwardSetFilterList) - .addComponent(scrollAwardSetFilterList)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblAwardBonusStyle) - .addComponent(comboAwardBonusStyle)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblAwardTierSize) - .addComponent(spnAwardTierSize)) - .addComponent(chkEnableAutoAwards) - .addComponent(chkIssuePosthumousAwards) - .addComponent(chkIssueBestAwardOnly) - .addComponent(chkIgnoreStandardSet) - .addComponent(autoAwardsPanel) - .addComponent(lblAwardSetFilterList) - .addComponent(scrollAwardSetFilterList)); - - return panel; - } - - private JPanel createAutoAwardsPanel() { - // Create Panel Components - chkEnableContractAwards = new JCheckBox(resources.getString("chkEnableContractAwards.text")); - chkEnableContractAwards.setToolTipText(resources.getString("chkEnableContractAwards.toolTipText")); - chkEnableContractAwards.setName("chkEnableContractAwards"); - - chkEnableFactionHunterAwards = new JCheckBox(resources.getString("chkEnableFactionHunterAwards.text")); - chkEnableFactionHunterAwards - .setToolTipText(resources.getString("chkEnableFactionHunterAwards.toolTipText")); - chkEnableFactionHunterAwards.setName("chkEnableFactionHunterAwards"); - - chkEnableInjuryAwards = new JCheckBox(resources.getString("chkEnableInjuryAwards.text")); - chkEnableInjuryAwards.setToolTipText(resources.getString("chkEnableInjuryAwards.toolTipText")); - chkEnableInjuryAwards.setName("chkEnableInjuryAwards"); - - chkEnableIndividualKillAwards = new JCheckBox( - resources.getString("chkEnableIndividualKillAwards.text")); - chkEnableIndividualKillAwards - .setToolTipText(resources.getString("chkEnableIndividualKillAwards.toolTipText")); - chkEnableIndividualKillAwards.setName("chkEnableIndividualKillAwards"); - - chkEnableFormationKillAwards = new JCheckBox(resources.getString("chkEnableFormationKillAwards.text")); - chkEnableFormationKillAwards - .setToolTipText(resources.getString("chkEnableFormationKillAwards.toolTipText")); - chkEnableFormationKillAwards.setName("chkEnableFormationKillAwards"); - - chkEnableRankAwards = new JCheckBox(resources.getString("chkEnableRankAwards.text")); - chkEnableRankAwards.setToolTipText(resources.getString("chkEnableRankAwards.toolTipText")); - chkEnableRankAwards.setName("chkEnableRankAwards"); - - chkEnableScenarioAwards = new JCheckBox(resources.getString("chkEnableScenarioAwards.text")); - chkEnableScenarioAwards.setToolTipText(resources.getString("chkEnableScenarioAwards.toolTipText")); - chkEnableScenarioAwards.setName("chkEnableScenarioAwards"); - - chkEnableSkillAwards = new JCheckBox(resources.getString("chkEnableSkillAwards.text")); - chkEnableSkillAwards.setToolTipText(resources.getString("chkEnableSkillAwards.toolTipText")); - chkEnableSkillAwards.setName("chkEnableSkillAwards"); - - chkEnableTheatreOfWarAwards = new JCheckBox(resources.getString("chkEnableTheatreOfWarAwards.text")); - chkEnableTheatreOfWarAwards - .setToolTipText(resources.getString("chkEnableTheatreOfWarAwards.toolTipText")); - chkEnableTheatreOfWarAwards.setName("chkEnableTheatreOfWarAwards"); - - chkEnableTimeAwards = new JCheckBox(resources.getString("chkEnableTimeAwards.text")); - chkEnableTimeAwards.setToolTipText(resources.getString("chkEnableTimeAwards.toolTipText")); - chkEnableTimeAwards.setName("chkEnableTimeAwards"); - - chkEnableTrainingAwards = new JCheckBox(resources.getString("chkEnableTrainingAwards.text")); - chkEnableTrainingAwards.setToolTipText(resources.getString("chkEnableTrainingAwards.toolTipText")); - chkEnableTrainingAwards.setName("chkEnableTrainingAwards"); - - chkEnableMiscAwards = new JCheckBox(resources.getString("chkEnableMiscAwards.text")); - chkEnableMiscAwards.setToolTipText(resources.getString("chkEnableMiscAwards.toolTipText")); - chkEnableMiscAwards.setName("chkEnableMiscAwards"); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("autoAwardsPanel.title"))); - panel.setName("autoAwardsPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createParallelGroup(Alignment.BASELINE) - .addGroup(layout.createSequentialGroup() - .addComponent(chkEnableContractAwards) - .addComponent(chkEnableFactionHunterAwards) - .addComponent(chkEnableInjuryAwards) - .addComponent(chkEnableIndividualKillAwards)) - .addGroup(layout.createSequentialGroup() - .addComponent(chkEnableRankAwards) - .addComponent(chkEnableScenarioAwards) - .addComponent(chkEnableSkillAwards) - .addComponent(chkEnableFormationKillAwards)) - .addGroup(layout.createSequentialGroup() - .addComponent(chkEnableTheatreOfWarAwards) - .addComponent(chkEnableTimeAwards) - .addComponent(chkEnableTrainingAwards) - .addComponent(chkEnableMiscAwards))); - - layout.setHorizontalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup() - .addComponent(chkEnableContractAwards) - .addComponent(chkEnableFactionHunterAwards) - .addComponent(chkEnableInjuryAwards) - .addComponent(chkEnableIndividualKillAwards)) - .addGroup(layout.createParallelGroup() - .addComponent(chkEnableFormationKillAwards) - .addComponent(chkEnableRankAwards) - .addComponent(chkEnableScenarioAwards) - .addComponent(chkEnableSkillAwards)) - .addGroup(layout.createParallelGroup() - .addComponent(chkEnableTheatreOfWarAwards) - .addComponent(chkEnableTimeAwards) - .addComponent(chkEnableTrainingAwards) - .addComponent(chkEnableMiscAwards))); - - return panel; - } - - private Component createAdministratorsPanel() { - chkAdminsHaveNegotiation = new JCheckBox(resources.getString("chkAdminsHaveNegotiation.text")); - chkAdminsHaveNegotiation.setToolTipText(resources.getString("chkAdminsHaveNegotiation.toolTipText")); - chkAdminsHaveNegotiation.setName("chkAdminsHaveNegotiation"); - - chkAdminExperienceLevelIncludeNegotiation = new JCheckBox( - resources.getString("chkAdminExperienceLevelIncludeNegotiation.text")); - chkAdminExperienceLevelIncludeNegotiation - .setToolTipText(resources - .getString("chkAdminExperienceLevelIncludeNegotiation.toolTipText")); - chkAdminExperienceLevelIncludeNegotiation.setName("chkAdminExperienceLevelIncludeNegotiation"); - - chkAdminsHaveScrounge = new JCheckBox(resources.getString("chkAdminsHaveScrounge.text")); - chkAdminsHaveScrounge.setToolTipText(resources.getString("chkAdminsHaveScrounge.toolTipText")); - chkAdminsHaveScrounge.setName("chkAdminsHaveScrounge"); - - chkAdminExperienceLevelIncludeScrounge = new JCheckBox( - resources.getString("chkAdminExperienceLevelIncludeScrounge.text")); - chkAdminExperienceLevelIncludeScrounge - .setToolTipText(resources - .getString("chkAdminExperienceLevelIncludeScrounge.toolTipText")); - chkAdminExperienceLevelIncludeScrounge.setName("chkAdminExperienceLevelIncludeScrounge"); - - administratorsPanel - .setBorder(BorderFactory - .createTitledBorder(resources.getString("administratorsPanel.title"))); - administratorsPanel.setName("administratorsPanel"); - - final GroupLayout layout = new GroupLayout(administratorsPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - administratorsPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkAdminsHaveNegotiation) - .addComponent(chkAdminExperienceLevelIncludeNegotiation) - .addGap(15) - .addComponent(chkAdminsHaveScrounge) - .addComponent(chkAdminExperienceLevelIncludeScrounge)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkAdminsHaveNegotiation) - .addComponent(chkAdminExperienceLevelIncludeNegotiation) - .addComponent(chkAdminsHaveScrounge) - .addComponent(chkAdminExperienceLevelIncludeScrounge)); - return administratorsPanel; - } - - private JPanel createMedicalPanel() { - // Create Panel Components - chkUseAdvancedMedical = new JCheckBox(resources.getString("chkUseAdvancedMedical.text")); - chkUseAdvancedMedical.setToolTipText(resources.getString("chkUseAdvancedMedical.toolTipText")); - chkUseAdvancedMedical.setName("chkUseAdvancedMedical"); - - final JLabel lblHealWaitingPeriod = new JLabel(resources.getString("lblHealWaitingPeriod.text")); - lblHealWaitingPeriod.setToolTipText(resources.getString("lblHealWaitingPeriod.toolTipText")); - lblHealWaitingPeriod.setName("lblHealWaitingPeriod"); - - spnHealWaitingPeriod = new JSpinner(new SpinnerNumberModel(1, 1, 30, 1)); - spnHealWaitingPeriod.setToolTipText(resources.getString("lblHealWaitingPeriod.toolTipText")); - spnHealWaitingPeriod.setName("spnHealWaitingPeriod"); - - final JLabel lblNaturalHealWaitingPeriod = new JLabel( - resources.getString("lblNaturalHealWaitingPeriod.text")); - lblNaturalHealWaitingPeriod - .setToolTipText(resources.getString("lblNaturalHealWaitingPeriod.toolTipText")); - lblNaturalHealWaitingPeriod.setName("lblNaturalHealWaitingPeriod"); - - spnNaturalHealWaitingPeriod = new JSpinner(new SpinnerNumberModel(1, 1, 365, 1)); - spnNaturalHealWaitingPeriod - .setToolTipText(resources.getString("lblNaturalHealWaitingPeriod.toolTipText")); - spnNaturalHealWaitingPeriod.setName("spnNaturalHealWaitingPeriod"); - - final JLabel lblMinimumHitsForVehicles = new JLabel( - resources.getString("lblMinimumHitsForVehicles.text")); - lblMinimumHitsForVehicles.setToolTipText(resources.getString("lblMinimumHitsForVehicles.toolTipText")); - lblMinimumHitsForVehicles.setName("lblMinimumHitsForVehicles"); - - spnMinimumHitsForVehicles = new JSpinner(new SpinnerNumberModel(1, 1, 5, 1)); - spnMinimumHitsForVehicles.setToolTipText(resources.getString("lblMinimumHitsForVehicles.toolTipText")); - spnMinimumHitsForVehicles.setName("spnMinimumHitsForVehicles"); - ((DefaultEditor) spnMinimumHitsForVehicles.getEditor()).getTextField().setEditable(false); - - chkUseRandomHitsForVehicles = new JCheckBox(resources.getString("chkUseRandomHitsForVehicles.text")); - chkUseRandomHitsForVehicles - .setToolTipText(resources.getString("chkUseRandomHitsForVehicles.toolTipText")); - chkUseRandomHitsForVehicles.setName("chkUseRandomHitsForVehicles"); - - chkUseTougherHealing = new JCheckBox(resources.getString("chkUseTougherHealing.text")); - chkUseTougherHealing.setToolTipText(resources.getString("chkUseTougherHealing.toolTipText")); - chkUseTougherHealing.setName("chkUseTougherHealing"); - - final JLabel lblMaximumPatients = new JLabel(resources.getString("lblMaximumPatients.text")); - lblMaximumPatients.setToolTipText(resources.getString("lblMaximumPatients.toolTipText")); - lblMaximumPatients.setName("lblMaximumPatients"); - - spnMaximumPatients = new JSpinner(new SpinnerNumberModel(25, 1, 100, 1)); - spnMaximumPatients.setToolTipText(resources.getString("lblMaximumPatients.toolTipText")); - spnMaximumPatients.setName("spnMaximumPatients"); - - // Programmatically Assign Accessibility Labels - lblHealWaitingPeriod.setLabelFor(spnHealWaitingPeriod); - lblNaturalHealWaitingPeriod.setLabelFor(spnNaturalHealWaitingPeriod); - lblMinimumHitsForVehicles.setLabelFor(spnMinimumHitsForVehicles); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("medicalPanel.title"))); - panel.setName("medicalPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseAdvancedMedical) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblHealWaitingPeriod) - .addComponent(spnHealWaitingPeriod, Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblNaturalHealWaitingPeriod) - .addComponent(spnNaturalHealWaitingPeriod, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblMinimumHitsForVehicles) - .addComponent(spnMinimumHitsForVehicles, - Alignment.LEADING)) - .addComponent(chkUseRandomHitsForVehicles) - .addComponent(chkUseTougherHealing) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblMaximumPatients) - .addComponent(spnMaximumPatients, Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseAdvancedMedical) - .addGroup(layout.createSequentialGroup() - .addComponent(lblHealWaitingPeriod) - .addComponent(spnHealWaitingPeriod)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblNaturalHealWaitingPeriod) - .addComponent(spnNaturalHealWaitingPeriod)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblMinimumHitsForVehicles) - .addComponent(spnMinimumHitsForVehicles)) - .addComponent(chkUseRandomHitsForVehicles) - .addComponent(chkUseTougherHealing) - .addGroup(layout.createSequentialGroup() - .addComponent(lblMaximumPatients) - .addComponent(spnMaximumPatients))); - - return panel; - } - - private JPanel createPrisonerPanel() { - // Create Panel Components - final JLabel lblPrisonerCaptureStyle = new JLabel(resources.getString("lblPrisonerCaptureStyle.text")); - lblPrisonerCaptureStyle.setToolTipText(resources.getString("lblPrisonerCaptureStyle.toolTipText")); - lblPrisonerCaptureStyle.setName("lblPrisonerCaptureStyle"); - - comboPrisonerCaptureStyle = new MMComboBox<>("comboPrisonerCaptureStyle", - PrisonerCaptureStyle.values()); - comboPrisonerCaptureStyle.setToolTipText(resources.getString("lblPrisonerCaptureStyle.toolTipText")); - comboPrisonerCaptureStyle.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof PrisonerCaptureStyle) { - list.setToolTipText(((PrisonerCaptureStyle) value).getToolTipText()); - } - return this; - } - }); - - final JLabel lblPrisonerStatus = new JLabel(resources.getString("lblPrisonerStatus.text")); - lblPrisonerStatus.setToolTipText(resources.getString("lblPrisonerStatus.toolTipText")); - lblPrisonerStatus.setName("lblPrisonerStatus"); - - final DefaultComboBoxModel prisonerStatusModel = new DefaultComboBoxModel<>( - PrisonerStatus.values()); - prisonerStatusModel.removeElement(PrisonerStatus.FREE); // we don't want this as a standard use case for - // prisoners - comboPrisonerStatus = new MMComboBox<>("comboPrisonerStatus", prisonerStatusModel); - comboPrisonerStatus.setToolTipText(resources.getString("lblPrisonerStatus.toolTipText")); - - chkPrisonerBabyStatus = new JCheckBox(resources.getString("chkPrisonerBabyStatus.text")); - chkPrisonerBabyStatus.setToolTipText(resources.getString("chkPrisonerBabyStatus.toolTipText")); - chkPrisonerBabyStatus.setName("chkPrisonerBabyStatus"); - - chkAtBPrisonerDefection = new JCheckBox(resources.getString("chkAtBPrisonerDefection.text")); - chkAtBPrisonerDefection.setToolTipText(resources.getString("chkAtBPrisonerDefection.toolTipText")); - chkAtBPrisonerDefection.setName("chkAtBPrisonerDefection"); - - chkAtBPrisonerRansom = new JCheckBox(resources.getString("chkAtBPrisonerRansom.text")); - chkAtBPrisonerRansom.setToolTipText(resources.getString("chkAtBPrisonerRansom.toolTipText")); - chkAtBPrisonerRansom.setName("chkAtBPrisonerRansom"); - - // Programmatically Assign Accessibility Labels - lblPrisonerCaptureStyle.setLabelFor(comboPrisonerCaptureStyle); - lblPrisonerStatus.setLabelFor(comboPrisonerStatus); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("prisonerPanel.title"))); - panel.setName("prisonerPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPrisonerCaptureStyle) - .addComponent(comboPrisonerCaptureStyle, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPrisonerStatus) - .addComponent(comboPrisonerStatus, Alignment.LEADING)) - .addComponent(chkPrisonerBabyStatus) - .addComponent(chkAtBPrisonerDefection) - .addComponent(chkAtBPrisonerRansom)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPrisonerCaptureStyle) - .addComponent(comboPrisonerCaptureStyle)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPrisonerStatus) - .addComponent(comboPrisonerStatus)) - .addComponent(chkPrisonerBabyStatus) - .addComponent(chkAtBPrisonerDefection) - .addComponent(chkAtBPrisonerRansom)); - - return panel; - } - - private JPanel createTurnoverAndRetentionHeaderPanel() { - chkUseRandomRetirement = new JCheckBox(resources.getString("chkUseRandomRetirement.text")); - chkUseRandomRetirement.setToolTipText(resources.getString("chkUseRandomRetirement.toolTipText")); - chkUseRandomRetirement.setName("chkUseRandomRetirement"); - chkUseRandomRetirement.addActionListener(evt -> { - boolean isEnabled = chkUseRandomRetirement.isSelected(); - - // General Handlers - for (Component component : turnoverAndRetentionSettingsPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - for (Component component : turnoverAndRetentionModifiersPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - for (Component component : turnoverAndRetentionPayoutPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - for (Component component : turnoverAndRetentionUnitCohesionPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - // Border Handlers - turnoverAndRetentionSettingsPanel.setEnabled(isEnabled); - turnoverAndRetentionModifiersPanel.setEnabled(isEnabled); - turnoverAndRetentionPayoutPanel.setEnabled(isEnabled); - turnoverAndRetentionUnitCohesionPanel.setEnabled(isEnabled); - - // Special Case Handlers - // These are elements whose status is influenced by other campaign options. - lblTurnoverFixedTargetNumber.setEnabled(isEnabled); - spnTurnoverFixedTargetNumber.setEnabled(isEnabled); - - boolean isUseLoyaltyModifiers = chkUseLoyaltyModifiers.isSelected(); - loyaltySubPanel.setEnabled((isEnabled) && (isUseLoyaltyModifiers)); - chkUseHideLoyalty.setEnabled((isEnabled) && (isUseLoyaltyModifiers)); - - boolean isUseServiceBonus = chkUsePayoutServiceBonus.isSelected(); - payoutServiceBonusSubPanel.setEnabled((isEnabled) && (isUseServiceBonus)); - lblPayoutServiceBonusRate.setEnabled((isEnabled) && (isUseServiceBonus)); - spnPayoutServiceBonusRate.setEnabled((isEnabled) && (isUseServiceBonus)); - - boolean isUseAdministrativeStrain = chkUseAdministrativeStrain.isSelected(); - administrativeStrainSubPanel.setEnabled((isEnabled) && (isUseAdministrativeStrain)); - lblAdministrativeCapacity.setEnabled((isEnabled) && (isUseAdministrativeStrain)); - spnAdministrativeCapacity.setEnabled((isEnabled) && (isUseAdministrativeStrain)); - lblMultiCrewStrainDivider.setEnabled((isEnabled) && (isUseAdministrativeStrain)); - spnMultiCrewStrainDivider.setEnabled((isEnabled) && (isUseAdministrativeStrain)); - - boolean isUseManagementSkill = chkUseManagementSkill.isSelected(); - managementSkillSubPanel.setEnabled((isEnabled) && (isUseManagementSkill)); - chkUseCommanderLeadershipOnly.setEnabled((isEnabled) && (isUseManagementSkill)); - lblManagementSkillPenalty.setEnabled((isEnabled) && (isUseManagementSkill)); - spnManagementSkillPenalty.setEnabled((isEnabled) && (isUseManagementSkill)); - }); - - final JPanel turnoverAndRetentionHeaderPanel = new JPanel(); - turnoverAndRetentionHeaderPanel.setName("turnoverAndRetentionHeaderPanel"); - - final GroupLayout layout = new GroupLayout(turnoverAndRetentionHeaderPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - turnoverAndRetentionHeaderPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseRandomRetirement)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseRandomRetirement)); - - return turnoverAndRetentionHeaderPanel; - } - - private JPanel createTurnoverAndRetentionSettingsPanel() { - boolean isUseTurnover = campaign.getCampaignOptions().isUseRandomRetirement(); - - lblTurnoverFixedTargetNumber = new JLabel(resources.getString("lblTurnoverFixedTargetNumber.text")); - lblTurnoverFixedTargetNumber - .setToolTipText(resources.getString("lblTurnoverFixedTargetNumber.toolTipText")); - lblTurnoverFixedTargetNumber.setName("lblTurnoverFixedTargetNumber"); - lblTurnoverFixedTargetNumber.setEnabled(isUseTurnover); - - spnTurnoverFixedTargetNumber = new JSpinner(new SpinnerNumberModel(3, 0, 10, 1)); - spnTurnoverFixedTargetNumber - .setToolTipText(resources.getString("lblTurnoverFixedTargetNumber.toolTipText")); - spnTurnoverFixedTargetNumber.setName("spnTurnoverFixedTargetNumber"); - spnTurnoverFixedTargetNumber.setEnabled(isUseTurnover); - - lblTurnoverFixedTargetNumber = new JLabel(resources.getString("lblTurnoverFixedTargetNumber.text")); - lblTurnoverFixedTargetNumber - .setToolTipText(resources.getString("lblTurnoverFixedTargetNumber.toolTipText")); - lblTurnoverFixedTargetNumber.setName("lblTurnoverFixedTargetNumber"); - lblTurnoverFixedTargetNumber.setEnabled(isUseTurnover); - - JLabel lblTurnoverFrequency = new JLabel(resources.getString("lblTurnoverFrequency.text")); - lblTurnoverFrequency.setToolTipText(resources.getString("lblTurnoverFrequency.toolTipText")); - lblTurnoverFrequency.setName("lblTurnoverFrequency"); - lblTurnoverFrequency.setEnabled(isUseTurnover); - - comboTurnoverFrequency = new MMComboBox<>("comboTurnoverFrequency", TurnoverFrequency.values()); - comboTurnoverFrequency.setToolTipText(resources.getString("lblTurnoverFrequency.toolTipText")); - comboTurnoverFrequency.setEnabled(isUseTurnover); - comboTurnoverFrequency.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof TurnoverFrequency) { - list.setToolTipText(((TurnoverFrequency) value).getToolTipText()); - } - return this; - } - }); - - chkUseContractCompletionRandomRetirement = new JCheckBox( - resources.getString("chkUseContractCompletionRandomRetirement.text")); - chkUseContractCompletionRandomRetirement - .setToolTipText(resources - .getString("chkUseContractCompletionRandomRetirement.toolTipText")); - chkUseContractCompletionRandomRetirement.setName("chkUseContractCompletionRandomRetirement"); - chkUseContractCompletionRandomRetirement.setEnabled(isUseTurnover); - - chkUseRandomFounderTurnover = new JCheckBox(resources.getString("chkUseRandomFounderTurnover.text")); - chkUseRandomFounderTurnover - .setToolTipText(resources.getString("chkUseRandomFounderTurnover.toolTipText")); - chkUseRandomFounderTurnover.setName("chkUseRandomFounderTurnover"); - chkUseRandomFounderTurnover.setEnabled(isUseTurnover); - - chkUseFounderRetirement = new JCheckBox(resources.getString("chkUseFounderRetirement.text")); - chkUseFounderRetirement.setToolTipText(resources.getString("chkUseFounderRetirement.toolTipText")); - chkUseFounderRetirement.setName("chkUseFounderRetirement"); - chkUseFounderRetirement.setEnabled(isUseTurnover); - - chkTrackOriginalUnit = new JCheckBox(resources.getString("chkTrackOriginalUnit.text")); - chkTrackOriginalUnit.setToolTipText(resources.getString("chkTrackOriginalUnit.toolTipText")); - chkTrackOriginalUnit.setName("chkTrackOriginalUnit"); - chkTrackOriginalUnit.setEnabled(isUseTurnover); - - chkAeroRecruitsHaveUnits = new JCheckBox(resources.getString("chkAeroRecruitsHaveUnits.text")); - chkAeroRecruitsHaveUnits.setToolTipText(resources.getString("chkAeroRecruitsHaveUnits.toolTipText")); - chkAeroRecruitsHaveUnits.setName("chkAeroRecruitsHaveUnits"); - chkAeroRecruitsHaveUnits.setEnabled(isUseTurnover); - - chkUseSubContractSoldiers = new JCheckBox(resources.getString("chkUseSubContractSoldiers.text")); - chkUseSubContractSoldiers.setToolTipText(resources.getString("chkUseSubContractSoldiers.toolTipText")); - chkUseSubContractSoldiers.setName("chkUseSubContractSoldiers"); - chkUseSubContractSoldiers.setEnabled(isUseTurnover); - - JLabel lblServiceContractDuration = new JLabel(resources.getString("lblServiceContractDuration.text")); - lblServiceContractDuration.setToolTipText(resources.getString("lblServiceContractDuration.toolTipText")); - lblServiceContractDuration.setName("lblServiceContractDuration"); - lblServiceContractDuration.setEnabled(isUseTurnover); - - spnServiceContractDuration = new JSpinner(new SpinnerNumberModel(36, 0, 120, 1)); - spnServiceContractDuration - .setToolTipText(resources.getString("lblServiceContractDuration.toolTipText")); - spnServiceContractDuration.setName("spnServiceContractDuration"); - spnServiceContractDuration.setEnabled(isUseTurnover); - - JLabel lblServiceContractModifier = new JLabel(resources.getString("lblServiceContractModifier.text")); - lblServiceContractModifier.setToolTipText(resources.getString("lblServiceContractModifier.toolTipText")); - lblServiceContractModifier.setName("lblServiceContractModifier"); - lblServiceContractModifier.setEnabled(isUseTurnover); - - spnServiceContractModifier = new JSpinner(new SpinnerNumberModel(3, 0, 10, 1)); - spnServiceContractModifier - .setToolTipText(resources.getString("lblServiceContractModifier.toolTipText")); - spnServiceContractModifier.setName("spnServiceContractModifier"); - spnServiceContractModifier.setEnabled(isUseTurnover); - - chkPayBonusDefault = new JCheckBox(resources.getString("chkPayBonusDefault.text")); - chkPayBonusDefault.setToolTipText(resources.getString("chkPayBonusDefault.toolTipText")); - chkPayBonusDefault.setName("chkPayBonusDefault"); - chkPayBonusDefault.setEnabled(isUseTurnover); - chkPayBonusDefault.addActionListener(evt -> { - boolean isEnabled = chkPayBonusDefault.isSelected(); - - lblPayBonusDefaultThreshold.setEnabled(isEnabled); - spnPayBonusDefaultThreshold.setEnabled(isEnabled); - }); - - lblPayBonusDefaultThreshold = new JLabel(resources.getString("lblPayBonusDefaultThreshold.text")); - lblPayBonusDefaultThreshold - .setToolTipText(resources.getString("lblPayBonusDefaultThreshold.toolTipText")); - lblPayBonusDefaultThreshold.setName("lblPayBonusDefaultThreshold"); - lblPayBonusDefaultThreshold - .setEnabled((isUseTurnover) && (campaign.getCampaignOptions().isPayBonusDefault())); - - spnPayBonusDefaultThreshold = new JSpinner(new SpinnerNumberModel(3, 0, 12, 1)); - spnPayBonusDefaultThreshold - .setToolTipText(resources.getString("lblPayBonusDefaultThreshold.toolTipText")); - spnPayBonusDefaultThreshold.setName("spnPayBonusDefaultThreshold"); - spnPayBonusDefaultThreshold - .setEnabled((isUseTurnover) && (campaign.getCampaignOptions().isPayBonusDefault())); - - turnoverAndRetentionSettingsPanel.setBorder( - BorderFactory.createTitledBorder( - resources.getString("turnoverAndRetentionSettingsPanel.title"))); - turnoverAndRetentionSettingsPanel.setName("turnoverAndRetentionSettingsPanel"); - turnoverAndRetentionSettingsPanel.setEnabled(isUseTurnover); - - final GroupLayout layout = new GroupLayout(turnoverAndRetentionSettingsPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - turnoverAndRetentionSettingsPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblTurnoverFixedTargetNumber) - .addComponent(spnTurnoverFixedTargetNumber, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblTurnoverFrequency) - .addComponent(comboTurnoverFrequency, - Alignment.LEADING)) - .addComponent(chkUseContractCompletionRandomRetirement) - .addComponent(chkUseRandomFounderTurnover) - .addComponent(chkUseFounderRetirement) - .addComponent(chkTrackOriginalUnit) - .addComponent(chkAeroRecruitsHaveUnits) - .addComponent(chkUseSubContractSoldiers) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblServiceContractDuration) - .addComponent(spnServiceContractDuration, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblServiceContractModifier) - .addComponent(spnServiceContractModifier, - Alignment.LEADING)) - .addComponent(chkPayBonusDefault) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPayBonusDefaultThreshold) - .addComponent(spnPayBonusDefaultThreshold, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblTurnoverFixedTargetNumber) - .addComponent(spnTurnoverFixedTargetNumber)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblTurnoverFrequency) - .addComponent(comboTurnoverFrequency)) - .addComponent(chkUseContractCompletionRandomRetirement) - .addComponent(chkUseRandomFounderTurnover) - .addComponent(chkUseFounderRetirement) - .addComponent(chkTrackOriginalUnit) - .addComponent(chkAeroRecruitsHaveUnits) - .addComponent(chkUseSubContractSoldiers) - .addGroup(layout.createSequentialGroup() - .addComponent(lblServiceContractDuration) - .addComponent(spnServiceContractDuration)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblServiceContractModifier) - .addComponent(spnServiceContractModifier)) - .addComponent(chkPayBonusDefault) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPayBonusDefaultThreshold) - .addComponent(spnPayBonusDefaultThreshold))); - - return turnoverAndRetentionSettingsPanel; - } - - private JPanel createTurnoverAndRetentionModifiersPanel() { - boolean isUseTurnover = campaign.getCampaignOptions().isUseRandomRetirement(); - - chkUseCustomRetirementModifiers = new JCheckBox( - resources.getString("chkUseCustomRetirementModifiers.text")); - chkUseCustomRetirementModifiers - .setToolTipText(resources.getString("chkUseCustomRetirementModifiers.toolTipText")); - chkUseCustomRetirementModifiers.setName("chkUseCustomRetirementModifiers"); - chkUseCustomRetirementModifiers.setEnabled(campaign.getCampaignOptions().isUseRandomRetirement()); - - chkUseFatigueModifiers = new JCheckBox(resources.getString("chkUseFatigueModifiers.text")); - chkUseFatigueModifiers.setToolTipText(resources.getString("chkUseFatigueModifiers.toolTipText")); - chkUseFatigueModifiers.setName("chkUseFatigueModifiers"); - chkUseFatigueModifiers.setEnabled((isUseTurnover) && (campaign.getCampaignOptions().isUseFatigue())); - - chkUseSkillModifiers = new JCheckBox(resources.getString("chkUseSkillModifiers.text")); - chkUseSkillModifiers.setToolTipText(resources.getString("chkUseSkillModifiers.toolTipText")); - chkUseSkillModifiers.setName("chkUseSkillModifiers"); - chkUseSkillModifiers.setEnabled(isUseTurnover); - - chkUseAgeModifiers = new JCheckBox(resources.getString("chkUseAgeModifiers.text")); - chkUseAgeModifiers.setToolTipText(resources.getString("chkUseAgeModifiers.toolTipText")); - chkUseAgeModifiers.setName("chkUseAgeModifiers"); - chkUseAgeModifiers.setEnabled(isUseTurnover); - - chkUseUnitRatingModifiers = new JCheckBox(resources.getString("chkUseUnitRatingModifiers.text")); - chkUseUnitRatingModifiers.setToolTipText(resources.getString("chkUseUnitRatingModifiers.toolTipText")); - chkUseUnitRatingModifiers.setName("chkUseUnitRatingModifiers"); - chkUseUnitRatingModifiers.setEnabled(isUseTurnover); - - chkUseFactionModifiers = new JCheckBox(resources.getString("chkUseFactionModifiers.text")); - chkUseFactionModifiers.setToolTipText(resources.getString("chkUseFactionModifiers.toolTipText")); - chkUseFactionModifiers.setName("chkUseFactionModifiers"); - chkUseFactionModifiers.setEnabled(isUseTurnover); - - chkUseMissionStatusModifiers = new JCheckBox(resources.getString("chkUseMissionStatusModifiers.text")); - chkUseMissionStatusModifiers - .setToolTipText(resources.getString("chkUseMissionStatusModifiers.toolTipText")); - chkUseMissionStatusModifiers.setName("chkUseMissionStatusModifiers"); - chkUseMissionStatusModifiers.setEnabled(isUseTurnover); - - chkUseHostileTerritoryModifiers = new JCheckBox( - resources.getString("chkUseHostileTerritoryModifiers.text")); - chkUseHostileTerritoryModifiers - .setToolTipText(resources.getString("chkUseHostileTerritoryModifiers.toolTipText")); - chkUseHostileTerritoryModifiers.setName("chkUseHostileTerritoryModifiers"); - chkUseHostileTerritoryModifiers.setEnabled(isUseTurnover); - - chkUseFamilyModifiers = new JCheckBox(resources.getString("chkUseFamilyModifiers.text")); - chkUseFamilyModifiers.setToolTipText(resources.getString("chkUseFamilyModifiers.toolTipText")); - chkUseFamilyModifiers.setName("chkUseFamilyModifiers"); - chkUseFamilyModifiers.setEnabled(isUseTurnover); - - chkUseLoyaltyModifiers = new JCheckBox(resources.getString("chkUseLoyaltyModifiers.text")); - chkUseLoyaltyModifiers.setToolTipText(resources.getString("chkUseLoyaltyModifiers.toolTipText")); - chkUseLoyaltyModifiers.setName("chkUseLoyaltyModifiers"); - chkUseLoyaltyModifiers.setEnabled(isUseTurnover); - chkUseLoyaltyModifiers.addActionListener(evt -> { - final boolean isEnabled = chkUseLoyaltyModifiers.isSelected(); - - for (Component component : loyaltySubPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - loyaltySubPanel.setEnabled(isEnabled); - }); - - createLoyaltySubPanel(isUseTurnover); - - turnoverAndRetentionModifiersPanel.setBorder( - BorderFactory.createTitledBorder( - resources.getString("turnoverAndRetentionModifiersPanel.title"))); - turnoverAndRetentionModifiersPanel.setName("turnoverAndRetentionModifiersPanel"); - turnoverAndRetentionModifiersPanel.setEnabled(isUseTurnover); - - final GroupLayout layout = new GroupLayout(turnoverAndRetentionModifiersPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - turnoverAndRetentionModifiersPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseCustomRetirementModifiers) - .addComponent(chkUseFatigueModifiers) - .addComponent(chkUseSkillModifiers) - .addComponent(chkUseAgeModifiers) - .addComponent(chkUseUnitRatingModifiers) - .addComponent(chkUseFactionModifiers) - .addComponent(chkUseHostileTerritoryModifiers) - .addComponent(chkUseMissionStatusModifiers) - .addComponent(chkUseFamilyModifiers) - .addGap(15) - .addComponent(chkUseLoyaltyModifiers) - .addComponent(loyaltySubPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseCustomRetirementModifiers) - .addComponent(chkUseFatigueModifiers) - .addComponent(chkUseSkillModifiers) - .addComponent(chkUseAgeModifiers) - .addComponent(chkUseUnitRatingModifiers) - .addComponent(chkUseFactionModifiers) - .addComponent(chkUseHostileTerritoryModifiers) - .addComponent(chkUseMissionStatusModifiers) - .addComponent(chkUseFamilyModifiers) - .addComponent(chkUseLoyaltyModifiers) - .addComponent(loyaltySubPanel)); - - return turnoverAndRetentionModifiersPanel; - } - - private void createLoyaltySubPanel(boolean isUseTurnover) { - chkUseHideLoyalty = new JCheckBox(resources.getString("chkUseHideLoyalty.text")); - chkUseHideLoyalty.setToolTipText(resources.getString("chkUseHideLoyalty.toolTipText")); - chkUseHideLoyalty.setName("chkUseHideLoyalty"); - chkUseHideLoyalty.setEnabled(isUseTurnover && campaign.getCampaignOptions().isUseLoyaltyModifiers()); - - loyaltySubPanel.setBorder(BorderFactory.createTitledBorder("")); - loyaltySubPanel.setName("loyaltySubPanel"); - loyaltySubPanel.setEnabled(isUseTurnover && campaign.getCampaignOptions().isUseLoyaltyModifiers()); - - final GroupLayout layout = new GroupLayout(loyaltySubPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - loyaltySubPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseHideLoyalty)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseHideLoyalty)); - } - - private JPanel createTurnoverAndRetentionPayoutPanel() { - boolean isUseTurnover = campaign.getCampaignOptions().isUseRandomRetirement(); - - JLabel lblPayoutRateOfficer = new JLabel(resources.getString("lblPayoutRateOfficer.text")); - lblPayoutRateOfficer.setToolTipText(resources.getString("lblPayoutRateOfficer.toolTipText")); - lblPayoutRateOfficer.setName("lblPayoutRateOfficer"); - lblPayoutRateOfficer.setEnabled(isUseTurnover); - - spnPayoutRateOfficer = new JSpinner(new SpinnerNumberModel(3, 0, 12, 1)); - spnPayoutRateOfficer.setToolTipText(resources.getString("lblPayoutRateOfficer.toolTipText")); - spnPayoutRateOfficer.setName("spnPayoutRateOfficer"); - spnPayoutRateOfficer.setEnabled(isUseTurnover); - - JLabel lblPayoutRateEnlisted = new JLabel(resources.getString("lblPayoutRateEnlisted.text")); - lblPayoutRateEnlisted.setToolTipText(resources.getString("lblPayoutRateEnlisted.toolTipText")); - lblPayoutRateEnlisted.setName("lblPayoutRateEnlisted"); - lblPayoutRateEnlisted.setEnabled(isUseTurnover); - - spnPayoutRateEnlisted = new JSpinner(new SpinnerNumberModel(3, 0, 12, 1)); - spnPayoutRateEnlisted.setToolTipText(resources.getString("lblPayoutRateEnlisted.toolTipText")); - spnPayoutRateEnlisted.setName("lblPayoutRateEnlisted"); - spnPayoutRateEnlisted.setEnabled(isUseTurnover); - - JLabel lblPayoutRetirementMultiplier = new JLabel(resources.getString("lblPayoutRetirementMultiplier.text")); - lblPayoutRetirementMultiplier.setToolTipText(resources.getString("lblPayoutRetirementMultiplier.toolTipText")); - lblPayoutRetirementMultiplier.setName("lblPayoutRetirementMultiplier"); - lblPayoutRetirementMultiplier.setEnabled(isUseTurnover); - - spnPayoutRetirementMultiplier = new JSpinner(new SpinnerNumberModel(24, 1, 120, 1)); - spnPayoutRetirementMultiplier.setToolTipText(resources.getString("lblPayoutRateEnlisted.toolTipText")); - spnPayoutRetirementMultiplier.setName("spnPayoutRetirementMultiplier"); - spnPayoutRetirementMultiplier.setEnabled(isUseTurnover); - - chkUsePayoutServiceBonus = new JCheckBox(resources.getString("chkUsePayoutServiceBonus.text")); - chkUsePayoutServiceBonus.setToolTipText(resources.getString("chkUsePayoutServiceBonus.toolTipText")); - chkUsePayoutServiceBonus.setName("chkUsePayoutServiceBonus"); - chkUsePayoutServiceBonus.setEnabled(isUseTurnover); - chkUsePayoutServiceBonus.addActionListener(evt -> { - final boolean isEnabled = chkUsePayoutServiceBonus.isSelected(); - - for (Component component : payoutServiceBonusSubPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - payoutServiceBonusSubPanel.setEnabled(isEnabled); - }); - - createPayoutServiceBonusSubPanel(isUseTurnover); - - turnoverAndRetentionPayoutPanel.setBorder( - BorderFactory.createTitledBorder( - resources.getString("turnoverAndRetentionPayoutPanel.title"))); - turnoverAndRetentionPayoutPanel.setName("turnoverAndRetentionPayoutPanel"); - turnoverAndRetentionPayoutPanel.setEnabled(isUseTurnover); - - final GroupLayout layout = new GroupLayout(turnoverAndRetentionPayoutPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - turnoverAndRetentionPayoutPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPayoutRateOfficer) - .addComponent(spnPayoutRateOfficer, Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPayoutRateEnlisted) - .addComponent(spnPayoutRateEnlisted, Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPayoutRetirementMultiplier) - .addComponent(spnPayoutRetirementMultiplier, - Alignment.LEADING)) - .addGap(15) - .addComponent(chkUsePayoutServiceBonus) - .addComponent(payoutServiceBonusSubPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPayoutRateOfficer) - .addComponent(spnPayoutRateOfficer)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPayoutRateEnlisted) - .addComponent(spnPayoutRateEnlisted)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPayoutRetirementMultiplier) - .addComponent(spnPayoutRetirementMultiplier)) - .addComponent(chkUsePayoutServiceBonus) - .addComponent(payoutServiceBonusSubPanel)); - - return turnoverAndRetentionPayoutPanel; - } - - private void createPayoutServiceBonusSubPanel(boolean isUseTurnover) { - boolean isUseServiceBonus = campaign.getCampaignOptions().isUsePayoutServiceBonus(); - - lblPayoutServiceBonusRate = new JLabel(resources.getString("lblPayoutServiceBonusRate.text")); - lblPayoutServiceBonusRate.setToolTipText(resources.getString("lblPayoutServiceBonusRate.toolTipText")); - lblPayoutServiceBonusRate.setName("lblPayoutServiceBonusRate"); - lblPayoutServiceBonusRate.setEnabled((isUseTurnover) && (isUseServiceBonus)); - - spnPayoutServiceBonusRate = new JSpinner(new SpinnerNumberModel(10, 1, 100, 1)); - spnPayoutServiceBonusRate.setToolTipText(resources.getString("lblPayoutServiceBonusRate.toolTipText")); - spnPayoutServiceBonusRate.setName("spnPayoutServiceBonusRate"); - spnPayoutServiceBonusRate.setEnabled((isUseTurnover) && (isUseServiceBonus)); - - payoutServiceBonusSubPanel.setBorder(BorderFactory.createTitledBorder("")); - payoutServiceBonusSubPanel.setName("payoutServiceBonusSubPanel"); - payoutServiceBonusSubPanel.setEnabled((isUseTurnover) && (isUseServiceBonus)); - - final GroupLayout layout = new GroupLayout(payoutServiceBonusSubPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - payoutServiceBonusSubPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPayoutServiceBonusRate) - .addComponent(spnPayoutServiceBonusRate, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPayoutServiceBonusRate) - .addComponent(spnPayoutServiceBonusRate))); - } - - private JPanel createTurnoverAndRetentionUnitCohesionPanel() { - boolean isUseTurnover = campaign.getCampaignOptions().isUseRandomRetirement(); - - chkUseAdministrativeStrain = new JCheckBox(resources.getString("chkUseAdministrativeStrain.text")); - chkUseAdministrativeStrain - .setToolTipText(resources.getString("chkUseAdministrativeStrain.toolTipText")); - chkUseAdministrativeStrain.setName("chkUseAdministrativeStrain"); - chkUseAdministrativeStrain.setEnabled(isUseTurnover); - chkUseAdministrativeStrain.addActionListener(evt -> { - final boolean isEnabled = chkUseAdministrativeStrain.isSelected(); - - for (Component component : administrativeStrainSubPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - administrativeStrainSubPanel.setEnabled(isEnabled); - }); - - createAdministrativeStrainSubPanel(isUseTurnover); - - chkUseManagementSkill = new JCheckBox(resources.getString("chkUseManagementSkill.text")); - chkUseManagementSkill.setToolTipText(resources.getString("chkUseManagementSkill.toolTipText")); - chkUseManagementSkill.setName("chkUseManagementSkill"); - chkUseManagementSkill.setEnabled(isUseTurnover); - chkUseManagementSkill.addActionListener(evt -> { - final boolean isEnabled = chkUseManagementSkill.isSelected(); - - for (Component component : managementSkillSubPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - managementSkillSubPanel.setEnabled(isEnabled); - }); - - createManagementSkillSubPanel(isUseTurnover); - - turnoverAndRetentionUnitCohesionPanel.setBorder( - BorderFactory.createTitledBorder( - resources.getString("turnoverAndRetentionUnitCohesionPanel.title"))); - turnoverAndRetentionUnitCohesionPanel.setName("turnoverAndRetentionUnitCohesionPanel"); - turnoverAndRetentionUnitCohesionPanel.setEnabled(isUseTurnover); - - final GroupLayout layout = new GroupLayout(turnoverAndRetentionUnitCohesionPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - turnoverAndRetentionUnitCohesionPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseAdministrativeStrain) - .addComponent(administrativeStrainSubPanel) - .addGap(15) - .addComponent(chkUseManagementSkill) - .addComponent(managementSkillSubPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseAdministrativeStrain) - .addComponent(administrativeStrainSubPanel) - .addComponent(chkUseManagementSkill) - .addComponent(managementSkillSubPanel)); - - return turnoverAndRetentionUnitCohesionPanel; - } - - private void createAdministrativeStrainSubPanel(boolean isUseTurnover) { - boolean isUseAdministrativeStrain = campaign.getCampaignOptions().isUseAdministrativeStrain(); - - lblAdministrativeCapacity = new JLabel(resources.getString("lblAdministrativeCapacity.text")); - lblAdministrativeCapacity.setToolTipText(resources.getString("lblAdministrativeCapacity.toolTipText")); - lblAdministrativeCapacity.setName("lblAdministrativeCapacity"); - lblAdministrativeCapacity.setEnabled((isUseTurnover) && (isUseAdministrativeStrain)); - - spnAdministrativeCapacity = new JSpinner(new SpinnerNumberModel(10, 1, 30, 1)); - spnAdministrativeCapacity.setToolTipText(resources.getString("lblAdministrativeCapacity.toolTipText")); - spnAdministrativeCapacity.setName("spnAdministrativeCapacity"); - spnAdministrativeCapacity.setEnabled((isUseTurnover) && (isUseAdministrativeStrain)); - - lblMultiCrewStrainDivider = new JLabel(resources.getString("lblMultiCrewStrainDivider.text")); - lblMultiCrewStrainDivider.setToolTipText(resources.getString("lblMultiCrewStrainDivider.toolTipText")); - lblMultiCrewStrainDivider.setName("lblMultiCrewStrainDivider"); - lblMultiCrewStrainDivider.setEnabled((isUseTurnover) && (isUseAdministrativeStrain)); - - spnMultiCrewStrainDivider = new JSpinner(new SpinnerNumberModel(5, 1, 25, 1)); - spnMultiCrewStrainDivider.setToolTipText(resources.getString("lblMultiCrewStrainDivider.toolTipText")); - spnMultiCrewStrainDivider.setName("spnMultiCrewStrainDivider"); - spnMultiCrewStrainDivider.setEnabled((isUseTurnover) && (isUseAdministrativeStrain)); - - administrativeStrainSubPanel.setBorder(BorderFactory.createTitledBorder("")); - administrativeStrainSubPanel.setName("administrativeStrainSubPanel"); - administrativeStrainSubPanel.setEnabled((isUseTurnover) && (isUseAdministrativeStrain)); - - final GroupLayout layout = new GroupLayout(administrativeStrainSubPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - administrativeStrainSubPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblAdministrativeCapacity) - .addComponent(spnAdministrativeCapacity, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblMultiCrewStrainDivider) - .addComponent(spnMultiCrewStrainDivider, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblAdministrativeCapacity) - .addComponent(spnAdministrativeCapacity)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblMultiCrewStrainDivider) - .addComponent(spnMultiCrewStrainDivider))); - } - - private void createManagementSkillSubPanel(boolean isUseTurnover) { - boolean isUseManagementSkill = campaign.getCampaignOptions().isUseManagementSkill(); - - chkUseCommanderLeadershipOnly = new JCheckBox( - resources.getString("chkUseCommanderLeadershipOnly.text")); - chkUseCommanderLeadershipOnly - .setToolTipText(resources.getString("chkUseCommanderLeadershipOnly.toolTipText")); - chkUseCommanderLeadershipOnly.setName("chkUseCommanderLeadershipOnly"); - chkUseCommanderLeadershipOnly.setEnabled((isUseTurnover) && (isUseManagementSkill)); - - lblManagementSkillPenalty = new JLabel(resources.getString("lblManagementSkillPenalty.text")); - lblManagementSkillPenalty.setToolTipText(resources.getString("lblManagementSkillPenalty.toolTipText")); - lblManagementSkillPenalty.setName("lblManagementSkillPenalty"); - lblManagementSkillPenalty.setEnabled((isUseTurnover) && (isUseManagementSkill)); - - spnManagementSkillPenalty = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - spnManagementSkillPenalty.setToolTipText(resources.getString("lblManagementSkillPenalty.toolTipText")); - spnManagementSkillPenalty.setName("spnManagementSkillPenalty"); - spnManagementSkillPenalty.setEnabled((isUseTurnover) && (isUseManagementSkill)); - - managementSkillSubPanel.setBorder(BorderFactory.createTitledBorder("")); - managementSkillSubPanel.setName("managementSkillSubPanel"); - managementSkillSubPanel.setEnabled((isUseTurnover) && (isUseManagementSkill)); - - final GroupLayout layout = new GroupLayout(managementSkillSubPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - managementSkillSubPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseCommanderLeadershipOnly) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblManagementSkillPenalty) - .addComponent(spnManagementSkillPenalty, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseCommanderLeadershipOnly) - .addGroup(layout.createSequentialGroup() - .addComponent(lblManagementSkillPenalty) - .addComponent(spnManagementSkillPenalty))); - } - - private JPanel createPersonnelRandomizationPanel() { - // Create Panel Components - chkUseDylansRandomXP = new JCheckBox(resources.getString("chkUseDylansRandomXP.text")); - chkUseDylansRandomXP.setToolTipText(resources.getString("chkUseDylansRandomXP.toolTipText")); - chkUseDylansRandomXP.setName("chkUseDylansRandomXP"); - - 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 - .createTitledBorder(resources.getString("personnelRandomizationPanel.title"))); - panel.setName("personnelRandomizationPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseDylansRandomXP) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblNonBinaryDiceSize) - .addComponent(spnNonBinaryDiceSize, Alignment.LEADING)) - ); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseDylansRandomXP) - .addGroup(layout.createSequentialGroup() - .addComponent(lblNonBinaryDiceSize) - .addComponent(spnNonBinaryDiceSize)) - ); - - return panel; - } - - private JPanel createRandomHistoriesPanel() { - randomOriginOptionsPanel = new RandomOriginOptionsPanel(getFrame(), campaign, comboFaction); - - chkUseRandomPersonalities = new JCheckBox(resources.getString("chkUseRandomPersonalities.text")); - chkUseRandomPersonalities.setToolTipText(resources.getString("chkUseRandomPersonalities.toolTipText")); - chkUseRandomPersonalities.setName("chkUseRandomPersonalities"); - - chkUseRandomPersonalityReputation = new JCheckBox( - resources.getString("chkUseRandomPersonalityReputation.text")); - chkUseRandomPersonalityReputation - .setToolTipText(resources.getString("chkUseRandomPersonalityReputation.toolTipText")); - chkUseRandomPersonalityReputation.setName("chkUseRandomPersonalityReputation"); - - chkUseIntelligenceXpMultiplier = new JCheckBox( - resources.getString("chkUseIntelligenceXpMultiplier.text")); - chkUseIntelligenceXpMultiplier - .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"))); - panel.setName("randomHistoriesPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(randomOriginOptionsPanel) - .addComponent(chkUseRandomPersonalities) - .addComponent(chkUseRandomPersonalityReputation) - .addComponent(chkUseIntelligenceXpMultiplier) - .addComponent(chkUseSimulatedRelationships) - ); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(randomOriginOptionsPanel) - .addComponent(chkUseRandomPersonalities) - .addComponent(chkUseRandomPersonalityReputation) - .addComponent(chkUseIntelligenceXpMultiplier) - .addComponent(chkUseSimulatedRelationships) - ); - - return panel; - } - - private JPanel createFamilyPanel() { - // Create Panel Components - final JLabel lblFamilyDisplayLevel = new JLabel(resources.getString("lblFamilyDisplayLevel.text")); - lblFamilyDisplayLevel.setToolTipText(resources.getString("lblFamilyDisplayLevel.toolTipText")); - lblFamilyDisplayLevel.setName("lblFamilyDisplayLevel"); - - comboFamilyDisplayLevel = new MMComboBox<>("comboFamilyDisplayLevel", - FamilialRelationshipDisplayLevel.values()); - comboFamilyDisplayLevel.setToolTipText(resources.getString("lblFamilyDisplayLevel.toolTipText")); - comboFamilyDisplayLevel.setName("comboFamilyDisplayLevel"); - - // Programmatically Assign Accessibility Labels - lblFamilyDisplayLevel.setLabelFor(comboFamilyDisplayLevel); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("familyPanel.title"))); - panel.setName("familyPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblFamilyDisplayLevel) - .addComponent(comboFamilyDisplayLevel, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblFamilyDisplayLevel) - .addComponent(comboFamilyDisplayLevel))); - - return panel; - } - - private JPanel createAnniversaryPanel() { - chkAnnounceBirthdays = new JCheckBox(resources.getString("chkAnnounceBirthdays.text")); - chkAnnounceBirthdays.setToolTipText(resources.getString("chkAnnounceBirthdays.toolTipText")); - chkAnnounceBirthdays.setName("chkAnnounceBirthdays"); - chkAnnounceBirthdays.addActionListener(evt -> { - final boolean isEnabled = chkAnnounceBirthdays.isSelected(); - - chkAnnounceChildBirthdays.setEnabled(isEnabled); - }); - - chkAnnounceRecruitmentAnniversaries = new JCheckBox(resources.getString("chkAnnounceRecruitmentAnniversaries.text")); - chkAnnounceRecruitmentAnniversaries.setToolTipText(resources.getString("chkAnnounceRecruitmentAnniversaries.toolTipText")); - chkAnnounceRecruitmentAnniversaries.setName("chkAnnounceRecruitmentAnniversaries"); - - chkAnnounceOfficersOnly = new JCheckBox(resources.getString("chkAnnounceOfficersOnly.text")); - chkAnnounceOfficersOnly.setToolTipText(resources.getString("chkAnnounceOfficersOnly.toolTipText")); - chkAnnounceOfficersOnly.setName("chkAnnounceOfficersOnly"); - chkAnnounceOfficersOnly.setEnabled(campaign.getCampaignOptions().isAnnounceBirthdays()); - - chkAnnounceChildBirthdays = new JCheckBox(resources.getString("chkAnnounceChildBirthdays.text")); - chkAnnounceChildBirthdays.setToolTipText(resources.getString("chkAnnounceChildBirthdays.toolTipText")); - chkAnnounceChildBirthdays.setName("chkAnnounceChildBirthdays"); - chkAnnounceChildBirthdays.setEnabled(campaign.getCampaignOptions().isAnnounceBirthdays()); - - anniversaryPanel.setBorder( - BorderFactory.createTitledBorder(resources.getString("anniversaryPanel.title"))); - anniversaryPanel.setName("anniversaryPanel"); - - final GroupLayout layout = new GroupLayout(anniversaryPanel); - anniversaryPanel.setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkAnnounceBirthdays) - .addComponent(chkAnnounceRecruitmentAnniversaries) - .addComponent(chkAnnounceOfficersOnly) - .addComponent(chkAnnounceChildBirthdays)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkAnnounceBirthdays) - .addComponent(chkAnnounceRecruitmentAnniversaries) - .addComponent(chkAnnounceOfficersOnly) - .addComponent(chkAnnounceChildBirthdays)); - - return anniversaryPanel; - } - - private JPanel createDependentPanel() { - // Create Panel Components - chkUseRandomDependentAddition = new JCheckBox(resources.getString("chkUseRandomDependentAddition.text")); - chkUseRandomDependentAddition.setToolTipText(resources.getString("chkUseRandomDependentAddition.toolTipText")); - chkUseRandomDependentAddition.setName("chkUseRandomDependentAddition"); - - chkUseRandomDependentRemoval = new JCheckBox(resources.getString("chkUseRandomDependentRemoval.text")); - chkUseRandomDependentRemoval.setToolTipText(resources.getString("chkUseRandomDependentRemoval.toolTipText")); - chkUseRandomDependentRemoval.setName("chkUseRandomDependentRemoval"); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("dependentPanel.title"))); - panel.setToolTipText(resources.getString("dependentPanel.toolTipText")); - panel.setName("dependentPanel"); - - final GroupLayout layout = new GroupLayout(panel); - panel.setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseRandomDependentAddition) - .addComponent(chkUseRandomDependentRemoval) - ); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseRandomDependentAddition) - .addComponent(chkUseRandomDependentRemoval) - ); - - return panel; - } - - private JPanel createPersonnelRemovalPanel() { - // Create Panel Components - chkUsePersonnelRemoval = new JCheckBox(resources.getString("chkUsePersonnelRemoval.text")); - chkUsePersonnelRemoval.setToolTipText(resources.getString("chkUsePersonnelRemoval.toolTipText")); - chkUsePersonnelRemoval.setName("chkUsePersonnelRemoval"); - chkUsePersonnelRemoval.addActionListener(evt -> { - final boolean isEnabled = chkUsePersonnelRemoval.isSelected(); - - for (Component component : personnelRemovalSubPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - personnelRemovalSubPanel.setEnabled(isEnabled); - }); - - personnelRemovalSubPanel = createPersonnelRemovalSubPanel(); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("personnelRemovalPanel.title"))); - panel.setToolTipText(resources.getString("personnelRemovalPanel.toolTipText")); - panel.setName("personnelRemovalPanel"); - - final GroupLayout layout = new GroupLayout(panel); - panel.setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUsePersonnelRemoval) - .addComponent(personnelRemovalSubPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUsePersonnelRemoval) - .addComponent(personnelRemovalSubPanel)); - - return panel; - } - - private JPanel createPersonnelRemovalSubPanel() { - boolean isEnabled = campaign.getCampaignOptions().isUsePersonnelRemoval(); - - // Create Panel Components - chkUseRemovalExemptCemetery = new JCheckBox(resources.getString("chkUseRemovalExemptCemetery.text")); - chkUseRemovalExemptCemetery - .setToolTipText(resources.getString("chkUseRemovalExemptCemetery.toolTipText")); - chkUseRemovalExemptCemetery.setName("chkUseRemovalExemptCemetery"); - chkUseRemovalExemptCemetery.setEnabled(isEnabled); - - chkUseRemovalExemptRetirees = new JCheckBox(resources.getString("chkUseRemovalExemptRetirees.text")); - chkUseRemovalExemptRetirees - .setToolTipText(resources.getString("chkUseRemovalExemptRetirees.toolTipText")); - chkUseRemovalExemptRetirees.setName("chkUseRemovalExemptRetirees"); - chkUseRemovalExemptRetirees.setEnabled(isEnabled); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder("")); - panel.setToolTipText(resources.getString("personnelRemovalPanel.toolTipText")); - panel.setName("personnelRemovalPanel"); - - final GroupLayout layout = new GroupLayout(panel); - panel.setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseRemovalExemptCemetery) - .addComponent(chkUseRemovalExemptRetirees)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseRemovalExemptCemetery) - .addComponent(chkUseRemovalExemptRetirees)); - - return panel; - } - - private JPanel createTurnoverAndRetentionFatiguePanel() { - chkUseFatigue = new JCheckBox(resources.getString("chkUseFatigue.text")); - chkUseFatigue.setToolTipText(resources.getString("chkUseFatigue.toolTipText")); - chkUseFatigue.setName("chkUseFatigue"); - chkUseFatigue.addActionListener(evt -> { - final boolean isEnabled = chkUseFatigue.isSelected(); - - for (Component component : fatigueSubPanel.getComponents()) { - component.setEnabled(isEnabled); - } - - fatigueSubPanel.setEnabled(isEnabled); - - chkUseFatigueModifiers.setEnabled(isEnabled); - }); - - createFatigueSubPanel(); - - turnoverAndRetentionFatiguePanel.setBorder( - BorderFactory.createTitledBorder( - resources.getString("turnoverAndRetentionFatiguePanel.title"))); - turnoverAndRetentionFatiguePanel.setName("turnoverAndRetentionFatiguePanel"); - - final GroupLayout layout = new GroupLayout(turnoverAndRetentionFatiguePanel); - turnoverAndRetentionFatiguePanel.setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseFatigue) - .addComponent(fatigueSubPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseFatigue) - .addComponent(fatigueSubPanel)); - - return turnoverAndRetentionFatiguePanel; - } - - private JPanel createSalaryPanel() { - // Create Panel Components - chkDisableSecondaryRoleSalary = new JCheckBox( - resources.getString("chkDisableSecondaryRoleSalary.text")); - chkDisableSecondaryRoleSalary - .setToolTipText(resources.getString("chkDisableSecondaryRoleSalary.toolTipText")); - chkDisableSecondaryRoleSalary.setName("chkDisableSecondaryRoleSalary"); - - final JPanel salaryMultiplierPanel = createSalaryMultiplierPanel(); - - final JPanel salaryExperienceModifierPanel = createSalaryExperienceMultiplierPanel(); - - final JPanel baseSalaryPanel = createBaseSalaryPanel(); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("salaryPanel.title"))); - panel.setName("salaryPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkDisableSecondaryRoleSalary) - .addComponent(salaryMultiplierPanel) - .addComponent(salaryExperienceModifierPanel) - .addComponent(baseSalaryPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkDisableSecondaryRoleSalary) - .addComponent(salaryMultiplierPanel) - .addComponent(salaryExperienceModifierPanel) - .addComponent(baseSalaryPanel)); - - return panel; - } - - private JPanel createSalaryMultiplierPanel() { - // Create Panel Components - final JLabel lblAntiMekSalary = new JLabel(resources.getString("lblAntiMekSalary.text")); - lblAntiMekSalary.setToolTipText(resources.getString("lblAntiMekSalary.toolTipText")); - lblAntiMekSalary.setName("lblAntiMekSalary"); - - spnAntiMekSalary = new JSpinner(new SpinnerNumberModel(0, 0, 100, 0.05)); - spnAntiMekSalary.setToolTipText(resources.getString("lblAntiMekSalary.toolTipText")); - spnAntiMekSalary.setName("spnAntiMekSalary"); - - final JLabel lblSpecialistInfantrySalary = new JLabel( - resources.getString("lblSpecialistInfantrySalary.text")); - lblSpecialistInfantrySalary - .setToolTipText(resources.getString("lblSpecialistInfantrySalary.toolTipText")); - lblSpecialistInfantrySalary.setName("lblSpecialistInfantrySalary"); - - spnSpecialistInfantrySalary = new JSpinner(new SpinnerNumberModel(0, 0, 100, 0.05)); - spnSpecialistInfantrySalary - .setToolTipText(resources.getString("lblSpecialistInfantrySalary.toolTipText")); - spnSpecialistInfantrySalary.setName("spnSpecialistInfantrySalary"); - - // Programmatically Assign Accessibility Labels\ - lblAntiMekSalary.setLabelFor(spnAntiMekSalary); - lblSpecialistInfantrySalary.setLabelFor(spnSpecialistInfantrySalary); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("salaryMultiplierPanel.title"))); - panel.setToolTipText(resources.getString("salaryMultiplierPanel.toolTipText")); - panel.setName("salaryMultiplierPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblAntiMekSalary) - .addComponent(spnAntiMekSalary) - .addComponent(lblSpecialistInfantrySalary) - .addComponent(spnSpecialistInfantrySalary, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblAntiMekSalary) - .addComponent(spnAntiMekSalary) - .addComponent(lblSpecialistInfantrySalary) - .addComponent(spnSpecialistInfantrySalary))); - - return panel; - } - - private JPanel createSalaryExperienceMultiplierPanel() { - final JPanel panel = new JPanel(new GridLayout(2, 8)); - panel.setBorder(BorderFactory - .createTitledBorder(resources.getString("salaryExperienceMultiplierPanel.title"))); - panel.setToolTipText(resources.getString("salaryExperienceMultiplierPanel.toolTipText")); - panel.setName("salaryExperienceMultiplierPanel"); - - spnSalaryExperienceMultipliers = new HashMap<>(); - for (final SkillLevel skillLevel : Skills.SKILL_LEVELS) { - final String toolTipText = String.format( - resources.getString("lblSalaryExperienceMultiplier.toolTipText"), - skillLevel); - - final JLabel label = new JLabel(skillLevel.toString()); - label.setToolTipText(toolTipText); - label.setName("lbl" + skillLevel); - panel.add(label); - - spnSalaryExperienceMultipliers.put(skillLevel, - new JSpinner(new SpinnerNumberModel(0, 0, 10, 0.05))); - spnSalaryExperienceMultipliers.get(skillLevel).setToolTipText(toolTipText); - spnSalaryExperienceMultipliers.get(skillLevel).setName("spn" + skillLevel); - panel.add(spnSalaryExperienceMultipliers.get(skillLevel)); - - label.setLabelFor(spnSalaryExperienceMultipliers.get(skillLevel)); - } - - return panel; - } - - private JPanel createBaseSalaryPanel() { - final PersonnelRole[] personnelRoles = PersonnelRole.values(); - final JPanel panel = new JPanel( - new GridLayout((int) Math.ceil((double) (personnelRoles.length - 1) / 3.0), 6)); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("baseSalaryPanel.title"))); - panel.setPreferredSize(new Dimension(200, 200)); - - spnBaseSalary = new JSpinner[personnelRoles.length]; - for (final PersonnelRole personnelRole : personnelRoles) { - // Create Reused Values - final String toolTipText = String.format(resources.getString("lblBaseSalary.toolTipText"), - personnelRole.toString()); - - // Create Panel Components - final JLabel label = new JLabel(personnelRole.toString()); - label.setToolTipText(toolTipText); - label.setName("lbl" + personnelRole.name()); - panel.add(label); - - final JSpinner salarySpinner = new JSpinner(new SpinnerNumberModel(0.0, 0.0, null, 10.0)); - salarySpinner.setToolTipText(toolTipText); - salarySpinner.setName("spn" + personnelRole.name()); - panel.add(salarySpinner); - - // Programmatically Assign Accessibility Labels - label.setLabelFor(salarySpinner); - - // Component Tracking Assignment - spnBaseSalary[personnelRole.ordinal()] = salarySpinner; - } - - return panel; - } - - private JPanel createMarriagePanel() { - // Create Panel Components - chkUseManualMarriages = new JCheckBox(resources.getString("chkUseManualMarriages.text")); - chkUseManualMarriages.setToolTipText(resources.getString("chkUseManualMarriages.toolTipText")); - chkUseManualMarriages.setName("chkUseManualMarriages"); - - chkUseClanPersonnelMarriages = new JCheckBox(resources.getString("chkUseClanPersonnelMarriages.text")); - chkUseClanPersonnelMarriages - .setToolTipText(resources.getString("chkUseClanPersonnelMarriages.toolTipText")); - chkUseClanPersonnelMarriages.setName("chkUseClanPersonnelMarriages"); - chkUseClanPersonnelMarriages.addActionListener(evt -> { - final RandomMarriageMethod method = comboRandomMarriageMethod.getSelectedItem(); - if (method == null) { - return; - } - chkUseRandomClanPersonnelMarriages - .setEnabled(!method.isNone() && chkUseClanPersonnelMarriages.isSelected()); - }); - - chkUsePrisonerMarriages = new JCheckBox(resources.getString("chkUsePrisonerMarriages.text")); - chkUsePrisonerMarriages.setToolTipText(resources.getString("chkUsePrisonerMarriages.toolTipText")); - chkUsePrisonerMarriages.setName("chkUsePrisonerMarriages"); - chkUsePrisonerMarriages.addActionListener(evt -> { - final RandomMarriageMethod method = comboRandomMarriageMethod.getSelectedItem(); - if (method == null) { - return; - } - chkUseRandomPrisonerMarriages - .setEnabled(!method.isNone() && chkUsePrisonerMarriages.isSelected()); - }); - - final JLabel lblNoInterestInMarriageDiceSize = new JLabel(resources.getString("lblNoInterestInMarriageDiceSize.text")); - lblNoInterestInMarriageDiceSize.setToolTipText(wordWrap(resources.getString("lblNoInterestInMarriageDiceSize.toolTipText"))); - lblNoInterestInMarriageDiceSize.setName("lblNoInterestInMarriageDiceSize"); - - 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")); - lblCheckMutualAncestorsDepth - .setToolTipText(resources.getString("lblCheckMutualAncestorsDepth.toolTipText")); - lblCheckMutualAncestorsDepth.setName("lblCheckMutualAncestorsDepth"); - - spnCheckMutualAncestorsDepth = new JSpinner(new SpinnerNumberModel(4, 0, 20, 1)); - spnCheckMutualAncestorsDepth - .setToolTipText(resources.getString("lblCheckMutualAncestorsDepth.toolTipText")); - spnCheckMutualAncestorsDepth.setName("spnCheckMutualAncestorsDepth"); - - chkLogMarriageNameChanges = new JCheckBox(resources.getString("chkLogMarriageNameChanges.text")); - chkLogMarriageNameChanges.setToolTipText(resources.getString("chkLogMarriageNameChanges.toolTipText")); - chkLogMarriageNameChanges.setName("chkLogMarriageNameChanges"); - - final JPanel marriageSurnameWeightsPanel = createMarriageSurnameWeightsPanel(); - - final JPanel randomMarriagePanel = createRandomMarriagePanel(); - - // Programmatically Assign Accessibility Labels - lblCheckMutualAncestorsDepth.setLabelFor(spnCheckMutualAncestorsDepth); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("marriagePanel.title"))); - panel.setName("marriagePanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseManualMarriages) - .addComponent(chkUseClanPersonnelMarriages) - .addComponent(chkUsePrisonerMarriages) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblNoInterestInMarriageDiceSize) - .addComponent(spnNoInterestInMarriageDiceSize, Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblCheckMutualAncestorsDepth) - .addComponent(spnCheckMutualAncestorsDepth, - Alignment.LEADING)) - .addComponent(chkLogMarriageNameChanges) - .addComponent(marriageSurnameWeightsPanel) - .addComponent(randomMarriagePanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseManualMarriages) - .addComponent(chkUseClanPersonnelMarriages) - .addComponent(chkUsePrisonerMarriages) - .addGroup(layout.createSequentialGroup() - .addComponent(lblNoInterestInMarriageDiceSize) - .addComponent(spnNoInterestInMarriageDiceSize)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblCheckMutualAncestorsDepth) - .addComponent(spnCheckMutualAncestorsDepth)) - .addComponent(chkLogMarriageNameChanges) - .addComponent(marriageSurnameWeightsPanel) - .addComponent(randomMarriagePanel)); - - return panel; - } - - private JPanel createMarriageSurnameWeightsPanel() { - final JPanel panel = new JPanel(new GridLayout(0, 6)); - panel.setBorder(BorderFactory - .createTitledBorder(resources.getString("marriageSurnameWeightsPanel.title"))); - panel.setToolTipText(resources.getString("marriageSurnameWeightsPanel.toolTipText")); - panel.setName("marriageSurnameWeightsPanel"); - - spnMarriageSurnameWeights = new HashMap<>(); - for (final MergingSurnameStyle style : MergingSurnameStyle.values()) { - if (style.isWeighted()) { - continue; - } - final JLabel label = new JLabel(style.toString()); - label.setToolTipText(style.getToolTipText()); - label.setName("lbl" + style); - panel.add(label); - - final JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, 100, 0.1)); - spinner.setToolTipText(style.getToolTipText()); - spinner.setName("spn" + style); - spnMarriageSurnameWeights.put(style, spinner); - panel.add(spinner); - - label.setLabelFor(spinner); - } - - return panel; - } - - private JPanel createRandomMarriagePanel() { - // Initialize Components Used in ActionListeners - final JLabel lblRandomMarriageAgeRange = new JLabel(); - final JPanel percentageRandomMarriagePanel = new JDisableablePanel("percentageRandomMarriagePanel"); - - // Create Panel Components - final JLabel lblRandomMarriageMethod = new JLabel(resources.getString("lblRandomMarriageMethod.text")); - lblRandomMarriageMethod.setToolTipText(resources.getString("lblRandomMarriageMethod.toolTipText")); - lblRandomMarriageMethod.setName("lblRandomMarriageMethod"); - - comboRandomMarriageMethod = new MMComboBox<>("comboRandomMarriageMethod", - RandomMarriageMethod.values()); - comboRandomMarriageMethod.setToolTipText(resources.getString("lblRandomMarriageMethod.toolTipText")); - comboRandomMarriageMethod.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof RandomMarriageMethod) { - list.setToolTipText(((RandomMarriageMethod) value).getToolTipText()); - } - return this; - } - }); - comboRandomMarriageMethod.addActionListener(evt -> { - final RandomMarriageMethod method = comboRandomMarriageMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean enabled = !method.isNone(); - final boolean percentageEnabled = method.isDiceRoll(); - chkUseRandomClanPersonnelMarriages.setEnabled(enabled && chkUseClanPersonnelMarriages.isSelected()); - chkUseRandomPrisonerMarriages.setEnabled(enabled && chkUsePrisonerMarriages.isSelected()); - lblRandomMarriageAgeRange.setEnabled(enabled); - spnRandomMarriageAgeRange.setEnabled(enabled); - percentageRandomMarriagePanel.setEnabled(percentageEnabled); - }); - - chkUseRandomClanPersonnelMarriages = new JCheckBox( - resources.getString("chkUseRandomClanPersonnelMarriages.text")); - chkUseRandomClanPersonnelMarriages - .setToolTipText(resources.getString("chkUseRandomClanPersonnelMarriages.toolTipText")); - chkUseRandomClanPersonnelMarriages.setName("chkUseRandomClanPersonnelMarriages"); - - chkUseRandomPrisonerMarriages = new JCheckBox( - resources.getString("chkUseRandomPrisonerMarriages.text")); - chkUseRandomPrisonerMarriages - .setToolTipText(resources.getString("chkUseRandomPrisonerMarriages.toolTipText")); - chkUseRandomPrisonerMarriages.setName("chkUseRandomPrisonerMarriages"); - - lblRandomMarriageAgeRange.setText(resources.getString("lblRandomMarriageAgeRange.text")); - lblRandomMarriageAgeRange.setToolTipText(resources.getString("lblRandomMarriageAgeRange.toolTipText")); - lblRandomMarriageAgeRange.setName("lblRandomMarriageAgeRange"); - - spnRandomMarriageAgeRange = new JSpinner(new SpinnerNumberModel(10, 0, null, 1.0)); - spnRandomMarriageAgeRange.setToolTipText(resources.getString("lblRandomMarriageAgeRange.toolTipText")); - spnRandomMarriageAgeRange.setName("spnRandomMarriageAgeRange"); - - createPercentageRandomMarriagePanel(percentageRandomMarriagePanel); - - // Programmatically Assign Accessibility Labels - lblRandomMarriageMethod.setLabelFor(comboRandomMarriageMethod); - lblRandomMarriageAgeRange.setLabelFor(spnRandomMarriageAgeRange); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("randomMarriagePanel.title"))); - panel.setName("randomMarriagePanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomMarriageMethod) - .addComponent(comboRandomMarriageMethod, Alignment.LEADING)) - .addComponent(chkUseRandomClanPersonnelMarriages) - .addComponent(chkUseRandomPrisonerMarriages) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomMarriageAgeRange) - .addComponent(spnRandomMarriageAgeRange, - Alignment.LEADING)) - .addComponent(percentageRandomMarriagePanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomMarriageMethod) - .addComponent(comboRandomMarriageMethod)) - .addComponent(chkUseRandomClanPersonnelMarriages) - .addComponent(chkUseRandomPrisonerMarriages) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomMarriageAgeRange) - .addComponent(spnRandomMarriageAgeRange)) - .addComponent(percentageRandomMarriagePanel)); - - return panel; - } - - private void createPercentageRandomMarriagePanel(final JPanel panel) { - // Create Panel Components - final JLabel lblRandomMarriageOppositeSexDiceSize = new JLabel(resources.getString("lblRandomMarriageOppositeSexDiceSize.text")); - lblRandomMarriageOppositeSexDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomMarriageOppositeSexDiceSize.toolTipText"))); - lblRandomMarriageOppositeSexDiceSize.setName("lblRandomMarriageOppositeSexDiceSize"); - - spnRandomMarriageDiceSize = new JSpinner(new SpinnerNumberModel(5000, 0, 100000, 1)); - spnRandomMarriageDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomMarriageOppositeSexDiceSize.toolTipText"))); - spnRandomMarriageDiceSize.setName("spnPercentageRandomMarriageOppositeSexChance"); - - final JLabel lblRandomSameSexMarriageDiceSize = new JLabel(resources.getString("lblRandomSameSexMarriageDiceSize.text")); - lblRandomSameSexMarriageDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomSameSexMarriageDiceSize.toolTipText"))); - lblRandomSameSexMarriageDiceSize.setName("lblRandomSameSexMarriageDiceSize"); - - spnRandomSameSexMarriageDiceSize = new JSpinner(new SpinnerNumberModel(14, 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(20, 0, 100000, 1)); - spnRandomNewDependentMarriage.setToolTipText(wordWrap(resources.getString("lblRandomNewDependentMarriage.toolTipText"))); - spnRandomNewDependentMarriage.setName("spnRandomNewDependentMarriage"); - - // Programmatically Assign Accessibility Labels - lblRandomMarriageOppositeSexDiceSize.setLabelFor(spnRandomMarriageDiceSize); - - // Layout the Panel - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("percentageRandomMarriagePanel.title"))); - panel.setToolTipText(RandomMarriageMethod.DICE_ROLL.getToolTipText()); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomMarriageOppositeSexDiceSize) - .addComponent(spnRandomMarriageDiceSize, Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomSameSexMarriageDiceSize) - .addComponent(spnRandomSameSexMarriageDiceSize, Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomNewDependentMarriage) - .addComponent(spnRandomNewDependentMarriage, Alignment.LEADING)) - ); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomMarriageOppositeSexDiceSize) - .addComponent(spnRandomMarriageDiceSize)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomSameSexMarriageDiceSize) - .addComponent(spnRandomSameSexMarriageDiceSize)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomNewDependentMarriage) - .addComponent(spnRandomNewDependentMarriage)) - ); - } - - private JPanel createDivorcePanel() { - // Create Panel Components - chkUseManualDivorce = new JCheckBox(resources.getString("chkUseManualDivorce.text")); - chkUseManualDivorce.setToolTipText(resources.getString("chkUseManualDivorce.toolTipText")); - chkUseManualDivorce.setName("chkUseManualDivorce"); - - chkUseClanPersonnelDivorce = new JCheckBox(resources.getString("chkUseClanPersonnelDivorce.text")); - chkUseClanPersonnelDivorce - .setToolTipText(resources.getString("chkUseClanPersonnelDivorce.toolTipText")); - chkUseClanPersonnelDivorce.setName("chkUseClanPersonnelDivorce"); - chkUseClanPersonnelDivorce.addActionListener(evt -> { - final RandomDivorceMethod method = comboRandomDivorceMethod.getSelectedItem(); - if (method == null) { - return; - } - chkUseRandomClanPersonnelDivorce - .setEnabled(!method.isNone() && chkUseClanPersonnelDivorce.isSelected()); - }); - - chkUsePrisonerDivorce = new JCheckBox(resources.getString("chkUsePrisonerDivorce.text")); - chkUsePrisonerDivorce.setToolTipText(resources.getString("chkUsePrisonerDivorce.toolTipText")); - chkUsePrisonerDivorce.setName("chkUsePrisonerDivorce"); - chkUsePrisonerDivorce.addActionListener(evt -> { - final RandomDivorceMethod method = comboRandomDivorceMethod.getSelectedItem(); - if (method == null) { - return; - } - chkUseRandomPrisonerDivorce.setEnabled(!method.isNone() && chkUsePrisonerDivorce.isSelected()); - }); - - final JPanel divorceSurnameWeightsPanel = createDivorceSurnameWeightsPanel(); - - final JPanel randomDivorcePanel = createRandomDivorcePanel(); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("divorcePanel.title"))); - panel.setName("divorcePanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseManualDivorce) - .addComponent(chkUseClanPersonnelDivorce) - .addComponent(chkUsePrisonerDivorce) - .addComponent(divorceSurnameWeightsPanel) - .addComponent(randomDivorcePanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseManualDivorce) - .addComponent(chkUseClanPersonnelDivorce) - .addComponent(chkUsePrisonerDivorce) - .addComponent(divorceSurnameWeightsPanel) - .addComponent(randomDivorcePanel)); - - return panel; - } - - private JPanel createDivorceSurnameWeightsPanel() { - final JPanel panel = new JPanel(new GridLayout(0, 4)); - panel.setBorder(BorderFactory - .createTitledBorder(resources.getString("divorceSurnameWeightsPanel.title"))); - panel.setToolTipText(resources.getString("divorceSurnameWeightsPanel.toolTipText")); - panel.setName("divorceSurnameWeightsPanel"); - - spnDivorceSurnameWeights = new HashMap<>(); - for (final SplittingSurnameStyle style : SplittingSurnameStyle.values()) { - if (style.isWeighted()) { - continue; - } - final JLabel label = new JLabel(style.toString()); - label.setToolTipText(style.getToolTipText()); - label.setName("lbl" + style); - panel.add(label); - - final JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, 100, 0.1)); - spinner.setToolTipText(style.getToolTipText()); - spinner.setName("spn" + style); - spnDivorceSurnameWeights.put(style, spinner); - panel.add(spinner); - - label.setLabelFor(spinner); - } - - return panel; - } - - private JPanel createRandomDivorcePanel() { - // Initialize Components Used in ActionListeners - final JPanel percentageRandomDivorcePanel = new JDisableablePanel("percentageRandomDivorcePanel"); - - // Create Panel Components - final JLabel lblRandomDivorceMethod = new JLabel(resources.getString("lblRandomDivorceMethod.text")); - lblRandomDivorceMethod.setToolTipText(resources.getString("lblRandomDivorceMethod.toolTipText")); - lblRandomDivorceMethod.setName("lblRandomDivorceMethod"); - - comboRandomDivorceMethod = new MMComboBox<>("comboRandomDivorceMethod", RandomDivorceMethod.values()); - comboRandomDivorceMethod.setToolTipText(resources.getString("lblRandomDivorceMethod.toolTipText")); - comboRandomDivorceMethod.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof RandomDivorceMethod) { - list.setToolTipText(((RandomDivorceMethod) value).getToolTipText()); - } - return this; - } - }); - comboRandomDivorceMethod.addActionListener(evt -> { - final RandomDivorceMethod method = comboRandomDivorceMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean enabled = !method.isNone(); - final boolean percentageEnabled = method.isDiceRoll(); - chkUseRandomOppositeSexDivorce.setEnabled(enabled); - chkUseRandomSameSexDivorce.setEnabled(enabled); - chkUseRandomClanPersonnelDivorce.setEnabled(enabled && chkUseClanPersonnelDivorce.isSelected()); - chkUseRandomPrisonerDivorce.setEnabled(enabled && chkUsePrisonerDivorce.isSelected()); - percentageRandomDivorcePanel.setEnabled(percentageEnabled); - lblRandomDivorceDiceSize.setEnabled(percentageEnabled); - spnRandomDivorceDiceSize.setEnabled(percentageEnabled); - }); - - chkUseRandomOppositeSexDivorce = new JCheckBox( - resources.getString("chkUseRandomOppositeSexDivorce.text")); - chkUseRandomOppositeSexDivorce - .setToolTipText(resources.getString("chkUseRandomOppositeSexDivorce.toolTipText")); - chkUseRandomOppositeSexDivorce.setName("chkUseRandomOppositeSexDivorce"); - - chkUseRandomSameSexDivorce = new JCheckBox(resources.getString("chkUseRandomSameSexDivorce.text")); - chkUseRandomSameSexDivorce - .setToolTipText(resources.getString("chkUseRandomSameSexDivorce.toolTipText")); - chkUseRandomSameSexDivorce.setName("chkUseRandomSameSexDivorce"); - - chkUseRandomClanPersonnelDivorce = new JCheckBox( - resources.getString("chkUseRandomClanPersonnelDivorce.text")); - chkUseRandomClanPersonnelDivorce - .setToolTipText(resources.getString("chkUseRandomClanPersonnelDivorce.toolTipText")); - chkUseRandomClanPersonnelDivorce.setName("chkUseRandomClanPersonnelDivorce"); - - chkUseRandomPrisonerDivorce = new JCheckBox(resources.getString("chkUseRandomPrisonerDivorce.text")); - chkUseRandomPrisonerDivorce - .setToolTipText(resources.getString("chkUseRandomPrisonerDivorce.toolTipText")); - chkUseRandomPrisonerDivorce.setName("chkUseRandomPrisonerDivorce"); - - createPercentageRandomDivorcePanel(percentageRandomDivorcePanel); - - // Programmatically Assign Accessibility Labels - lblRandomDivorceMethod.setLabelFor(comboRandomDivorceMethod); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("randomDivorcePanel.title"))); - panel.setName("randomDivorcePanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomDivorceMethod) - .addComponent(comboRandomDivorceMethod, - Alignment.LEADING)) - .addComponent(chkUseRandomOppositeSexDivorce) - .addComponent(chkUseRandomSameSexDivorce) - .addComponent(chkUseRandomClanPersonnelDivorce) - .addComponent(chkUseRandomPrisonerDivorce) - .addComponent(percentageRandomDivorcePanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomDivorceMethod) - .addComponent(comboRandomDivorceMethod)) - .addComponent(chkUseRandomOppositeSexDivorce) - .addComponent(chkUseRandomSameSexDivorce) - .addComponent(chkUseRandomClanPersonnelDivorce) - .addComponent(chkUseRandomPrisonerDivorce) - .addComponent(percentageRandomDivorcePanel)); - - return panel; - } - - private void createPercentageRandomDivorcePanel(final JPanel panel) { - // Create Panel Components - lblRandomDivorceDiceSize = new JLabel(resources.getString("lblRandomDivorceDiceSize.text")); - lblRandomDivorceDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomDivorceDiceSize.toolTipText"))); - lblRandomDivorceDiceSize.setName("lblRandomDivorceDiceSize"); - - spnRandomDivorceDiceSize = new JSpinner(new SpinnerNumberModel(900, 0, 100000, 1)); - spnRandomDivorceDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomDivorceDiceSize.toolTipText"))); - spnRandomDivorceDiceSize.setName("spnRandomDivorceDiceSize"); - - // Programmatically Assign Accessibility Labels - lblRandomDivorceDiceSize.setLabelFor(spnRandomDivorceDiceSize); - - // Layout the Panel - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("percentageRandomDivorcePanel.title"))); - panel.setToolTipText(RandomDivorceMethod.DICE_ROLL.getToolTipText()); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomDivorceDiceSize) - .addComponent(spnRandomDivorceDiceSize, Alignment.LEADING)) - ); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomDivorceDiceSize) - .addComponent(spnRandomDivorceDiceSize)) - ); - } - - private JPanel createProcreationPanel() { - // Create Panel Components - chkUseManualProcreation = new JCheckBox(resources.getString("chkUseManualProcreation.text")); - chkUseManualProcreation.setToolTipText(resources.getString("chkUseManualProcreation.toolTipText")); - chkUseManualProcreation.setName("chkUseManualProcreation"); - - chkUseClanPersonnelProcreation = new JCheckBox( - resources.getString("chkUseClanPersonnelProcreation.text")); - chkUseClanPersonnelProcreation - .setToolTipText(resources.getString("chkUseClanPersonnelProcreation.toolTipText")); - chkUseClanPersonnelProcreation.setName("chkUseClanPersonnelProcreation"); - chkUseClanPersonnelProcreation.addActionListener(evt -> { - final RandomProcreationMethod method = comboRandomProcreationMethod.getSelectedItem(); - if (method == null) { - return; - } - chkUseRandomClanPersonnelProcreation - .setEnabled(!method.isNone() && chkUseClanPersonnelProcreation.isSelected()); - }); - - chkUsePrisonerProcreation = new JCheckBox(resources.getString("chkUsePrisonerProcreation.text")); - chkUsePrisonerProcreation.setToolTipText(resources.getString("chkUsePrisonerProcreation.toolTipText")); - chkUsePrisonerProcreation.setName("chkUsePrisonerProcreation"); - chkUsePrisonerProcreation.addActionListener(evt -> { - final RandomProcreationMethod method = comboRandomProcreationMethod.getSelectedItem(); - if (method == null) { - return; - } - chkUseRandomPrisonerProcreation - .setEnabled(!method.isNone() && chkUsePrisonerProcreation.isSelected()); - }); - - final JLabel lblMultiplePregnancyOccurrences = new JLabel( - resources.getString("lblMultiplePregnancyOccurrences.text")); - lblMultiplePregnancyOccurrences - .setToolTipText(resources.getString("lblMultiplePregnancyOccurrences.toolTipText")); - lblMultiplePregnancyOccurrences.setName("lblMultiplePregnancyOccurrences"); - - spnMultiplePregnancyOccurrences = new JSpinner(new SpinnerNumberModel(50, 1, 1000, 1)); - spnMultiplePregnancyOccurrences - .setToolTipText(resources.getString("lblMultiplePregnancyOccurrences.toolTipText")); - spnMultiplePregnancyOccurrences.setName("spnMultiplePregnancyOccurrences"); - - final JLabel lblMultiplePregnancyOccurrencesEnd = new JLabel( - resources.getString("lblMultiplePregnancyOccurrencesEnd.text")); - lblMultiplePregnancyOccurrencesEnd - .setToolTipText(resources.getString("lblMultiplePregnancyOccurrences.toolTipText")); - lblMultiplePregnancyOccurrencesEnd.setName("lblMultiplePregnancyOccurrencesEnd"); - - final JLabel lblBabySurnameStyle = new JLabel(resources.getString("lblBabySurnameStyle.text")); - lblBabySurnameStyle.setToolTipText(resources.getString("lblBabySurnameStyle.toolTipText")); - lblBabySurnameStyle.setName("lblBabySurnameStyle"); - - comboBabySurnameStyle = new MMComboBox<>("comboBabySurnameStyle", BabySurnameStyle.values()); - comboBabySurnameStyle.setToolTipText(resources.getString("lblBabySurnameStyle.toolTipText")); - comboBabySurnameStyle.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof BabySurnameStyle) { - list.setToolTipText(((BabySurnameStyle) value).getToolTipText()); - } - return this; - } - }); - - chkAssignNonPrisonerBabiesFounderTag = new JCheckBox( - resources.getString("chkAssignNonPrisonerBabiesFounderTag.text")); - chkAssignNonPrisonerBabiesFounderTag - .setToolTipText(resources - .getString("chkAssignNonPrisonerBabiesFounderTag.toolTipText")); - chkAssignNonPrisonerBabiesFounderTag.setName("chkAssignNonPrisonerBabiesFounderTag"); - - chkAssignChildrenOfFoundersFounderTag = new JCheckBox( - resources.getString("chkAssignChildrenOfFoundersFounderTag.text")); - chkAssignChildrenOfFoundersFounderTag - .setToolTipText(resources - .getString("chkAssignChildrenOfFoundersFounderTag.toolTipText")); - chkAssignChildrenOfFoundersFounderTag.setName("chkAssignChildrenOfFoundersFounderTag"); - - chkDetermineFatherAtBirth = new JCheckBox(resources.getString("chkDetermineFatherAtBirth.text")); - chkDetermineFatherAtBirth.setToolTipText(resources.getString("chkDetermineFatherAtBirth.toolTipText")); - chkDetermineFatherAtBirth.setName("chkDetermineFatherAtBirth"); - - chkDisplayTrueDueDate = new JCheckBox(resources.getString("chkDisplayTrueDueDate.text")); - 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"); - - final JPanel randomProcreationPanel = createRandomProcreationPanel(); - - // Programmatically Assign Accessibility Labels - lblMultiplePregnancyOccurrences.setLabelFor(spnMultiplePregnancyOccurrences); - lblBabySurnameStyle.setLabelFor(comboBabySurnameStyle); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("procreationPanel.title"))); - panel.setName("procreationPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseManualProcreation) - .addComponent(chkUseClanPersonnelProcreation) - .addComponent(chkUsePrisonerProcreation) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblMultiplePregnancyOccurrences) - .addComponent(spnMultiplePregnancyOccurrences) - .addComponent(lblMultiplePregnancyOccurrencesEnd, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblBabySurnameStyle) - .addComponent(comboBabySurnameStyle, Alignment.LEADING)) - .addComponent(chkAssignNonPrisonerBabiesFounderTag) - .addComponent(chkAssignChildrenOfFoundersFounderTag) - .addComponent(chkDetermineFatherAtBirth) - .addComponent(chkDisplayTrueDueDate) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblNoInterestInChildrenDiceSize) - .addComponent(spnNoInterestInChildrenDiceSize, Alignment.LEADING)) - .addComponent(chkUseMaternityLeave) - .addComponent(chkLogProcreation) - .addComponent(randomProcreationPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseManualProcreation) - .addComponent(chkUseClanPersonnelProcreation) - .addComponent(chkUsePrisonerProcreation) - .addGroup(layout.createSequentialGroup() - .addComponent(lblMultiplePregnancyOccurrences) - .addComponent(spnMultiplePregnancyOccurrences) - .addComponent(lblMultiplePregnancyOccurrencesEnd)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblBabySurnameStyle) - .addComponent(comboBabySurnameStyle)) - .addComponent(chkAssignNonPrisonerBabiesFounderTag) - .addComponent(chkAssignChildrenOfFoundersFounderTag) - .addComponent(chkDetermineFatherAtBirth) - .addComponent(chkDisplayTrueDueDate) - .addGroup(layout.createSequentialGroup() - .addComponent(lblNoInterestInChildrenDiceSize) - .addComponent(spnNoInterestInChildrenDiceSize)) - .addComponent(chkUseMaternityLeave) - .addComponent(chkLogProcreation) - .addComponent(randomProcreationPanel)); - - return panel; - } - - private JPanel createEducationPanel() { - // General Settings - lblCurriculumXpRate = new JLabel(resources.getString("lblCurriculumXpRate.text")); - lblCurriculumXpRate.setToolTipText(resources.getString("lblCurriculumXpRate.toolTip")); - lblCurriculumXpRate.setName("lblCurriculumXpRate"); - - spnCurriculumXpRate = new JSpinner(new SpinnerNumberModel(3, 1, 10, 1)); - spnCurriculumXpRate.setToolTipText(resources.getString("lblCurriculumXpRate.toolTip")); - spnCurriculumXpRate.setName("spnCurriculumXpRate"); - - lblMaximumJumpCount = new JLabel(resources.getString("lblMaximumJumpCount.text")); - lblMaximumJumpCount.setToolTipText(resources.getString("lblMaximumJumpCount.toolTip")); - lblMaximumJumpCount.setName("lblMaximumJumpCount"); - - spnMaximumJumpCount = new JSpinner(new SpinnerNumberModel(5, 1, 200, 1)); - spnMaximumJumpCount.setToolTipText(resources.getString("lblMaximumJumpCount.toolTip")); - spnMaximumJumpCount.setName("spnMaximumJumpCount"); - - chkUseReeducationCamps = new JCheckBox(resources.getString("chkUseReeducationCamps.text")); - chkUseReeducationCamps.setToolTipText(resources.getString("chkUseReeducationCamps.toolTip")); - chkUseReeducationCamps.setName("chkUseReeducationCamps"); - - // Academy Set Enable - final JPanel enableStandardSetsPanel = createStandardSetsPanel(); - - // Show Ineligible Enable - chkShowIneligibleAcademies = new JCheckBox(resources.getString("chkShowIneligibleAcademies.text")); - chkShowIneligibleAcademies.setToolTipText(resources.getString("chkShowIneligibleAcademies.toolTip")); - chkShowIneligibleAcademies.setName("chkShowIneligibleAcademies"); - - chkEnableOverrideRequirements = new JCheckBox( - resources.getString("chkEnableOverrideRequirements.text")); - chkEnableOverrideRequirements - .setToolTipText(resources.getString("chkEnableOverrideRequirements.toolTip")); - chkEnableOverrideRequirements.setName("chkEnableOverrideRequirements"); - - // Entrance Exams - lblEntranceExamBaseTargetNumber = new JLabel( - resources.getString("lblEntranceExamBaseTargetNumber.text")); - lblEntranceExamBaseTargetNumber - .setToolTipText(wordWrap( - resources.getString("lblEntranceExamBaseTargetNumber.toolTip"))); - lblEntranceExamBaseTargetNumber.setName("lblEntranceExamBaseTargetNumber"); - - spnEntranceExamBaseTargetNumber = new JSpinner(new SpinnerNumberModel(14, 0, 20, 1)); - spnEntranceExamBaseTargetNumber - .setToolTipText(wordWrap( - resources.getString("lblEntranceExamBaseTargetNumber.toolTip"))); - spnEntranceExamBaseTargetNumber.setName("spnEntranceExamBaseTargetNumber"); - - // XP & Skill Bonuses - final JPanel xpAndSkillBonusesPanel = createXpAndSkillBonusesPanel(); - - // Dropout Chances - final JPanel dropoutChancePanel = createDropoutChancePanel(); - - // Random Fatal Accidents & Events - final JPanel accidentsAndEventsPanel = createAccidentsAndEventsPanel(); - - // Global Enable - chkUseEducationModule = new JCheckBox(resources.getString("chkUseEducationModule.text")); - chkUseEducationModule.setToolTipText(resources.getString("chkUseEducationModule.toolTip")); - chkUseEducationModule.setName("chkUseEducationModule"); - chkUseEducationModule.addActionListener(evt -> { - final boolean isEnabled = chkUseEducationModule.isSelected(); - - lblCurriculumXpRate.setEnabled(isEnabled); - spnCurriculumXpRate.setEnabled(isEnabled); - lblMaximumJumpCount.setEnabled(isEnabled); - spnMaximumJumpCount.setEnabled(isEnabled); - chkUseReeducationCamps.setEnabled(isEnabled); - - enableStandardSetsPanel.setEnabled(isEnabled); - chkEnableLocalAcademies.setEnabled(isEnabled); - chkEnablePrestigiousAcademies.setEnabled(isEnabled); - chkEnableUnitEducation.setEnabled(isEnabled); - - chkEnableOverrideRequirements.setEnabled(isEnabled); - chkShowIneligibleAcademies.setEnabled(isEnabled); - - lblEntranceExamBaseTargetNumber.setEnabled(isEnabled); - spnEntranceExamBaseTargetNumber.setEnabled(isEnabled); - - xpAndSkillBonusesPanel.setEnabled(isEnabled); - chkEnableBonuses.setEnabled(isEnabled); - lblFacultyXpMultiplier.setEnabled(isEnabled); - spnFacultyXpMultiplier.setEnabled(isEnabled); - - dropoutChancePanel.setEnabled(isEnabled); - lblAdultDropoutChance.setEnabled(isEnabled); - spnAdultDropoutChance.setEnabled(isEnabled); - lblChildrenDropoutChance.setEnabled(isEnabled); - spnChildrenDropoutChance.setEnabled(isEnabled); - - accidentsAndEventsPanel.setEnabled(isEnabled); - chkAllAges.setEnabled(isEnabled); - lblMilitaryAcademyAccidents.setEnabled(isEnabled); - spnMilitaryAcademyAccidents.setEnabled(isEnabled); - }); - - // this prevents a really annoying bug where disabled options don't stay - // disabled when - // reloading Campaign Options - lblCurriculumXpRate.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - spnCurriculumXpRate.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - lblMaximumJumpCount.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - spnMaximumJumpCount.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - chkUseReeducationCamps.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - enableStandardSetsPanel.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - chkEnableOverrideRequirements.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - chkShowIneligibleAcademies.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - lblEntranceExamBaseTargetNumber.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - lblEntranceExamBaseTargetNumber.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - xpAndSkillBonusesPanel.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - dropoutChancePanel.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - accidentsAndEventsPanel.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("educationPanel.title"))); - panel.setName("educationPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseEducationModule) - .addGap(10) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addComponent(lblCurriculumXpRate) - .addComponent(spnCurriculumXpRate)) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addComponent(lblMaximumJumpCount) - .addComponent(spnMaximumJumpCount)) - .addComponent(chkUseReeducationCamps) - .addComponent(enableStandardSetsPanel) - .addComponent(chkEnableOverrideRequirements) - .addComponent(chkShowIneligibleAcademies) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addComponent(lblEntranceExamBaseTargetNumber) - .addComponent(spnEntranceExamBaseTargetNumber)) - .addComponent(xpAndSkillBonusesPanel) - .addComponent(dropoutChancePanel) - .addComponent(accidentsAndEventsPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseEducationModule) - .addGap(10) - .addGroup(layout.createSequentialGroup() - .addComponent(lblCurriculumXpRate) - .addComponent(spnCurriculumXpRate)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblMaximumJumpCount) - .addComponent(spnMaximumJumpCount)) - .addComponent(chkUseReeducationCamps) - .addComponent(enableStandardSetsPanel) - .addComponent(chkEnableOverrideRequirements) - .addComponent(chkShowIneligibleAcademies) - .addGroup(layout.createSequentialGroup() - .addComponent(lblEntranceExamBaseTargetNumber) - .addComponent(spnEntranceExamBaseTargetNumber)) - .addComponent(xpAndSkillBonusesPanel) - .addComponent(dropoutChancePanel) - .addComponent(accidentsAndEventsPanel)); - return panel; - } - - private JPanel createStandardSetsPanel() { - chkEnableLocalAcademies = new JCheckBox(resources.getString("chkEnableLocalAcademies.text")); - chkEnableLocalAcademies.setToolTipText(resources.getString("chkEnableLocalAcademies.toolTip")); - chkEnableLocalAcademies.setName("chkEnableLocalAcademies"); - - chkEnablePrestigiousAcademies = new JCheckBox( - resources.getString("chkEnablePrestigiousAcademies.text")); - chkEnablePrestigiousAcademies - .setToolTipText(resources.getString("chkEnablePrestigiousAcademies.toolTip")); - chkEnablePrestigiousAcademies.setName("chkEnablePrestigiousAcademies"); - - chkEnableUnitEducation = new JCheckBox(resources.getString("chkEnableUnitEducation.text")); - chkEnableUnitEducation.setToolTipText(resources.getString("chkEnableUnitEducation.toolTip")); - chkEnableUnitEducation.setName("chkEnableUnitEducation"); - - // these prevent a really annoying bug where disabled options don't stay - // disabled when - // reloading Campaign Options - chkEnableLocalAcademies.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - chkEnablePrestigiousAcademies.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - chkEnableUnitEducation.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - - // creating the layout - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("standardSets.title"))); - panel.setName("standardSetsPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkEnableLocalAcademies) - .addComponent(chkEnablePrestigiousAcademies) - .addComponent(chkEnableUnitEducation)); - - layout.setHorizontalGroup( - layout.createSequentialGroup() - .addComponent(chkEnableLocalAcademies) - .addComponent(chkEnablePrestigiousAcademies) - .addComponent(chkEnableUnitEducation)); - - return panel; - } - - private JPanel createXpAndSkillBonusesPanel() { - chkEnableBonuses = new JCheckBox(resources.getString("chkEnableBonuses.text")); - chkEnableBonuses.setToolTipText(resources.getString("chkEnableBonuses.toolTip")); - chkEnableBonuses.setName("chkEnableBonuses"); - - lblFacultyXpMultiplier = new JLabel(resources.getString("lblFacultyXPMultiplier.text")); - lblFacultyXpMultiplier.setToolTipText(resources.getString("lblFacultyXPMultiplier.toolTip")); - lblFacultyXpMultiplier.setName("lblFacultyXpMultiplier"); - - spnFacultyXpMultiplier = new JSpinner(new SpinnerNumberModel(1.00, 0.00, 10.00, 0.01)); - spnFacultyXpMultiplier.setToolTipText(resources.getString("lblFacultyXPMultiplier.toolTip")); - spnFacultyXpMultiplier.setName("spnFacultyXpMultiplier"); - - chkEnableBonuses.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - - // creating the layout - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("xpAndSkillBonuses.title"))); - panel.setName("xpAndSkillBonusesPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkEnableBonuses) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblFacultyXpMultiplier) - .addComponent(spnFacultyXpMultiplier, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkEnableBonuses) - .addGroup(layout.createSequentialGroup() - .addComponent(lblFacultyXpMultiplier) - .addComponent(spnFacultyXpMultiplier))); - - return panel; - } - - private JPanel createDropoutChancePanel() { - lblAdultDropoutChance = new JLabel(resources.getString("lblAdultDropoutChance.text")); - lblAdultDropoutChance.setToolTipText(resources.getString("lblAdultDropoutChance.toolTip")); - lblAdultDropoutChance.setName("lblAdultDropoutChance"); - spnAdultDropoutChance = new JSpinner(new SpinnerNumberModel(1000, 0, 100000, 1)); - spnAdultDropoutChance.setToolTipText(resources.getString("lblAdultDropoutChance.toolTip")); - spnAdultDropoutChance.setName("spnAdultDropoutChance"); - - lblChildrenDropoutChance = new JLabel(resources.getString("lblChildrenDropoutChance.text")); - lblChildrenDropoutChance.setToolTipText(resources.getString("lblChildrenDropoutChance.toolTip")); - lblChildrenDropoutChance.setName("lblChildrenDropoutChance"); - spnChildrenDropoutChance = new JSpinner(new SpinnerNumberModel(10000, 0, 100000, 1)); - spnChildrenDropoutChance.setToolTipText(resources.getString("lblChildrenDropoutChance.toolTip")); - spnChildrenDropoutChance.setName("spnChildrenDropoutChance"); - - // These prevent a really annoying bug where disabled options don't stay - // disabled when - // reloading Campaign Options - lblAdultDropoutChance.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - spnAdultDropoutChance.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - lblChildrenDropoutChance.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - spnChildrenDropoutChance.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - - // creating the layout - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("dropoutChance.title"))); - panel.setName("dropoutChancePanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblAdultDropoutChance) - .addComponent(spnAdultDropoutChance, Alignment.LEADING) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblChildrenDropoutChance) - .addComponent(spnChildrenDropoutChance, - Alignment.LEADING)))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblAdultDropoutChance) - .addComponent(spnAdultDropoutChance) - .addGroup(layout.createSequentialGroup() - .addComponent(lblChildrenDropoutChance) - .addComponent(spnChildrenDropoutChance)))); - - return panel; - } - - private JPanel createAccidentsAndEventsPanel() { - chkAllAges = new JCheckBox(resources.getString("chkAllAges.text")); - chkAllAges.setToolTipText(resources.getString("chkAllAges.toolTip")); - chkAllAges.setName("chkAllAges"); - - lblMilitaryAcademyAccidents = new JLabel(resources.getString("lblMilitaryAcademyAccidents.text")); - lblMilitaryAcademyAccidents.setToolTipText(resources.getString("lblMilitaryAcademyAccidents.toolTip")); - lblMilitaryAcademyAccidents.setName("lblMilitaryAcademyAccidents"); - spnMilitaryAcademyAccidents = new JSpinner(new SpinnerNumberModel(10000, 0, 100000, 1)); - spnMilitaryAcademyAccidents.setToolTipText(resources.getString("lblMilitaryAcademyAccidents.toolTip")); - spnMilitaryAcademyAccidents.setName("spnMilitaryAcademyAccidents"); - - // These prevent a really annoying bug where disabled options don't stay - // disabled when - // reloading Campaign Options - chkAllAges.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - lblMilitaryAcademyAccidents.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - spnMilitaryAcademyAccidents.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); - - // creating the layout - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("accidentsAndEvents.title"))); - panel.setName("accidentsAndEventsPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkAllAges) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblMilitaryAcademyAccidents) - .addComponent(spnMilitaryAcademyAccidents, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkAllAges) - .addGroup(layout.createSequentialGroup() - .addComponent(lblMilitaryAcademyAccidents) - .addComponent(spnMilitaryAcademyAccidents))); - - return panel; - } - - private JPanel createRandomProcreationPanel() { - // Initialize Components Used in ActionListeners - final JPanel percentageRandomProcreationPanel = new JDisableablePanel( - "percentageRandomProcreationPanel"); - - // Create Panel Components - final JLabel lblRandomProcreationMethod = new JLabel( - resources.getString("lblRandomProcreationMethod.text")); - lblRandomProcreationMethod - .setToolTipText(resources.getString("lblRandomProcreationMethod.toolTipText")); - lblRandomProcreationMethod.setName("lblRandomProcreationMethod"); - - comboRandomProcreationMethod = new MMComboBox<>("comboRandomProcreationMethod", - RandomProcreationMethod.values()); - comboRandomProcreationMethod - .setToolTipText(resources.getString("lblRandomProcreationMethod.toolTipText")); - comboRandomProcreationMethod.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof RandomProcreationMethod) { - list.setToolTipText(((RandomProcreationMethod) value).getToolTipText()); - } - return this; - } - }); - comboRandomProcreationMethod.addActionListener(evt -> { - final RandomProcreationMethod method = comboRandomProcreationMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean enabled = !method.isNone(); - 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); - lblRandomProcreationRelationshiplessDiceSize.setEnabled(relationshiplessEnabled && percentageEnabled); - spnRandomProcreationRelationshiplessDiceSize.setEnabled(relationshiplessEnabled && percentageEnabled); - }); - - chkUseRelationshiplessRandomProcreation = new JCheckBox( - resources.getString("chkUseRelationshiplessRandomProcreation.text")); - chkUseRelationshiplessRandomProcreation - .setToolTipText(resources - .getString("chkUseRelationshiplessRandomProcreation.toolTipText")); - chkUseRelationshiplessRandomProcreation.setName("chkUseRelationshiplessRandomProcreation"); - chkUseRelationshiplessRandomProcreation.addActionListener(evt -> { - final RandomProcreationMethod method = comboRandomProcreationMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean sameSexEnabled = chkUseRelationshiplessRandomProcreation.isEnabled() - && chkUseRelationshiplessRandomProcreation.isSelected(); - final boolean percentageEnabled = sameSexEnabled && method.isDiceRoll(); - lblRandomProcreationRelationshiplessDiceSize.setEnabled(percentageEnabled); - spnRandomProcreationRelationshiplessDiceSize.setEnabled(percentageEnabled); - }); - - chkUseRandomClanPersonnelProcreation = new JCheckBox( - resources.getString("chkUseRandomClanPersonnelProcreation.text")); - chkUseRandomClanPersonnelProcreation - .setToolTipText(resources - .getString("chkUseRandomClanPersonnelProcreation.toolTipText")); - chkUseRandomClanPersonnelProcreation.setName("chkUseRandomClanPersonnelProcreation"); - - chkUseRandomPrisonerProcreation = new JCheckBox( - resources.getString("chkUseRandomPrisonerProcreation.text")); - chkUseRandomPrisonerProcreation - .setToolTipText(resources.getString("chkUseRandomPrisonerProcreation.toolTipText")); - chkUseRandomPrisonerProcreation.setName("chkUseRandomPrisonerProcreation"); - - createPercentageRandomProcreationPanel(percentageRandomProcreationPanel); - - // Programmatically Assign Accessibility Labels - lblRandomProcreationMethod.setLabelFor(comboRandomProcreationMethod); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("randomProcreationPanel.title"))); - panel.setName("randomProcreationPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomProcreationMethod) - .addComponent(comboRandomProcreationMethod, - Alignment.LEADING)) - .addComponent(chkUseRelationshiplessRandomProcreation) - .addComponent(chkUseRandomClanPersonnelProcreation) - .addComponent(chkUseRandomPrisonerProcreation) - .addComponent(percentageRandomProcreationPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomProcreationMethod) - .addComponent(comboRandomProcreationMethod)) - .addComponent(chkUseRelationshiplessRandomProcreation) - .addComponent(chkUseRandomClanPersonnelProcreation) - .addComponent(chkUseRandomPrisonerProcreation) - .addComponent(percentageRandomProcreationPanel)); - - return panel; - } - - private void createPercentageRandomProcreationPanel(final JPanel panel) { - // Create Panel Components - 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 - lblRandomProcreationRelationshipDiceSize.setLabelFor(spnRandomProcreationRelationshipDiceSize); - lblRandomProcreationRelationshiplessDiceSize.setLabelFor(spnRandomProcreationRelationshipDiceSize); - - // Layout the Panel - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("percentageRandomProcreationPanel.title"))); - panel.setToolTipText(RandomProcreationMethod.DICE_ROLL.getToolTipText()); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomProcreationRelationshipDiceSize) - .addComponent(spnRandomProcreationRelationshipDiceSize, Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomProcreationRelationshiplessDiceSize) - .addComponent(spnRandomProcreationRelationshiplessDiceSize, Alignment.LEADING)) - ); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomProcreationRelationshipDiceSize) - .addComponent(spnRandomProcreationRelationshipDiceSize)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomProcreationRelationshiplessDiceSize) - .addComponent(spnRandomProcreationRelationshiplessDiceSize)) - ); - } - - private JPanel createDeathPanel() { - // Create Panel Components - chkKeepMarriedNameUponSpouseDeath = new JCheckBox( - resources.getString("chkKeepMarriedNameUponSpouseDeath.text")); - chkKeepMarriedNameUponSpouseDeath - .setToolTipText(resources.getString("chkKeepMarriedNameUponSpouseDeath.toolTipText")); - chkKeepMarriedNameUponSpouseDeath.setName("chkKeepMarriedNameUponSpouseDeath"); - - final JPanel randomDeathPanel = createRandomDeathPanel(); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("deathPanel.title"))); - panel.setName("deathPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkKeepMarriedNameUponSpouseDeath) - .addComponent(randomDeathPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkKeepMarriedNameUponSpouseDeath) - .addComponent(randomDeathPanel)); - - return panel; - } - - private JPanel createRandomDeathPanel() { - // Initialize Components Used in ActionListeners - final JPanel enabledRandomDeathAgeGroupsPanel = new JDisableablePanel( - "enabledRandomDeathAgeGroupsPanel"); - - final JPanel percentageRandomDeathPanel = new JDisableablePanel("percentageRandomDeathPanel"); - - final JPanel exponentialRandomDeathPanel = new JDisableablePanel("exponentialRandomDeathPanel"); - - final JPanel ageRangeRandomDeathPanel = new JDisableablePanel("ageRangeRandomDeathPanel"); - - // Create Panel Components - final JLabel lblRandomDeathMethod = new JLabel(resources.getString("lblRandomDeathMethod.text")); - lblRandomDeathMethod.setToolTipText(resources.getString("lblRandomDeathMethod.toolTipText")); - lblRandomDeathMethod.setName("lblRandomDeathMethod"); - - comboRandomDeathMethod = new MMComboBox<>("comboRandomDeathMethod", RandomDeathMethod.values()); - comboRandomDeathMethod.setToolTipText(resources.getString("lblRandomDeathMethod.toolTipText")); - comboRandomDeathMethod.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof RandomDeathMethod) { - list.setToolTipText(((RandomDeathMethod) value).getToolTipText()); - } - return this; - } - }); - comboRandomDeathMethod.addActionListener(evt -> { - final RandomDeathMethod method = comboRandomDeathMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean enabled = !method.isNone(); - enabledRandomDeathAgeGroupsPanel.setEnabled(enabled); - chkUseRandomClanPersonnelDeath.setEnabled(enabled); - chkUseRandomPrisonerDeath.setEnabled(enabled); - chkUseRandomDeathSuicideCause.setEnabled(enabled); - percentageRandomDeathPanel.setEnabled(method.isPercentage()); - exponentialRandomDeathPanel.setEnabled(method.isExponential()); - ageRangeRandomDeathPanel.setEnabled(method.isAgeRange()); - }); - - createEnabledRandomDeathAgeGroupsPanel(enabledRandomDeathAgeGroupsPanel); - - chkUseRandomClanPersonnelDeath = new JCheckBox( - resources.getString("chkUseRandomClanPersonnelDeath.text")); - chkUseRandomClanPersonnelDeath - .setToolTipText(resources.getString("chkUseRandomClanPersonnelDeath.toolTipText")); - chkUseRandomClanPersonnelDeath.setName("chkUseRandomClanPersonnelDeath"); - - chkUseRandomPrisonerDeath = new JCheckBox(resources.getString("chkUseRandomPrisonerDeath.text")); - chkUseRandomPrisonerDeath.setToolTipText(resources.getString("chkUseRandomPrisonerDeath.toolTipText")); - chkUseRandomPrisonerDeath.setName("chkUseRandomPrisonerDeath"); - - chkUseRandomDeathSuicideCause = new JCheckBox( - resources.getString("chkUseRandomDeathSuicideCause.text")); - chkUseRandomDeathSuicideCause - .setToolTipText(resources.getString("chkUseRandomDeathSuicideCause.toolTipText")); - chkUseRandomDeathSuicideCause.setName("chkUseRandomDeathSuicideCause"); - - createPercentageRandomDeathPanel(percentageRandomDeathPanel); - - createExponentialRandomDeathPanel(exponentialRandomDeathPanel); - - createAgeRangeRandomDeathPanel(ageRangeRandomDeathPanel); - - // Programmatically Assign Accessibility Labels - lblRandomDeathMethod.setLabelFor(comboRandomDeathMethod); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("randomDeathPanel.title"))); - panel.setName("randomDeathPanel"); - final GroupLayout layout = new GroupLayout(panel); - panel.setLayout(layout); - - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomDeathMethod) - .addComponent(comboRandomDeathMethod, - Alignment.LEADING)) - .addComponent(enabledRandomDeathAgeGroupsPanel) - .addComponent(chkUseRandomClanPersonnelDeath) - .addComponent(chkUseRandomPrisonerDeath) - .addComponent(chkUseRandomDeathSuicideCause) - .addComponent(percentageRandomDeathPanel) - .addComponent(exponentialRandomDeathPanel) - .addComponent(ageRangeRandomDeathPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomDeathMethod) - .addComponent(comboRandomDeathMethod)) - .addComponent(enabledRandomDeathAgeGroupsPanel) - .addComponent(chkUseRandomClanPersonnelDeath) - .addComponent(chkUseRandomPrisonerDeath) - .addComponent(chkUseRandomDeathSuicideCause) - .addComponent(percentageRandomDeathPanel) - .addComponent(exponentialRandomDeathPanel) - .addComponent(ageRangeRandomDeathPanel)); - - return panel; - } - - private void createEnabledRandomDeathAgeGroupsPanel(final JPanel panel) { - // Initialize Variables Required - final AgeGroup[] ageGroups = AgeGroup.values(); - - chkEnabledRandomDeathAgeGroups = new HashMap<>(); - - // Create the Panel - panel.setBorder( - BorderFactory.createTitledBorder( - resources.getString("enabledRandomDeathAgeGroupsPanel.title"))); - panel.setToolTipText(resources.getString("enabledRandomDeathAgeGroupsPanel.toolTipText")); - panel.setLayout(new GridLayout(1, ageGroups.length)); - - // Create the primary panel - for (final AgeGroup ageGroup : ageGroups) { - // Create Panel Components - final JCheckBox checkBox = new JCheckBox(ageGroup.toString()); - checkBox.setToolTipText(ageGroup.getToolTipText()); - checkBox.setName("chk" + ageGroup); - panel.add(checkBox); - chkEnabledRandomDeathAgeGroups.put(ageGroup, checkBox); - } - } - - private void createPercentageRandomDeathPanel(final JPanel panel) { - // Create Panel Components - final JLabel lblPercentageRandomDeathChance = new JLabel( - resources.getString("lblPercentageRandomDeathChance.text")); - lblPercentageRandomDeathChance - .setToolTipText(resources.getString("lblPercentageRandomDeathChance.toolTipText")); - lblPercentageRandomDeathChance.setName("lblPercentageRandomDeathChance"); - - spnPercentageRandomDeathChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 0.000001)); - spnPercentageRandomDeathChance - .setToolTipText(resources.getString("lblPercentageRandomDeathChance.toolTipText")); - spnPercentageRandomDeathChance.setName("spnPercentageRandomDeathChance"); - spnPercentageRandomDeathChance.setEditor(new NumberEditor(spnPercentageRandomDeathChance, "0.000000")); - - // Programmatically Assign Accessibility Labels - lblPercentageRandomDeathChance.setLabelFor(spnPercentageRandomDeathChance); - - // Layout the Panel - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("percentageRandomDeathPanel.title"))); - panel.setToolTipText(RandomProcreationMethod.DICE_ROLL.getToolTipText()); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPercentageRandomDeathChance) - .addComponent(spnPercentageRandomDeathChance, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPercentageRandomDeathChance) - .addComponent(spnPercentageRandomDeathChance))); - } - - private void createExponentialRandomDeathPanel(final JPanel panel) { - // Create Panel Components - final JPanel exponentialRandomDeathMalePanel = createExponentialRandomDeathMalePanel(); - - final JPanel exponentialRandomDeathFemalePanel = createExponentialRandomDeathFemalePanel(); - - // Layout the Panel - panel.setBorder(BorderFactory - .createTitledBorder(resources.getString("exponentialRandomDeathPanel.title"))); - panel.setToolTipText(RandomDeathMethod.EXPONENTIAL.getToolTipText()); - final GroupLayout layout = new GroupLayout(panel); - panel.setLayout(layout); - - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(exponentialRandomDeathMalePanel) - .addComponent(exponentialRandomDeathFemalePanel, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(exponentialRandomDeathMalePanel) - .addComponent(exponentialRandomDeathFemalePanel))); - } - - private JPanel createExponentialRandomDeathMalePanel() { - // Create Panel Components - spnExponentialRandomDeathMaleValues = new JSpinner[3]; - - spnExponentialRandomDeathMaleValues[0] = new JSpinner(new SpinnerNumberModel(0.0, 0.0, 10.0, 0.0001)); - spnExponentialRandomDeathMaleValues[0].setToolTipText(RandomDeathMethod.EXPONENTIAL.getToolTipText()); - spnExponentialRandomDeathMaleValues[0].setName("spnExponentialRandomDeathMaleC"); - ((NumberEditor) spnExponentialRandomDeathMaleValues[0].getEditor()).getFormat() - .setMaximumFractionDigits(4); - - final JLabel lblPowerOfTen = new JLabel(resources.getString("PowerOfTen.text")); - lblPowerOfTen.setName("lblPowerOfTen"); - - spnExponentialRandomDeathMaleValues[1] = new JSpinner(new SpinnerNumberModel(0.0, -100.0, 100.0, 1.0)); - spnExponentialRandomDeathMaleValues[1].setToolTipText(RandomDeathMethod.EXPONENTIAL.getToolTipText()); - spnExponentialRandomDeathMaleValues[1].setName("spnExponentialRandomDeathMaleN"); - - final JLabel lblExponential = new JLabel(resources.getString("Exponential.text")); - lblExponential.setName("lblExponential"); - - spnExponentialRandomDeathMaleValues[2] = new JSpinner( - new SpinnerNumberModel(0.0, -100.0, 100.0, 0.0001)); - spnExponentialRandomDeathMaleValues[2].setToolTipText(RandomDeathMethod.EXPONENTIAL.getToolTipText()); - spnExponentialRandomDeathMaleValues[2].setName("spnExponentialRandomDeathMaleK"); - ((NumberEditor) spnExponentialRandomDeathMaleValues[2].getEditor()).getFormat() - .setMaximumFractionDigits(4); - - final JLabel lblExponentialRandomDeathAge = new JLabel( - resources.getString("lblExponentialRandomDeathAge.text")); - lblExponential.setName("lblExponentialRandomDeathAge"); - - // Layout the Panel - final JPanel panel = new JDisableablePanel("exponentialRandomDeathMalePanel"); - panel.setBorder(BorderFactory.createTitledBorder(Gender.MALE.toString())); - final GroupLayout layout = new GroupLayout(panel); - panel.setLayout(layout); - - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(spnExponentialRandomDeathMaleValues[0]) - .addComponent(lblPowerOfTen) - .addComponent(spnExponentialRandomDeathMaleValues[1]) - .addComponent(lblExponential) - .addComponent(spnExponentialRandomDeathMaleValues[2]) - .addComponent(lblExponentialRandomDeathAge, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(spnExponentialRandomDeathMaleValues[0]) - .addComponent(lblPowerOfTen) - .addComponent(spnExponentialRandomDeathMaleValues[1]) - .addComponent(lblExponential) - .addComponent(spnExponentialRandomDeathMaleValues[2]) - .addComponent(lblExponentialRandomDeathAge))); - - return panel; - } - - private JPanel createExponentialRandomDeathFemalePanel() { - // Create Panel Components - spnExponentialRandomDeathFemaleValues = new JSpinner[3]; - - spnExponentialRandomDeathFemaleValues[0] = new JSpinner(new SpinnerNumberModel(0.0, 0.0, 10.0, 0.0001)); - spnExponentialRandomDeathFemaleValues[0].setToolTipText(RandomDeathMethod.EXPONENTIAL.getToolTipText()); - spnExponentialRandomDeathFemaleValues[0].setName("spnExponentialRandomDeathFemaleC"); - ((NumberEditor) spnExponentialRandomDeathFemaleValues[0].getEditor()).getFormat() - .setMaximumFractionDigits(4); - - final JLabel lblPowerOfTen = new JLabel(resources.getString("PowerOfTen.text")); - lblPowerOfTen.setName("lblPowerOfTen"); - - spnExponentialRandomDeathFemaleValues[1] = new JSpinner( - new SpinnerNumberModel(0.0, -100.0, 100.0, 1.0)); - spnExponentialRandomDeathFemaleValues[1].setToolTipText(RandomDeathMethod.EXPONENTIAL.getToolTipText()); - spnExponentialRandomDeathFemaleValues[1].setName("spnExponentialRandomDeathFemaleN"); - - final JLabel lblExponential = new JLabel(resources.getString("Exponential.text")); - lblExponential.setName("lblExponential"); - - spnExponentialRandomDeathFemaleValues[2] = new JSpinner( - new SpinnerNumberModel(0.0, -100.0, 100.0, 0.0001)); - spnExponentialRandomDeathFemaleValues[2].setToolTipText(RandomDeathMethod.EXPONENTIAL.getToolTipText()); - spnExponentialRandomDeathFemaleValues[2].setName("spnExponentialRandomDeathFemaleK"); - ((NumberEditor) spnExponentialRandomDeathFemaleValues[2].getEditor()).getFormat() - .setMaximumFractionDigits(4); - - final JLabel lblExponentialRandomDeathAge = new JLabel( - resources.getString("lblExponentialRandomDeathAge.text")); - lblExponential.setName("lblExponentialRandomDeathAge"); - - // Layout the Panel - final JPanel panel = new JDisableablePanel("exponentialRandomDeathFemalePanel"); - panel.setBorder(BorderFactory.createTitledBorder(Gender.FEMALE.toString())); - final GroupLayout layout = new GroupLayout(panel); - panel.setLayout(layout); - - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(spnExponentialRandomDeathFemaleValues[0]) - .addComponent(lblPowerOfTen) - .addComponent(spnExponentialRandomDeathFemaleValues[1]) - .addComponent(lblExponential) - .addComponent(spnExponentialRandomDeathFemaleValues[2]) - .addComponent(lblExponentialRandomDeathAge, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(spnExponentialRandomDeathFemaleValues[0]) - .addComponent(lblPowerOfTen) - .addComponent(spnExponentialRandomDeathFemaleValues[1]) - .addComponent(lblExponential) - .addComponent(spnExponentialRandomDeathFemaleValues[2]) - .addComponent(lblExponentialRandomDeathAge))); - - return panel; - } - - private void createAgeRangeRandomDeathPanel(final JPanel panel) { - // Initialize Variables Required - final TenYearAgeRange[] ageRanges = TenYearAgeRange.values(); - - spnAgeRangeRandomDeathMaleValues = new HashMap<>(); - - spnAgeRangeRandomDeathFemaleValues = new HashMap<>(); - - // Create the Panel - panel.setBorder(BorderFactory - .createTitledBorder(resources.getString("ageRangeRandomDeathPanel.title"))); - panel.setToolTipText(RandomDeathMethod.AGE_RANGE.getToolTipText()); - panel.setLayout(new GridLayout(ageRanges.length + 1, 3)); - - // Create Title Row - final JLabel lblAgeRange = new JLabel(resources.getString("lblAgeRange.text")); - lblAgeRange.setName("lblAgeRange"); - panel.add(lblAgeRange); - - final JLabel lblMale = new JLabel(Gender.MALE.toString()); - lblMale.setName("lblMale"); - panel.add(lblMale); - - final JLabel lblFemale = new JLabel(Gender.FEMALE.toString()); - lblFemale.setName("lblFemale"); - panel.add(lblFemale); - - // Create the primary panel - for (final TenYearAgeRange ageRange : ageRanges) { - // Create Panel Components - final JLabel label = new JLabel(ageRange.toString()); - label.setName("lbl" + ageRange); - panel.add(label); - - JSpinner spinner = new JSpinner(new SpinnerNumberModel(0.0, 0.0, 100000.0, 10.0)); - spinner.setToolTipText(RandomDeathMethod.AGE_RANGE.getToolTipText()); - spinner.setName("spnAgeRangeRandomDeathMale" + ageRange); - ((NumberEditor) spinner.getEditor()).getFormat().applyPattern("###,###.0"); - ((NumberEditor) spinner.getEditor()).getFormat().setMaximumFractionDigits(1); - panel.add(spinner); - spnAgeRangeRandomDeathMaleValues.put(ageRange, spinner); - - spinner = new JSpinner(new SpinnerNumberModel(0.0, 0.0, 100000.0, 10.0)); - spinner.setToolTipText(RandomDeathMethod.AGE_RANGE.getToolTipText()); - spinner.setName("spnAgeRangeRandomDeathFemale" + ageRange); - ((NumberEditor) spinner.getEditor()).getFormat().applyPattern("###,###.0"); - ((NumberEditor) spinner.getEditor()).getFormat().setMaximumFractionDigits(1); - panel.add(spinner); - spnAgeRangeRandomDeathFemaleValues.put(ageRange, spinner); - } - } - // endregion Personnel Tab - - // region Finances Tab - private JPanel createPriceModifiersPanel(boolean reverseQualities) { - // Create Panel Components - final JLabel lblCommonPartPriceMultiplier = new JLabel( - resources.getString("lblCommonPartPriceMultiplier.text")); - lblCommonPartPriceMultiplier - .setToolTipText(resources.getString("lblCommonPartPriceMultiplier.toolTipText")); - lblCommonPartPriceMultiplier.setName("lblCommonPartPriceMultiplier"); - - spnCommonPartPriceMultiplier = new JSpinner(new SpinnerNumberModel(1.0, 0.1, null, 0.1)); - spnCommonPartPriceMultiplier - .setToolTipText(resources.getString("lblCommonPartPriceMultiplier.toolTipText")); - spnCommonPartPriceMultiplier.setName("spnCommonPartPriceMultiplier"); - - final JLabel lblInnerSphereUnitPriceMultiplier = new JLabel( - resources.getString("lblInnerSphereUnitPriceMultiplier.text")); - lblInnerSphereUnitPriceMultiplier - .setToolTipText(resources.getString("lblInnerSphereUnitPriceMultiplier.toolTipText")); - lblInnerSphereUnitPriceMultiplier.setName("lblInnerSphereUnitPriceMultiplier"); - - spnInnerSphereUnitPriceMultiplier = new JSpinner(new SpinnerNumberModel(1.0, 0.1, null, 0.1)); - spnInnerSphereUnitPriceMultiplier - .setToolTipText(resources.getString("lblInnerSphereUnitPriceMultiplier.toolTipText")); - spnInnerSphereUnitPriceMultiplier.setName("spnInnerSphereUnitPriceMultiplier"); - - final JLabel lblInnerSpherePartPriceMultiplier = new JLabel( - resources.getString("lblInnerSpherePartPriceMultiplier.text")); - lblInnerSpherePartPriceMultiplier - .setToolTipText(resources.getString("lblInnerSpherePartPriceMultiplier.toolTipText")); - lblInnerSpherePartPriceMultiplier.setName("lblInnerSpherePartPriceMultiplier"); - - spnInnerSpherePartPriceMultiplier = new JSpinner(new SpinnerNumberModel(1.0, 0.1, null, 0.1)); - spnInnerSpherePartPriceMultiplier - .setToolTipText(resources.getString("lblInnerSpherePartPriceMultiplier.toolTipText")); - spnInnerSpherePartPriceMultiplier.setName("spnInnerSpherePartPriceMultiplier"); - - final JLabel lblClanUnitPriceMultiplier = new JLabel( - resources.getString("lblClanUnitPriceMultiplier.text")); - lblClanUnitPriceMultiplier - .setToolTipText(resources.getString("lblClanUnitPriceMultiplier.toolTipText")); - lblClanUnitPriceMultiplier.setName("lblClanUnitPriceMultiplier"); - - spnClanUnitPriceMultiplier = new JSpinner(new SpinnerNumberModel(1.0, 0.1, null, 0.1)); - spnClanUnitPriceMultiplier - .setToolTipText(resources.getString("lblClanUnitPriceMultiplier.toolTipText")); - spnClanUnitPriceMultiplier.setName("spnClanUnitPriceMultiplier"); - - final JLabel lblClanPartPriceMultiplier = new JLabel( - resources.getString("lblClanPartPriceMultiplier.text")); - lblClanPartPriceMultiplier - .setToolTipText(resources.getString("lblClanPartPriceMultiplier.toolTipText")); - lblClanPartPriceMultiplier.setName("lblClanPartPriceMultiplier"); - - spnClanPartPriceMultiplier = new JSpinner(new SpinnerNumberModel(1.0, 0.1, null, 0.1)); - spnClanPartPriceMultiplier - .setToolTipText(resources.getString("lblClanPartPriceMultiplier.toolTipText")); - spnClanPartPriceMultiplier.setName("spnClanPartPriceMultiplier"); - - final JLabel lblMixedTechUnitPriceMultiplier = new JLabel( - resources.getString("lblMixedTechUnitPriceMultiplier.text")); - lblMixedTechUnitPriceMultiplier - .setToolTipText(resources.getString("lblMixedTechUnitPriceMultiplier.toolTipText")); - lblMixedTechUnitPriceMultiplier.setName("lblMixedTechUnitPriceMultiplier"); - - spnMixedTechUnitPriceMultiplier = new JSpinner(new SpinnerNumberModel(1.0, 0.1, null, 0.1)); - spnMixedTechUnitPriceMultiplier - .setToolTipText(resources.getString("lblMixedTechUnitPriceMultiplier.toolTipText")); - spnMixedTechUnitPriceMultiplier.setName("spnMixedTechUnitPriceMultiplier"); - - final JPanel usedPartsValueMultipliersPanel = createUsedPartsValueMultipliersPanel(reverseQualities); - - final JLabel lblDamagedPartsValueMultiplier = new JLabel( - resources.getString("lblDamagedPartsValueMultiplier.text")); - lblDamagedPartsValueMultiplier - .setToolTipText(resources.getString("lblDamagedPartsValueMultiplier.toolTipText")); - lblDamagedPartsValueMultiplier.setName("lblDamagedPartsValueMultiplier"); - - spnDamagedPartsValueMultiplier = new JSpinner(new SpinnerNumberModel(0.33, 0.00, 1.00, 0.05)); - spnDamagedPartsValueMultiplier - .setToolTipText(resources.getString("lblDamagedPartsValueMultiplier.toolTipText")); - spnDamagedPartsValueMultiplier.setName("spnDamagedPartsValueMultiplier"); - spnDamagedPartsValueMultiplier.setEditor(new NumberEditor(spnDamagedPartsValueMultiplier, "0.00")); - - final JLabel lblUnrepairablePartsValueMultiplier = new JLabel( - resources.getString("lblUnrepairablePartsValueMultiplier.text")); - lblUnrepairablePartsValueMultiplier - .setToolTipText(resources.getString("lblUnrepairablePartsValueMultiplier.toolTipText")); - lblUnrepairablePartsValueMultiplier.setName("lblUnrepairablePartsValueMultiplier"); - - spnUnrepairablePartsValueMultiplier = new JSpinner(new SpinnerNumberModel(0.10, 0.00, 1.00, 0.05)); - spnUnrepairablePartsValueMultiplier - .setToolTipText(resources.getString("lblUnrepairablePartsValueMultiplier.toolTipText")); - spnUnrepairablePartsValueMultiplier.setName("spnUnrepairablePartsValueMultiplier"); - spnUnrepairablePartsValueMultiplier - .setEditor(new NumberEditor(spnUnrepairablePartsValueMultiplier, "0.00")); - - final JLabel lblCancelledOrderRefundMultiplier = new JLabel( - resources.getString("lblCancelledOrderRefundMultiplier.text")); - lblCancelledOrderRefundMultiplier - .setToolTipText(resources.getString("lblCancelledOrderRefundMultiplier.toolTipText")); - lblCancelledOrderRefundMultiplier.setName("lblCancelledOrderRefundMultiplier"); - - spnCancelledOrderRefundMultiplier = new JSpinner(new SpinnerNumberModel(0.50, 0.00, 1.00, 0.05)); - spnCancelledOrderRefundMultiplier - .setToolTipText(resources.getString("lblCancelledOrderRefundMultiplier.toolTipText")); - spnCancelledOrderRefundMultiplier.setName("spnCancelledOrderRefundMultiplier"); - spnCancelledOrderRefundMultiplier - .setEditor(new NumberEditor(spnCancelledOrderRefundMultiplier, "0.00")); - - // Programmatically Assign Accessibility Labels - lblCommonPartPriceMultiplier.setLabelFor(spnCommonPartPriceMultiplier); - lblInnerSphereUnitPriceMultiplier.setLabelFor(spnInnerSphereUnitPriceMultiplier); - lblInnerSpherePartPriceMultiplier.setLabelFor(spnInnerSpherePartPriceMultiplier); - lblClanUnitPriceMultiplier.setLabelFor(spnClanUnitPriceMultiplier); - lblClanPartPriceMultiplier.setLabelFor(spnClanPartPriceMultiplier); - lblMixedTechUnitPriceMultiplier.setLabelFor(spnMixedTechUnitPriceMultiplier); - lblDamagedPartsValueMultiplier.setLabelFor(spnDamagedPartsValueMultiplier); - lblUnrepairablePartsValueMultiplier.setLabelFor(spnUnrepairablePartsValueMultiplier); - lblCancelledOrderRefundMultiplier.setLabelFor(spnCancelledOrderRefundMultiplier); - - // Layout the Panel - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("priceMultipliersPanel.title"))); - panel.setName("priceMultipliersPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblCommonPartPriceMultiplier) - .addComponent(spnCommonPartPriceMultiplier, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblInnerSphereUnitPriceMultiplier) - .addComponent(spnInnerSphereUnitPriceMultiplier, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblInnerSpherePartPriceMultiplier) - .addComponent(spnInnerSpherePartPriceMultiplier, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblClanUnitPriceMultiplier) - .addComponent(spnClanUnitPriceMultiplier, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblClanPartPriceMultiplier) - .addComponent(spnClanPartPriceMultiplier, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblMixedTechUnitPriceMultiplier) - .addComponent(spnMixedTechUnitPriceMultiplier, - Alignment.LEADING)) - .addComponent(usedPartsValueMultipliersPanel) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblDamagedPartsValueMultiplier) - .addComponent(spnDamagedPartsValueMultiplier, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblUnrepairablePartsValueMultiplier) - .addComponent(spnUnrepairablePartsValueMultiplier, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblCancelledOrderRefundMultiplier) - .addComponent(spnCancelledOrderRefundMultiplier, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblCommonPartPriceMultiplier) - .addComponent(spnCommonPartPriceMultiplier)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblInnerSphereUnitPriceMultiplier) - .addComponent(spnInnerSphereUnitPriceMultiplier)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblInnerSpherePartPriceMultiplier) - .addComponent(spnInnerSpherePartPriceMultiplier)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblClanUnitPriceMultiplier) - .addComponent(spnClanUnitPriceMultiplier)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblClanPartPriceMultiplier) - .addComponent(spnClanPartPriceMultiplier)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblMixedTechUnitPriceMultiplier) - .addComponent(spnMixedTechUnitPriceMultiplier)) - .addComponent(usedPartsValueMultipliersPanel) - .addGroup(layout.createSequentialGroup() - .addComponent(lblDamagedPartsValueMultiplier) - .addComponent(spnDamagedPartsValueMultiplier)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblUnrepairablePartsValueMultiplier) - .addComponent(spnUnrepairablePartsValueMultiplier)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblCancelledOrderRefundMultiplier) - .addComponent(spnCancelledOrderRefundMultiplier))); - - return panel; - } - - private JPanel createUsedPartsValueMultipliersPanel(boolean reverseQualities) { - final JPanel panel = new JPanel(new GridLayout(0, 2)); - panel.setBorder(BorderFactory - .createTitledBorder(resources.getString("usedPartsValueMultipliersPanel.title"))); - panel.setName("usedPartsValueMultipliersPanel"); - - spnUsedPartPriceMultipliers = new JSpinner[PartQuality.QUALITY_F.toNumeric() + 1]; - - for (PartQuality quality : PartQuality.allQualities()) { - final String qualityLevel = quality.toName(reverseQualities); - - final JLabel label = new JLabel(qualityLevel); - label.setToolTipText(resources.getString("lblUsedPartPriceMultiplier.toolTipText")); - label.setName("lbl" + qualityLevel); - panel.add(label); - - spnUsedPartPriceMultipliers[quality.toNumeric()] = new JSpinner( - new SpinnerNumberModel(0.00, 0.00, 1.00, 0.05)); - spnUsedPartPriceMultipliers[quality.toNumeric()] - .setToolTipText(resources.getString("lblUsedPartPriceMultiplier.toolTipText")); - spnUsedPartPriceMultipliers[quality.toNumeric()].setName("spn" + qualityLevel); - spnUsedPartPriceMultipliers[quality.toNumeric()] - .setEditor(new NumberEditor(spnUsedPartPriceMultipliers[quality.toNumeric()], "0.00")); - panel.add(spnUsedPartPriceMultipliers[quality.toNumeric()]); - - label.setLabelFor(spnUsedPartPriceMultipliers[quality.toNumeric()]); - } - - return panel; - } - - private JPanel createTaxesPanel() { - taxesSubPanel = createTaxesSubPanel(); - - chkUseTaxes = new JCheckBox(resources.getString("chkUseTaxes.text")); - chkUseTaxes.setToolTipText(resources.getString("chkUseTaxes.toolTipText")); - chkUseTaxes.setName("chkUseTaxes"); - chkUseTaxes.addActionListener(evt -> { - final boolean isEnabled = chkUseTaxes.isSelected(); - - for (Component component : taxesSubPanel.getComponents()) { - component.setEnabled(isEnabled); - } - }); - - final JPanel taxesPanel = new JPanel(); - taxesPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("taxesPanel.title"))); - taxesPanel.setName("taxesPanel"); - - final GroupLayout layout = new GroupLayout(taxesPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - taxesPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseTaxes) - .addComponent(taxesSubPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseTaxes) - .addComponent(taxesSubPanel)); - - return taxesPanel; - } - - private JPanel createTaxesSubPanel() { - boolean isEnabled = campaign.getCampaignOptions().isUseTaxes(); - - JLabel lblTaxesPercentage = new JLabel(); - lblTaxesPercentage.setText(resources.getString("lblTaxesPercentage.text")); - lblTaxesPercentage.setToolTipText(resources.getString("lblTaxesPercentage.toolTipText")); - lblTaxesPercentage.setName("lblTaxesPercentage"); - lblTaxesPercentage.setEnabled(isEnabled); - - spnTaxesPercentage = new JSpinner(new SpinnerNumberModel(30, 1, 100, 1)); - spnTaxesPercentage.setToolTipText(resources.getString("lblTaxesPercentage.toolTipText")); - spnTaxesPercentage.setName("spnTaxesPercentage"); - spnTaxesPercentage.setEnabled(isEnabled); - - taxesSubPanel.setBorder(BorderFactory.createTitledBorder("")); - taxesSubPanel.setName("taxesSubPanel"); - - final GroupLayout layout = new GroupLayout(taxesSubPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - taxesSubPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblTaxesPercentage) - .addComponent(spnTaxesPercentage, Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblTaxesPercentage) - .addComponent(spnTaxesPercentage))); - - return taxesSubPanel; - } - - private JPanel createSharesPanel() { - chkUseShareSystem = new JCheckBox(resources.getString("chkUseShareSystem.text")); - chkUseShareSystem.setToolTipText(resources.getString("chkUseShareSystem.toolTipText")); - chkUseShareSystem.setName("chkUseShareSystem"); - - sharesSubPanel = createSharesSubPanel(); - - JPanel sharesPanel = new JDisableablePanel("sharesPanel"); - sharesPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("sharesPanel.title"))); - - final GroupLayout layout = new GroupLayout(sharesPanel); - sharesPanel.setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(chkUseShareSystem) - .addComponent(sharesSubPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseShareSystem) - .addComponent(sharesSubPanel)); - - return sharesPanel; - } - - private JPanel createSharesSubPanel() { - chkSharesForAll = new JCheckBox(resources.getString("chkSharesForAll.text")); - chkSharesForAll.setToolTipText(resources.getString("chkSharesForAll.toolTipText")); - chkSharesForAll.setName("chkSharesForAll"); - - sharesSubPanel = new JDisableablePanel("sharesSubPanel"); - - final GroupLayout layout = new GroupLayout(sharesSubPanel); - sharesSubPanel.setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setVerticalGroup(layout - .createSequentialGroup() - .addComponent(chkSharesForAll)); - - layout.setHorizontalGroup(layout - .createParallelGroup(Alignment.LEADING) - .addComponent(chkSharesForAll)); - - return sharesSubPanel; - } - // endregion Finances Tab - - // region Rank Systems Tab - private JScrollPane createRankSystemsTab() { - rankSystemsPane = new RankSystemsPane(getFrame(), getCampaign()); - return rankSystemsPane; - } - // endregion Rank Systems Tab - - // region Markets Tab - private JScrollPane createMarketsTab() { - final AbstractMHQScrollablePanel marketsPanel = new DefaultMHQScrollablePanel(getFrame(), - "marketsPanel", - new GridBagLayout()); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.anchor = GridBagConstraints.NORTH; - marketsPanel.add(createPersonnelMarketPanel(), gbc); - - gbc.gridx++; - marketsPanel.add(createContractMarketPanel(), gbc); - - gbc.gridx = 0; - gbc.gridy++; - marketsPanel.add(createUnitMarketPanel(), gbc); - - JScrollPane scrollMarkets = new JScrollPaneWithSpeed(marketsPanel); - scrollMarkets.setPreferredSize(new Dimension(500, 400)); - - return scrollMarkets; - } - - private JPanel createPersonnelMarketPanel() { - // Initialize Labels Used in ActionListeners - final JPanel personnelMarketRandomRemovalTargetsPanel = new JDisableablePanel( - "personnelMarketRandomRemovalTargetsPanel"); - final JLabel lblPersonnelMarketDylansWeight = new JLabel(); - - // Create Panel Components - final JLabel lblPersonnelMarketType = new JLabel(resources.getString("lblPersonnelMarket.text")); - lblPersonnelMarketType.setToolTipText(resources.getString("lblPersonnelMarketType.toolTipText")); - lblPersonnelMarketType.setName("lblPersonnelMarketType"); - - final DefaultComboBoxModel personnelMarketTypeModel = new DefaultComboBoxModel<>(); - for (final PersonnelMarketMethod method : PersonnelMarketServiceManager.getInstance() - .getAllServices(true)) { - personnelMarketTypeModel.addElement(method.getModuleName()); - } - comboPersonnelMarketType = new MMComboBox<>("comboPersonnelMarketType", personnelMarketTypeModel); - comboPersonnelMarketType.setToolTipText(resources.getString("lblPersonnelMarketType.toolTipText")); - comboPersonnelMarketType.addActionListener(evt -> { - final boolean isDylan = new PersonnelMarketDylan().getModuleName() - .equals(comboPersonnelMarketType.getSelectedItem()); - final boolean enabled = isDylan - || new PersonnelMarketRandom().getModuleName() - .equals(comboPersonnelMarketType.getSelectedItem()); - personnelMarketRandomRemovalTargetsPanel.setEnabled(enabled); - lblPersonnelMarketDylansWeight.setEnabled(isDylan); - spnPersonnelMarketDylansWeight.setEnabled(isDylan); - }); - - chkPersonnelMarketReportRefresh = new JCheckBox( - resources.getString("chkPersonnelMarketReportRefresh.text")); - chkPersonnelMarketReportRefresh - .setToolTipText(resources.getString("chkPersonnelMarketReportRefresh.toolTipText")); - chkPersonnelMarketReportRefresh.setName("chkPersonnelMarketReportRefresh"); - - createPersonnelMarketRandomRemovalTargetsPanel(personnelMarketRandomRemovalTargetsPanel); - - lblPersonnelMarketDylansWeight.setText(resources.getString("lblPersonnelMarketDylansWeight.text")); - lblPersonnelMarketDylansWeight - .setToolTipText(resources.getString("lblPersonnelMarketDylansWeight.toolTipText")); - lblPersonnelMarketDylansWeight.setName("lblPersonnelMarketDylansWeight"); - - spnPersonnelMarketDylansWeight = new JSpinner(new SpinnerNumberModel(0.3, 0, 1, 0.1)); - spnPersonnelMarketDylansWeight - .setToolTipText(resources.getString("lblPersonnelMarketDylansWeight.toolTipText")); - spnPersonnelMarketDylansWeight.setName("spnPersonnelMarketDylansWeight"); - - chkUsePersonnelHireHiringHallOnly = new JCheckBox( - resources.getString("chkUsePersonnelHireHiringHallOnly.text")); - chkUsePersonnelHireHiringHallOnly - .setToolTipText(resources.getString("chkUsePersonnelHireHiringHallOnly.toolTipText")); - chkUsePersonnelHireHiringHallOnly.setName("chkUsePersonnelHireHiringHallOnly"); - - // Programmatically Assign Accessibility Labels - lblPersonnelMarketType.setLabelFor(comboPersonnelMarketType); - lblPersonnelMarketDylansWeight.setLabelFor(spnPersonnelMarketDylansWeight); - - // Layout the UI - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("personnelMarketPanel.title"))); - panel.setName("personnelMarketPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPersonnelMarketType) - .addComponent(comboPersonnelMarketType, - Alignment.LEADING)) - .addComponent(chkPersonnelMarketReportRefresh) - .addComponent(personnelMarketRandomRemovalTargetsPanel) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPersonnelMarketDylansWeight) - .addComponent(spnPersonnelMarketDylansWeight, - Alignment.LEADING)) - .addComponent(chkUsePersonnelHireHiringHallOnly)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPersonnelMarketType) - .addComponent(comboPersonnelMarketType)) - .addComponent(chkPersonnelMarketReportRefresh) - .addComponent(personnelMarketRandomRemovalTargetsPanel) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPersonnelMarketDylansWeight) - .addComponent(spnPersonnelMarketDylansWeight)) - .addComponent(chkUsePersonnelHireHiringHallOnly)); - - return panel; - } - - private void createPersonnelMarketRandomRemovalTargetsPanel(final JPanel panel) { - spnPersonnelMarketRandomRemovalTargets = new HashMap<>(); - - // Create the Panel - panel.setBorder(BorderFactory - .createTitledBorder( - resources.getString("personnelMarketRandomRemovalTargetsPanel.title"))); - panel.setToolTipText(resources.getString("personnelMarketRandomRemovalTargetsPanel.toolTipText")); - panel.setLayout(new GridLayout(Skills.SKILL_LEVELS.length, 2)); - - // Fill out the Panel - for (final SkillLevel skillLevel : Skills.SKILL_LEVELS) { - final JLabel label = new JLabel(skillLevel.toString()); - label.setToolTipText( - resources.getString("personnelMarketRandomRemovalTargetsPanel.toolTipText")); - label.setName("lbl" + skillLevel); - panel.add(label); - - final JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, 12, 1)); - spinner.setToolTipText( - resources.getString("personnelMarketRandomRemovalTargetsPanel.toolTipText")); - spinner.setName("spn" + skillLevel); - spnPersonnelMarketRandomRemovalTargets.put(skillLevel, spinner); - panel.add(spinner); - - label.setLabelFor(spinner); - } - } - - private JPanel createUnitMarketPanel() { - // Create Panel Components - final JLabel lblUnitMarketMethod = new JLabel(resources.getString("lblUnitMarketMethod.text")); - lblUnitMarketMethod.setToolTipText(resources.getString("lblUnitMarketMethod.toolTipText")); - lblUnitMarketMethod.setName("lblUnitMarketMethod"); - - comboUnitMarketMethod = new MMComboBox<>("comboUnitMarketMethod", UnitMarketMethod.values()); - comboUnitMarketMethod.setToolTipText(resources.getString("lblUnitMarketMethod.toolTipText")); - comboUnitMarketMethod.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof UnitMarketMethod) { - list.setToolTipText(((UnitMarketMethod) value).getToolTipText()); - } - return this; - } - }); - comboUnitMarketMethod.addActionListener(evt -> { - final UnitMarketMethod method = comboUnitMarketMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean enabled = !method.isNone(); - chkUnitMarketRegionalMekVariations.setEnabled(enabled); - lblUnitMarketSpecialUnitChance.setEnabled(enabled); - spnUnitMarketSpecialUnitChance.setEnabled(enabled); - lblUnitMarketRarityModifier.setEnabled(enabled); - spnUnitMarketRarityModifier.setEnabled(enabled); - chkInstantUnitMarketDelivery.setEnabled(enabled); - chkUnitMarketReportRefresh.setEnabled(enabled); - }); - - chkUnitMarketRegionalMekVariations = new JCheckBox( - resources.getString("chkUnitMarketRegionalMekVariations.text")); - chkUnitMarketRegionalMekVariations - .setToolTipText(resources.getString("chkUnitMarketRegionalMekVariations.toolTipText")); - chkUnitMarketRegionalMekVariations.setName("chkUnitMarketRegionalMekVariations"); - - lblUnitMarketSpecialUnitChance = new JLabel(); - lblUnitMarketSpecialUnitChance.setText(resources.getString("lblUnitMarketSpecialUnitChance.text")); - lblUnitMarketSpecialUnitChance - .setToolTipText(resources.getString("lblUnitMarketSpecialUnitChance.toolTipText")); - lblUnitMarketSpecialUnitChance.setName("lblUnitMarketSpecialUnitChance"); - - spnUnitMarketSpecialUnitChance = new JSpinner(new SpinnerNumberModel(30, 0, 100, 1)); - spnUnitMarketSpecialUnitChance - .setToolTipText(resources.getString("lblUnitMarketSpecialUnitChance.toolTipText")); - spnUnitMarketSpecialUnitChance.setName("spnUnitMarketSpecialUnitChance"); - - lblUnitMarketRarityModifier = new JLabel(resources.getString("lblUnitMarketRarityModifier.text")); - lblUnitMarketRarityModifier - .setToolTipText(resources.getString("lblUnitMarketRarityModifier.toolTipText")); - lblUnitMarketRarityModifier.setName("lblUnitMarketRarityModifier"); - - spnUnitMarketRarityModifier = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1)); - spnUnitMarketRarityModifier - .setToolTipText(resources.getString("lblUnitMarketRarityModifier.toolTipText")); - spnUnitMarketRarityModifier.setName("spnUnitMarketRarityModifier"); - - chkInstantUnitMarketDelivery = new JCheckBox(resources.getString("chkInstantUnitMarketDelivery.text")); - chkInstantUnitMarketDelivery - .setToolTipText(resources.getString("chkInstantUnitMarketDelivery.toolTipText")); - chkInstantUnitMarketDelivery.setName("chkInstantUnitMarketDelivery"); - - chkUnitMarketReportRefresh = new JCheckBox(resources.getString("chkUnitMarketReportRefresh.text")); - chkUnitMarketReportRefresh - .setToolTipText(resources.getString("chkUnitMarketReportRefresh.toolTipText")); - chkUnitMarketReportRefresh.setName("chkUnitMarketReportRefresh"); - - // Programmatically Assign Accessibility Labels - lblUnitMarketMethod.setLabelFor(comboUnitMarketMethod); - - // Layout the UI - final JPanel panel = new JPanel(); - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("unitMarketPanel.title"))); - panel.setName("unitMarketPanel"); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblUnitMarketMethod) - .addComponent(comboUnitMarketMethod, Alignment.LEADING)) - .addComponent(chkUnitMarketRegionalMekVariations) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblUnitMarketSpecialUnitChance) - .addComponent(spnUnitMarketSpecialUnitChance, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblUnitMarketRarityModifier) - .addComponent(spnUnitMarketRarityModifier, - Alignment.LEADING)) - .addComponent(chkInstantUnitMarketDelivery) - .addComponent(chkUnitMarketReportRefresh)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblUnitMarketMethod) - .addComponent(comboUnitMarketMethod)) - .addComponent(chkUnitMarketRegionalMekVariations) - .addGroup(layout.createSequentialGroup() - .addComponent(lblUnitMarketSpecialUnitChance) - .addComponent(spnUnitMarketSpecialUnitChance)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblUnitMarketRarityModifier) - .addComponent(spnUnitMarketRarityModifier)) - .addComponent(chkInstantUnitMarketDelivery) - .addComponent(chkUnitMarketReportRefresh)); - - return panel; - } - - private JPanel createContractMarketPanel() { - // Initialize Labels Used in ActionListeners - final JLabel lblContractSearchRadius = new JLabel(); - final JLabel lblCoontractMaxSalvagePercentage = new JLabel(); - - // Create Panel Components - final JLabel lblContractMarketMethod = new JLabel(resources.getString("lblContractMarketMethod.text")); - lblContractMarketMethod.setToolTipText(resources.getString("lblContractMarketMethod.toolTipText")); - lblContractMarketMethod.setName("lblContractMarketMethod"); - - comboContractMarketMethod = new MMComboBox<>("comboContractMarketMethod", - ContractMarketMethod.values()); - comboContractMarketMethod.setToolTipText(resources.getString("lblContractMarketMethod.toolTipText")); - comboContractMarketMethod.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof ContractMarketMethod) { - list.setToolTipText(wordWrap(((ContractMarketMethod) value).getToolTipText())); - } - return this; - } - }); - comboContractMarketMethod.addActionListener(evt -> { - final ContractMarketMethod method = comboContractMarketMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean enabled = !method.isNone(); - lblContractSearchRadius.setEnabled(enabled); - spnContractSearchRadius.setEnabled(enabled); - chkVariableContractLength.setEnabled(enabled); - chkContractMarketReportRefresh.setEnabled(enabled); - spnContractMaxSalvagePercentage.setEnabled(enabled); - }); - comboContractMarketMethod.setEnabled(true); - - lblContractSearchRadius.setText(resources.getString("lblContractSearchRadius.text")); - lblContractSearchRadius.setToolTipText(resources.getString("lblContractSearchRadius.toolTipText")); - lblContractSearchRadius.setName("lblContractSearchRadius"); - - spnContractSearchRadius = new JSpinner(new SpinnerNumberModel(300, 100, 2500, 100)); - spnContractSearchRadius.setToolTipText(resources.getString("lblContractSearchRadius.toolTipText")); - spnContractSearchRadius.setName("spnContractSearchRadius"); - - chkVariableContractLength = new JCheckBox(resources.getString("chkVariableContractLength.text")); - chkVariableContractLength.setToolTipText(resources.getString("chkVariableContractLength.toolTipText")); - chkVariableContractLength.setName("chkVariableContractLength"); - - chkContractMarketReportRefresh = new JCheckBox( - resources.getString("chkContractMarketReportRefresh.text")); - chkContractMarketReportRefresh - .setToolTipText(resources.getString("chkContractMarketReportRefresh.toolTipText")); - chkContractMarketReportRefresh.setName("chkContractMarketReportRefresh"); - - lblCoontractMaxSalvagePercentage.setText(resources.getString("lblContractMaxSalvagePercentage.text")); - lblCoontractMaxSalvagePercentage - .setToolTipText(resources.getString("lblContractMaxSalvagePercentage.toolTipText")); - lblCoontractMaxSalvagePercentage.setName("lblContractSearchRadius"); - - spnContractMaxSalvagePercentage = new JSpinner(new SpinnerNumberModel(100, 0, 100, 10)); - spnContractMaxSalvagePercentage - .setToolTipText(resources.getString("lblContractMaxSalvagePercentage.toolTipText")); - spnContractMaxSalvagePercentage.setName("spnContractMaxSalvagePercentage"); - - JLabel lblDropShipBonusPercentage = new JLabel(resources.getString("lblDropShipBonusPercentage.text")); - lblDropShipBonusPercentage - .setToolTipText(wordWrap( - resources.getString("lblDropShipBonusPercentage.toolTipText"))); - lblDropShipBonusPercentage.setName("lblDropShipBonusPercentage"); - - spnDropShipBonusPercentage = new JSpinner(new SpinnerNumberModel(0, 0, 20, 5)); - spnDropShipBonusPercentage - .setToolTipText(resources.getString("lblDropShipBonusPercentage.toolTipText")); - spnDropShipBonusPercentage.setName("spnDropShipBonusPercentage"); - - // Programmatically Assign Accessibility Labels - lblContractMarketMethod.setLabelFor(comboContractMarketMethod); - lblContractSearchRadius.setLabelFor(spnContractSearchRadius); - lblCoontractMaxSalvagePercentage.setLabelFor(spnContractMaxSalvagePercentage); - - // Layout the UI - contractMarketPanel = new JDisableablePanel("contractMarketPanel"); - contractMarketPanel - .setBorder(BorderFactory - .createTitledBorder(resources.getString("contractMarketPanel.title"))); - - final GroupLayout layout = new GroupLayout(contractMarketPanel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - contractMarketPanel.setLayout(layout); - - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblContractMarketMethod) - .addComponent(comboContractMarketMethod, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblContractSearchRadius) - .addComponent(spnContractSearchRadius, - Alignment.LEADING)) - .addComponent(chkVariableContractLength) - .addComponent(chkContractMarketReportRefresh) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblCoontractMaxSalvagePercentage) - .addComponent(spnContractMaxSalvagePercentage, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblDropShipBonusPercentage) - .addComponent(spnDropShipBonusPercentage, - Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblContractMarketMethod) - .addComponent(comboContractMarketMethod)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblContractSearchRadius) - .addComponent(spnContractSearchRadius)) - .addComponent(chkVariableContractLength) - .addComponent(chkContractMarketReportRefresh) - .addGroup(layout.createSequentialGroup() - .addComponent(lblCoontractMaxSalvagePercentage) - .addComponent(spnContractMaxSalvagePercentage)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblDropShipBonusPercentage) - .addComponent(spnDropShipBonusPercentage))); - - return contractMarketPanel; - } - // endregion Markets Tab - - // region RATs Tab - private JScrollPane createRATTab() { - // Initialize Components Used in ActionListeners - final JDisableablePanel traditionalRATPanel = new JDisableablePanel("traditionalRATPanel"); - - // Initialize Parsing Variables - final ButtonGroup group = new ButtonGroup(); - - // Create the Panel - final AbstractMHQScrollablePanel panel = new DefaultMHQScrollablePanel(getFrame(), "ratPanel", - new GridBagLayout()); - - // Create Panel Components - btnUseRATGenerator = new JRadioButton(resources.getString("btnUseRATGenerator.text")); - btnUseRATGenerator.setToolTipText(resources.getString("btnUseRATGenerator.tooltip")); - btnUseRATGenerator.setName("btnUseRATGenerator"); - group.add(btnUseRATGenerator); - final GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.anchor = GridBagConstraints.NORTH; - panel.add(btnUseRATGenerator, gbc); - - btnUseStaticRATs = new JRadioButton(resources.getString("btnUseStaticRATs.text")); - btnUseStaticRATs.setToolTipText(resources.getString("btnUseStaticRATs.tooltip")); - btnUseStaticRATs.setName("btnUseStaticRATs"); - btnUseStaticRATs.addItemListener(ev -> traditionalRATPanel.setEnabled(btnUseStaticRATs.isSelected())); - group.add(btnUseStaticRATs); - gbc.gridy++; - panel.add(btnUseStaticRATs, gbc); - - createTraditionalRATPanel(traditionalRATPanel); - gbc.gridy++; - panel.add(traditionalRATPanel, gbc); - - // Disable Panel Portions by Default - btnUseStaticRATs.setSelected(true); - btnUseStaticRATs.doClick(); - - return new JScrollPaneWithSpeed(panel); - } - - private void createTraditionalRATPanel(final JDisableablePanel panel) { - // Initialize Components Used in ActionListeners - final JList chosenRATs = new JList<>(); - - // Create Panel Components - final JTextArea txtRATInstructions = new JTextArea(resources.getString("txtRATInstructions.text")); - txtRATInstructions.setEditable(false); - txtRATInstructions.setLineWrap(true); - txtRATInstructions.setWrapStyleWord(true); - - final JLabel lblAvailableRATs = new JLabel(resources.getString("lblAvailableRATs.text")); - - availableRATModel = new DefaultListModel<>(); - for (final String rat : RATManager.getAllRATCollections().keySet()) { - final List eras = RATManager.getAllRATCollections().get(rat); - if (eras != null) { - final StringBuilder displayName = new StringBuilder(rat); - if (!eras.isEmpty()) { - displayName.append(" (").append(eras.get(0)); - if (eras.size() > 1) { - displayName.append('-').append(eras.get(eras.size() - 1)); - } - displayName.append(')'); - } - availableRATModel.addElement(displayName.toString()); - } - } - final JList availableRATs = new JList<>(availableRATModel); - availableRATs.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - final JButton btnAddRAT = new MMButton("btnAddRAT", resources, "btnAddRAT.text", - "btnAddRAT.toolTipText", evt -> { - final int selectedIndex = availableRATs.getSelectedIndex(); - if (selectedIndex < 0) { - return; - } - chosenRATModel.addElement(availableRATs.getSelectedValue()); - availableRATModel.removeElementAt(availableRATs.getSelectedIndex()); - availableRATs.setSelectedIndex( - Math.min(selectedIndex, availableRATModel.size() - 1)); - }); - - final JButton btnRemoveRAT = new MMButton("btnRemoveRAT", resources, "btnRemoveRAT.text", - "btnRemoveRAT.toolTipText", evt -> { - final int selectedIndex = chosenRATs.getSelectedIndex(); - if (selectedIndex < 0) { - return; - } - availableRATModel.addElement(chosenRATs.getSelectedValue()); - chosenRATModel.removeElementAt(chosenRATs.getSelectedIndex()); - chosenRATs.setSelectedIndex(Math.min(selectedIndex, chosenRATModel.size() - 1)); - }); - - final JButton btnMoveRATUp = new MMButton("btnMoveRATUp", resources, "btnMoveRATUp.text", - "btnMoveRATUp.toolTipText", evt -> { - final int selectedIndex = chosenRATs.getSelectedIndex(); - if (selectedIndex < 0) { - return; - } - final String element = chosenRATModel.getElementAt(selectedIndex); - chosenRATModel.setElementAt(chosenRATModel.getElementAt(selectedIndex - 1), - selectedIndex); - chosenRATModel.setElementAt(element, selectedIndex - 1); - chosenRATs.setSelectedIndex(selectedIndex - 1); - }); - - final JButton btnMoveRATDown = new MMButton("btnMoveRATDown", resources, "btnMoveRATDown.text", - "btnMoveRATDown.toolTipText", evt -> { - final int selectedIndex = chosenRATs.getSelectedIndex(); - if (selectedIndex < 0) { - return; - } - final String element = chosenRATModel.getElementAt(selectedIndex); - chosenRATModel.setElementAt(chosenRATModel.getElementAt(selectedIndex + 1), - selectedIndex); - chosenRATModel.setElementAt(element, selectedIndex + 1); - chosenRATs.setSelectedIndex(selectedIndex + 1); - }); - - final JLabel lblChosenRATs = new JLabel(resources.getString("lblChosenRATs.text")); - - chosenRATModel = new DefaultListModel<>(); - chosenRATs.setModel(chosenRATModel); - chosenRATs.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - chosenRATs.addListSelectionListener(evt -> { - btnRemoveRAT.setEnabled(chosenRATs.getSelectedIndex() >= 0); - btnMoveRATUp.setEnabled(chosenRATs.getSelectedIndex() > 0); - btnMoveRATDown.setEnabled(chosenRATModel.size() > chosenRATs.getSelectedIndex() + 1); - }); - - chkIgnoreRATEra = new JCheckBox(resources.getString("chkIgnoreRATEra.text")); - chkIgnoreRATEra.setToolTipText(resources.getString("chkIgnoreRATEra.toolTipText")); - chkIgnoreRATEra.setName("chkIgnoreRATEra"); - - // Add Previously Impossible Listeners - availableRATs.addListSelectionListener( - evt -> btnAddRAT.setEnabled(availableRATs.getSelectedIndex() >= 0)); - - // Programmatically Assign Accessibility Labels - lblAvailableRATs.setLabelFor(availableRATs); - lblChosenRATs.setLabelFor(chosenRATs); - - // Layout the UI - panel.setBorder(BorderFactory.createTitledBorder(resources.getString("traditionalRATPanel.title"))); - panel.setLayout(new BorderLayout()); - - final GroupLayout layout = new GroupLayout(panel); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - panel.setLayout(layout); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(txtRATInstructions) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addComponent(lblAvailableRATs) - .addComponent(availableRATs)) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addComponent(btnAddRAT) - .addComponent(btnRemoveRAT) - .addComponent(btnMoveRATUp) - .addComponent(btnMoveRATDown)) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addComponent(lblChosenRATs) - .addComponent(chosenRATs))) - .addComponent(chkIgnoreRATEra)); - - layout.setVerticalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(txtRATInstructions) - .addPreferredGap(ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblAvailableRATs) - .addComponent(lblChosenRATs)) - .addPreferredGap(ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addComponent(availableRATs) - .addGroup(layout.createSequentialGroup() - .addComponent(btnAddRAT) - .addPreferredGap( - ComponentPlacement.RELATED) - .addComponent(btnRemoveRAT) - .addPreferredGap( - ComponentPlacement.RELATED) - .addComponent(btnMoveRATUp) - .addPreferredGap( - ComponentPlacement.RELATED) - .addComponent(btnMoveRATDown)) - .addComponent(chosenRATs)) - .addComponent(chkIgnoreRATEra))); - } - // endregion RATs Tab - - // region Against the Bot Tab - // endregion Against the Bot Tab - // endregion Modern Initialization - // endregion Initialization - - // region Options - public void setOptions(@Nullable CampaignOptions options, - @Nullable RandomSkillPreferences randomSkillPreferences) { - // Use the provided options and preferences when possible, but flip if they are - // null to be safe - if (options != null) { - this.options = options; - } else { - options = this.options; - } - - if (randomSkillPreferences != null) { - this.rSkillPrefs = randomSkillPreferences; - } else { - randomSkillPreferences = this.rSkillPrefs; - } - - // region General Tab - unitRatingMethodCombo.setSelectedItem(options.getUnitRatingMethod()); - manualUnitRatingModifier.setValue(options.getManualUnitRatingModifier()); - // endregion General Tab - - // region Repair and Maintenance Tab - useEraModsCheckBox.setSelected(options.isUseEraMods()); - assignedTechFirstCheckBox.setSelected(options.isAssignedTechFirst()); - resetToFirstTechCheckBox.setSelected(options.isResetToFirstTech()); - useQuirksBox.setSelected(options.isUseQuirks()); - useAeroSystemHitsBox.setSelected(options.isUseAeroSystemHits()); - if (useDamageMargin.isSelected() != options.isDestroyByMargin()) { - useDamageMargin.doClick(); - } - spnDamageMargin.setValue(options.getDestroyMargin()); - spnDestroyPartTarget.setValue(options.getDestroyPartTarget()); - - if (checkMaintenance.isSelected() != options.isCheckMaintenance()) { - checkMaintenance.doClick(); - } - spnMaintenanceDays.setValue(options.getMaintenanceCycleDays()); - spnMaintenanceBonus.setValue(options.getMaintenanceBonus()); - spnDefaultMaintenanceTime.setValue(options.getDefaultMaintenanceTime()); - useQualityMaintenance.setSelected(options.isUseQualityMaintenance()); - reverseQualityNames.setSelected(options.isReverseQualityNames()); - chkUseRandomUnitQualities.setSelected(options.isUseRandomUnitQualities()); - chkUsePlanetaryModifiers.setSelected(options.isUsePlanetaryModifiers()); - useUnofficialMaintenance.setSelected(options.isUseUnofficialMaintenance()); - logMaintenance.setSelected(options.isLogMaintenance()); - // endregion Repair and Maintenance Tab - - // region Supplies and Acquisitions Tab - spnAcquireWaitingPeriod.setValue(options.getWaitingPeriod()); - choiceAcquireSkill.setSelectedItem(options.getAcquisitionSkill()); - chkSupportStaffOnly.setSelected(options.isAcquisitionSupportStaffOnly()); - spnAcquireClanPenalty.setValue(options.getClanAcquisitionPenalty()); - spnAcquireIsPenalty.setValue(options.getIsAcquisitionPenalty()); - spnMaxAcquisitions.setValue(options.getMaxAcquisitions()); - - spnNDiceTransitTime.setValue(options.getNDiceTransitTime()); - spnConstantTransitTime.setValue(options.getConstantTransitTime()); - choiceTransitTimeUnits - .setSelectedItem(CampaignOptions.getTransitUnitName(options.getUnitTransitTime())); - spnAcquireMinimum.setValue(options.getAcquireMinimumTime()); - choiceAcquireMinimumUnit - .setSelectedItem(CampaignOptions - .getTransitUnitName(options.getAcquireMinimumTimeUnit())); - spnAcquireMosBonus.setValue(options.getAcquireMosBonus()); - choiceAcquireMosUnits.setSelectedItem(CampaignOptions.getTransitUnitName(options.getAcquireMosUnit())); - - usePlanetaryAcquisitions.setSelected(options.isUsePlanetaryAcquisition()); - spnMaxJumpPlanetaryAcquisitions.setValue(options.getMaxJumpsPlanetaryAcquisition()); - comboPlanetaryAcquisitionsFactionLimits.setSelectedItem(options.getPlanetAcquisitionFactionLimit()); - disallowPlanetaryAcquisitionClanCrossover.setSelected(options.isPlanetAcquisitionNoClanCrossover()); - disallowClanPartsFromIS.setSelected(options.isNoClanPartsFromIS()); - spnPenaltyClanPartsFromIS.setValue(options.getPenaltyClanPartsFromIS()); - usePlanetaryAcquisitionsVerbose.setSelected(options.isPlanetAcquisitionVerbose()); - for (int i = EquipmentType.RATING_A; i <= EquipmentType.RATING_F; i++) { - spnPlanetAcquireTechBonus[i].setValue(options.getPlanetTechAcquisitionBonus(i)); - spnPlanetAcquireIndustryBonus[i].setValue(options.getPlanetIndustryAcquisitionBonus(i)); - spnPlanetAcquireOutputBonus[i].setValue(options.getPlanetOutputAcquisitionBonus(i)); - } - // endregion Supplies and Acquisitions Tab - - // region Tech Limits Tab - if (limitByYearBox.isSelected() != options.isLimitByYear()) { - limitByYearBox.doClick(); - } - disallowExtinctStuffBox.setSelected(options.isDisallowExtinctStuff()); - allowClanPurchasesBox.setSelected(options.isAllowClanPurchases()); - allowISPurchasesBox.setSelected(options.isAllowISPurchases()); - allowCanonOnlyBox.setSelected(options.isAllowCanonOnly()); - allowCanonRefitOnlyBox.setSelected(options.isAllowCanonRefitOnly()); - choiceTechLevel.setSelectedIndex(options.getTechLevel()); - variableTechLevelBox.setSelected(options.isVariableTechLevel() && options.isLimitByYear()); - factionIntroDateBox.setSelected(options.isFactionIntroDate()); - useAmmoByTypeBox.setSelected(options.isUseAmmoByType()); - // endregion Tech Limits Tab - - // region Personnel Tab - // General Personnel - chkUseTactics.setSelected(options.isUseTactics()); - chkUseInitiativeBonus.setSelected(options.isUseInitiativeBonus()); - chkUseToughness.setSelected(options.isUseToughness()); - chkUseRandomToughness.setSelected(options.isUseRandomToughness()); - chkUseArtillery.setSelected(options.isUseArtillery()); - chkUseAbilities.setSelected(options.isUseAbilities()); - if (chkUseEdge.isSelected() != options.isUseEdge()) { - chkUseEdge.doClick(); - } - chkUseSupportEdge.setSelected(options.isUseSupportEdge()); - chkUseImplants.setSelected(options.isUseImplants()); - chkUseAlternativeQualityAveraging.setSelected(options.isAlternativeQualityAveraging()); - chkUseTransfers.setSelected(options.isUseTransfers()); - chkUseExtendedTOEForceName.setSelected(options.isUseExtendedTOEForceName()); - chkPersonnelLogSkillGain.setSelected(options.isPersonnelLogSkillGain()); - chkPersonnelLogAbilityGain.setSelected(options.isPersonnelLogAbilityGain()); - chkPersonnelLogEdgeGain.setSelected(options.isPersonnelLogEdgeGain()); - chkDisplayPersonnelLog.setSelected(options.isDisplayPersonnelLog()); - chkDisplayScenarioLog.setSelected(options.isDisplayScenarioLog()); - chkDisplayKillRecord.setSelected(options.isDisplayKillRecord()); - - // Expanded Personnel Information - if (chkUseTimeInService.isSelected() != options.isUseTimeInService()) { - chkUseTimeInService.doClick(); - } - comboTimeInServiceDisplayFormat.setSelectedItem(options.getTimeInServiceDisplayFormat()); - if (chkUseTimeInRank.isSelected() != options.isUseTimeInRank()) { - chkUseTimeInRank.doClick(); - } - comboTimeInRankDisplayFormat.setSelectedItem(options.getTimeInRankDisplayFormat()); - chkTrackTotalEarnings.setSelected(options.isTrackTotalEarnings()); - chkTrackTotalXPEarnings.setSelected(options.isTrackTotalXPEarnings()); - chkShowOriginFaction.setSelected(options.isShowOriginFaction()); - - // Administrators - chkAdminsHaveNegotiation.setSelected(options.isAdminsHaveNegotiation()); - chkAdminExperienceLevelIncludeNegotiation - .setSelected(options.isAdminExperienceLevelIncludeNegotiation()); - chkAdminsHaveScrounge.setSelected(options.isAdminsHaveScrounge()); - chkAdminExperienceLevelIncludeScrounge.setSelected(options.isAdminExperienceLevelIncludeScrounge()); - - // Medical - chkUseAdvancedMedical.setSelected(options.isUseAdvancedMedical()); - spnHealWaitingPeriod.setValue(options.getHealingWaitingPeriod()); - spnNaturalHealWaitingPeriod.setValue(options.getNaturalHealingWaitingPeriod()); - spnMinimumHitsForVehicles.setValue(options.getMinimumHitsForVehicles()); - chkUseRandomHitsForVehicles.setSelected(options.isUseRandomHitsForVehicles()); - chkUseTougherHealing.setSelected(options.isTougherHealing()); - spnMaximumPatients.setValue(options.getMaximumPatients()); - - // Prisoners - comboPrisonerCaptureStyle.setSelectedItem(options.getPrisonerCaptureStyle()); - comboPrisonerStatus.setSelectedItem(options.getDefaultPrisonerStatus()); - chkPrisonerBabyStatus.setSelected(options.isPrisonerBabyStatus()); - chkAtBPrisonerDefection.setSelected(options.isUseAtBPrisonerDefection()); - chkAtBPrisonerRansom.setSelected(options.isUseAtBPrisonerRansom()); - - // Dependent - chkUseRandomDependentAddition.setSelected(options.isUseRandomDependentAddition()); - chkUseRandomDependentRemoval.setSelected(options.isUseRandomDependentRemoval()); - - // Personnel Removal - chkUsePersonnelRemoval.setSelected(options.isUsePersonnelRemoval()); - chkUseRemovalExemptCemetery.setSelected(options.isUseRemovalExemptCemetery()); - chkUseRemovalExemptRetirees.setSelected(options.isUseRemovalExemptRetirees()); - - // Salary - chkDisableSecondaryRoleSalary.setSelected(options.isDisableSecondaryRoleSalary()); - spnAntiMekSalary.setValue(options.getSalaryAntiMekMultiplier()); - spnSpecialistInfantrySalary.setValue(options.getSalarySpecialistInfantryMultiplier()); - for (final Entry entry : spnSalaryExperienceMultipliers.entrySet()) { - entry.getValue().setValue(options.getSalaryXPMultipliers().get(entry.getKey())); - } - - for (int i = 0; i < spnBaseSalary.length; i++) { - spnBaseSalary[i].setValue(options.getRoleBaseSalaries()[i].getAmount().doubleValue()); - } - - // Awards - comboAwardBonusStyle.setSelectedItem(options.getAwardBonusStyle()); - chkEnableAutoAwards.setSelected(options.isEnableAutoAwards()); - chkIssuePosthumousAwards.setSelected(options.isIssuePosthumousAwards()); - chkIssueBestAwardOnly.setSelected(options.isIssueBestAwardOnly()); - chkIgnoreStandardSet.setSelected(options.isIgnoreStandardSet()); - spnAwardTierSize.setValue(options.getAwardTierSize()); - chkEnableContractAwards.setSelected(options.isEnableContractAwards()); - chkEnableFactionHunterAwards.setSelected(options.isEnableFactionHunterAwards()); - chkEnableInjuryAwards.setSelected(options.isEnableInjuryAwards()); - chkEnableIndividualKillAwards.setSelected(options.isEnableIndividualKillAwards()); - chkEnableFormationKillAwards.setSelected(options.isEnableFormationKillAwards()); - chkEnableRankAwards.setSelected(options.isEnableRankAwards()); - chkEnableScenarioAwards.setSelected(options.isEnableScenarioAwards()); - chkEnableSkillAwards.setSelected(options.isEnableSkillAwards()); - chkEnableTheatreOfWarAwards.setSelected(options.isEnableTheatreOfWarAwards()); - chkEnableTimeAwards.setSelected(options.isEnableTimeAwards()); - chkEnableTrainingAwards.setSelected(options.isEnableTimeAwards()); - chkEnableMiscAwards.setSelected(options.isEnableMiscAwards()); - txtAwardSetFilterList.setText(options.getAwardSetFilterList()); - // endregion Personnel Tab - - // region Turnover and Retention Tab - // Header - chkUseRandomRetirement.setSelected(options.isUseRandomRetirement()); - - // Settings - spnTurnoverFixedTargetNumber.setValue(options.getTurnoverFixedTargetNumber()); - comboTurnoverFrequency.setSelectedItem(options.getTurnoverFrequency()); - chkUseContractCompletionRandomRetirement.setSelected(options.isUseContractCompletionRandomRetirement()); - chkUseRandomFounderTurnover.setSelected(options.isUseRandomFounderTurnover()); - chkUseFounderRetirement.setSelected(options.isUseFounderRetirement()); - chkTrackOriginalUnit.setSelected(options.isTrackOriginalUnit()); - chkAeroRecruitsHaveUnits.setSelected(options.isAeroRecruitsHaveUnits()); - chkUseSubContractSoldiers.setSelected(options.isUseSubContractSoldiers()); - spnServiceContractDuration.setValue(options.getServiceContractDuration()); - spnServiceContractModifier.setValue(options.getServiceContractModifier()); - chkPayBonusDefault.setSelected(options.isPayBonusDefault()); - spnPayBonusDefaultThreshold.setValue(options.getPayBonusDefaultThreshold()); - - // Modifiers - chkUseCustomRetirementModifiers.setSelected(options.isUseCustomRetirementModifiers()); - chkUseFatigueModifiers.setSelected(options.isUseFatigueModifiers()); - chkUseSkillModifiers.setSelected(options.isUseSkillModifiers()); - chkUseAgeModifiers.setSelected(options.isUseAgeModifiers()); - chkUseUnitRatingModifiers.setSelected(options.isUseUnitRatingModifiers()); - chkUseFactionModifiers.setSelected(options.isUseFactionModifiers()); - chkUseHostileTerritoryModifiers.setSelected(options.isUseHostileTerritoryModifiers()); - chkUseMissionStatusModifiers.setSelected(options.isUseMissionStatusModifiers()); - chkUseFamilyModifiers.setSelected(options.isUseFamilyModifiers()); - chkUseLoyaltyModifiers.setSelected(options.isUseLoyaltyModifiers()); - chkUseHideLoyalty.setSelected(options.isUseHideLoyalty()); - - // Payouts - spnPayoutRateOfficer.setValue(options.getPayoutRateOfficer()); - spnPayoutRateEnlisted.setValue(options.getPayoutRateEnlisted()); - spnPayoutRetirementMultiplier.setValue(options.getPayoutRetirementMultiplier()); - chkUsePayoutServiceBonus.setSelected(options.isUsePayoutServiceBonus()); - spnPayoutServiceBonusRate.setValue(options.getPayoutServiceBonusRate()); - - // Unit Cohesion - chkUseAdministrativeStrain.setSelected(options.isUseAdministrativeStrain()); - chkUseManagementSkill.setSelected(options.isUseManagementSkill()); - - spnAdministrativeCapacity.setValue(options.getAdministrativeCapacity()); - spnMultiCrewStrainDivider.setValue(options.getMultiCrewStrainDivider()); - - chkUseCommanderLeadershipOnly.setSelected(options.isUseCommanderLeadershipOnly()); - spnManagementSkillPenalty.setValue(options.getManagementSkillPenalty()); - - // Fatigue - chkUseFatigue.setSelected(options.isUseFatigue()); - spnFatigueRate.setValue(options.getFatigueRate()); - chkUseInjuryFatigue.setSelected(options.isUseInjuryFatigue()); - spnFieldKitchenCapacity.setValue(options.getFieldKitchenCapacity()); - chkFieldKitchenIgnoreNonCombatants.setSelected(options.isUseFieldKitchenIgnoreNonCombatants()); - spnFatigueLeaveThreshold.setValue(options.getFatigueLeaveThreshold()); - // endregion Turnover and Retention Tab - - // 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()); - - // Anniversaries - chkAnnounceBirthdays.setSelected(options.isAnnounceBirthdays()); - chkAnnounceRecruitmentAnniversaries.setSelected(options.isAnnounceRecruitmentAnniversaries()); - chkAnnounceOfficersOnly.setSelected(options.isAnnounceOfficersOnly()); - chkAnnounceChildBirthdays.setSelected(options.isAnnounceChildBirthdays()); - - // Marriage - chkUseManualMarriages.setSelected(options.isUseManualMarriages()); - chkUseClanPersonnelMarriages.setSelected(options.isUseClanPersonnelMarriages()); - chkUsePrisonerMarriages.setSelected(options.isUsePrisonerMarriages()); - 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()); - chkUseRandomClanPersonnelMarriages.setSelected(options.isUseRandomClanPersonnelMarriages()); - chkUseRandomPrisonerMarriages.setSelected(options.isUseRandomPrisonerMarriages()); - spnRandomMarriageAgeRange.setValue(options.getRandomMarriageAgeRange()); - spnRandomMarriageDiceSize.setValue(options.getRandomMarriageDiceSize()); - spnRandomSameSexMarriageDiceSize.setValue(options.getRandomSameSexMarriageDiceSize()); - spnRandomNewDependentMarriage.setValue(options.getRandomNewDependentMarriage()); - - // Divorce - chkUseManualDivorce.setSelected(options.isUseManualDivorce()); - chkUseClanPersonnelDivorce.setSelected(options.isUseClanPersonnelDivorce()); - chkUsePrisonerDivorce.setSelected(options.isUsePrisonerDivorce()); - for (final Entry entry : spnDivorceSurnameWeights.entrySet()) { - entry.getValue().setValue(options.getDivorceSurnameWeights().get(entry.getKey()) / 10.0); - } - comboRandomDivorceMethod.setSelectedItem(options.getRandomDivorceMethod()); - if (chkUseRandomOppositeSexDivorce.isSelected() != options.isUseRandomOppositeSexDivorce()) { - if (chkUseRandomOppositeSexDivorce.isEnabled()) { - chkUseRandomOppositeSexDivorce.doClick(); - } else { - chkUseRandomOppositeSexDivorce.setSelected(options.isUseRandomOppositeSexDivorce()); - } - } - - if (chkUseRandomSameSexDivorce.isSelected() != options.isUseRandomSameSexDivorce()) { - if (chkUseRandomSameSexDivorce.isEnabled()) { - chkUseRandomSameSexDivorce.doClick(); - } else { - chkUseRandomSameSexDivorce.setSelected(options.isUseRandomSameSexDivorce()); - } - } - chkUseRandomClanPersonnelDivorce.setSelected(options.isUseRandomClanPersonnelDivorce()); - chkUseRandomPrisonerDivorce.setSelected(options.isUseRandomPrisonerDivorce()); - spnRandomDivorceDiceSize.setValue(options.getRandomDivorceDiceSize()); - - // Procreation - chkUseManualProcreation.setSelected(options.isUseManualProcreation()); - chkUseClanPersonnelProcreation.setSelected(options.isUseClanPersonnelProcreation()); - chkUsePrisonerProcreation.setSelected(options.isUsePrisonerProcreation()); - spnMultiplePregnancyOccurrences.setValue(options.getMultiplePregnancyOccurrences()); - comboBabySurnameStyle.setSelectedItem(options.getBabySurnameStyle()); - chkAssignNonPrisonerBabiesFounderTag.setSelected(options.isAssignNonPrisonerBabiesFounderTag()); - 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 - .isUseRelationshiplessRandomProcreation()) { - if (chkUseRelationshiplessRandomProcreation.isEnabled()) { - chkUseRelationshiplessRandomProcreation.doClick(); - } else { - chkUseRelationshiplessRandomProcreation - .setSelected(options.isUseRelationshiplessRandomProcreation()); - } - } - chkUseRandomClanPersonnelProcreation.setSelected(options.isUseRandomClanPersonnelProcreation()); - chkUseRandomPrisonerProcreation.setSelected(options.isUseRandomPrisonerProcreation()); - spnRandomProcreationRelationshipDiceSize.setValue(options.getRandomProcreationRelationshipDiceSize()); - spnRandomProcreationRelationshiplessDiceSize.setValue(options.getRandomProcreationRelationshiplessDiceSize()); - - // Education - chkUseEducationModule.setSelected(options.isUseEducationModule()); - spnCurriculumXpRate.setValue(options.getCurriculumXpRate()); - spnMaximumJumpCount.setValue(options.getMaximumJumpCount()); - chkUseReeducationCamps.setSelected(options.isUseReeducationCamps()); - chkEnableLocalAcademies.setSelected(options.isEnableLocalAcademies()); - chkEnablePrestigiousAcademies.setSelected(options.isEnablePrestigiousAcademies()); - chkEnableUnitEducation.setSelected(options.isEnableUnitEducation()); - chkEnableOverrideRequirements.setSelected(options.isEnableOverrideRequirements()); - chkShowIneligibleAcademies.setSelected(options.isEnableShowIneligibleAcademies()); - spnEntranceExamBaseTargetNumber.setValue(options.getEntranceExamBaseTargetNumber()); - spnFacultyXpMultiplier.setValue(options.getFacultyXpRate()); - chkEnableBonuses.setSelected(options.isEnableBonuses()); - spnAdultDropoutChance.setValue(options.getAdultDropoutChance()); - spnChildrenDropoutChance.setValue(options.getChildrenDropoutChance()); - chkAllAges.setSelected(options.isAllAges()); - spnMilitaryAcademyAccidents.setValue(options.getMilitaryAcademyAccidents()); - - // Death - chkKeepMarriedNameUponSpouseDeath.setSelected(options.isKeepMarriedNameUponSpouseDeath()); - comboRandomDeathMethod.setSelectedItem(options.getRandomDeathMethod()); - for (final AgeGroup ageGroup : AgeGroup.values()) { - chkEnabledRandomDeathAgeGroups.get(ageGroup) - .setSelected(options.getEnabledRandomDeathAgeGroups().get(ageGroup)); - } - chkUseRandomClanPersonnelDeath.setSelected(options.isUseRandomClanPersonnelDeath()); - chkUseRandomPrisonerDeath.setSelected(options.isUseRandomPrisonerDeath()); - chkUseRandomDeathSuicideCause.setSelected(options.isUseRandomDeathSuicideCause()); - spnPercentageRandomDeathChance.setValue(options.getPercentageRandomDeathChance()); - for (int i = 0; i < spnExponentialRandomDeathMaleValues.length; i++) { - spnExponentialRandomDeathMaleValues[i] - .setValue(options.getExponentialRandomDeathMaleValues()[i]); - spnExponentialRandomDeathFemaleValues[i] - .setValue(options.getExponentialRandomDeathFemaleValues()[i]); - } - for (final TenYearAgeRange ageRange : TenYearAgeRange.values()) { - spnAgeRangeRandomDeathMaleValues.get(ageRange) - .setValue(options.getAgeRangeRandomDeathMaleValues().get(ageRange)); - spnAgeRangeRandomDeathFemaleValues.get(ageRange) - .setValue(options.getAgeRangeRandomDeathFemaleValues().get(ageRange)); - } - // endregion Life Paths Tab - - // region Finances Tab - payForPartsBox.setSelected(options.isPayForParts()); - payForRepairsBox.setSelected(options.isPayForRepairs()); - payForUnitsBox.setSelected(options.isPayForUnits()); - payForSalariesBox.setSelected(options.isPayForSalaries()); - payForOverheadBox.setSelected(options.isPayForOverhead()); - payForMaintainBox.setSelected(options.isPayForMaintain()); - payForTransportBox.setSelected(options.isPayForTransport()); - sellUnitsBox.setSelected(options.isSellUnits()); - sellPartsBox.setSelected(options.isSellParts()); - payForRecruitmentBox.setSelected(options.isPayForRecruitment()); - useLoanLimitsBox.setSelected(options.isUseLoanLimits()); - usePercentageMaintenanceBox.setSelected(options.isUsePercentageMaint()); - useInfantryDoseNotCountBox.setSelected(options.isInfantryDontCount()); - usePeacetimeCostBox.setSelected(options.isUsePeacetimeCost()); - useExtendedPartsModifierBox.setSelected(options.isUseExtendedPartsModifier()); - showPeacetimeCostBox.setSelected(options.isShowPeacetimeCost()); - comboFinancialYearDuration.setSelectedItem(options.getFinancialYearDuration()); - newFinancialYearFinancesToCSVExportBox.setSelected(options.isNewFinancialYearFinancesToCSVExport()); - - // Price Multipliers - spnCommonPartPriceMultiplier.setValue(options.getCommonPartPriceMultiplier()); - spnInnerSphereUnitPriceMultiplier.setValue(options.getInnerSphereUnitPriceMultiplier()); - spnInnerSpherePartPriceMultiplier.setValue(options.getInnerSpherePartPriceMultiplier()); - spnClanUnitPriceMultiplier.setValue(options.getClanUnitPriceMultiplier()); - spnClanPartPriceMultiplier.setValue(options.getClanPartPriceMultiplier()); - spnMixedTechUnitPriceMultiplier.setValue(options.getMixedTechUnitPriceMultiplier()); - for (int i = 0; i < spnUsedPartPriceMultipliers.length; i++) { - spnUsedPartPriceMultipliers[i].setValue(options.getUsedPartPriceMultipliers()[i]); - } - spnDamagedPartsValueMultiplier.setValue(options.getDamagedPartsValueMultiplier()); - spnUnrepairablePartsValueMultiplier.setValue(options.getUnrepairablePartsValueMultiplier()); - spnCancelledOrderRefundMultiplier.setValue(options.getCancelledOrderRefundMultiplier()); - - // Shares - chkUseShareSystem.setSelected(options.isUseShareSystem()); - chkSharesForAll.setSelected(options.isSharesForAll()); - - // Taxes - chkUseTaxes.setSelected(options.isUseTaxes()); - spnTaxesPercentage.setValue(options.getTaxesPercentage()); - // endregion Finances Tab - - // region Mercenary Tab - if (options.isEquipmentContractBase()) { - btnContractEquipment.setSelected(true); - } else { - btnContractPersonnel.setSelected(true); - } - spnEquipPercent.setValue(options.getEquipmentContractPercent()); - spnDropShipPercent.setValue(options.getDropShipContractPercent()); - spnJumpShipPercent.setValue(options.getJumpShipContractPercent()); - spnWarShipPercent.setValue(options.getWarShipContractPercent()); - chkEquipContractSaleValue.setSelected(options.isEquipmentContractSaleValue()); - chkBLCSaleValue.setSelected(options.isBLCSaleValue()); - chkOverageRepaymentInFinalPayment.setSelected(options.isOverageRepaymentInFinalPayment()); - // endregion Mercenary Tab - - // region Experience Tab - spnXpCostMultiplier.setValue(options.getXpCostMultiplier()); - spnScenarioXP.setValue(options.getScenarioXP()); - spnKillXP.setValue(options.getKillXPAward()); - spnKills.setValue(options.getKillsForXP()); - spnTaskXP.setValue(options.getTaskXP()); - spnNTasksXP.setValue(options.getNTasksXP()); - spnSuccessXP.setValue(options.getSuccessXP()); - spnMistakeXP.setValue(options.getMistakeXP()); - spnIdleXP.setValue(options.getIdleXP()); - spnMonthsIdleXP.setValue(options.getMonthsIdleXP()); - spnTargetIdleXP.setValue(options.getTargetIdleXP()); - spnContractNegotiationXP.setValue(options.getContractNegotiationXP()); - spnAdminWeeklyXP.setValue(options.getAdminXP()); - spnAdminWeeklyXPPeriod.setValue(options.getAdminXPPeriod()); - spnMissionXpFail.setValue(options.getMissionXpFail()); - spnMissionXpSuccess.setValue(options.getMissionXpSuccess()); - spnMissionXpOutstandingSuccess.setValue(options.getMissionXpOutstandingSuccess()); - spnEdgeCost.setValue(options.getEdgeCost()); - // endregion Experience Tab - - // region Skills Tab - // endregion Skills Tab - - // region Special Abilities Tab - // endregion Special Abilities Tab - - // region Skill Randomization Tab - chkExtraRandom.setSelected(randomSkillPreferences.randomizeSkill()); - final int[] phenotypeProbabilities = options.getPhenotypeProbabilities(); - for (int i = 0; i < phenotypeSpinners.length; i++) { - phenotypeSpinners[i].setValue(phenotypeProbabilities[i]); - } - spnProbAntiMek.setValue(rSkillPrefs.getAntiMekProb()); - spnOverallRecruitBonus.setValue(rSkillPrefs.getOverallRecruitBonus()); - for (int i = 0; i < spnTypeRecruitBonus.length; i++) { - spnTypeRecruitBonus[i].setValue(rSkillPrefs.getRecruitBonuses()[i]); - } - spnArtyProb.setValue(rSkillPrefs.getArtilleryProb()); - spnArtyBonus.setValue(rSkillPrefs.getArtilleryBonus()); - spnSecondProb.setValue(rSkillPrefs.getSecondSkillProb()); - spnSecondBonus.setValue(rSkillPrefs.getSecondSkillBonus()); - spnTacticsGreen.setValue(rSkillPrefs.getTacticsMod(SkillType.EXP_GREEN)); - spnTacticsReg.setValue(rSkillPrefs.getTacticsMod(SkillType.EXP_REGULAR)); - spnTacticsVet.setValue(rSkillPrefs.getTacticsMod(SkillType.EXP_VETERAN)); - spnTacticsElite.setValue(rSkillPrefs.getTacticsMod(SkillType.EXP_ELITE)); - spnAbilityGreen.setValue(rSkillPrefs.getSpecialAbilityBonus(SkillType.EXP_GREEN)); - spnAbilityReg.setValue(rSkillPrefs.getSpecialAbilityBonus(SkillType.EXP_REGULAR)); - spnAbilityVet.setValue(rSkillPrefs.getSpecialAbilityBonus(SkillType.EXP_VETERAN)); - spnAbilityElite.setValue(rSkillPrefs.getSpecialAbilityBonus(SkillType.EXP_ELITE)); - spnCombatSA.setValue(rSkillPrefs.getCombatSmallArmsBonus()); - spnSupportSA.setValue(rSkillPrefs.getSupportSmallArmsBonus()); - // endregion Skill Randomization Tab - - // region Rank System Tab - // endregion Rank System Tab - - // region Name and Portrait Generation Tab - if (chkUseOriginFactionForNames.isSelected() != options.isUseOriginFactionForNames()) { - chkUseOriginFactionForNames.doClick(); - } - - boolean allSelected = true; - boolean noneSelected = true; - final boolean[] usePortraitForRole = options.isUsePortraitForRoles(); - for (int i = 0; i < chkUsePortrait.length; i++) { - chkUsePortrait[i].setSelected(usePortraitForRole[i]); - if (usePortraitForRole[i]) { - noneSelected = false; - } else { - allSelected = false; - } - } - if (allSelected != allPortraitsBox.isSelected()) { - allPortraitsBox.doClick(); - } - - if (noneSelected != noPortraitsBox.isSelected()) { - noPortraitsBox.doClick(); - } - - chkAssignPortraitOnRoleChange.setSelected(options.isAssignPortraitOnRoleChange()); - // endregion Name and Portrait Generation Tab - - // region Markets Tab - comboPersonnelMarketType.setSelectedItem(options.getPersonnelMarketName()); - chkPersonnelMarketReportRefresh.setSelected(options.isPersonnelMarketReportRefresh()); - for (final Entry entry : spnPersonnelMarketRandomRemovalTargets.entrySet()) { - entry.getValue().setValue(options.getPersonnelMarketRandomRemovalTargets().get(entry.getKey())); - } - spnPersonnelMarketDylansWeight.setValue(options.getPersonnelMarketDylansWeight()); - chkUsePersonnelHireHiringHallOnly.setSelected(options.isUsePersonnelHireHiringHallOnly()); - - // Unit Market - comboUnitMarketMethod.setSelectedItem(options.getUnitMarketMethod()); - chkUnitMarketRegionalMekVariations.setSelected(options.isUnitMarketRegionalMekVariations()); - spnUnitMarketSpecialUnitChance.setValue(options.getUnitMarketSpecialUnitChance()); - spnUnitMarketRarityModifier.setValue(options.getUnitMarketRarityModifier()); - chkInstantUnitMarketDelivery.setSelected(options.isInstantUnitMarketDelivery()); - chkUnitMarketReportRefresh.setSelected(options.isUnitMarketReportRefresh()); - - // Contract Market - comboContractMarketMethod.setSelectedItem(options.getContractMarketMethod()); - spnContractSearchRadius.setValue(options.getContractSearchRadius()); - chkVariableContractLength.setSelected(options.isVariableContractLength()); - chkContractMarketReportRefresh.setSelected(options.isContractMarketReportRefresh()); - spnContractMaxSalvagePercentage.setValue(options.getContractMaxSalvagePercentage()); - spnDropShipBonusPercentage.setValue(options.getDropShipBonusPercentage()); - // endregion Markets Tab - - // region RATs Tab - btnUseRATGenerator.setSelected(!options.isUseStaticRATs()); - if (options.isUseStaticRATs() != btnUseStaticRATs.isSelected()) { - btnUseStaticRATs.doClick(); - } - for (final String rat : options.getRATs()) { - final List eras = RATManager.getAllRATCollections().get(rat); - if (eras != null) { - final StringBuilder displayName = new StringBuilder(rat); - if (!eras.isEmpty()) { - displayName.append(" (").append(eras.get(0)); - if (eras.size() > 1) { - displayName.append('-').append(eras.get(eras.size() - 1)); - } - displayName.append(')'); - } - - if (availableRATModel.contains(displayName.toString())) { - chosenRATModel.addElement(displayName.toString()); - availableRATModel.removeElement(displayName.toString()); - } - } - } - chkIgnoreRATEra.setSelected(options.isIgnoreRATEra()); - // endregion RATs Tab - - // region Against the Bot Tab - if (chkUseAtB.isSelected() != options.isUseAtB()) { - chkUseAtB.doClick(); - } - chkUseStratCon.setSelected(options.isUseStratCon()); - comboSkillLevel.setSelectedItem(options.getSkillLevel()); - chkUseAero.setSelected(options.isUseAero()); - chkUseVehicles.setSelected(options.isUseVehicles()); - chkClanVehicles.setSelected(options.isClanVehicles()); - chkAutoConfigMunitions.setSelected(options.isAutoConfigMunitions()); - chkMercSizeLimited.setSelected(options.isMercSizeLimited()); - chkRestrictPartsByMission.setSelected(options.isRestrictPartsByMission()); - spnBonusPartExchangeValue.setValue(options.getBonusPartExchangeValue()); - spnBonusPartMaxExchangeCount.setValue(options.getBonusPartMaxExchangeCount()); - chkLimitLanceWeight.setSelected(options.isLimitLanceWeight()); - chkLimitLanceNumUnits.setSelected(options.isLimitLanceNumUnits()); - chkUseStrategy.setSelected(options.isUseStrategy()); - spnBaseStrategyDeployment.setValue(options.getBaseStrategyDeployment()); - spnAdditionalStrategyDeployment.setValue(options.getAdditionalStrategyDeployment()); - chkAdjustPaymentForStrategy.setSelected(options.isAdjustPaymentForStrategy()); - spnAtBBattleChance[AtBLanceRole.FIGHTING.ordinal()] - .setValue(options.getAtBBattleChance(AtBLanceRole.FIGHTING)); - spnAtBBattleChance[AtBLanceRole.DEFENCE.ordinal()] - .setValue(options.getAtBBattleChance(AtBLanceRole.DEFENCE)); - spnAtBBattleChance[AtBLanceRole.SCOUTING.ordinal()] - .setValue(options.getAtBBattleChance(AtBLanceRole.SCOUTING)); - spnAtBBattleChance[AtBLanceRole.TRAINING.ordinal()] - .setValue(options.getAtBBattleChance(AtBLanceRole.TRAINING)); - btnIntensityUpdate.doClick(); - chkGenerateChases.setSelected(options.isGenerateChases()); - - chkUseGenericBattleValue.setSelected(options.isUseGenericBattleValue()); - chkUseVerboseBidding.setSelected(options.isUseVerboseBidding()); - chkDoubleVehicles.setSelected(options.isDoubleVehicles()); - spnOpForLanceTypeMeks.setValue(options.getOpForLanceTypeMeks()); - spnOpForLanceTypeMixed.setValue(options.getOpForLanceTypeMixed()); - spnOpForLanceTypeVehicles.setValue(options.getOpForLanceTypeVehicles()); - chkOpForUsesVTOLs.setSelected(options.isOpForUsesVTOLs()); - chkOpForUsesAero.setSelected(options.isAllowOpForAeros()); - spnOpForAeroChance.setValue(options.getOpForAeroChance()); - chkOpForUsesLocalForces.setSelected(options.isAllowOpForLocalUnits()); - spnOpForLocalForceChance.setValue(options.getOpForLocalUnitChance()); - chkAdjustPlayerVehicles.setSelected(options.isAdjustPlayerVehicles()); - spnFixedMapChance.setValue(options.getFixedMapChance()); - spnSPAUpgradeIntensity.setValue(options.getSpaUpgradeIntensity()); - spnScenarioModMax.setValue(options.getScenarioModMax()); - spnScenarioModChance.setValue(options.getScenarioModChance()); - spnScenarioModBV.setValue(options.getScenarioModBV()); - chkRegionalMekVariations.setSelected(options.isRegionalMekVariations()); - chkAttachedPlayerCamouflage.setSelected(options.isAttachedPlayerCamouflage()); - chkPlayerControlsAttachedUnits.setSelected(options.isPlayerControlsAttachedUnits()); - chkUseDropShips.setSelected(options.isUseDropShips()); - chkUseWeatherConditions.setSelected(options.isUseWeatherConditions()); - chkUseLightConditions.setSelected(options.isUseLightConditions()); - chkUsePlanetaryConditions.setSelected(options.isUsePlanetaryConditions()); - // endregion Against the Bot Tab - } - - public void updateOptions() { - try { - campaign.setName(txtName.getText()); - if (isStartup()) { - getCampaign().getForces().setName(getCampaign().getName()); - } - campaign.setLocalDate(date); - - if ((campaign.getCampaignStartDate() == null) - || (campaign.getCampaignStartDate().isAfter(campaign.getLocalDate()))) { - campaign.setCampaignStartDate(date); - } - // Ensure that the MegaMek year GameOption matches the campaign year - campaign.getGameOptions().getOption(OptionsConstants.ALLOWED_YEAR) - .setValue(campaign.getGameYear()); - // Null state handled during validation - campaign.setFaction(comboFaction.getSelectedItem().getFaction()); - RandomNameGenerator.getInstance().setChosenFaction(comboFactionNames.getSelectedItem()); - RandomGenderGenerator.setPercentFemale(sldGender.getValue()); - rankSystemsPane.applyToCampaign(); - campaign.setCamouflage(camouflage); - campaign.setColour(colour); - campaign.setUnitIcon(unitIcon); - - for (int i = 0; i < chkUsePortrait.length; i++) { - options.setUsePortraitForRole(i, chkUsePortrait[i].isSelected()); - } - - updateSkillTypes(); - updateXPCosts(); - - // Rules panel - options.setEraMods(useEraModsCheckBox.isSelected()); - options.setAssignedTechFirst(assignedTechFirstCheckBox.isSelected()); - options.setResetToFirstTech(resetToFirstTechCheckBox.isSelected()); - options.setQuirks(useQuirksBox.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.ADVANCED_STRATOPS_QUIRKS) - .setValue(useQuirksBox.isSelected()); - options.setUnitRatingMethod(unitRatingMethodCombo.getSelectedItem()); - options.setManualUnitRatingModifier((Integer) manualUnitRatingModifier.getValue()); - options.setUseOriginFactionForNames(chkUseOriginFactionForNames.isSelected()); - options.setDestroyByMargin(useDamageMargin.isSelected()); - options.setDestroyMargin((Integer) spnDamageMargin.getValue()); - options.setDestroyPartTarget((Integer) spnDestroyPartTarget.getValue()); - options.setUseAeroSystemHits(useAeroSystemHitsBox.isSelected()); - options.setCheckMaintenance(checkMaintenance.isSelected()); - options.setUseQualityMaintenance(useQualityMaintenance.isSelected()); - options.setReverseQualityNames(reverseQualityNames.isSelected()); - options.setUseRandomUnitQualities(chkUseRandomUnitQualities.isSelected()); - options.setUsePlanetaryModifiers(chkUsePlanetaryModifiers.isSelected()); - options.setUseUnofficialMaintenance(useUnofficialMaintenance.isSelected()); - options.setLogMaintenance(logMaintenance.isSelected()); - options.setMaintenanceBonus((Integer) spnMaintenanceBonus.getValue()); - options.setDefaultMaintenanceTime((Integer) spnDefaultMaintenanceTime.getValue()); - options.setMaintenanceCycleDays((Integer) spnMaintenanceDays.getValue()); - options.setPayForParts(payForPartsBox.isSelected()); - options.setPayForRepairs(payForRepairsBox.isSelected()); - options.setPayForUnits(payForUnitsBox.isSelected()); - options.setPayForSalaries(payForSalariesBox.isSelected()); - options.setPayForOverhead(payForOverheadBox.isSelected()); - options.setPayForMaintain(payForMaintainBox.isSelected()); - options.setPayForTransport(payForTransportBox.isSelected()); - options.setPayForRecruitment(payForRecruitmentBox.isSelected()); - options.setLoanLimits(useLoanLimitsBox.isSelected()); - options.setUsePercentageMaint(usePercentageMaintenanceBox.isSelected()); - options.setUseInfantryDontCount(useInfantryDoseNotCountBox.isSelected()); - options.setSellUnits(sellUnitsBox.isSelected()); - options.setSellParts(sellPartsBox.isSelected()); - options.setUsePeacetimeCost(usePeacetimeCostBox.isSelected()); - options.setUseExtendedPartsModifier(useExtendedPartsModifierBox.isSelected()); - options.setShowPeacetimeCost(showPeacetimeCostBox.isSelected()); - options.setNewFinancialYearFinancesToCSVExport( - newFinancialYearFinancesToCSVExportBox.isSelected()); - options.setFinancialYearDuration(comboFinancialYearDuration.getSelectedItem()); - options.setAssignPortraitOnRoleChange(chkAssignPortraitOnRoleChange.isSelected()); - - options.setEquipmentContractBase(btnContractEquipment.isSelected()); - options.setEquipmentContractPercent((Double) spnEquipPercent.getValue()); - options.setDropShipContractPercent((Double) spnDropShipPercent.getValue()); - options.setJumpShipContractPercent((Double) spnJumpShipPercent.getValue()); - options.setWarShipContractPercent((Double) spnWarShipPercent.getValue()); - options.setEquipmentContractSaleValue(chkEquipContractSaleValue.isSelected()); - options.setBLCSaleValue(chkBLCSaleValue.isSelected()); - options.setOverageRepaymentInFinalPayment(chkOverageRepaymentInFinalPayment.isSelected()); - - options.setWaitingPeriod((Integer) spnAcquireWaitingPeriod.getValue()); - options.setAcquisitionSkill(choiceAcquireSkill.getSelectedItem()); - options.setAcquisitionSupportStaffOnly(chkSupportStaffOnly.isSelected()); - options.setClanAcquisitionPenalty((Integer) spnAcquireClanPenalty.getValue()); - options.setIsAcquisitionPenalty((Integer) spnAcquireIsPenalty.getValue()); - options.setMaxAcquisitions((Integer) spnMaxAcquisitions.getValue()); - - options.setNDiceTransitTime((Integer) spnNDiceTransitTime.getValue()); - options.setConstantTransitTime((Integer) spnConstantTransitTime.getValue()); - options.setUnitTransitTime(choiceTransitTimeUnits.getSelectedIndex()); - options.setAcquireMosBonus((Integer) spnAcquireMosBonus.getValue()); - options.setAcquireMinimumTime((Integer) spnAcquireMinimum.getValue()); - options.setAcquireMinimumTimeUnit(choiceAcquireMinimumUnit.getSelectedIndex()); - options.setAcquireMosUnit(choiceAcquireMosUnits.getSelectedIndex()); - options.setPlanetaryAcquisition(usePlanetaryAcquisitions.isSelected()); - options.setDisallowClanPartsFromIS(disallowClanPartsFromIS.isSelected()); - options.setPlanetAcquisitionVerboseReporting(usePlanetaryAcquisitionsVerbose.isSelected()); - options.setDisallowPlanetAcquisitionClanCrossover( - disallowPlanetaryAcquisitionClanCrossover.isSelected()); - options.setMaxJumpsPlanetaryAcquisition((int) spnMaxJumpPlanetaryAcquisitions.getValue()); - options.setPenaltyClanPartsFromIS((int) spnPenaltyClanPartsFromIS.getValue()); - options.setPlanetAcquisitionFactionLimit( - comboPlanetaryAcquisitionsFactionLimits.getSelectedItem()); - - for (int i = ITechnology.RATING_A; i <= ITechnology.RATING_F; i++) { - options.setPlanetTechAcquisitionBonus((int) spnPlanetAcquireTechBonus[i].getValue(), i); - options.setPlanetIndustryAcquisitionBonus( - (int) spnPlanetAcquireIndustryBonus[i].getValue(), i); - options.setPlanetOutputAcquisitionBonus((int) spnPlanetAcquireOutputBonus[i].getValue(), - i); - - } - - options.setXpCostMultiplier((Double) spnXpCostMultiplier.getValue()); - options.setScenarioXP((Integer) spnScenarioXP.getValue()); - options.setKillsForXP((Integer) spnKills.getValue()); - options.setKillXPAward((Integer) spnKillXP.getValue()); - - options.setTaskXP((Integer) spnTaskXP.getValue()); - options.setNTasksXP((Integer) spnNTasksXP.getValue()); - options.setSuccessXP((Integer) spnSuccessXP.getValue()); - options.setMistakeXP((Integer) spnMistakeXP.getValue()); - options.setIdleXP((Integer) spnIdleXP.getValue()); - options.setMonthsIdleXP((Integer) spnMonthsIdleXP.getValue()); - options.setContractNegotiationXP((Integer) spnContractNegotiationXP.getValue()); - options.setAdminXP((Integer) spnAdminWeeklyXP.getValue()); - options.setAdminXPPeriod((Integer) spnAdminWeeklyXPPeriod.getValue()); - options.setEdgeCost((Integer) spnEdgeCost.getValue()); - options.setTargetIdleXP((Integer) spnTargetIdleXP.getValue()); - options.setMissionXpFail((Integer) spnMissionXpFail.getValue()); - options.setMissionXpSuccess((Integer) spnMissionXpSuccess.getValue()); - options.setMissionXpOutstandingSuccess((Integer) spnMissionXpOutstandingSuccess.getValue()); - - options.setLimitByYear(limitByYearBox.isSelected()); - options.setDisallowExtinctStuff(disallowExtinctStuffBox.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.ALLOWED_SHOW_EXTINCT) - .setValue(!disallowExtinctStuffBox.isSelected()); - options.setAllowClanPurchases(allowClanPurchasesBox.isSelected()); - options.setAllowISPurchases(allowISPurchasesBox.isSelected()); - options.setAllowCanonOnly(allowCanonOnlyBox.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.ALLOWED_CANON_ONLY) - .setValue(allowCanonOnlyBox.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.ALLOWED_ERA_BASED) - .setValue(variableTechLevelBox.isSelected()); - options.setVariableTechLevel(variableTechLevelBox.isSelected() && options.isLimitByYear()); - options.setFactionIntroDate(factionIntroDateBox.isSelected()); - campaign.updateTechFactionCode(); - options.setAllowCanonRefitOnly(allowCanonRefitOnlyBox.isSelected()); - options.setUseAmmoByType(useAmmoByTypeBox.isSelected()); - options.setTechLevel(choiceTechLevel.getSelectedIndex()); - campaign.getGameOptions().getOption(OptionsConstants.ALLOWED_TECHLEVEL) - .setValue(choiceTechLevel.getSelectedItem()); - - rSkillPrefs.setOverallRecruitBonus((Integer) spnOverallRecruitBonus.getValue()); - for (int i = 0; i < spnTypeRecruitBonus.length; i++) { - rSkillPrefs.setRecruitBonus(i, (Integer) spnTypeRecruitBonus[i].getValue()); - } - rSkillPrefs.setRandomizeSkill(chkExtraRandom.isSelected()); - rSkillPrefs.setAntiMekProb((Integer) spnProbAntiMek.getValue()); - rSkillPrefs.setArtilleryProb((Integer) spnArtyProb.getValue()); - rSkillPrefs.setArtilleryBonus((Integer) spnArtyBonus.getValue()); - rSkillPrefs.setSecondSkillProb((Integer) spnSecondProb.getValue()); - rSkillPrefs.setSecondSkillBonus((Integer) spnSecondBonus.getValue()); - rSkillPrefs.setTacticsMod(SkillType.EXP_GREEN, (Integer) spnTacticsGreen.getValue()); - rSkillPrefs.setTacticsMod(SkillType.EXP_REGULAR, (Integer) spnTacticsReg.getValue()); - rSkillPrefs.setTacticsMod(SkillType.EXP_VETERAN, (Integer) spnTacticsVet.getValue()); - rSkillPrefs.setTacticsMod(SkillType.EXP_ELITE, (Integer) spnTacticsElite.getValue()); - rSkillPrefs.setCombatSmallArmsBonus((Integer) spnCombatSA.getValue()); - rSkillPrefs.setSupportSmallArmsBonus((Integer) spnSupportSA.getValue()); - rSkillPrefs.setSpecialAbilityBonus(SkillType.EXP_GREEN, (Integer) spnAbilityGreen.getValue()); - rSkillPrefs.setSpecialAbilityBonus(SkillType.EXP_REGULAR, (Integer) spnAbilityReg.getValue()); - rSkillPrefs.setSpecialAbilityBonus(SkillType.EXP_VETERAN, (Integer) spnAbilityVet.getValue()); - rSkillPrefs.setSpecialAbilityBonus(SkillType.EXP_ELITE, (Integer) spnAbilityElite.getValue()); - campaign.setRandomSkillPreferences(rSkillPrefs); - - for (int i = 0; i < phenotypeSpinners.length; i++) { - options.setPhenotypeProbability(i, (Integer) phenotypeSpinners[i].getValue()); - } - - // region Personnel Tab - // General Personnel - options.setUseTactics(chkUseTactics.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.RPG_COMMAND_INIT) - .setValue(chkUseTactics.isSelected()); - options.setUseInitiativeBonus(chkUseInitiativeBonus.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.RPG_INDIVIDUAL_INITIATIVE) - .setValue(chkUseInitiativeBonus.isSelected()); - options.setUseToughness(chkUseToughness.isSelected()); - options.setUseRandomToughness(chkUseRandomToughness.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.RPG_TOUGHNESS) - .setValue(chkUseToughness.isSelected()); - options.setUseArtillery(chkUseArtillery.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.RPG_ARTILLERY_SKILL) - .setValue(chkUseArtillery.isSelected()); - options.setUseAbilities(chkUseAbilities.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.RPG_PILOT_ADVANTAGES) - .setValue(chkUseAbilities.isSelected()); - options.setUseEdge(chkUseEdge.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.EDGE).setValue(chkUseEdge.isSelected()); - options.setUseSupportEdge(chkUseEdge.isSelected() && chkUseSupportEdge.isSelected()); - options.setUseImplants(chkUseImplants.isSelected()); - campaign.getGameOptions().getOption(OptionsConstants.RPG_MANEI_DOMINI) - .setValue(chkUseImplants.isSelected()); - options.setAlternativeQualityAveraging(chkUseAlternativeQualityAveraging.isSelected()); - options.setUseTransfers(chkUseTransfers.isSelected()); - options.setUseExtendedTOEForceName(chkUseExtendedTOEForceName.isSelected()); - options.setPersonnelLogSkillGain(chkPersonnelLogSkillGain.isSelected()); - options.setPersonnelLogAbilityGain(chkPersonnelLogAbilityGain.isSelected()); - options.setPersonnelLogEdgeGain(chkPersonnelLogEdgeGain.isSelected()); - options.setDisplayPersonnelLog(chkDisplayPersonnelLog.isSelected()); - options.setDisplayScenarioLog(chkDisplayScenarioLog.isSelected()); - options.setDisplayKillRecord(chkDisplayKillRecord.isSelected()); - - // Expanded Personnel Information - options.setUseTimeInService(chkUseTimeInService.isSelected()); - options.setTimeInServiceDisplayFormat(comboTimeInServiceDisplayFormat.getSelectedItem()); - options.setUseTimeInRank(chkUseTimeInRank.isSelected()); - options.setTimeInRankDisplayFormat(comboTimeInRankDisplayFormat.getSelectedItem()); - options.setTrackTotalEarnings(chkTrackTotalEarnings.isSelected()); - options.setTrackTotalXPEarnings(chkTrackTotalXPEarnings.isSelected()); - options.setShowOriginFaction(chkShowOriginFaction.isSelected()); - - // Administrators - options.setAdminsHaveNegotiation(chkAdminsHaveNegotiation.isSelected()); - options.setAdminExperienceLevelIncludeNegotiation( - chkAdminExperienceLevelIncludeNegotiation.isSelected()); - options.setAdminsHaveScrounge(chkAdminsHaveScrounge.isSelected()); - options.setAdminExperienceLevelIncludeScrounge( - chkAdminExperienceLevelIncludeScrounge.isSelected()); - - // autoAwards - options.setEnableAutoAwards(chkEnableAutoAwards.isSelected()); - options.setAwardBonusStyle(comboAwardBonusStyle.getSelectedItem()); - options.setIssuePosthumousAwards(chkIssuePosthumousAwards.isSelected()); - options.setIssueBestAwardOnly(chkIssueBestAwardOnly.isSelected()); - options.setIgnoreStandardSet(chkIgnoreStandardSet.isSelected()); - options.setAwardTierSize((int) spnAwardTierSize.getValue()); - options.setEnableContractAwards(chkEnableContractAwards.isSelected()); - options.setEnableFactionHunterAwards(chkEnableFactionHunterAwards.isSelected()); - options.setEnableInjuryAwards(chkEnableInjuryAwards.isSelected()); - options.setEnableIndividualKillAwards(chkEnableIndividualKillAwards.isSelected()); - options.setEnableFormationKillAwards(chkEnableFormationKillAwards.isSelected()); - options.setEnableRankAwards(chkEnableRankAwards.isSelected()); - options.setEnableScenarioAwards(chkEnableScenarioAwards.isSelected()); - options.setEnableSkillAwards(chkEnableSkillAwards.isSelected()); - options.setEnableTheatreOfWarAwards(chkEnableTheatreOfWarAwards.isSelected()); - options.setEnableTimeAwards(chkEnableTimeAwards.isSelected()); - options.setEnableTimeAwards(chkEnableTrainingAwards.isSelected()); - options.setEnableMiscAwards(chkEnableMiscAwards.isSelected()); - options.setAwardSetFilterList(txtAwardSetFilterList.getText()); - - // Medical - options.setUseAdvancedMedical(chkUseAdvancedMedical.isSelected()); - // we need to reset healing time options through the campaign because we may - // need to - // loop through personnel to make adjustments - campaign.setHealingTimeOptions((Integer) spnHealWaitingPeriod.getValue(), - (Integer) spnNaturalHealWaitingPeriod.getValue()); - options.setMinimumHitsForVehicles((Integer) spnMinimumHitsForVehicles.getValue()); - options.setUseRandomHitsForVehicles(chkUseRandomHitsForVehicles.isSelected()); - options.setTougherHealing(chkUseTougherHealing.isSelected()); - options.setMaximumPatients((int) spnMaximumPatients.getValue()); - - // Prisoners - options.setPrisonerCaptureStyle(comboPrisonerCaptureStyle.getSelectedItem()); - options.setDefaultPrisonerStatus(comboPrisonerStatus.getSelectedItem()); - options.setPrisonerBabyStatus(chkPrisonerBabyStatus.isSelected()); - options.setUseAtBPrisonerDefection(chkAtBPrisonerDefection.isSelected()); - options.setUseAtBPrisonerRansom(chkAtBPrisonerRansom.isSelected()); - - // Dependent - options.setUseRandomDependentAddition(chkUseRandomDependentAddition.isSelected()); - options.setUseRandomDependentRemoval(chkUseRandomDependentRemoval.isSelected()); - - // Personnel Removal - options.setUsePersonnelRemoval(chkUsePersonnelRemoval.isSelected()); - options.setUseRemovalExemptCemetery(chkUseRemovalExemptCemetery.isSelected()); - options.setUseRemovalExemptRetirees(chkUseRemovalExemptRetirees.isSelected()); - - // Salary - options.setDisableSecondaryRoleSalary(chkDisableSecondaryRoleSalary.isSelected()); - options.setSalaryAntiMekMultiplier((Double) spnAntiMekSalary.getValue()); - options.setSalarySpecialistInfantryMultiplier((Double) spnSpecialistInfantrySalary.getValue()); - for (final Entry entry : spnSalaryExperienceMultipliers.entrySet()) { - options.getSalaryXPMultipliers().put(entry.getKey(), - (Double) entry.getValue().getValue()); - } - - for (final PersonnelRole personnelRole : PersonnelRole.values()) { - options.setRoleBaseSalary(personnelRole, - (double) spnBaseSalary[personnelRole.ordinal()].getValue()); - } - // endregion Personnel Tab - - // region Turnover and Retention - // Header - options.setUseRandomRetirement(chkUseRandomRetirement.isSelected()); - - // Settings - options.setTurnoverFixedTargetNumber((Integer) spnTurnoverFixedTargetNumber.getValue()); - options.setTurnoverFrequency(comboTurnoverFrequency.getSelectedItem()); - options.setUseContractCompletionRandomRetirement( - chkUseContractCompletionRandomRetirement.isSelected()); - options.setUseRandomFounderTurnover(chkUseRandomFounderTurnover.isSelected()); - options.setUseFounderRetirement(chkUseFounderRetirement.isSelected()); - options.setAeroRecruitsHaveUnits(chkAeroRecruitsHaveUnits.isSelected()); - options.setUseSubContractSoldiers(chkUseSubContractSoldiers.isSelected()); - options.setServiceContractDuration((Integer) spnServiceContractDuration.getValue()); - options.setServiceContractModifier((Integer) spnServiceContractModifier.getValue()); - options.setPayBonusDefault(chkPayBonusDefault.isSelected()); - options.setPayBonusDefaultThreshold((Integer) spnPayBonusDefaultThreshold.getValue()); - - // Modifiers - options.setUseCustomRetirementModifiers(chkUseCustomRetirementModifiers.isSelected()); - options.setUseFatigueModifiers(chkUseFatigueModifiers.isSelected()); - options.setUseSkillModifiers(chkUseSkillModifiers.isSelected()); - options.setUseAgeModifiers(chkUseAgeModifiers.isSelected()); - options.setUseUnitRatingModifiers(chkUseUnitRatingModifiers.isSelected()); - options.setUseFactionModifiers(chkUseFactionModifiers.isSelected()); - options.setUseHostileTerritoryModifiers(chkUseHostileTerritoryModifiers.isSelected()); - options.setUseMissionStatusModifiers(chkUseMissionStatusModifiers.isSelected()); - options.setUseFamilyModifiers(chkUseFamilyModifiers.isSelected()); - options.setUseLoyaltyModifiers(chkUseLoyaltyModifiers.isSelected()); - options.setUseHideLoyalty(chkUseHideLoyalty.isSelected()); - - // Payouts - options.setPayoutRateOfficer((Integer) spnPayoutRateOfficer.getValue()); - options.setPayoutRateEnlisted((Integer) spnPayoutRateEnlisted.getValue()); - options.setPayoutRetirementMultiplier((Integer) spnPayoutRetirementMultiplier.getValue()); - options.setUsePayoutServiceBonus(chkUsePayoutServiceBonus.isSelected()); - options.setPayoutServiceBonusRate((Integer) spnPayoutServiceBonusRate.getValue()); - - // Unit Cohesion - options.setUseAdministrativeStrain(chkUseAdministrativeStrain.isSelected()); - options.setUseManagementSkill(chkUseManagementSkill.isSelected()); - - options.setAdministrativeCapacity((Integer) spnAdministrativeCapacity.getValue()); - options.setMultiCrewStrainDivider((Integer) spnMultiCrewStrainDivider.getValue()); - - options.setUseCommanderLeadershipOnly(chkUseCommanderLeadershipOnly.isSelected()); - options.setManagementSkillPenalty((Integer) spnManagementSkillPenalty.getValue()); - - // Fatigue - options.setUseFatigue(chkUseFatigue.isSelected()); - options.setFatigueRate((Integer) spnFatigueRate.getValue()); - options.setUseInjuryFatigue(chkUseInjuryFatigue.isSelected()); - options.setFieldKitchenCapacity((Integer) spnFieldKitchenCapacity.getValue()); - options.setFieldKitchenIgnoreNonCombatants(chkFieldKitchenIgnoreNonCombatants.isSelected()); - options.setFatigueLeaveThreshold((Integer) spnFatigueLeaveThreshold.getValue()); - // endregion Turnover and Retention - - // 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()); - - // Family - options.setFamilyDisplayLevel(comboFamilyDisplayLevel.getSelectedItem()); - - // Anniversaries - options.setAnnounceBirthdays(chkAnnounceBirthdays.isSelected()); - options.setAnnounceRecruitmentAnniversaries(chkAnnounceRecruitmentAnniversaries.isSelected()); - options.setAnnounceOfficersOnly(chkAnnounceOfficersOnly.isSelected()); - options.setAnnounceChildBirthdays(chkAnnounceChildBirthdays.isSelected()); - - // Marriage - options.setUseManualMarriages(chkUseManualMarriages.isSelected()); - options.setUseClanPersonnelMarriages(chkUseClanPersonnelMarriages.isSelected()); - options.setUsePrisonerMarriages(chkUsePrisonerMarriages.isSelected()); - options.setNoInterestInMarriageDiceSize((int) spnNoInterestInMarriageDiceSize.getValue()); - options.setCheckMutualAncestorsDepth((Integer) spnCheckMutualAncestorsDepth.getValue()); - options.setLogMarriageNameChanges(chkLogMarriageNameChanges.isSelected()); - for (final Entry entry : spnMarriageSurnameWeights.entrySet()) { - options.getMarriageSurnameWeights().put(entry.getKey(), - (int) Math.round((Double) entry.getValue().getValue() * 10.0)); - } - options.setRandomMarriageMethod(comboRandomMarriageMethod.getSelectedItem()); - options.setUseRandomClanPersonnelMarriages(chkUseRandomClanPersonnelMarriages.isSelected()); - options.setUseRandomPrisonerMarriages(chkUseRandomPrisonerMarriages.isSelected()); - options.setRandomMarriageAgeRange((Integer) spnRandomMarriageAgeRange.getValue()); - options.setRandomMarriageDiceSize((int) spnRandomMarriageDiceSize.getValue()); - options.setRandomSameSexMarriageDiceSize((int) spnRandomSameSexMarriageDiceSize.getValue()); - options.setRandomNewDependentMarriage((int) spnRandomNewDependentMarriage.getValue()); - - // Divorce - options.setUseManualDivorce(chkUseManualDivorce.isSelected()); - options.setUseClanPersonnelDivorce(chkUseClanPersonnelDivorce.isSelected()); - options.setUsePrisonerDivorce(chkUsePrisonerDivorce.isSelected()); - for (final Entry entry : spnDivorceSurnameWeights.entrySet()) { - options.getDivorceSurnameWeights().put(entry.getKey(), - (int) Math.round((Double) entry.getValue().getValue() * 10.0)); - } - options.setRandomDivorceMethod(comboRandomDivorceMethod.getSelectedItem()); - options.setUseRandomOppositeSexDivorce(chkUseRandomOppositeSexDivorce.isSelected()); - options.setUseRandomSameSexDivorce(chkUseRandomSameSexDivorce.isSelected()); - options.setUseRandomClanPersonnelDivorce(chkUseRandomClanPersonnelDivorce.isSelected()); - options.setUseRandomPrisonerDivorce(chkUseRandomPrisonerDivorce.isSelected()); - options.setRandomDivorceDiceSize((int) spnRandomDivorceDiceSize.getValue()); - - // Procreation - options.setUseManualProcreation(chkUseManualProcreation.isSelected()); - options.setUseClanPersonnelProcreation(chkUseClanPersonnelProcreation.isSelected()); - options.setUsePrisonerProcreation(chkUsePrisonerProcreation.isSelected()); - options.setMultiplePregnancyOccurrences((Integer) spnMultiplePregnancyOccurrences.getValue()); - options.setBabySurnameStyle(comboBabySurnameStyle.getSelectedItem()); - options.setAssignNonPrisonerBabiesFounderTag(chkAssignNonPrisonerBabiesFounderTag.isSelected()); - options.setAssignChildrenOfFoundersFounderTag( - 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.setRandomProcreationRelationshipDiceSize((Integer) spnRandomProcreationRelationshipDiceSize.getValue()); - options.setRandomProcreationRelationshiplessDiceSize((Integer) spnRandomProcreationRelationshiplessDiceSize.getValue()); - - // Education - options.setUseEducationModule(chkUseEducationModule.isSelected()); - options.setCurriculumXpRate((Integer) spnCurriculumXpRate.getValue()); - options.setMaximumJumpCount((Integer) spnMaximumJumpCount.getValue()); - options.setUseReeducationCamps(chkUseReeducationCamps.isSelected()); - options.setEnableLocalAcademies(chkEnableLocalAcademies.isSelected()); - options.setEnablePrestigiousAcademies(chkEnablePrestigiousAcademies.isSelected()); - options.setEnableUnitEducation(chkEnableUnitEducation.isSelected()); - options.setEnableOverrideRequirements(chkEnableOverrideRequirements.isSelected()); - options.setEnableShowIneligibleAcademies(chkShowIneligibleAcademies.isSelected()); - options.setEntranceExamBaseTargetNumber((int) spnEntranceExamBaseTargetNumber.getValue()); - options.setFacultyXpRate((Double) spnFacultyXpMultiplier.getValue()); - options.setEnableBonuses(chkEnableBonuses.isSelected()); - options.setAdultDropoutChance((Integer) spnAdultDropoutChance.getValue()); - options.setChildrenDropoutChance((Integer) spnChildrenDropoutChance.getValue()); - options.setAllAges(chkAllAges.isSelected()); - options.setMilitaryAcademyAccidents((Integer) spnMilitaryAcademyAccidents.getValue()); - - // Death - options.setKeepMarriedNameUponSpouseDeath(chkKeepMarriedNameUponSpouseDeath.isSelected()); - options.setRandomDeathMethod(comboRandomDeathMethod.getSelectedItem()); - for (final AgeGroup ageGroup : AgeGroup.values()) { - options.getEnabledRandomDeathAgeGroups().put(ageGroup, - chkEnabledRandomDeathAgeGroups.get(ageGroup).isSelected()); - } - options.setUseRandomClanPersonnelDeath(chkUseRandomClanPersonnelDeath.isSelected()); - options.setUseRandomPrisonerDeath(chkUseRandomPrisonerDeath.isSelected()); - options.setUseRandomDeathSuicideCause(chkUseRandomDeathSuicideCause.isSelected()); - options.setPercentageRandomDeathChance((Double) spnPercentageRandomDeathChance.getValue()); - for (int i = 0; i < spnExponentialRandomDeathMaleValues.length; i++) { - options.getExponentialRandomDeathMaleValues()[i] = (double) spnExponentialRandomDeathMaleValues[i] - .getValue(); - options.getExponentialRandomDeathFemaleValues()[i] = (double) spnExponentialRandomDeathFemaleValues[i] - .getValue(); - } - for (final TenYearAgeRange ageRange : TenYearAgeRange.values()) { - options.getAgeRangeRandomDeathMaleValues().put(ageRange, - (double) spnAgeRangeRandomDeathMaleValues.get(ageRange).getValue()); - options.getAgeRangeRandomDeathFemaleValues().put(ageRange, - (double) spnAgeRangeRandomDeathFemaleValues.get(ageRange).getValue()); - } - // endregion Life Paths Tab - - // region Finances Tab - // Price Multipliers - options.setCommonPartPriceMultiplier((Double) spnCommonPartPriceMultiplier.getValue()); - options.setInnerSphereUnitPriceMultiplier( - (Double) spnInnerSphereUnitPriceMultiplier.getValue()); - options.setInnerSpherePartPriceMultiplier( - (Double) spnInnerSpherePartPriceMultiplier.getValue()); - options.setClanUnitPriceMultiplier((Double) spnClanUnitPriceMultiplier.getValue()); - options.setClanPartPriceMultiplier((Double) spnClanPartPriceMultiplier.getValue()); - options.setMixedTechUnitPriceMultiplier((Double) spnMixedTechUnitPriceMultiplier.getValue()); - for (int i = 0; i < spnUsedPartPriceMultipliers.length; i++) { - options.getUsedPartPriceMultipliers()[i] = (Double) spnUsedPartPriceMultipliers[i] - .getValue(); - } - options.setDamagedPartsValueMultiplier((Double) spnDamagedPartsValueMultiplier.getValue()); - options.setUnrepairablePartsValueMultiplier( - (Double) spnUnrepairablePartsValueMultiplier.getValue()); - options.setCancelledOrderRefundMultiplier( - (Double) spnCancelledOrderRefundMultiplier.getValue()); - - options.setUseShareSystem(chkUseShareSystem.isSelected()); - options.setSharesForAll(chkSharesForAll.isSelected()); - - // region Taxes - options.setUseTaxes(chkUseTaxes.isSelected()); - options.setTaxesPercentage((Integer) spnTaxesPercentage.getValue()); - // endregion Taxes - // endregion Finances Tab - - // start SPA - SpecialAbility.replaceSpecialAbilities(getCurrentSPA()); - // end SPA - - // region Markets Tab - // Personnel Market - options.setPersonnelMarketName(comboPersonnelMarketType.getSelectedItem()); - if (comboPersonnelMarketType.getSelectedItem().equals("Campaign Ops")) { - campaign.getPersonnelMarket().setPaidRecruitment(false); - } - options.setPersonnelMarketReportRefresh(chkPersonnelMarketReportRefresh.isSelected()); - for (final Entry entry : spnPersonnelMarketRandomRemovalTargets - .entrySet()) { - options.getPersonnelMarketRandomRemovalTargets().put(entry.getKey(), - (int) entry.getValue().getValue()); - } - options.setPersonnelMarketDylansWeight((Double) spnPersonnelMarketDylansWeight.getValue()); - options.setUsePersonnelHireHiringHallOnly(chkUsePersonnelHireHiringHallOnly.isSelected()); - - // Unit Market - options.setUnitMarketMethod(comboUnitMarketMethod.getSelectedItem()); - options.setUnitMarketRegionalMekVariations(chkUnitMarketRegionalMekVariations.isSelected()); - options.setUnitMarketSpecialUnitChance((Integer) spnUnitMarketSpecialUnitChance.getValue()); - options.setUnitMarketRarityModifier((int) spnUnitMarketRarityModifier.getValue()); - options.setInstantUnitMarketDelivery(chkInstantUnitMarketDelivery.isSelected()); - options.setUnitMarketReportRefresh(chkUnitMarketReportRefresh.isSelected()); - - // Contract Market - options.setContractMarketMethod(comboContractMarketMethod.getSelectedItem()); - options.setContractSearchRadius((Integer) spnContractSearchRadius.getValue()); - options.setVariableContractLength(chkVariableContractLength.isSelected()); - options.setContractMarketReportRefresh(chkContractMarketReportRefresh.isSelected()); - options.setContractMaxSalvagePercentage((Integer) spnContractMaxSalvagePercentage.getValue()); - options.setDropShipBonusPercentage((int) spnDropShipBonusPercentage.getValue()); - // endregion Markets Tab - - // region RATs Tab - options.setUseStaticRATs(btnUseStaticRATs.isSelected()); - // We use a stream to strip dates used in display name - options.setRATs(IntStream.range(0, chosenRATModel.size()) - .mapToObj(i -> chosenRATModel.elementAt(i).replaceFirst(" \\(.*?\\)", "")) - .toArray(String[]::new)); - options.setIgnoreRATEra(chkIgnoreRATEra.isSelected()); - // endregion RATs Tab - - // region Against the Bot - options.setUseAtB(chkUseAtB.isSelected()); - options.setUseStratCon(chkUseStratCon.isSelected()); - options.setSkillLevel(comboSkillLevel.getSelectedItem()); - options.setLimitLanceWeight(chkLimitLanceWeight.isSelected()); - options.setLimitLanceNumUnits(chkLimitLanceNumUnits.isSelected()); - options.setUseStrategy(chkUseStrategy.isSelected()); - options.setBaseStrategyDeployment((Integer) spnBaseStrategyDeployment.getValue()); - options.setAdditionalStrategyDeployment((Integer) spnAdditionalStrategyDeployment.getValue()); - options.setAdjustPaymentForStrategy(chkAdjustPaymentForStrategy.isSelected()); - - options.setUseAero(chkUseAero.isSelected()); - options.setUseVehicles(chkUseVehicles.isSelected()); - options.setClanVehicles(chkClanVehicles.isSelected()); - options.setAutoConfigMunitions(chkAutoConfigMunitions.isSelected()); - options.setUseGenericBattleValue(chkUseGenericBattleValue.isSelected()); - options.setUseVerboseBidding(chkUseVerboseBidding.isSelected()); - options.setDoubleVehicles(chkDoubleVehicles.isSelected()); - options.setAdjustPlayerVehicles(chkAdjustPlayerVehicles.isSelected()); - options.setOpForLanceTypeMeks((Integer) spnOpForLanceTypeMeks.getValue()); - options.setOpForLanceTypeMixed((Integer) spnOpForLanceTypeMixed.getValue()); - options.setOpForLanceTypeVehicles((Integer) spnOpForLanceTypeVehicles.getValue()); - options.setOpForUsesVTOLs(chkOpForUsesVTOLs.isSelected()); - options.setAllowOpForAeros(chkOpForUsesAero.isSelected()); - options.setAllowOpForLocalUnits(chkOpForUsesLocalForces.isSelected()); - options.setOpForAeroChance((Integer) spnOpForAeroChance.getValue()); - options.setOpForLocalUnitChance((Integer) spnOpForLocalForceChance.getValue()); - options.setFixedMapChance((Integer) spnFixedMapChance.getValue()); - options.setSpaUpgradeIntensity((Integer) spnSPAUpgradeIntensity.getValue()); - options.setScenarioModMax((Integer) spnScenarioModMax.getValue()); - options.setScenarioModChance((Integer) spnScenarioModChance.getValue()); - options.setScenarioModBV((Integer) spnScenarioModBV.getValue()); - options.setUseDropShips(chkUseDropShips.isSelected()); - - for (int i = 0; i < spnAtBBattleChance.length; i++) { - options.setAtBBattleChance(i, (Integer) spnAtBBattleChance[i].getValue()); - } - options.setGenerateChases(chkGenerateChases.isSelected()); - options.setMercSizeLimited(chkMercSizeLimited.isSelected()); - options.setRestrictPartsByMission(chkRestrictPartsByMission.isSelected()); - options.setBonusPartExchangeValue((Integer) spnBonusPartExchangeValue.getValue()); - options.setBonusPartMaxExchangeCount((Integer) spnBonusPartMaxExchangeCount.getValue()); - options.setRegionalMekVariations(chkRegionalMekVariations.isSelected()); - options.setAttachedPlayerCamouflage(chkAttachedPlayerCamouflage.isSelected()); - options.setPlayerControlsAttachedUnits(chkPlayerControlsAttachedUnits.isSelected()); - options.setUseWeatherConditions(chkUseWeatherConditions.isSelected()); - options.setUseLightConditions(chkUseLightConditions.isSelected()); - options.setUsePlanetaryConditions(chkUsePlanetaryConditions.isSelected()); - // endregion Against the Bot - - campaign.setCampaignOptions(options); - - recalculateStrategicFormations(campaign); - - MekHQ.triggerEvent(new OptionsChangedEvent(campaign, options)); - } catch (Exception ex) { - logger.error(ex, "Campaign Options update failure, please check the logs for the exception reason.", - "Error Updating Campaign Options"); - } - } - - /** - * Validates the data contained in this panel, returning the current state of - * validation. - * - * @param display to display dialogs containing the messages or not - * @return ValidationState.SUCCESS if the data validates successfully, - * ValidationState.WARNING - * if a warning was issued, or ValidationState.FAILURE if validation - * fails - */ - public ValidationState validateOptions(final boolean display) { - // region Errors - // Name Validation - if (txtName.getText().isBlank()) { - if (display) { - JOptionPane.showMessageDialog(getFrame(), - resources.getString("CampaignOptionsPane.EmptyCampaignName.text"), - resources.getString("InvalidOptions.title"), - JOptionPane.ERROR_MESSAGE); - } - return ValidationState.FAILURE; - } - - // Faction Validation - final FactionDisplay factionDisplay = comboFaction.getSelectedItem(); - if (factionDisplay == null) { - if (display) { - JOptionPane.showMessageDialog(getFrame(), - resources.getString("CampaignOptionsPane.NullFactionSelected.text"), - resources.getString("InvalidOptions.title"), - JOptionPane.ERROR_MESSAGE); - } - return ValidationState.FAILURE; - } else if (StringUtility.isNullOrBlank(factionDisplay.getFaction().getShortName())) { - if (display) { - JOptionPane.showMessageDialog(getFrame(), - String.format(resources.getString( - "CampaignOptionsPane.FactionWithoutFactionCodeSelected.text"), - factionDisplay), - resources.getString("InvalidOptions.title"), - JOptionPane.ERROR_MESSAGE); - } - return ValidationState.FAILURE; - } - // endregion Errors - - // The options specified are correct, and thus can be saved - return ValidationState.SUCCESS; - } - // endregion Options - - // region Unsorted Or Legacy Methods - // Define what is legacy and not and remove in Milestone after 0.49.19 - public void applyPreset(final @Nullable CampaignPreset preset) { - if (preset == null) { - return; - } - - if (isStartup()) { - if (preset.getFaction() != null) { - comboFaction.setSelectedItem(new FactionDisplay(preset.getFaction(), date)); - } - - if (preset.getRankSystem() != null) { - if (preset.getRankSystem().getType().isCampaign()) { - rankSystemsPane.getRankSystemModel().addElement(preset.getRankSystem()); - } - rankSystemsPane.getComboRankSystems().setSelectedItem(preset.getRankSystem()); - } - } - - // Handle CampaignOptions and RandomSkillPreferences - if (preset.getCampaignOptions() != null) { - setOptions(preset.getCampaignOptions(), preset.getRandomSkillPreferences()); - } - - // Handle SPAs - if (!preset.getSpecialAbilities().isEmpty()) { - tempSPA = preset.getSpecialAbilities(); - recreateSPAPanel(!getUnusedSPA().isEmpty()); - } - - if (!preset.getSkills().isEmpty()) { - // Overwriting XP Table - tableXP.setModel(new DefaultTableModel(getSkillCostsArray(preset.getSkills()), - TABLE_XP_COLUMN_NAMES)); - ((DefaultTableModel) tableXP.getModel()).fireTableDataChanged(); - - // Overwriting Skill List - for (final String skillName : SkillType.getSkillList()) { - final JSpinner spnTarget = hashSkillTargets.get(skillName); - if (spnTarget == null) { - continue; - } - final SkillType skillType = preset.getSkills().get(skillName); - - spnTarget.setValue(skillType.getTarget()); - hashGreenSkill.get(skillName).setValue(skillType.getGreenLevel()); - hashRegSkill.get(skillName).setValue(skillType.getRegularLevel()); - hashVetSkill.get(skillName).setValue(skillType.getVeteranLevel()); - hashEliteSkill.get(skillName).setValue(skillType.getEliteLevel()); - } - } - } - - public static String[][] getSkillCostsArray(Map skillHash) { - String[][] array = new String[SkillType.getSkillList().length][11]; - int i = 0; - for (final String name : SkillType.getSkillList()) { - SkillType type = skillHash.get(name); - for (int j = 0; j < 11; j++) { - array[i][j] = Integer.toString(type.getCost(j)); - } - i++; - } - return array; - } - - private void updateXPCosts() { - for (int i = 0; i < SkillType.skillList.length; i++) { - for (int j = 0; j < 11; j++) { - try { - int cost = Integer.parseInt((String) tableXP.getValueAt(i, j)); - SkillType.setCost(SkillType.skillList[i], cost, j); - } catch (Exception ex) { - String message = String.format("unreadable value in skill cost table for {}", - SkillType.skillList[i]); - logger.error(ex, message); - } - } - } - } - - private void updateSkillTypes() { - for (final String skillName : SkillType.getSkillList()) { - SkillType type = SkillType.getType(skillName); - if (hashSkillTargets.get(skillName) != null) { - type.setTarget((Integer) hashSkillTargets.get(skillName).getValue()); - } - - if (hashGreenSkill.get(skillName) != null) { - type.setGreenLevel((Integer) hashGreenSkill.get(skillName).getValue()); - } - - if (hashRegSkill.get(skillName) != null) { - type.setRegularLevel((Integer) hashRegSkill.get(skillName).getValue()); - } - - if (hashVetSkill.get(skillName) != null) { - type.setVeteranLevel((Integer) hashVetSkill.get(skillName).getValue()); - } - - if (hashEliteSkill.get(skillName) != null) { - type.setEliteLevel((Integer) hashEliteSkill.get(skillName).getValue()); - } - } - } - - private void btnDateActionPerformed(ActionEvent evt) { - // show the date chooser - DateChooser dc = new DateChooser(getFrame(), date); - // user can either choose a date or cancel by closing - if (dc.showDateChooser() == DateChooser.OK_OPTION) { - setDate(dc.getDate()); - } - } - - private void setDate(final @Nullable LocalDate date) { - if (date == null) { - return; - } - - this.date = date; - btnDate.setText(MekHQ.getMHQOptions().getDisplayFormattedDate(date)); - - final FactionDisplay factionDisplay = comboFaction.getSelectedItem(); - comboFaction.removeAllItems(); - ((DefaultComboBoxModel) comboFaction.getModel()).addAll(FactionDisplay - .getSortedValidFactionDisplays(Factions.getInstance().getChoosableFactions(), date)); - comboFaction.setSelectedItem(factionDisplay); - } - - private void btnCamoActionPerformed(ActionEvent evt) { - CamoChooserDialog ccd = new CamoChooserDialog(getFrame(), camouflage); - if (ccd.showDialog().isConfirmed()) { - camouflage = ccd.getSelectedItem(); - btnCamo.setIcon(camouflage.getImageIcon()); - } - } - - private Vector getUnusedSPA() { - Vector unused = new Vector<>(); - PersonnelOptions poptions = new PersonnelOptions(); - for (final Enumeration i = poptions.getGroups(); i.hasMoreElements();) { - IOptionGroup group = i.nextElement(); - - if (!group.getKey().equalsIgnoreCase(PersonnelOptions.LVL3_ADVANTAGES)) { - continue; - } - - for (final Enumeration j = group.getOptions(); j.hasMoreElements();) { - IOption option = j.nextElement(); - if (getCurrentSPA().get(option.getName()) == null) { - unused.add(option.getName()); - } - } - } - - for (final String key : SpecialAbility.getDefaultSpecialAbilities().keySet()) { - if ((getCurrentSPA().get(key) == null) && !unused.contains(key)) { - unused.add(key); - } - } - - return unused; - } - - public Map getCurrentSPA() { - return tempSPA; - } - - private void btnAddSPA() { - SelectUnusedAbilityDialog suad = new SelectUnusedAbilityDialog(getFrame(), getUnusedSPA(), - getCurrentSPA()); - suad.setVisible(true); - - recreateSPAPanel(!getUnusedSPA().isEmpty()); - } - - public void btnRemoveSPA(String name) { - getCurrentSPA().remove(name); - - // we also need to cycle through the existing SPAs and remove this one from any - // prereqs - for (final String key : getCurrentSPA().keySet()) { - SpecialAbility otherAbil = getCurrentSPA().get(key); - Vector prereq = otherAbil.getPrereqAbilities(); - Vector invalid = otherAbil.getInvalidAbilities(); - Vector remove = otherAbil.getRemovedAbilities(); - if (prereq.remove(name)) { - otherAbil.setPrereqAbilities(prereq); - } - - if (invalid.remove(name)) { - otherAbil.setInvalidAbilities(invalid); - } - - if (remove.remove(name)) { - otherAbil.setRemovedAbilities(remove); - } - } - - recreateSPAPanel(true); - } - - public void recreateSPAPanel(boolean enableAddSPA) { - panSpecialAbilities.removeAll(); - - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 0.0; - panSpecialAbilities.add(btnAddSPA, gridBagConstraints); - btnAddSPA.setEnabled(enableAddSPA); - - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.gridy = 1; - gridBagConstraints.weighty = 1.0; - - NaturalOrderComparator naturalOrderComparator = new NaturalOrderComparator(); - getCurrentSPA().values().stream() - .sorted((o1, o2) -> naturalOrderComparator.compare(o1.getDisplayName(), - o2.getDisplayName())) - .forEach(spa -> { - panSpecialAbilities.add(new SpecialAbilityPanel(spa, this), gridBagConstraints); - gridBagConstraints.gridy++; - }); - panSpecialAbilities.revalidate(); - panSpecialAbilities.repaint(); - } - - /** - * Recreates the finances panel to reverse the quality labels. - * - * @param reverseQualities boolean for if the qualities are reversed. - */ - private void recreateFinancesPanel(boolean reverseQualities) { - int financesTabIndex = indexOfTab(resources.getString("financesPanel.title")); - removeTabAt(financesTabIndex); - insertTab(resources.getString("financesPanel.title"), null, createFinancesTab(reverseQualities), null, - financesTabIndex); - - // The following setters and getter calls are so that recreating the tab doesn't - // reset all - // options to their default values - - // General finances panel options - payForPartsBox.setSelected(options.isPayForParts()); - payForRepairsBox.setSelected(options.isPayForRepairs()); - payForUnitsBox.setSelected(options.isPayForUnits()); - payForSalariesBox.setSelected(options.isPayForSalaries()); - payForOverheadBox.setSelected(options.isPayForOverhead()); - payForMaintainBox.setSelected(options.isPayForMaintain()); - payForTransportBox.setSelected(options.isPayForTransport()); - sellUnitsBox.setSelected(options.isSellUnits()); - sellPartsBox.setSelected(options.isSellParts()); - payForRecruitmentBox.setSelected(options.isPayForRecruitment()); - useLoanLimitsBox.setSelected(options.isUseLoanLimits()); - usePercentageMaintenanceBox.setSelected(options.isUsePercentageMaint()); - useInfantryDoseNotCountBox.setSelected(options.isInfantryDontCount()); - usePeacetimeCostBox.setSelected(options.isUsePeacetimeCost()); - useExtendedPartsModifierBox.setSelected(options.isUseExtendedPartsModifier()); - showPeacetimeCostBox.setSelected(options.isShowPeacetimeCost()); - comboFinancialYearDuration.setSelectedItem(options.getFinancialYearDuration()); - newFinancialYearFinancesToCSVExportBox.setSelected(options.isNewFinancialYearFinancesToCSVExport()); - - // Taxes - chkUseTaxes.setSelected(options.isUseTaxes()); - spnTaxesPercentage.setValue(options.getTaxesPercentage()); - - // Price Multipliers Panel - spnCommonPartPriceMultiplier.setValue(options.getCommonPartPriceMultiplier()); - spnInnerSphereUnitPriceMultiplier.setValue(options.getInnerSphereUnitPriceMultiplier()); - spnInnerSpherePartPriceMultiplier.setValue(options.getInnerSpherePartPriceMultiplier()); - spnClanUnitPriceMultiplier.setValue(options.getClanUnitPriceMultiplier()); - spnClanPartPriceMultiplier.setValue(options.getClanPartPriceMultiplier()); - spnMixedTechUnitPriceMultiplier.setValue(options.getMixedTechUnitPriceMultiplier()); - spnDamagedPartsValueMultiplier.setValue(options.getDamagedPartsValueMultiplier()); - spnUnrepairablePartsValueMultiplier.setValue(options.getUnrepairablePartsValueMultiplier()); - spnCancelledOrderRefundMultiplier.setValue(options.getCancelledOrderRefundMultiplier()); - - // Used Parts Multiplier Panel - for (int index = PartQuality.QUALITY_A.toNumeric(); index <= PartQuality.QUALITY_F.toNumeric(); index++) { - spnUsedPartPriceMultipliers[index].setValue(options.getUsedPartPriceMultipliers()[index]); - } - - // Shares System - chkUseShareSystem.setSelected(options.isUseShareSystem()); - chkSharesForAll.setSelected(options.isSharesForAll()); - } - - private void enableAtBComponents(JPanel panel, boolean enabled) { - for (final Component c : panel.getComponents()) { - if (c.equals(chkUseAtB)) { - continue; - } - - if (c instanceof JPanel) { - enableAtBComponents((JPanel) c, enabled); - } else { - c.setEnabled(enabled); - } - } - } - - private double determineAtBBattleIntensity() { - double intensity = 0.0; - - int x = (Integer) spnAtBBattleChance[AtBLanceRole.FIGHTING.ordinal()].getValue(); - intensity += ((-3.0 / 2.0) * (2.0 * x - 1.0)) / (2.0 * x - 201.0); - - x = (Integer) spnAtBBattleChance[AtBLanceRole.DEFENCE.ordinal()].getValue(); - intensity += ((-4.0) * (2.0 * x - 1.0)) / (2.0 * x - 201.0); - - x = (Integer) spnAtBBattleChance[AtBLanceRole.SCOUTING.ordinal()].getValue(); - intensity += ((-2.0 / 3.0) * (2.0 * x - 1.0)) / (2.0 * x - 201.0); - - x = (Integer) spnAtBBattleChance[AtBLanceRole.TRAINING.ordinal()].getValue(); - intensity += ((-9.0) * (2.0 * x - 1.0)) / (2.0 * x - 201.0); - - intensity = intensity / 4.0; - - if (intensity > 100.0) { - intensity = 100.0; - } - - return Math.round(intensity * 10.0) / 10.0; - } - - private class AtBBattleIntensityChangeListener implements ChangeListener { - @Override - public void stateChanged(ChangeEvent e) { - double intensity = (Double) spnAtBBattleIntensity.getValue(); - - if (intensity >= AtBContract.MINIMUM_INTENSITY) { - int value = (int) Math.min( - Math.round(400.0 * intensity / (4.0 * intensity + 6.0) + 0.05), 100); - spnAtBBattleChance[AtBLanceRole.FIGHTING.ordinal()].setValue(value); - value = (int) Math.min(Math.round(200.0 * intensity / (2.0 * intensity + 8.0) + 0.05), - 100); - spnAtBBattleChance[AtBLanceRole.DEFENCE.ordinal()].setValue(value); - value = (int) Math.min(Math.round(600.0 * intensity / (6.0 * intensity + 4.0) + 0.05), - 100); - spnAtBBattleChance[AtBLanceRole.SCOUTING.ordinal()].setValue(value); - value = (int) Math.min(Math.round(100.0 * intensity / (intensity + 9.0) + 0.05), 100); - spnAtBBattleChance[AtBLanceRole.TRAINING.ordinal()].setValue(value); - } else { - spnAtBBattleChance[AtBLanceRole.FIGHTING.ordinal()].setValue(0); - spnAtBBattleChance[AtBLanceRole.DEFENCE.ordinal()].setValue(0); - spnAtBBattleChance[AtBLanceRole.SCOUTING.ordinal()].setValue(0); - spnAtBBattleChance[AtBLanceRole.TRAINING.ordinal()].setValue(0); - } - } - } - - /* - * Taken from: - * http://tips4java.wordpress.com/2008/11/18/row-number-table/ - * Use a JTable as a renderer for row numbers of a given main table. - * This table must be added to the row header of the scrollpane that - * contains the main table. - */ - public static class RowNamesTable extends JTable implements ChangeListener, PropertyChangeListener { - private final JTable main; - - public RowNamesTable(JTable table) { - main = table; - main.addPropertyChangeListener(this); - - setFocusable(false); - setAutoCreateColumnsFromModel(false); - setModel(main.getModel()); - setSelectionModel(main.getSelectionModel()); - - TableColumn column = new TableColumn(); - column.setHeaderValue(" "); - addColumn(column); - column.setCellRenderer(new RowNumberRenderer()); - - getColumnModel().getColumn(0).setPreferredWidth(120); - setPreferredScrollableViewportSize(getPreferredSize()); - } - - @Override - public void addNotify() { - super.addNotify(); - Component c = getParent(); - // Keep scrolling of the row table in sync with the main table. - if (c instanceof JViewport viewport) { - viewport.addChangeListener(this); - } - } - - /* - * Delegate method to main table - */ - @Override - public int getRowCount() { - return main.getRowCount(); - } - - @Override - public int getRowHeight(int row) { - return main.getRowHeight(row); - } - - /* - * This table does not use any data from the main TableModel, - * so return a value based on the row parameter. - */ - @Override - public Object getValueAt(int row, int column) { - return SkillType.skillList[row]; - } - - /* - * Don't edit data in the main TableModel by mistake - */ - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - - // - // Implement the ChangeListener - // - @Override - public void stateChanged(ChangeEvent e) { - // Keep the scrolling of the row table in sync with main table - JViewport viewport = (JViewport) e.getSource(); - JScrollPane scrollPane = (JScrollPane) viewport.getParent(); - scrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y); - } - - // - // Implement the PropertyChangeListener - // - @Override - public void propertyChange(PropertyChangeEvent e) { - // Keep the row table in sync with the main table - - if ("selectionModel".equals(e.getPropertyName())) { - setSelectionModel(main.getSelectionModel()); - } - - if ("model".equals(e.getPropertyName())) { - setModel(main.getModel()); - } - } - - /* - * Borrow the renderer from JDK1.4.2 table header - */ - private static class RowNumberRenderer extends DefaultTableCellRenderer { - public RowNumberRenderer() { - setHorizontalAlignment(JLabel.LEFT); - } - - @Override - public Component getTableCellRendererComponent(final JTable table, final Object value, - final boolean isSelected, - final boolean hasFocus, final int row, - final int column) { - if (table != null) { - JTableHeader header = table.getTableHeader(); - - if (header != null) { - setForeground(header.getForeground()); - setBackground(header.getBackground()); - setFont(header.getFont()); - } - } - - if (isSelected) { - setFont(getFont().deriveFont(Font.BOLD)); - } - - setText((value == null) ? "" : value.toString()); - setBorder(UIManager.getBorder("TableHeader.cellBorder")); - - return this; - } - } - } - // endregion Unsorted Or Legacy Methods -} diff --git a/MekHQ/src/mekhq/gui/panes/RankSystemsPane.java b/MekHQ/src/mekhq/gui/panes/RankSystemsPane.java index fe305f05b79..6d6e6409233 100644 --- a/MekHQ/src/mekhq/gui/panes/RankSystemsPane.java +++ b/MekHQ/src/mekhq/gui/panes/RankSystemsPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,21 +18,6 @@ */ package mekhq.gui.panes; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.GridLayout; -import java.io.File; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.swing.*; -import javax.swing.table.TableColumn; - import megamek.client.ui.baseComponents.MMButton; import megamek.client.ui.baseComponents.MMComboBox; import megamek.client.ui.baseComponents.SpinnerCellEditor; @@ -54,6 +39,14 @@ import mekhq.gui.model.RankTableModel; import mekhq.gui.utilities.JScrollPaneWithSpeed; +import javax.swing.*; +import javax.swing.GroupLayout.Alignment; +import javax.swing.table.TableColumn; +import java.awt.*; +import java.io.File; +import java.util.List; +import java.util.*; + public class RankSystemsPane extends AbstractMHQScrollPane { private static final MMLogger logger = MMLogger.create(RankSystemsPane.class); @@ -180,16 +173,6 @@ protected void initialize() { gbc.fill = GridBagConstraints.HORIZONTAL; gbc.anchor = GridBagConstraints.NORTHWEST; - final JTextArea txtInstructionsRanks = new JTextArea(resources.getString("txtInstructionsRanks.text")); - txtInstructionsRanks.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder(resources.getString("txtInstructionsRanks.title")), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - txtInstructionsRanks.setOpaque(false); - txtInstructionsRanks.setEditable(false); - txtInstructionsRanks.setLineWrap(true); - txtInstructionsRanks.setWrapStyleWord(true); - rankSystemsPanel.add(txtInstructionsRanks, gbc); - gbc.gridy++; rankSystemsPanel.add(createRankSystemPanel(), gbc); @@ -288,14 +271,14 @@ public Component getListCellRendererComponent(final JList list, final Object layout.setVerticalGroup( layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(lblRankSystem) .addComponent(getComboRankSystems()) .addComponent(getComboRankSystemType()) - .addComponent(btnCreateCustomRankSystem, GroupLayout.Alignment.LEADING))); + .addComponent(btnCreateCustomRankSystem, Alignment.LEADING))); layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) + layout.createParallelGroup(Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(lblRankSystem) .addComponent(getComboRankSystems()) diff --git a/MekHQ/src/mekhq/gui/renderers/CampaignPresetRenderer.java b/MekHQ/src/mekhq/gui/renderers/CampaignPresetRenderer.java deleted file mode 100644 index 25b81fe3d7b..00000000000 --- a/MekHQ/src/mekhq/gui/renderers/CampaignPresetRenderer.java +++ /dev/null @@ -1,66 +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.gui.renderers; - -import mekhq.campaign.CampaignPreset; -import mekhq.gui.panels.CampaignPresetPanel; - -import javax.swing.*; -import java.awt.*; - -public class CampaignPresetRenderer extends CampaignPresetPanel implements ListCellRenderer { - //region Constructors - public CampaignPresetRenderer(final JFrame frame) { - super(frame, null, null); - } - //endregion Constructors - - @Override - public Component getListCellRendererComponent(final JList list, - final CampaignPreset value, final int index, - final boolean isSelected, - final boolean cellHasFocus) { - // JTextArea::setForeground and JTextArea::setBackground don't work properly with the - // default return, but by recreating the colour it works properly - final Color foreground = new Color((isSelected - ? list.getSelectionForeground() : list.getForeground()).getRGB()); - final Color background = new Color((isSelected - ? list.getSelectionBackground() : list.getBackground()).getRGB()); - setForeground(foreground); - setBackground(background); - - getTxtDescription().setForeground(foreground); - getTxtDescription().setBackground(background); - - updateFromPreset(value); - this.revalidate(); - - return this; - } - - @Override - public Dimension getMinimumSize() { - return new Dimension(300, 100); - } - - @Override - public Dimension getPreferredSize() { - return new Dimension(400, 120); - } -} diff --git a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java index da0b596dabb..22f73b6481f 100644 --- a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java +++ b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java @@ -19,6 +19,9 @@ package mekhq.gui.stratcon; +import megamek.client.ui.swing.util.UIUtil; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; import mekhq.campaign.stratcon.StratconCampaignState; import mekhq.campaign.stratcon.StratconRulesManager; import mekhq.campaign.stratcon.StratconTrackState; @@ -27,20 +30,25 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; +import java.util.ResourceBundle; /** * This class handles the UI for campaign VP/SP management * @author NickAragua */ public class CampaignManagementDialog extends JDialog { + private Campaign campaign; private StratconCampaignState currentCampaignState; private final StratconTab parent; private JButton btnRemoveCVP; - private JButton btnConvertSPtoBonusPart; + private JButton btnGMRemoveSP; private JButton btnGMAddVP; private JButton btnGMAddSP; private JLabel lblTrackScenarioOdds; + final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", + MekHQ.getMHQOptions().getLocale()); + public CampaignManagementDialog(StratconTab parent) { this.parent = parent; this.setTitle("Manage SP/CVP"); @@ -50,58 +58,84 @@ public CampaignManagementDialog(StratconTab parent) { /** * Show the dialog for a given campaign state, and whether GM mode is on or not */ - public void display(StratconCampaignState campaignState, StratconTrackState currentTrack, boolean gmMode) { + public void display(Campaign campaign, StratconCampaignState campaignState, + StratconTrackState currentTrack, boolean gmMode) { currentCampaignState = campaignState; btnRemoveCVP.setEnabled(currentCampaignState.getVictoryPoints() > 0); - btnConvertSPtoBonusPart.setEnabled(currentCampaignState.getSupportPoints() > 0); + btnGMRemoveSP.setEnabled(currentCampaignState.getSupportPoints() > 0); btnGMAddVP.setEnabled(gmMode); btnGMAddSP.setEnabled(gmMode); lblTrackScenarioOdds.setVisible(gmMode); if (gmMode) { - lblTrackScenarioOdds.setText(String.format("Track Scenario Odds: %d%%", - StratconRulesManager.calculateScenarioOdds(currentTrack, campaignState.getContract(), false))); + lblTrackScenarioOdds.setText(String.format(resources.getString("trackScenarioOdds.text"), + StratconRulesManager.calculateScenarioOdds(currentTrack, campaignState.getContract(), + false))); } + + this.campaign = campaign; } /** * One-time set up for all the buttons. */ private void initializeUI() { - GridLayout layout = new GridLayout(); - layout.setColumns(2); - layout.setRows(0); - layout.setHgap(1); - layout.setVgap(1); - getContentPane().removeAll(); + + // Set up GridBagLayout and constraints + GridBagLayout layout = new GridBagLayout(); getContentPane().setLayout(layout); - btnRemoveCVP = new JButton(); - btnRemoveCVP.setText("Remove CVP (GM)"); - btnRemoveCVP.addActionListener(this::removeCVP); - getContentPane().add(btnRemoveCVP); + GridBagConstraints gbc = new GridBagConstraints(); + int insertSize = UIUtil.scaleForGUI(8); + gbc.insets = new Insets(insertSize, insertSize, insertSize, insertSize); + gbc.fill = GridBagConstraints.HORIZONTAL; - btnConvertSPtoBonusPart = new JButton(); - btnConvertSPtoBonusPart.setText("Convert SP to bonus part"); - btnConvertSPtoBonusPart.addActionListener(this::convertSPtoBonusPartHandler); - getContentPane().add(btnConvertSPtoBonusPart); + // Add the "Track Scenario Odds" label + lblTrackScenarioOdds = new JLabel(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 2; + gbc.anchor = GridBagConstraints.CENTER; + getContentPane().add(lblTrackScenarioOdds, gbc); + + // Add the "Remove SP" button + btnGMRemoveSP = new JButton(resources.getString("btnRemoveSP.text")); + btnGMRemoveSP.addActionListener(evt -> { + dispose(); + removeSP(evt); + }); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 1; + getContentPane().add(btnGMRemoveSP, gbc); + + // Add the "Add SP (GM)" button + btnGMAddSP = new JButton(resources.getString("btnAddSP.text")); + btnGMAddSP.addActionListener(this::gmAddSPHandler); + gbc.gridx = 1; + gbc.gridy = 1; + getContentPane().add(btnGMAddSP, gbc); - btnGMAddVP = new JButton(); - btnGMAddVP.setText("Add CVP (GM)"); + // Add the "Add CVP (GM)" button + btnGMAddVP = new JButton(resources.getString("btnAddCVP.text")); btnGMAddVP.addActionListener(this::gmAddVPHandler); - getContentPane().add(btnGMAddVP); + gbc.gridx = 0; + gbc.gridy = 2; + getContentPane().add(btnGMAddVP, gbc); - btnGMAddSP = new JButton(); - btnGMAddSP.setText("Add SP (GM)"); - btnGMAddSP.addActionListener(this::gmAddSPHandler); - getContentPane().add(btnGMAddSP); - - lblTrackScenarioOdds = new JLabel(); - getContentPane().add(lblTrackScenarioOdds); + // Add the "Remove CVP (GM)" button + btnRemoveCVP = new JButton(resources.getString("btnRemoveCVP.text")); + btnRemoveCVP.addActionListener(this::removeCVP); + gbc.gridx = 1; + gbc.gridy = 2; + getContentPane().add(btnRemoveCVP, gbc); + // Finalize the dialog pack(); + setModal(true); + setResizable(false); } private void removeCVP(ActionEvent e) { @@ -110,10 +144,12 @@ private void removeCVP(ActionEvent e) { parent.updateCampaignState(); } - private void convertSPtoBonusPartHandler(ActionEvent e) { - currentCampaignState.useSupportPoint(); - currentCampaignState.getContract().addBonusParts(1); - btnConvertSPtoBonusPart.setEnabled(currentCampaignState.getSupportPoints() > 0); + private void removeSP(ActionEvent event) { + int currentSupportPoints = currentCampaignState.getSupportPoints(); + if (currentSupportPoints > 1) { + currentCampaignState.setSupportPoints(currentSupportPoints - 1); + } + parent.updateCampaignState(); } @@ -125,7 +161,7 @@ private void gmAddVPHandler(ActionEvent e) { private void gmAddSPHandler(ActionEvent e) { currentCampaignState.addSupportPoints(1); - btnConvertSPtoBonusPart.setEnabled(currentCampaignState.getSupportPoints() > 0); + btnGMRemoveSP.setEnabled(currentCampaignState.getSupportPoints() > 0); parent.updateCampaignState(); } } diff --git a/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardLanceModel.java b/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardLanceModel.java index c3dcc697790..ba4ba97c6f3 100644 --- a/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardLanceModel.java +++ b/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardLanceModel.java @@ -14,17 +14,15 @@ package mekhq.gui.stratcon; +import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; + +import javax.swing.*; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.List; -import javax.swing.DefaultListModel; - -import mekhq.campaign.Campaign; -import mekhq.campaign.force.Force; - /** * List data model for the StratCon scenario wizard. * @author NickAragua @@ -41,8 +39,7 @@ public ScenarioWizardLanceModel(Campaign campaign, Collection forceIDs) } // let's sort these guys by alphabetical order - Collections.sort(sortedForces, - (Comparator) (Force o1, Force o2) -> o1.getName().compareTo(o2.getName()) ); + sortedForces.sort(Comparator.comparing(Force::getName)); super.addAll(sortedForces); } diff --git a/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardLanceRenderer.java b/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardLanceRenderer.java index ff21a92f650..2befbc877cb 100644 --- a/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardLanceRenderer.java +++ b/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardLanceRenderer.java @@ -18,13 +18,19 @@ */ package mekhq.gui.stratcon; +import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.force.Force; -import mekhq.campaign.force.StrategicFormation; +import mekhq.campaign.icons.enums.OperationalStatus; import javax.swing.*; import java.awt.*; +import static mekhq.campaign.icons.enums.OperationalStatus.NOT_OPERATIONAL; +import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; + /** * Handles rendering of individual lances in the StratCon scenario wizard. * @author NickAragua @@ -38,7 +44,7 @@ public ScenarioWizardLanceRenderer(Campaign campaign) { } @Override - public Component getListCellRendererComponent(final JList list, final Force value, + public Component getListCellRendererComponent(final JList list, final Force force, final int index, final boolean isSelected, final boolean cellHasFocus) { // JTextArea::setForeground and JTextArea::setBackground don't work properly with the @@ -50,13 +56,37 @@ public Component getListCellRendererComponent(final JList list, setForeground(foreground); setBackground(background); - StrategicFormation lance = campaign.getStrategicFormationsTable().get(value.getId()); + // Determine name color + OperationalStatus operationalStatus = force.updateForceIconOperationalStatus(campaign).get(0); + + String statusOpenFormat = switch (operationalStatus) { + case NOT_OPERATIONAL -> ""; + case MARGINALLY_OPERATIONAL -> spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor()); + case SUBSTANTIALLY_OPERATIONAL -> spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorWarningHexColor()); + case FULLY_OPERATIONAL, FACTORY_FRESH -> spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor()); + }; + + String statusCloseFormat = operationalStatus == NOT_OPERATIONAL ? "" : CLOSING_SPAN_TAG; + + // Get combat role + CombatTeam combatTeam = campaign.getCombatTeamsTable().get(force.getId()); String roleString = ""; - if (lance != null) { - roleString = lance.getRole().toString() + ", "; + if (combatTeam != null) { + roleString = combatTeam.getRole().toString() + ", "; } - setText(String.format("%s (%sBV: %d)", value.getName(), roleString, value.getTotalBV(campaign, true))); + // Adjust force name to remove unnecessary information + String forceName = force.getFullName(); + String originNodeName = ", " + campaign.getForce(0).getName(); + forceName = forceName.replaceAll(originNodeName, ""); + + // Format string + setText(String.format("%s%s%s, %s - BV %s
         %s", + statusOpenFormat, force.getName(), statusCloseFormat, roleString, + force.getTotalBV(campaign, true), forceName)); return this; } diff --git a/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardUnitRenderer.java b/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardUnitRenderer.java index 8d642772692..3c8abf0a4e9 100644 --- a/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardUnitRenderer.java +++ b/MekHQ/src/mekhq/gui/stratcon/ScenarioWizardUnitRenderer.java @@ -13,13 +13,20 @@ */ package mekhq.gui.stratcon; +import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.force.Force; +import mekhq.campaign.icons.enums.OperationalStatus; import mekhq.campaign.unit.Unit; import javax.swing.*; import java.awt.*; +import static mekhq.campaign.icons.enums.OperationalStatus.NOT_OPERATIONAL; +import static mekhq.campaign.icons.enums.OperationalStatus.determineLayeredForceIconOperationalStatus; +import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; + /** * Handles rendering individual units in lists in the StratCon scenario wizard. * @author NickAragua @@ -30,13 +37,29 @@ public ScenarioWizardUnitRenderer() { } @Override - public Component getListCellRendererComponent(JList list, Unit value, int index, + public Component getListCellRendererComponent(JList list, Unit unit, int index, boolean isSelected, boolean cellHasFocus) { - Campaign campaign = value.getCampaign(); + Campaign campaign = unit.getCampaign(); - int valueForceId = value.getForceId(); + int valueForceId = unit.getForceId(); Force force = campaign.getForce(valueForceId); + // Determine name color + OperationalStatus operationalStatus = determineLayeredForceIconOperationalStatus(unit); + + String statusOpenFormat = switch (operationalStatus) { + case NOT_OPERATIONAL -> ""; + case MARGINALLY_OPERATIONAL -> spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor()); + case SUBSTANTIALLY_OPERATIONAL -> spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorWarningHexColor()); + case FULLY_OPERATIONAL, FACTORY_FRESH -> spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor()); + }; + + String statusCloseFormat = operationalStatus == NOT_OPERATIONAL ? "" : CLOSING_SPAN_TAG; + + // Adjust force name to remove unnecessary information String forceName = ""; if (force != null) { forceName = force.getFullName(); @@ -44,9 +67,10 @@ public Component getListCellRendererComponent(JList list, Unit v forceName = forceName.replaceAll(originNodeName, ""); } - setText(String.format("%s (%s/%s) - %s - Base BV: %d
    %s", - value.getName(), value.getEntity().getCrew().getGunnery(), value.getEntity().getCrew().getPiloting(), - value.getCondition(), value.getEntity().calculateBattleValue(true, true), + // Format string + setText(String.format("%s%s%s (%s/%s) - Base BV: %d
         %s", + statusOpenFormat, unit.getName(), statusCloseFormat, unit.getEntity().getCrew().getGunnery(), + unit.getEntity().getCrew().getPiloting(), unit.getEntity().calculateBattleValue(true, true), forceName)); if (isSelected) { diff --git a/MekHQ/src/mekhq/gui/stratcon/StratconScenarioWizard.java b/MekHQ/src/mekhq/gui/stratcon/StratconScenarioWizard.java index c1f2ab8c3ef..8382498e0f2 100644 --- a/MekHQ/src/mekhq/gui/stratcon/StratconScenarioWizard.java +++ b/MekHQ/src/mekhq/gui/stratcon/StratconScenarioWizard.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,19 +18,21 @@ */ package mekhq.gui.stratcon; +import megamek.client.ui.swing.util.UIUtil; import megamek.common.Minefield; +import megamek.common.TargetRoll; +import megamek.common.TargetRollModifier; +import megamek.common.annotations.Nullable; import megamek.logging.MMLogger; import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.campaign.Campaign.AdministratorSpecialization; import mekhq.campaign.force.Force; -import mekhq.campaign.mission.AtBDynamicScenarioFactory; import mekhq.campaign.mission.ScenarioForceTemplate; +import mekhq.campaign.personnel.Person; import mekhq.campaign.stratcon.StratconCampaignState; import mekhq.campaign.stratcon.StratconRulesManager; -import mekhq.campaign.stratcon.StratconRulesManager.ReinforcementEligibilityType; -import mekhq.campaign.stratcon.StratconRulesManager.ReinforcementResultsType; import mekhq.campaign.stratcon.StratconScenario; -import mekhq.campaign.stratcon.StratconScenario.ScenarioState; import mekhq.campaign.stratcon.StratconTrackState; import mekhq.campaign.unit.Unit; import mekhq.gui.utilities.JScrollPaneWithSpeed; @@ -42,16 +44,17 @@ import java.util.List; import java.util.*; -import static java.lang.Math.min; +import static mekhq.campaign.mission.AtBDynamicScenarioFactory.scaleObjectiveTimeLimits; import static mekhq.campaign.mission.AtBDynamicScenarioFactory.translateTemplateObjectives; import static mekhq.campaign.personnel.SkillType.S_LEADER; -import static mekhq.campaign.stratcon.StratconRulesManager.BASE_LEADERSHIP_BUDGET; +import static mekhq.campaign.stratcon.StratconRulesManager.*; import static mekhq.campaign.stratcon.StratconRulesManager.ReinforcementResultsType.DELAYED; import static mekhq.campaign.stratcon.StratconRulesManager.ReinforcementResultsType.FAILED; -import static mekhq.campaign.stratcon.StratconRulesManager.getEligibleLeadershipUnits; -import static mekhq.campaign.stratcon.StratconRulesManager.processReinforcementDeployment; -import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; -import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; +import static mekhq.campaign.stratcon.StratconScenario.ScenarioState.PRIMARY_FORCES_COMMITTED; +import static mekhq.campaign.stratcon.StratconScenario.ScenarioState.REINFORCEMENTS_COMMITTED; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerDescription; +import static mekhq.gui.baseComponents.MHQDialogImmersive.getSpeakerIcon; +import static mekhq.utilities.ImageUtilities.scaleImageIconToWidth; /** * UI for managing force/unit assignments for individual StratCon scenarios. @@ -61,7 +64,7 @@ public class StratconScenarioWizard extends JDialog { private final Campaign campaign; private StratconTrackState currentTrackState; private StratconCampaignState currentCampaignState; - private final transient ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", + private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", MekHQ.getMHQOptions().getLocale()); private final Map> availableForceLists = new HashMap<>(); @@ -70,6 +73,7 @@ public class StratconScenarioWizard extends JDialog { private JList availableInfantryUnits = new JList<>(); private JList availableLeadershipUnits = new JList<>(); + private JPanel contentPanel; private JButton btnCommit; private static final MMLogger logger = MMLogger.create(StratconScenarioWizard.class); @@ -80,53 +84,102 @@ public StratconScenarioWizard(Campaign campaign) { } /** - * Selects a scenario on a particular track in a particular campaign. + * Configures and sets the current Stratcon scenario, updating the associated track and campaign states + * as well as preparing the available forces and units for the scenario. + * + * @param scenario the {@link StratconScenario} to be set as the current scenario. + * @param trackState the {@link StratconTrackState} representing the state of the scenario's track. + * @param campaignState the {@link StratconCampaignState} representing the state of the overall campaign. + * @param isPrimaryForce a boolean flag indicating whether the primary force is being assigned for this scenario. + *
      + *
    • {@code true}: Indicates that the primary force is being deployed.
    • + *
    • {@code false}: Indicates that the scenario is being configured without primary force assignment.
    • + *
    + * + *

    Functionality and Process:

    + *
      + *
    • Sets the provided scenario as the {@code currentScenario}.
    • + *
    • Updates the {@link StratconCampaignState}, {@link StratconTrackState}, and clears previous force/unit lists.
    • + *
    • Initializes the user interface by calling {@link #setUI(boolean)}, passing the {@code isPrimaryForce} parameter.
    • + *
    */ public void setCurrentScenario(StratconScenario scenario, StratconTrackState trackState, - StratconCampaignState campaignState) { + StratconCampaignState campaignState, boolean isPrimaryForce) { currentScenario = scenario; currentCampaignState = campaignState; currentTrackState = trackState; availableForceLists.clear(); availableUnitLists.clear(); - setUI(); + setUI(isPrimaryForce); } /** - * Sets up the UI as appropriate for the currently selected scenario. + * Configures and initializes the user interface for the scenario setup wizard. + * This method dynamically assembles various UI components based on the scenario's state and + * whether the primary force is being assigned. + * + * @param isPrimaryForce a boolean flag indicating whether the primary force is being assigned: + *
      + *
    • {@code true}: Configures the UI with additional components for leadership and defensive points.
    • + *
    • {@code false}: Configures the UI for scenarios without primary force-specific components.
    • + *
    + * + *

    Process and Behavior:

    + *
      + *
    1. Sets the dialog window's title based on resource strings.
    2. + *
    3. Clears the existing content pane to rebuild the UI.
    4. + *
    5. Uses a {@link JPanel} with {@link GridBagLayout} to organize UI components.
    6. + *
    7. Adds components for instructions and force assignment.
    8. + *
    9. If {@code isPrimaryForce} is {@code true}, adds special UI components such as: + *
        + *
      • Leadership unit selection, sorted by force name (if leadership skill is greater than 0).
      • + *
      • Defensive points configuration.
      • + *
      + *
    10. + *
    11. Adds navigation buttons for controlling the wizard flow (Next, Back, Cancel, etc.).
    12. + *
    13. Wraps all content in a {@link JScrollPane} to handle large UI layouts with scrollbars.
    14. + *
    15. Finalizes the UI setup with {@code pack()} and {@code validate()} for proper rendering.
    16. + *
    + * + *

    Roles and Responsibilities:

    + *
      + *
    • Handles complex UI layouts dynamically based on scenario state and user input requirements.
    • + *
    • Ensures scalability for larger content using scrollbars.
    • + *
    */ - private void setUI() { - setTitle("Scenario Setup Wizard"); + private void setUI(boolean isPrimaryForce) { + setTitle(resources.getString("scenarioSetupWizard.title")); getContentPane().removeAll(); - GridBagConstraints gbc = new GridBagConstraints(); - getContentPane().setLayout(new GridBagLayout()); + // Create a new panel to hold all components + contentPanel = new JPanel(); + contentPanel.setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.anchor = GridBagConstraints.WEST; + + // Add instructions setInstructions(gbc); - if (Objects.requireNonNull(currentScenario.getCurrentState()) == ScenarioState.UNRESOLVED) { - gbc.gridy++; - setAssignForcesUI(gbc, false); - } else { - gbc.gridy++; - setAssignForcesUI(gbc, true); - gbc.gridy++; + // Move to the next row + gbc.gridy++; - int leadershipSkill = currentScenario.getBackingScenario().getLanceCommanderSkill( - S_LEADER, campaign); + // Add UI for assigning forces + setAssignForcesUI(gbc, isPrimaryForce); - List eligibleLeadershipUnits = getEligibleLeadershipUnits(campaign, - currentScenario.getPrimaryForceIDs(), leadershipSkill); + // Handle optional UI for eligible leadership, defensive points, etc. + if (isPrimaryForce) { + gbc.gridy++; + int leadershipSkill = currentScenario.getBackingScenario().getLanceCommanderSkill(S_LEADER, campaign); + List eligibleLeadershipUnits = getEligibleLeadershipUnits( + campaign, currentScenario.getPrimaryForceIDs(), leadershipSkill); eligibleLeadershipUnits.sort(Comparator.comparing(this::getForceNameReversed)); - if (!eligibleLeadershipUnits.isEmpty() && (leadershipSkill > 0)) { - setLeadershipUI(gbc, eligibleLeadershipUnits, leadershipSkill); - gbc.gridy++; - } + setLeadershipUI(gbc, eligibleLeadershipUnits, leadershipSkill); + gbc.gridy++; if (currentScenario.getNumDefensivePoints() > 0) { setDefensiveUI(gbc); @@ -134,9 +187,19 @@ private void setUI() { } } + // Add navigation buttons gbc.gridx = 0; gbc.gridy++; - setNavigationButtons(gbc); + setNavigationButtons(gbc, isPrimaryForce); + + // Wrap contentPanel in a scroll pane + JScrollPane scrollPane = new JScrollPane(contentPanel); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + // Add the scrollPane to the content pane of the dialog + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(scrollPane, BorderLayout.CENTER); pack(); validate(); @@ -191,114 +254,164 @@ private void setInstructions(GridBagConstraints gbc) { labelBuilder.append(""); if (currentTrackState.isGmRevealed() - || currentTrackState.getRevealedCoords().contains(currentScenario.getCoords()) || - (currentScenario.getDeploymentDate() != null)) { - labelBuilder.append(currentScenario.getInfo(campaign, true)); - } - - if (Objects.requireNonNull(currentScenario.getCurrentState()) == ScenarioState.UNRESOLVED) { - labelBuilder.append("primaryForceAssignmentInstructions.text"); - } else { - labelBuilder.append("reinforcementsAndSupportInstructions.text"); + || currentTrackState.getRevealedCoords().contains(currentScenario.getCoords()) + || (currentScenario.getDeploymentDate() != null)) { + labelBuilder.append(currentScenario.getInfo(campaign)); } labelBuilder.append("
    "); lblInfo.setText(labelBuilder.toString()); - getContentPane().add(lblInfo, gbc); + + contentPanel.add(lblInfo, gbc); } /** - * Worker function that sets up the "assign forces to scenario" UI elements. + * Configures and populates the user interface for assigning forces to the scenario. + * This method dynamically creates UI panels, one for each eligible force template, and displays + * the available forces for selection based on the reinforcement or primary force status. + * + * @param gbc the {@link GridBagConstraints} to manage the layout of the force assignment UI. + * @param isPrimaryForce a boolean flag indicating whether the UI is being configured for the primary force assignment: + *
      + *
    • {@code true}: Lists player forces designated as primary forces.
    • + *
    • {@code false}: Lists reinforcement forces that may require support points.
    • + *
    + * + *

    Process and Behavior:

    + *
      + *
    1. Retrieves a list of eligible force templates based on the value of {@code isPrimaryForce}. + *
        + *
      • For primary forces, {@link StratconScenarioTemplate#getAllPrimaryPlayerForces()} is used.
      • + *
      • For reinforcement forces, {@link StratconScenarioTemplate#getAllPlayerReinforcementForces()} is used.
      • + *
      + *
    2. + *
    3. For each eligible force template: + *
        + *
      • Creates a panel to hold UI components specific to that force.
      • + *
      • Adds instructional text for reinforcements if applicable, which adapts based on the available support points.
      • + *
      • If not a primary force: + *
          + *
        • Constructs a force selection list populated with available reinforcements.
        • + *
        • Tracks the selection through listener events, enabling force selection updates dynamically.
        • + *
        • Displays detailed information about the selected force beside the force list.
        • + *
        + *
      • + *
      • Attaches the created force panel to the main content panel managed by the {@code GridBagConstraints}.
      • + *
      + *
    4. + *
    5. Updates the {@code availableForceLists} map with force templates and their associated UI components for later reference.
    6. + *
    7. Increments the layout constraints for the force panel to ensure proper stacking in the UI layout.
    8. + *
    + * + *

    Roles and Responsibilities:

    + *
      + *
    • Adds and configures force-related UI elements dynamically, adapting to the scenario's configuration.
    • + *
    • Handles player force assignment and manages user input for reinforcement and primary force selection.
    • + *
    • Links the force selection lists to the backing logic for dynamic interaction and validation.
    • + *
    */ - private void setAssignForcesUI(GridBagConstraints gbc, boolean reinforcements) { - // generate a lance selector with the following parameters: - // all forces assigned to the current track that aren't already assigned - // elsewhere - // max number of items that can be selected = current scenario required lances - - List eligibleForceTemplates = reinforcements - ? currentScenario.getScenarioTemplate().getAllPlayerReinforcementForces() - : currentScenario.getScenarioTemplate().getAllPrimaryPlayerForces(); + private void setAssignForcesUI(GridBagConstraints gbc, boolean isPrimaryForce) { + // Get eligible templates depending on reinforcement status + List eligibleForceTemplates = isPrimaryForce + ? currentScenario.getScenarioTemplate().getAllPrimaryPlayerForces() + : currentScenario.getScenarioTemplate().getAllPlayerReinforcementForces(); for (ScenarioForceTemplate forceTemplate : eligibleForceTemplates) { + // Create a panel for each force template JPanel forcePanel = new JPanel(); forcePanel.setLayout(new GridBagLayout()); GridBagConstraints localGbc = new GridBagConstraints(); localGbc.gridx = 0; localGbc.gridy = 0; - String reinforcementMessage = currentCampaignState.getSupportPoints() > 0 ? - resourceMap.getString("selectReinforcementsForTemplate.Text") : - resourceMap.getString("selectReinforcementsForTemplateNoSupportPoints.Text"); - - String labelText = reinforcements ? reinforcementMessage - : resourceMap.getString("selectForceForTemplate.Text"); - - JLabel assignForceListInstructions = new JLabel(labelText); - forcePanel.add(assignForceListInstructions, localGbc); - - localGbc.gridy = 1; - JLabel selectedForceInfo = new JLabel(); - JList availableForceList = addAvailableForceList(forcePanel, localGbc, forceTemplate); - - availableForceList - .addListSelectionListener(e -> availableForceSelectorChanged(e, selectedForceInfo, reinforcements)); - - availableForceLists.put(forceTemplate.getForceName(), availableForceList); - - localGbc.gridx = 1; - forcePanel.add(selectedForceInfo, localGbc); + // Add instructions for assigning forces + String reinforcementMessage = currentCampaignState.getSupportPoints() > 0 + ? resources.getString("selectReinforcementsForTemplate.Text") + : resources.getString("selectReinforcementsForTemplateNoSupportPoints.Text"); + + JLabel assignForceListInstructions = new JLabel(reinforcementMessage); + + if (!isPrimaryForce) { + forcePanel.add(assignForceListInstructions, localGbc); + + // Add a list to display available forces + localGbc.gridy = 1; + JLabel selectedForceInfo = new JLabel(); + JList availableForceList = addAvailableForceList(forcePanel, localGbc, forceTemplate); + availableForceList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + // Add a listener to handle changes to the selected force + availableForceList + .addListSelectionListener(e -> { + availableForceSelectorChanged(e, selectedForceInfo, isPrimaryForce); + btnCommit.setEnabled(true); + }); + + // Store the list in the map for later reference + availableForceLists.put(forceTemplate.getForceName(), availableForceList); + + // Add the selected force info to the panel + localGbc.gridx = 1; + forcePanel.add(selectedForceInfo, localGbc); + } - getContentPane().add(forcePanel, gbc); + // Add the forcePanel to contentPanel (not getContentPane) + contentPanel.add(forcePanel, gbc); gbc.gridy++; } } /** - * Set up the UI for "defensive elements", such as infantry, gun emplacements, - * minefields, etc. + * Sets up the UI for "defensive elements", such as infantry, gun emplacements, minefields, etc. + * + * @param gbc GridBagConstraints for layout positioning. */ private void setDefensiveUI(GridBagConstraints gbc) { + // Label with defensive posture instructions gbc.anchor = GridBagConstraints.WEST; JLabel lblDefensivePostureInstructions = new JLabel( - resourceMap.getString("lblDefensivePostureInstructions.Text")); - getContentPane().add(lblDefensivePostureInstructions, gbc); + resources.getString("lblFrontlineInstructions.text")); + contentPanel.add(lblDefensivePostureInstructions, gbc); gbc.gridy++; + // Obtain eligible infantry units List eligibleInfantryUnits = StratconRulesManager.getEligibleDefensiveUnits(campaign); eligibleInfantryUnits.sort(Comparator.comparing(Unit::getName)); - availableInfantryUnits = addIndividualUnitSelector(eligibleInfantryUnits, gbc, - currentScenario.getNumDefensivePoints(), false); + // Add a unit selector for infantry units + availableInfantryUnits = addIndividualUnitSelector( + eligibleInfantryUnits, gbc, currentScenario.getNumDefensivePoints(), false); gbc.gridy++; gbc.anchor = GridBagConstraints.WEST; + // Label to display the minefield count JLabel lblDefensiveMinefieldCount = new JLabel( - String.format(resourceMap.getString("lblDefensiveMinefieldCount.text"), - currentScenario.getNumDefensivePoints())); + String.format(resources.getString("lblDefensiveMinefieldCount.text"), + currentScenario.getNumDefensivePoints())); - availableInfantryUnits - .addListSelectionListener(e -> availableInfantrySelectorChanged(lblDefensiveMinefieldCount)); + // Add a listener to update the minefield count label when infantry units are selected + availableInfantryUnits.addListSelectionListener( + e -> availableInfantrySelectorChanged(lblDefensiveMinefieldCount)); - getContentPane().add(lblDefensiveMinefieldCount, gbc); + contentPanel.add(lblDefensiveMinefieldCount, gbc); } private void setLeadershipUI(GridBagConstraints gbc, List eligibleUnits, int leadershipSkill) { // Leadership budget is capped at 5 levels - int leadershipBudget = min(BASE_LEADERSHIP_BUDGET * leadershipSkill, BASE_LEADERSHIP_BUDGET * 5); + int leadershipBudget = Math.min(BASE_LEADERSHIP_BUDGET * leadershipSkill, BASE_LEADERSHIP_BUDGET * 5); int maxSelectionSize = leadershipBudget - currentScenario.getLeadershipPointsUsed(); gbc.anchor = GridBagConstraints.WEST; - JLabel lblLeadershipInstructions = new JLabel(String.format(resourceMap.getString("lblLeadershipInstructions.Text"), - maxSelectionSize)); - getContentPane().add(lblLeadershipInstructions, gbc); + JLabel lblLeadershipInstructions = new JLabel( + String.format(resources.getString("lblLeadershipInstructions.Text"), maxSelectionSize)); + contentPanel.add(lblLeadershipInstructions, gbc); gbc.gridy++; - availableLeadershipUnits = addIndividualUnitSelector(eligibleUnits, gbc, maxSelectionSize, true); + availableLeadershipUnits = addIndividualUnitSelector(eligibleUnits, gbc, maxSelectionSize, + true); } /** @@ -324,58 +437,62 @@ private JList addAvailableForceList(JPanel parent, GridBagConstraints gbc } /** - * Adds an individual unit selector, given a list of individual units, a global - * grid bag constraint set + * Adds an individual unit selector, given a list of individual units, a global grid bag constraint set, * and a maximum selection size. * * @param units The list of units to use as a data source. - * @param gridBagConstraints Gridbag constraints to indicate where the control will go - * @param maxSelectionSize Maximum number of units that can be selected + * @param gridBagConstraints GridBagConstraints object to position the selector panel. + * @param maxSelectionSize Maximum number of units that can be selected. + * @param usesBV Whether to track the Battle Value (BV) of selected items or simply count. + * @return A JList of units that can be selected. */ private JList addIndividualUnitSelector(List units, GridBagConstraints gridBagConstraints, int maxSelectionSize, boolean usesBV) { + // Create the panel for the individual unit selector JPanel unitPanel = new JPanel(); unitPanel.setLayout(new GridBagLayout()); - GridBagConstraints localGridBagConstraints = new GridBagConstraints(); - localGridBagConstraints.gridx = 0; - localGridBagConstraints.gridy = 0; - localGridBagConstraints.anchor = GridBagConstraints.WEST; - JLabel instructions = new JLabel(); - instructions.setText(String.format(resourceMap.getString("lblSelectIndividualUnits.text"), - maxSelectionSize)); - unitPanel.add(instructions); + GridBagConstraints localGbc = new GridBagConstraints(); + localGbc.gridx = 0; + localGbc.gridy = 0; + localGbc.anchor = GridBagConstraints.WEST; - localGridBagConstraints.gridy++; + // Instructions for selecting units + JLabel instructions = new JLabel( + String.format(resources.getString("lblSelectIndividualUnits.text"), maxSelectionSize)); + unitPanel.add(instructions, localGbc); + + localGbc.gridy++; DefaultListModel availableModel = new DefaultListModel<>(); availableModel.addAll(units); + // Add labels for unit selection details JLabel unitStatusLabel = new JLabel(); + JLabel unitSelectionLabel = new JLabel(resources.getString("unitSelectLabelDefaultValue.text")); - // add the # units selected control - JLabel unitSelectionLabel = new JLabel(); - unitSelectionLabel.setText("0 selected"); - - localGridBagConstraints.gridy++; - unitPanel.add(unitSelectionLabel, localGridBagConstraints); + // Add the "# units selected" label + localGbc.gridy++; + unitPanel.add(unitSelectionLabel, localGbc); - JList availableUnits = new JList<>(); - availableUnits.setModel(availableModel); + // Create the unit selection list + JList availableUnits = new JList<>(availableModel); availableUnits.setCellRenderer(new ScenarioWizardUnitRenderer()); availableUnits.addListSelectionListener( - e -> availableUnitSelectorChanged(e, unitSelectionLabel, unitStatusLabel, maxSelectionSize, usesBV)); + e -> availableUnitSelectorChanged(e, unitSelectionLabel, unitStatusLabel, + maxSelectionSize, usesBV)); - JScrollPane infantryContainer = new JScrollPaneWithSpeed(); - infantryContainer.setViewportView(availableUnits); - localGridBagConstraints.gridy++; - unitPanel.add(infantryContainer, localGridBagConstraints); + // Scroll pane for the unit selection list + JScrollPane unitScrollPane = new JScrollPane(availableUnits); + localGbc.gridy++; + unitPanel.add(unitScrollPane, localGbc); - // add the 'status display' control - localGridBagConstraints.gridx++; - localGridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - unitPanel.add(unitStatusLabel, localGridBagConstraints); + // Add the 'unit status' label + localGbc.gridx++; + localGbc.anchor = GridBagConstraints.NORTHWEST; + unitPanel.add(unitStatusLabel, localGbc); - getContentPane().add(unitPanel, gridBagConstraints); + // Add the unitPanel to the contentPanel + contentPanel.add(unitPanel, gridBagConstraints); return availableUnits; } @@ -384,12 +501,12 @@ private JList addIndividualUnitSelector(List units, GridBagConstrain * Worker function that builds an "html-enabled" string indicating the brief * status of a force */ - private String buildForceStatus(Force f, boolean showForceCost) { + private String buildForceStatus(Force f, boolean hideForceCost) { StringBuilder sb = new StringBuilder(); sb.append(f.getFullName()); sb.append(": "); - if (showForceCost) { + if (!hideForceCost) { sb.append(buildForceCost(f.getId())); } sb.append("
    "); @@ -436,15 +553,15 @@ private String buildForceCost(int forceID) { StringBuilder costBuilder = new StringBuilder(); costBuilder.append('('); - switch (StratconRulesManager.getReinforcementType(forceID, currentTrackState, campaign, currentCampaignState)) { + switch (getReinforcementType(forceID, currentTrackState, campaign, currentCampaignState)) { case REGULAR: - costBuilder.append(resourceMap.getString("regular.text")); + costBuilder.append(resources.getString("regular.text")); break; case CHAINED_SCENARIO: - costBuilder.append(resourceMap.getString("fromChainedScenario.text")); + costBuilder.append(resources.getString("fromChainedScenario.text")); break; - case FIGHT_LANCE: - costBuilder.append(resourceMap.getString("lanceInFightRole.text")); + case AUXILIARY: + costBuilder.append(resources.getString("auxiliary.text")); break; default: costBuilder.append("Error: Invalid Reinforcement Type"); @@ -456,48 +573,289 @@ private String buildForceCost(int forceID) { } /** - * Sets the navigation buttons - commit, cancel, etc. + * Creates and configures the navigation buttons, specifically the "Commit" button, + * and adds it to the UI layout. The behavior of the "Commit" button is determined + * based on whether the scenario involves primary force assignment or reinforcements. + * + * @param constraints the {@link GridBagConstraints} used to define the position and + * alignment of the button within the panel. + * @param isPrimaryForce a boolean flag indicating the purpose of the button: + *
      + *
    • {@code true}: The "Commit" button triggers a direct commit + * for scenarios involving the primary force.
    • + *
    • {@code false}: The "Commit" button opens the reinforcement + * confirmation dialog and is only enabled when sufficient + * support points are available.
    • + *
    + * + *

    Behavior and Functionality:

    + *
      + *
    • When {@code isPrimaryForce} is {@code true}: + *
        + *
      • The "Commit" button invokes the {@link #btnCommitClicked(ActionEvent, Integer, boolean)} + * method to directly complete the action.
      • + *
      + *
    • + *
    • When {@code isPrimaryForce} is {@code false}: + *
        + *
      • The button opens the {@link #reinforcementConfirmDialog()}, which handles + * reinforcement confirmation logic.
      • + *
      • The button is only enabled if the current campaign has sufficient support points, + * as determined by {@link StratconCampaignState#getSupportPoints()}.
      • + *
      + *
    • + *
    • The button is added to the content panel, with its position controlled by the provided + * {@link GridBagConstraints}, ensuring proper alignment within the UI layout.
    • + *
    */ - private void setNavigationButtons(GridBagConstraints gbc) { - // you're on one of two screens: - // the 'primary force selection' screen - // the 'reinforcement selection' screen + private void setNavigationButtons(GridBagConstraints constraints, boolean isPrimaryForce) { + // Create the commit button btnCommit = new JButton("Commit"); btnCommit.setActionCommand("COMMIT_CLICK"); - btnCommit.addActionListener(this::btnCommitClicked); + if (isPrimaryForce) { + btnCommit.addActionListener(evt -> btnCommitClicked(evt, + null, false)); + } else { + btnCommit.addActionListener(evt -> reinforcementConfirmDialog()); + btnCommit.setEnabled(currentCampaignState.getSupportPoints() > 0); + } - gbc.gridheight = GridBagConstraints.REMAINDER; - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.anchor = GridBagConstraints.CENTER; + // Configure layout constraints for the button + constraints.gridheight = GridBagConstraints.REMAINDER; + constraints.gridwidth = GridBagConstraints.REMAINDER; + constraints.anchor = GridBagConstraints.CENTER; - getContentPane().add(btnCommit, gbc); + // Add the commit button to the content panel + contentPanel.add(btnCommit, constraints); + } + + /** + * Creates and displays the "Reinforcement Confirmation" dialog, allowing the user to review, + * adjust, and commit reinforcements to a scenario. The dialog provides information about + * the current target roll modifiers, the ability to adjust Support Points, and handles + * calculation and changes to the reinforcement target number dynamically. + * + *

    The dialog includes the following components:

    + *
      + *
    • Left Panel: Contains details about the speaker, including an icon (if available) + * and a description of their role in the campaign.
    • + *
    • Right Panel: Shows a breakdown of the current target roll modifiers and target number.
    • + *
    • Support Point Selector: A spinner that allows the user to adjust the number of + * Support Points they wish to spend to modify the target number. The maximum available + * Support Points are based on the current campaign state.
    • + *
    • Check/Confirm Buttons: Includes two confirm buttons (standard and GM-specific) + * that allow the user to commit reinforcements or additional actions, along with a cancel button + * to close the dialog without making any changes.
    • + *
    • Info Panel: A supplemental label with additional guidance or information displayed + * below the buttons, which can help inform the user about the reinforcement process or provide + * clarification about decisions made.
    • + *
    + */ + private void reinforcementConfirmDialog() { + final int LEFT_WIDTH = UIUtil.scaleForGUI(200); + final int RIGHT_WIDTH = UIUtil.scaleForGUI(400); + + JDialog dialog = new JDialog(); + dialog.setTitle(resources.getString("incomingTransmission.title")); + dialog.setLayout(new BorderLayout()); + + // Main Panel to hold left and right boxes + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(10, 10, 10, 10); + gbc.fill = GridBagConstraints.BOTH; + gbc.weighty = 1; + + // Left box (commandLiaison details) + JPanel leftBox = new JPanel(); + leftBox.setLayout(new BoxLayout(leftBox, BoxLayout.Y_AXIS)); + leftBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + // Get commandLiaison details + Person commandLiaison = campaign.getSeniorAdminPerson(AdministratorSpecialization.COMMAND); + String speakerName = (commandLiaison != null) ? commandLiaison.getFullTitle() : campaign.getName(); + + // Add commandLiaison image (icon) + ImageIcon speakerIcon = getSpeakerIcon(campaign, commandLiaison); + if (speakerIcon != null) { + speakerIcon = scaleImageIconToWidth(speakerIcon, 100); + } + JLabel imageLabel = new JLabel(); + imageLabel.setIcon(speakerIcon); + imageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Speaker description (below the icon) + StringBuilder speakerDescription = getSpeakerDescription(campaign, commandLiaison, speakerName); + JLabel leftDescription = new JLabel( + String.format("
    %s
    ", + LEFT_WIDTH, speakerDescription)); + leftDescription.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Add components to leftBox + leftBox.add(imageLabel); + leftBox.add(Box.createRigidArea(new Dimension(0, 10))); // Add spacing + leftBox.add(leftDescription); + + // Add leftBox to mainPanel + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 0; + mainPanel.add(leftBox, gbc); + + // Right box (message details) + JPanel rightBox = new JPanel(new BorderLayout()); + rightBox.setBorder(BorderFactory.createEtchedBorder()); + + TargetRoll reinforcementTargetNumber = calculateReinforcementTargetNumber( + campaign, currentScenario, commandLiaison, currentCampaignState, currentCampaignState.getContract()); + int targetNumber = reinforcementTargetNumber.getValue(); + + StringBuilder rightDescriptionMessage = new StringBuilder(); + rightDescriptionMessage.append(String.format( + resources.getString("reinforcementConfirmation.introduction"), + campaign.getCommanderAddress(false))); + rightDescriptionMessage.append(resources.getString("reinforcementConfirmation.breakdown")); + + StringBuilder breakdownContents = new StringBuilder(); + for (TargetRollModifier modifier : reinforcementTargetNumber.getModifiers()) { + breakdownContents.append(String.format("%s: %d
    ", modifier.getDesc(), modifier.getValue())); + } + breakdownContents.append(String.format("%s: %s", + resources.getString("reinforcementConfirmation.breakdown.total"), + reinforcementTargetNumber.getValue())).append('+'); + rightDescriptionMessage.append(breakdownContents); + + JLabel rightDescription = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH, rightDescriptionMessage) + ); + rightBox.add(rightDescription); + + // Add rightBox to mainPanel + gbc.gridx = 1; + gbc.weightx = 1; // Allow horizontal stretching + mainPanel.add(rightBox, gbc); + + // Add mainPanel to dialog + dialog.add(mainPanel, BorderLayout.CENTER); + + // Spinner Panel (Support Points) + JPanel spinnerPanel = new JPanel(); + spinnerPanel.setLayout(new BoxLayout(spinnerPanel, BoxLayout.X_AXIS)); + JLabel lblSpinner = new JLabel(String.format("%s: ", + resources.getString("reinforcementConfirmation.spinnerLabel"))); + lblSpinner.setToolTipText(resources.getString("reinforcementConfirmation.spinnerLabel.tooltip")); + lblSpinner.setAlignmentY(Component.CENTER_ALIGNMENT); + + int availableSupportPoints = currentCampaignState.getSupportPoints(); + JSpinner spnSupportPointCost = new JSpinner( + new SpinnerNumberModel( + availableSupportPoints > 0 ? 1 : 0, + availableSupportPoints > 0 ? 1 : 0, + availableSupportPoints, + 1)); + spnSupportPointCost.setToolTipText(resources.getString("reinforcementConfirmation.spinnerLabel.tooltip")); + spnSupportPointCost.setMaximumSize(spnSupportPointCost.getPreferredSize()); + spnSupportPointCost.setAlignmentY(Component.CENTER_ALIGNMENT); + spinnerPanel.add(lblSpinner); + spinnerPanel.add(spnSupportPointCost); + spinnerPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // Combine spinnerPanel and checkboxPanel into a vertical layout panel + JPanel subPanel = new JPanel(); + subPanel.setLayout(new BoxLayout(subPanel, BoxLayout.Y_AXIS)); + subPanel.add(spinnerPanel); + subPanel.add(Box.createRigidArea(new Dimension(0, 10))); // Add spacing between sections + + // Buttons panel + JPanel buttonPanel = new JPanel(); + + JButton reinforceButton = new JButton(resources.getString("reinforcementConfirmation.confirmButton")); + reinforceButton.setToolTipText(resources.getString("reinforcementConfirmation.confirmButton.tooltip")); + reinforceButton.addActionListener(evt -> { + int spentSupportPoints = (int) spnSupportPointCost.getValue(); + int finalTargetNumber = targetNumber - ((spentSupportPoints - 1) * 2); + + currentCampaignState.setSupportPoints(currentCampaignState.getSupportPoints() - spentSupportPoints); + + btnCommitClicked(evt, finalTargetNumber, false); + dialog.dispose(); + }); + + JButton reinforceButtonGM = new JButton(resources.getString("reinforcementConfirmation.confirmButton.gm")); + reinforceButtonGM.setToolTipText(resources.getString("reinforcementConfirmation.confirmButton.gm.tooltip")); + reinforceButtonGM.addActionListener(evt -> { + btnCommitClicked(evt, 0, true); + dialog.dispose(); + }); + reinforceButtonGM.setVisible(campaign.isGM()); + + JButton cancelButton = new JButton(resources.getString("reinforcementConfirmation.cancelButton")); + cancelButton.addActionListener(evt -> dialog.dispose()); + buttonPanel.add(reinforceButton); + buttonPanel.add(reinforceButtonGM); + buttonPanel.add(cancelButton); + + // Info label panel + JPanel infoPanel = new JPanel(new BorderLayout()); + JLabel lblInfo = new JLabel( + String.format("
    %s
    ", + RIGHT_WIDTH + LEFT_WIDTH, + String.format(resources.getString("reinforcementConfirmation.addendum")))); + lblInfo.setHorizontalAlignment(SwingConstants.CENTER); + infoPanel.add(lblInfo, BorderLayout.CENTER); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + + // South Panel + JPanel southPanel = new JPanel(new BorderLayout()); + southPanel.add(subPanel, BorderLayout.NORTH); + southPanel.add(buttonPanel, BorderLayout.CENTER); + southPanel.add(infoPanel, BorderLayout.SOUTH); + + // Add southPanel to the dialog + dialog.add(southPanel, BorderLayout.SOUTH); + + // Dialog settings + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.pack(); + dialog.setVisible(true); } /** - * Event handler for when the user clicks the 'commit' button. - * Behaves differently depending on the state of the scenario + * Handles the event when the user clicks the 'commit' button. + * This method processes the selected forces, reinforcements, and scenario states, + * committing primary forces, reinforcements, and units based on the current + * state of the scenario, and updating the scenario as appropriate. + * + *

    Depending on the current state of the scenario, this method either: + *

      + *
    • Commits primary forces to the scenario if in the unresolved state.
    • + *
    • Commits reinforcement forces and processes their deployment.
    • + *
    • Adds units (e.g., infantry and leadership units) to the scenario.
    • + *
    • Assigns deployed forces to the campaign track and updates scenario parameters (e.g., minefields).
    • + *
    • Publishes scenarios to the campaign and allows immediate play if forces have been committed.
    • + *
    + * + * @param evt the {@link ActionEvent} triggered by the button press + * @param reinforcementTargetNumber the number representing the reinforcement + * target threshold used when processing reinforcement deployment + * @param isGMReinforcement {@code true} if the player is using GM powers to bypass the + * reinforcement check, {@code false} otherwise. */ - private void btnCommitClicked(ActionEvent e) { + private void btnCommitClicked(ActionEvent evt, @Nullable Integer reinforcementTargetNumber, + boolean isGMReinforcement) { // go through all the force lists and add the selected forces to the scenario for (String templateID : availableForceLists.keySet()) { for (Force force : availableForceLists.get(templateID).getSelectedValuesList()) { - // if we are assigning reinforcements, pay the price if appropriate - if (currentScenario.getCurrentState() == ScenarioState.PRIMARY_FORCES_COMMITTED) { - if (currentCampaignState.getSupportPoints() <= 0) { - campaign.addReport(String.format(resourceMap.getString("reinforcementsNoSupportPoints.text"), - currentScenario.getName(), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); - continue; - } + if (currentScenario.getCurrentState() == PRIMARY_FORCES_COMMITTED) { + ReinforcementEligibilityType reinforcementType = getReinforcementType( + force.getId(), currentTrackState, campaign, currentCampaignState); - ReinforcementEligibilityType reinforcementType = StratconRulesManager.getReinforcementType( - force.getId(), currentTrackState, - campaign, currentCampaignState); - - // if we failed to deploy as reinforcements, move on to the next force - ReinforcementResultsType reinforcementResults = processReinforcementDeployment( - force, reinforcementType, currentCampaignState, currentScenario, campaign); + ReinforcementResultsType reinforcementResults = processReinforcementDeployment(force, + reinforcementType, currentCampaignState, currentScenario, campaign, + reinforcementTargetNumber, isGMReinforcement); if (reinforcementResults.ordinal() >= FAILED.ordinal()) { currentScenario.addFailedReinforcements(force.getId()); @@ -507,7 +865,8 @@ private void btnCommitClicked(ActionEvent e) { currentScenario.addForce(force, templateID, campaign); if (reinforcementResults == DELAYED) { - List delayedReinforcements = currentScenario.getBackingScenario().getFriendlyDelayedReinforcements(); + List delayedReinforcements = + currentScenario.getBackingScenario().getFriendlyDelayedReinforcements(); for (UUID unitId : force.getAllUnits(true)) { try { @@ -517,11 +876,6 @@ private void btnCommitClicked(ActionEvent e) { } } } - } else { - // In the event the player has selected multiple forces to act as the primary - // force, only commit the first force - currentScenario.addForce(force, templateID, campaign); - break; } } } @@ -546,39 +900,51 @@ private void btnCommitClicked(ActionEvent e) { forceID, campaign, currentTrackState, false); } - // scenarios that haven't had primary forces committed yet get those committed - // now and the scenario gets published to the campaign and may be played immediately - // from the briefing room that being said, give the player a chance to commit reinforcements too - if (currentScenario.getCurrentState() == ScenarioState.UNRESOLVED) { - // if we've already generated forces and applied modifiers, no need to do it - // twice - if (!currentScenario.getBackingScenario().isFinalized()) { - AtBDynamicScenarioFactory.finalizeScenario(currentScenario.getBackingScenario(), - currentCampaignState.getContract(), campaign); - StratconRulesManager.setScenarioParametersFromBiome(currentTrackState, currentScenario); - } + currentScenario.updateMinefieldCount(Minefield.TYPE_CONVENTIONAL, getNumMinefields()); - StratconRulesManager.commitPrimaryForces(campaign, currentScenario, currentTrackState); - setCurrentScenario(currentScenario, currentTrackState, currentCampaignState); - currentScenario.updateMinefieldCount(Minefield.TYPE_CONVENTIONAL, getNumMinefields()); - // if we've just committed reinforcements then simply close it down - } else { - currentScenario.updateMinefieldCount(Minefield.TYPE_CONVENTIONAL, getNumMinefields()); - setVisible(false); + if (currentScenario.getCurrentState().ordinal() < REINFORCEMENTS_COMMITTED.ordinal()) { + translateTemplateObjectives(currentScenario.getBackingScenario(), campaign); + scaleObjectiveTimeLimits(currentScenario.getBackingScenario(), campaign); } - translateTemplateObjectives(currentScenario.getBackingScenario(), campaign); - this.getParent().repaint(); + + dispose(); } /** - * Event handler for when the user makes a selection on the available force - * selector. + * Handles the event triggered when the user makes a selection in the available force selector UI component. + * This method updates the provided status label to display detailed information about the selected forces + * and refreshes the UI to reflect the changes. + * + * @param e the {@link ListSelectionEvent} representing the user's selection action. + * This event contains the source list and the selection details. + * @param forceStatusLabel the {@link JLabel} used to display the status of the selected forces. + * @param isPrimaryForce a boolean flag indicating whether the selected forces are part of the primary force: + *
      + *
    • {@code true}: Displays details relevant to the primary force (e.g., leadership, roles).
    • + *
    • {@code false}: Displays details relevant to reinforcement forces (e.g., support point requirements).
    • + *
    + * + *

    Behavior and Process:

    + *
      + *
    • Verifies that the event source is a {@link JList}. If the source is not a {@code JList}, the method returns immediately.
    • + *
    • Retrieves the list of selected forces from the {@code JList}.
    • + *
    • Builds an HTML-formatted string with status details for each selected force using the + * {@link #buildForceStatus(Force, boolean)} method.
    • + *
    • Updates the provided status label with the constructed HTML string, effectively updating the displayed information.
    • + *
    • Refreshes the UI by calling {@link #pack()} to ensure the dialog adjusts properly to any layout changes.
    • + *
    * - * @param e The event fired. + *

    Roles and Responsibilities:

    + *
      + *
    • Processes the user's selection of forces in the UI and dynamically updates the status display accordingly.
    • + *
    • Ensures that the appropriate details (based on whether the forces are primary or reinforcements) are shown in the UI.
    • + *
    • Maintains a responsive UI by packing the dialog after the update, adjusting its layout if necessary.
    • + *
    */ - private void availableForceSelectorChanged(ListSelectionEvent e, JLabel forceStatusLabel, boolean reinforcements) { + private void availableForceSelectorChanged(ListSelectionEvent e, JLabel forceStatusLabel, + boolean isPrimaryForce) { if (!(e.getSource() instanceof JList)) { return; } @@ -589,7 +955,7 @@ private void availableForceSelectorChanged(ListSelectionEvent e, JLabel forceSta statusBuilder.append(""); for (Force force : sourceList.getSelectedValuesList()) { - statusBuilder.append(buildForceStatus(force, reinforcements)); + statusBuilder.append(buildForceStatus(force, isPrimaryForce)); } statusBuilder.append(""); @@ -625,11 +991,13 @@ private void availableUnitSelectorChanged(ListSelectionEvent event, JLabel selec selectedItems = 0; for (Unit unit : changedList.getSelectedValuesList()) { selectedItems += unit.getEntity().calculateBattleValue(true, true); - selectionCountLabel.setText(String.format("%d selected (ignores crew skill)", selectedItems)); + selectionCountLabel.setText(String.format("%d %s", selectedItems, + resources.getString("unitsSelectedLabel.bv"))); } } else { selectedItems = changedList.getSelectedIndices().length; - selectionCountLabel.setText(String.format("%d selected", selectedItems)); + selectionCountLabel.setText(String.format("%d %s", selectedItems, + resources.getString("unitsSelectedLabel.count"))); } // if we've selected too many units here, change the label and disable the @@ -638,6 +1006,7 @@ private void availableUnitSelectorChanged(ListSelectionEvent event, JLabel selec selectionCountLabel.setForeground(MekHQ.getMHQOptions().getFontColorNegative()); btnCommit.setEnabled(false); } else { + selectionCountLabel.setForeground(null); btnCommit.setEnabled(true); } @@ -693,7 +1062,7 @@ private void unselectDuplicateUnits(JList listToProcess, List select * Updates the defensive minefield count */ private void availableInfantrySelectorChanged(JLabel defensiveMineCountLabel) { - defensiveMineCountLabel.setText(String.format(resourceMap.getString("lblDefensiveMinefieldCount.text"), + defensiveMineCountLabel.setText(String.format(resources.getString("lblDefensiveMinefieldCount.text"), getNumMinefields())); } diff --git a/MekHQ/src/mekhq/gui/stratcon/TrackForceAssignmentUI.java b/MekHQ/src/mekhq/gui/stratcon/TrackForceAssignmentUI.java index 55bf38c547f..1f9ee4b053e 100644 --- a/MekHQ/src/mekhq/gui/stratcon/TrackForceAssignmentUI.java +++ b/MekHQ/src/mekhq/gui/stratcon/TrackForceAssignmentUI.java @@ -72,12 +72,10 @@ private void initializeUI() { JScrollPane forceListContainer = new JScrollPaneWithSpeed(); - ScenarioWizardLanceModel lanceModel; - // if we're waiting to assign primary forces, we can only do so from the current track - lanceModel = new ScenarioWizardLanceModel(campaign, - StratconRulesManager.getAvailableForceIDs(ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX, - campaign, ownerPanel.getCurrentTrack(), false, null, currentCampaignState)); + ScenarioWizardLanceModel lanceModel = new ScenarioWizardLanceModel(campaign, + StratconRulesManager.getAvailableForceIDs(ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX, + campaign, ownerPanel.getCurrentTrack(), false, null, currentCampaignState)); availableForceList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); availableForceList.setModel(lanceModel); @@ -94,6 +92,7 @@ private void initializeUI() { pack(); repaint(); + setModal(true); } /** @@ -111,22 +110,17 @@ public void display(Campaign campaign, StratconCampaignState campaignState, Stra */ @Override public void actionPerformed(ActionEvent e) { - switch (e.getActionCommand()) { - case CMD_CONFIRM: - // sometimes the scenario templates take a little while to load, we don't want the user - // clicking the button fifty times and getting a bunch of scenarios. - btnConfirm.setEnabled(false); - for (Force force : availableForceList.getSelectedValuesList()) { - StratconRulesManager.deployForceToCoords( - ownerPanel.getSelectedCoords(), - force.getId(), - campaign, currentCampaignState.getContract(), - ownerPanel.getCurrentTrack(), false); - } - setVisible(false); - ownerPanel.repaint(); - btnConfirm.setEnabled(true); - break; + if (e.getActionCommand().equals(CMD_CONFIRM)) { + // sometimes the scenario templates take a little while to load, we don't want the user + // clicking the button fifty times and getting a bunch of scenarios. + btnConfirm.setEnabled(false); + for (Force force : availableForceList.getSelectedValuesList()) { + StratconRulesManager.deployForceToCoords(ownerPanel.getSelectedCoords(), + force.getId(), campaign, currentCampaignState.getContract(), ownerPanel.getCurrentTrack(), false); + } + setVisible(false); + ownerPanel.repaint(); + btnConfirm.setEnabled(true); } } } diff --git a/MekHQ/src/mekhq/gui/view/AtBScenarioViewPanel.java b/MekHQ/src/mekhq/gui/view/AtBScenarioViewPanel.java index ebee91f81a2..715e9b8a117 100644 --- a/MekHQ/src/mekhq/gui/view/AtBScenarioViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/AtBScenarioViewPanel.java @@ -30,6 +30,7 @@ import mekhq.MekHQ; import mekhq.Utilities; import mekhq.campaign.Campaign; +import mekhq.campaign.force.Force; import mekhq.campaign.force.ForceStub; import mekhq.campaign.force.UnitStub; import mekhq.campaign.mission.*; @@ -292,8 +293,14 @@ private void fillStats() { gridBagConstraints.gridwidth = 1; panStats.add(lblForce, gridBagConstraints); - if (null != scenario.getStrategicFormation(campaign)) { - lblForceDesc.setText(campaign.getForce(scenario.getStrategicFormationId()).getFullName()); + if (null != scenario.getCombatTeamById(campaign)) { + Force force = campaign.getForce(scenario.getCombatTeamId()); + + if (force != null) { + lblForceDesc.setText(campaign.getForce(scenario.getCombatTeamId()).getFullName()); + } else { + lblForceDesc.setText("Unknown Force ID: " + scenario.getCombatTeamId()); + } } else if (scenario instanceof AtBDynamicScenario) { StringBuilder forceBuilder = new StringBuilder(); forceBuilder.append(""); @@ -443,7 +450,7 @@ private void fillStats() { gridBagConstraints.insets = new Insets(0, 0, 5, 0); gridBagConstraints.fill = GridBagConstraints.NONE; gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - panStats.add(new JLabel("Scenario Costs & Payouts:"), gridBagConstraints); + panStats.add(new JLabel("Scenario Costs or Loot:"), gridBagConstraints); for (Loot loot : scenario.getLoot()) { gridBagConstraints.gridx = 0; @@ -927,11 +934,11 @@ public Component getTreeCellRendererComponent(final JTree tree, final Object val private class TreeMouseAdapter extends MouseInputAdapter implements ActionListener { private JTree tree; - int index; + int forceIndex; - public TreeMouseAdapter(JTree tree, int index) { + public TreeMouseAdapter(JTree tree, int forceIndex) { this.tree = tree; - this.index = index; + this.forceIndex = forceIndex; } @Override @@ -941,21 +948,20 @@ public void actionPerformed(ActionEvent action) { if (command.equalsIgnoreCase("CONFIG_BOT")) { BotConfigDialog pbd = new BotConfigDialog(frame, null, - scenario.getBotForce(index).getBehaviorSettings(), + scenario.getBotForce(forceIndex).getBehaviorSettings(), null); - pbd.setBotName(scenario.getBotForce(index).getName()); + pbd.setBotName(scenario.getBotForce(forceIndex).getName()); pbd.setVisible(true); if (!pbd.getResult().isCancelled()) { - scenario.getBotForce(index).setBehaviorSettings(pbd.getBehaviorSettings()); - scenario.getBotForce(index).setName(pbd.getBotName()); + scenario.getBotForce(forceIndex).setBehaviorSettings(pbd.getBehaviorSettings()); + scenario.getBotForce(forceIndex).setName(pbd.getBotName()); } } else if (command.equalsIgnoreCase("EDIT_UNIT")) { if ((tree.getSelectionCount() > 0) && (tree.getSelectionRows() != null)) { - // row 0 is root node - int i = tree.getSelectionRows()[0] - 1; - UnitEditorDialog med = new UnitEditorDialog(frame, - scenario.getBotForce(index).getFullEntityList(campaign).get(i)); - med.setVisible(true); + int unitIndex = tree.getSelectionRows()[0] - 1; + UnitEditorDialog editorDialog = new UnitEditorDialog(frame, + scenario.getBotForce(this.forceIndex).getFullEntityList(campaign).get(unitIndex)); + editorDialog.setVisible(true); } } } @@ -979,7 +985,8 @@ private void maybeShowPopup(MouseEvent e) { } JMenuItem menuItem; - if (path.getPathCount() > 1) { + if ((path.getPathCount() > 1) && (tree.getSelectionRows() != null) + && (tree.getSelectionRows()[0] != 0)) { menuItem = new JMenuItem("Edit Unit..."); menuItem.setActionCommand("EDIT_UNIT"); menuItem.addActionListener(this); diff --git a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java index de3b6673bff..c1db22c8bb0 100644 --- a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java +++ b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java @@ -234,7 +234,8 @@ public void mouseClicked(MouseEvent e) { gridBagConstraintsText.gridy = y; mainPanel.add(txtLocation, gridBagConstraintsText); - if (Systems.getInstance().getSystems().get(contract.getSystemId()) != null) { + if (contract instanceof AtBContract + && Systems.getInstance().getSystems().get(contract.getSystemId()) != null) { JLabel lblDistance = new JLabel(resourceMap.getString("lblDistance.text")); lblDistance.setName("lblDistance"); gridBagConstraintsLabels.gridy = ++y; @@ -302,6 +303,7 @@ public void mouseClicked(MouseEvent e) { JLabel lblOverhead = new JLabel(resourceMap.getString("lblOverhead.text")); lblOverhead.setName("lblOverhead"); + lblOverhead.setToolTipText(wordWrap(resourceMap.getString("lblOverhead.tooltip"))); gridBagConstraintsLabels.gridy = ++y; mainPanel.add(lblOverhead, gridBagConstraintsLabels); @@ -312,6 +314,7 @@ public void mouseClicked(MouseEvent e) { JLabel lblCommand = new JLabel(resourceMap.getString("lblCommand.text")); lblCommand.setName("lblCommand"); + lblCommand.setToolTipText(wordWrap(resourceMap.getString("lblCommand.tooltip"))); gridBagConstraintsLabels.gridy = ++y; mainPanel.add(lblCommand, gridBagConstraintsLabels); @@ -361,10 +364,11 @@ public void mouseClicked(MouseEvent e) { JLabel lblTransport = new JLabel(resourceMap.getString("lblTransport.text")); lblTransport.setName("lblTransport"); + lblTransport.setToolTipText(wordWrap(resourceMap.getString("lblTransport.tooltip"))); gridBagConstraintsLabels.gridy = ++y; mainPanel.add(lblTransport, gridBagConstraintsLabels); - txtTransport = new JLabel(contract.getTransportComp() + "%"); + txtTransport = new JLabel(contract.getTransportCompString()); txtTransport.setName("txtTransport"); // Then we determine if we just add it to the main panel, or if we combine it with a button @@ -393,7 +397,7 @@ public void mouseClicked(MouseEvent e) { campaign.getContractMarket().rerollClause((AtBContract) contract, AbstractContractMarket.CLAUSE_TRANSPORT, campaign); setTransportRerollButtonText((JButton) ev.getSource()); - txtTransport.setText(contract.getTransportComp() + "%"); + txtTransport.setText(contract.getTransportCompString()); if (campaign.getContractMarket().getRerollsUsed(contract, AbstractContractMarket.CLAUSE_TRANSPORT) >= tranRerolls) { btn.setEnabled(false); @@ -408,10 +412,11 @@ public void mouseClicked(MouseEvent e) { JLabel lblSalvageRights = new JLabel(resourceMap.getString("lblSalvageRights.text")); lblSalvageRights.setName("lblSalvageRights"); + lblSalvageRights.setToolTipText(wordWrap(resourceMap.getString("lblSalvageRights.tooltip"))); gridBagConstraintsLabels.gridy = ++y; mainPanel.add(lblSalvageRights, gridBagConstraintsLabels); - JLabel txtSalvageRights = new JLabel(contract.getSalvagePct() + "%" + JLabel txtSalvageRights = new JLabel(contract.getSalvagePctString() + (contract.isSalvageExchange() ? " (Exchange)" : "")); txtSalvageRights.setName("txtSalvageRights"); @@ -438,7 +443,7 @@ public void mouseClicked(MouseEvent e) { campaign.getContractMarket().rerollClause((AtBContract) contract, AbstractContractMarket.CLAUSE_SALVAGE, campaign); setSalvageRerollButtonText((JButton) ev.getSource()); - txtSalvageRights.setText(contract.getSalvagePct() + "%" + txtSalvageRights.setText(contract.getSalvagePctString() + (contract.isSalvageExchange() ? " (Exchange)" : "")); if (campaign.getContractMarket().getRerollsUsed(contract, AbstractContractMarket.CLAUSE_SALVAGE) >= logRerolls) { @@ -454,10 +459,11 @@ public void mouseClicked(MouseEvent e) { JLabel lblStraightSupport = new JLabel(resourceMap.getString("lblStraightSupport.text")); lblStraightSupport.setName("lblStraightSupport"); + lblStraightSupport.setToolTipText(wordWrap(resourceMap.getString("lblStraightSupport.tooltip"))); gridBagConstraintsLabels.gridy = ++y; mainPanel.add(lblStraightSupport, gridBagConstraintsLabels); - txtStraightSupport = new JLabel(contract.getStraightSupport() + "%"); + txtStraightSupport = new JLabel(contract.getStraightSupportString()); txtStraightSupport.setName("txtStraightSupport"); // Then we determine if we just add it to the main panel, or if we combine it with a button @@ -486,8 +492,8 @@ public void mouseClicked(MouseEvent e) { campaign.getContractMarket().rerollClause((AtBContract) contract, AbstractContractMarket.CLAUSE_SUPPORT, campaign); setSupportRerollButtonText((JButton) ev.getSource()); - txtStraightSupport.setText(contract.getStraightSupport() + "%"); - txtBattleLossComp.setText(contract.getBattleLossComp() + "%"); + txtStraightSupport.setText(contract.getStraightSupportString()); + txtBattleLossComp.setText(contract.getBattleLossCompString()); if (campaign.getContractMarket().getRerollsUsed(contract, AbstractContractMarket.CLAUSE_SUPPORT) >= logRerolls) { btn.setEnabled(false); @@ -502,10 +508,11 @@ public void mouseClicked(MouseEvent e) { JLabel lblBattleLossComp = new JLabel(resourceMap.getString("lblBattleLossComp.text")); lblBattleLossComp.setName("lblBattleLossComp"); + lblBattleLossComp.setToolTipText(wordWrap(resourceMap.getString("lblBattleLossComp.tooltip"))); gridBagConstraintsLabels.gridy = ++y; mainPanel.add(lblBattleLossComp, gridBagConstraintsLabels); - txtBattleLossComp = new JLabel(contract.getBattleLossComp() + "%"); + txtBattleLossComp = new JLabel(contract.getBattleLossCompString()); txtBattleLossComp.setName("txtBattleLossComp"); gridBagConstraintsText.gridy = y; mainPanel.add(txtBattleLossComp, gridBagConstraintsText); @@ -516,7 +523,7 @@ public void mouseClicked(MouseEvent e) { gridBagConstraintsLabels.gridy = ++y; mainPanel.add(lblRequiredLances, gridBagConstraintsLabels); - JLabel txtRequiredLances = new JLabel(String.valueOf(((AtBContract) contract).getRequiredLances())); + JLabel txtRequiredLances = new JLabel(String.valueOf(((AtBContract) contract).getRequiredCombatTeams())); txtRequiredLances.setName("txtRequiredLances"); gridBagConstraintsText.gridy = y; mainPanel.add(txtRequiredLances, gridBagConstraintsText); diff --git a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java index 0408600f33f..1b8730a5cd6 100644 --- a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2024 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2011-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -191,8 +191,19 @@ private void fillStats() { int nexty = 0; if (null != type) { - lblType.setName("lblCommander2"); - String forceType = (force.isCombatForce() ? "" : "Non-Combat ") + type + ' ' + resourceMap.getString("unit"); + lblType.setName("lblType"); + + String forceType; + if (force.isCombatForce()) { + forceType = type + ' ' + force.getFormationLevel().toString(); + } else { + if (force.isConvoyForce()) { + forceType = "Resupply " + force.getFormationLevel().toString(); + } else { + forceType = "Support " + force.getFormationLevel().toString(); + } + } + lblType.setText("" + forceType + ""); lblType.getAccessibleContext().setAccessibleDescription("Force Type: " + forceType); gridBagConstraints = new GridBagConstraints(); @@ -471,7 +482,7 @@ public String getForceSummary(Person person, Unit unit) { .append(SkillType.getColoredExperienceLevelName(person.getSkillLevel(campaign, false))) .append("
    ") .append(person.getRoleDesc()); - + toReturn.append("
    "); boolean isInjured = false; @@ -482,13 +493,10 @@ public String getForceSummary(Person person, Unit unit) { isInjured = true; int injuryCount = person.getInjuries().size(); - StringBuilder injuriesMessage = new StringBuilder(16); - injuriesMessage.append(' ') - .append(injuryCount) - .append(injuryCount == 1 ? " injury" : " injuries"); - + String injuriesMessage = " " + injuryCount + (injuryCount == 1 ? " injury" : " injuries"); + toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorNegativeHexColor(), injuriesMessage.toString())); + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), injuriesMessage)); } } else { @@ -497,16 +505,13 @@ public String getForceSummary(Person person, Unit unit) { isInjured = true; int hitCount = unit.getEntity().getCrew().getHits(); - StringBuilder hitsMessage = new StringBuilder(16); - hitsMessage.append(' ') - .append(hitCount) - .append(hitCount == 1 ? " hit" : " hits"); + String hitsMessage = " " + hitCount + (hitCount == 1 ? " hit" : " hits"); toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorNegativeHexColor(), hitsMessage.toString())); + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), hitsMessage)); } } - + if (campaign.getCampaignOptions().isUseFatigue() && (person.getEffectiveFatigue(campaign) > 0)) { isFatigued = true; if (isInjured) { @@ -514,15 +519,12 @@ public String getForceSummary(Person person, Unit unit) { } toReturn.append(' '); - StringBuilder fatigueMessage = new StringBuilder(16); - - fatigueMessage.append(person.getEffectiveFatigue(campaign)); - fatigueMessage.append(" fatigue"); + String fatigueMessage = person.getEffectiveFatigue(campaign) + " fatigue"; toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorWarningHexColor(), fatigueMessage.toString())); + MekHQ.getMHQOptions().getFontColorWarningHexColor(), fatigueMessage)); } - + if (!(isInjured || isFatigued)) { toReturn.append(" "); } @@ -589,6 +591,12 @@ public String getForceSummary(Unit unit) { toReturn += "
    " + "Infantry Bays: " + (int) unit.getCurrentInfantryCapacity() + " tons free."; } } + //Let's get preferred transport too + if (unit.hasTacticalTransportAssignment()) { + toReturn += "
    " + "Transported by: "; + toReturn += unit.getTacticalTransportAssignment().getTransport().getName(); + toReturn += ""; + } toReturn += "
    "; return toReturn; } diff --git a/MekHQ/src/mekhq/gui/view/LanceAssignmentView.java b/MekHQ/src/mekhq/gui/view/LanceAssignmentView.java index ed6a939a2c0..2b6cb937211 100644 --- a/MekHQ/src/mekhq/gui/view/LanceAssignmentView.java +++ b/MekHQ/src/mekhq/gui/view/LanceAssignmentView.java @@ -2,6 +2,7 @@ * LanceAssignmentView.java * * Copyright (c) 2014 Carl Spain. All rights reserved. + * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -24,10 +25,10 @@ import megamek.common.util.sorter.NaturalOrderComparator; import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.campaign.force.CombatTeam; import mekhq.campaign.force.Force; -import mekhq.campaign.force.StrategicFormation; import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.mission.enums.AtBLanceRole; +import mekhq.campaign.mission.enums.CombatRole; import mekhq.campaign.personnel.SkillType; import mekhq.gui.model.DataTableModel; import mekhq.gui.utilities.MekHqTableCellRenderer; @@ -43,6 +44,12 @@ import java.util.Comparator; import java.util.List; +import static megamek.client.ui.WrapLayout.wordWrap; +import static mekhq.campaign.mission.enums.CombatRole.FRONTLINE; +import static mekhq.campaign.mission.enums.CombatRole.MANEUVER; +import static mekhq.campaign.mission.enums.CombatRole.PATROL; +import static mekhq.campaign.mission.enums.CombatRole.TRAINING; + /** * Against the Bot * Shows how many lances are required to be deployed on active contracts and @@ -58,7 +65,7 @@ public class LanceAssignmentView extends JPanel { private JPanel panRequiredLances; private JPanel panAssignments; private JComboBox cbContract; - private JComboBox cbRole; + private JComboBox cbRole; public LanceAssignmentView(Campaign c) { campaign = c; @@ -75,7 +82,7 @@ public Component getListCellRendererComponent(JList list, Object value, int i } }); - cbRole = new JComboBox<>(AtBLanceRole.values()); + cbRole = new JComboBox<>(CombatRole.values()); cbRole.setName("cbRole"); cbRole.setRenderer(new DefaultListCellRenderer() { @Override @@ -83,8 +90,8 @@ public Component getListCellRendererComponent(final JList list, final Object final int index, final boolean isSelected, final boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof AtBLanceRole) { - list.setToolTipText(((AtBLanceRole) value).getToolTipText()); + if (value instanceof CombatRole) { + list.setToolTipText(wordWrap(((CombatRole) value).getToolTipText())); } return this; } @@ -172,8 +179,8 @@ public Component getTableCellRendererComponent(JTable table, Object value, RowFilter laFilter = new RowFilter<>() { @Override public boolean include(Entry entry) { - StrategicFormation strategicFormation = entry.getModel().getRow(entry.getIdentifier()); - return strategicFormation.isEligible(campaign); + CombatTeam combatTeam = entry.getModel().getRow(entry.getIdentifier()); + return combatTeam.isEligible(campaign); } }; final NaturalOrderComparator noc = new NaturalOrderComparator(); @@ -226,14 +233,14 @@ public void refresh() { cbContract.addItem(contract); } AtBContract defaultContract = activeContracts.isEmpty() ? null : activeContracts.get(0); - for (StrategicFormation strategicFormation : campaign.getStrategicFormationsTable().values()) { - if ((strategicFormation.getContract(campaign) == null) - || !strategicFormation.getContract(campaign).isActiveOn(campaign.getLocalDate(), true)) { - strategicFormation.setContract(defaultContract); + for (CombatTeam combatTeam : campaign.getCombatTeamsTable().values()) { + if ((combatTeam.getContract(campaign) == null) + || !combatTeam.getContract(campaign).isActiveOn(campaign.getLocalDate(), true)) { + combatTeam.setContract(defaultContract); } } ((DataTableModel) tblRequiredLances.getModel()).setData(activeContracts); - ((DataTableModel) tblAssignments.getModel()).setData(campaign.getAllStrategicFormations()); + ((DataTableModel) tblAssignments.getModel()).setData(campaign.getAllCombatTeams()); panRequiredLances.setVisible(tblRequiredLances.getRowCount() > 0); } @@ -293,7 +300,8 @@ class RequiredLancesTableModel extends DataTableModel { public RequiredLancesTableModel(final Campaign campaign) { this.campaign = campaign; data = new ArrayList(); - columnNames = new String[]{"Contract", "Total", "Fight", "Defend", "Scout", "Training"}; + columnNames = new String[]{"Contract", "Total", MANEUVER.toString(), FRONTLINE.toString(), + PATROL.toString(), TRAINING.toString()}; } @Override @@ -347,27 +355,27 @@ public Object getValueAt(int row, int column) { if (data.get(row) instanceof AtBContract contract) { if (column == COL_TOTAL) { int t = 0; - for (StrategicFormation strategicFormation : campaign.getAllStrategicFormations()) { - if (data.get(row).equals(strategicFormation.getContract(campaign)) - && (strategicFormation.getRole() != AtBLanceRole.UNASSIGNED) - && strategicFormation.isEligible(campaign)) { + for (CombatTeam combatTeam : campaign.getAllCombatTeams()) { + if (data.get(row).equals(combatTeam.getContract(campaign)) + && (combatTeam.getRole() != CombatRole.RESERVE) + && combatTeam.isEligible(campaign)) { t++; } } - if (t < contract.getRequiredLances()) { - return t + "/" + contract.getRequiredLances(); + if (t < contract.getRequiredCombatTeams()) { + return t + "/" + contract.getRequiredCombatTeams(); } - return Integer.toString(contract.getRequiredLances()); - } else if (contract.getContractType().getRequiredLanceRole().ordinal() == column - 2) { + return Integer.toString(contract.getRequiredCombatTeams()); + } else if (contract.getContractType().getRequiredCombatRole().ordinal() == column - 2) { int t = 0; - for (StrategicFormation strategicFormation : campaign.getAllStrategicFormations()) { - if (data.get(row).equals(strategicFormation.getContract(campaign)) - && (strategicFormation.getRole() == strategicFormation.getContract(campaign).getContractType().getRequiredLanceRole()) - && strategicFormation.isEligible(campaign)) { + for (CombatTeam combatTeam : campaign.getAllCombatTeams()) { + if (data.get(row).equals(combatTeam.getContract(campaign)) + && (combatTeam.getRole() == combatTeam.getContract(campaign).getContractType().getRequiredCombatRole()) + && combatTeam.isEligible(campaign)) { t++; } } - int required = Math.max(contract.getRequiredLances() / 2, 1); + int required = Math.max(contract.getRequiredCombatTeams() / 2, 1); if (t < required) { return t + "/" + required; } @@ -416,7 +424,7 @@ public Class getColumnClass(int c) { return switch (c) { case COL_FORCE -> Force.class; case COL_CONTRACT -> AtBContract.class; - case COL_ROLE -> AtBLanceRole.class; + case COL_ROLE -> CombatRole.class; default -> String.class; }; } @@ -426,8 +434,8 @@ public boolean isCellEditable(int row, int col) { return col > COL_WEIGHT_CLASS; } - public StrategicFormation getRow(int row) { - return (StrategicFormation) data.get(row); + public CombatTeam getRow(int row) { + return (CombatTeam) data.get(row); } @Override @@ -438,10 +446,10 @@ public Object getValueAt(int row, int column) { return ""; } return switch (column) { - case COL_FORCE -> campaign.getForce(((StrategicFormation) data.get(row)).getForceId()); - case COL_WEIGHT_CLASS -> WEIGHT_CODES[((StrategicFormation) data.get(row)).getWeightClass(campaign)]; - case COL_CONTRACT -> campaign.getMission(((StrategicFormation) data.get(row)).getMissionId()); - case COL_ROLE -> ((StrategicFormation) data.get(row)).getRole(); + case COL_FORCE -> campaign.getForce(((CombatTeam) data.get(row)).getForceId()); + case COL_WEIGHT_CLASS -> WEIGHT_CODES[((CombatTeam) data.get(row)).getWeightClass(campaign)]; + case COL_CONTRACT -> campaign.getMission(((CombatTeam) data.get(row)).getMissionId()); + case COL_ROLE -> ((CombatTeam) data.get(row)).getRole(); default -> "?"; }; } @@ -449,10 +457,10 @@ public Object getValueAt(int row, int column) { @Override public void setValueAt(Object value, int row, int col) { if (col == COL_CONTRACT) { - ((StrategicFormation) data.get(row)).setContract((AtBContract) value); + ((CombatTeam) data.get(row)).setContract((AtBContract) value); } else if (col == COL_ROLE) { - if (value instanceof AtBLanceRole) { - ((StrategicFormation) data.get(row)).setRole((AtBLanceRole) value); + if (value instanceof CombatRole) { + ((CombatTeam) data.get(row)).setRole((CombatRole) value); } } fireTableDataChanged(); diff --git a/MekHQ/src/mekhq/gui/view/MissionViewPanel.java b/MekHQ/src/mekhq/gui/view/MissionViewPanel.java index a457385ed63..d61d75372ec 100644 --- a/MekHQ/src/mekhq/gui/view/MissionViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/MissionViewPanel.java @@ -36,6 +36,7 @@ import java.util.ResourceBundle; import static megamek.client.ui.WrapLayout.wordWrap; +import static mekhq.campaign.mission.resupplyAndCaches.ResupplyUtilities.estimateCargoRequirements; /** * A custom panel that gets filled in with goodies from a scenario object @@ -89,10 +90,10 @@ public class MissionViewPanel extends JScrollablePanel { private JLabel txtMorale; private JLabel lblScore; private JLabel txtScore; - private JLabel lblBonusParts; - private JLabel txtBonusParts; private JLabel lblSharePct; private JLabel txtSharePct; + private JLabel lblCargoRequirement; + private JLabel txtCargoRequirement; protected JTable scenarioTable; @@ -591,10 +592,10 @@ private void fillStatsAtBContract() { txtMorale = new JLabel(); lblSharePct = new JLabel(); txtSharePct = new JLabel(); + lblCargoRequirement = new JLabel(); + txtCargoRequirement = new JLabel(); lblScore = new JLabel(); txtScore = new JLabel(); - lblBonusParts = new JLabel(); - txtBonusParts = new JLabel(); GridBagConstraints gridBagConstraints; pnlStats.setLayout(new GridBagLayout()); @@ -947,8 +948,14 @@ public void mouseClicked(MouseEvent e) { pnlStats.add(lblMorale, gridBagConstraints); txtMorale.setName("txtMorale"); - txtMorale.setText(contract.getMoraleLevel().toString()); - txtMorale.setToolTipText(wordWrap(contract.getMoraleLevel().getToolTipText())); + + if (contract.getContractType().isGarrisonDuty() && contract.getMoraleLevel().isRouted()) { + txtMorale.setText(resourceMap.getString("txtGarrisonMoraleRouted.text")); + txtMorale.setToolTipText(wordWrap(resourceMap.getString("txtGarrisonMoraleRouted.tooltip"))); + } else { + txtMorale.setText(contract.getMoraleLevel().toString()); + txtMorale.setToolTipText(wordWrap(contract.getMoraleLevel().getToolTipText())); + } gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = y++; @@ -961,6 +968,7 @@ public void mouseClicked(MouseEvent e) { if (campaign.getCampaignOptions().isUseShareSystem()) { lblSharePct.setName("lblSharePct"); lblSharePct.setText(resourceMap.getString("lblSharePct.text")); + lblSharePct.setToolTipText(wordWrap(contract.getMoraleLevel().getToolTipText())); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = y; @@ -970,6 +978,7 @@ public void mouseClicked(MouseEvent e) { txtSharePct.setName("txtSharePct"); txtSharePct.setText(contract.getSharesPercent() + "%"); + txtSharePct.setToolTipText(wordWrap(contract.getMoraleLevel().getToolTipText())); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = y++; @@ -980,6 +989,28 @@ public void mouseClicked(MouseEvent e) { pnlStats.add(txtSharePct, gridBagConstraints); } + if (campaign.getCampaignOptions().isUseStratCon()) { + lblCargoRequirement.setName("lblCargoRequirement"); + lblCargoRequirement.setText(resourceMap.getString("lblCargoRequirement.text")); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = y; + gridBagConstraints.fill = GridBagConstraints.NONE; + gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; + pnlStats.add(lblCargoRequirement, gridBagConstraints); + + txtCargoRequirement.setName("txtCargoRequirement"); + txtCargoRequirement.setText(estimateCargoRequirements(campaign, contract) + "t+"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = y++; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new Insets(0, 10, 0, 0); + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; + pnlStats.add(txtCargoRequirement, gridBagConstraints); + } + // for StratCon, contract score is irrelevant and only leads to confusion, so we // do not display it in that situation boolean showContractScore = !gui.getCampaign().getCampaignOptions().isUseStratCon() @@ -1008,26 +1039,6 @@ public void mouseClicked(MouseEvent e) { pnlStats.add(txtScore, gridBagConstraints); } - lblBonusParts.setName("lblBonusParts"); - lblBonusParts.setText(resourceMap.getString("lblBonusParts.text")); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = y; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - pnlStats.add(lblBonusParts, gridBagConstraints); - - txtBonusParts.setName("txtBonusParts"); - txtBonusParts.setText(Integer.toString(contract.getNumBonusParts())); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = y++; - gridBagConstraints.weightx = 0.5; - gridBagConstraints.insets = new Insets(0, 10, 0, 0); - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - pnlStats.add(txtBonusParts, gridBagConstraints); - txtDesc.setName("txtDesc"); txtDesc.setEditable(false); txtDesc.setContentType("text/html"); diff --git a/MekHQ/src/mekhq/module/api/MekHQModule.java b/MekHQ/src/mekhq/module/api/MekHQModule.java index 9b20043e80d..28014af9bc8 100644 --- a/MekHQ/src/mekhq/module/api/MekHQModule.java +++ b/MekHQ/src/mekhq/module/api/MekHQModule.java @@ -37,5 +37,5 @@ public interface MekHQModule { void initPlugin(Campaign c); void loadFieldsFromXml(Node node); - void writeToXML(final PrintWriter pw, int indent); + void writeToXML(PrintWriter pw, int indent); } diff --git a/MekHQ/src/mekhq/utilities/EntityUtilities.java b/MekHQ/src/mekhq/utilities/EntityUtilities.java new file mode 100644 index 00000000000..08e65d6b007 --- /dev/null +++ b/MekHQ/src/mekhq/utilities/EntityUtilities.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MekHQ. + * + * MegaMek 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. + * + * MegaMek 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 MegaMek. If not, see . + */ +package mekhq.utilities; + +import megamek.common.Entity; +import megamek.common.annotations.Nullable; +import mekhq.campaign.Campaign; +import mekhq.campaign.unit.Unit; + +import java.util.UUID; + +/** + * Utility class that provides helper methods for working with entities. + */ +public class EntityUtilities { + /** + * Retrieves an {@link Entity} associated with a specific unit ID. + * + *

    This method attempts to find a {@link Unit} in the given {@link Campaign} using the provided + * unit ID. If the unit exists, the method returns the associated {@link Entity}. If the unit + * does not exist or has no associated entity, the method returns {@code null}. + * + * @param campaign The {@link Campaign} instance from which to retrieve the {@link Unit}. + * @param unitID The {@link UUID} of the unit for which the associated {@link Entity} is requested. + * @return The {@link Entity} associated with the specified unit ID, or {@code null} if the unit + * is not found or has no associated entity. + */ + public static @Nullable Entity getEntityFromUnitId(Campaign campaign, UUID unitID) { + Unit unit = campaign.getUnit(unitID); + + if (unit == null) { + return null; + } + + return unit.getEntity(); + } +} diff --git a/MekHQ/src/mekhq/utilities/ImageUtilities.java b/MekHQ/src/mekhq/utilities/ImageUtilities.java new file mode 100644 index 00000000000..a84babbe998 --- /dev/null +++ b/MekHQ/src/mekhq/utilities/ImageUtilities.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 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.utilities; + +import megamek.client.ui.swing.util.UIUtil; + +import javax.swing.*; +import java.awt.*; + +public class ImageUtilities { + /** + * Scales an {@link ImageIcon} to the specified width while maintaining its aspect ratio. + * The method dynamically determines the scaled height proportional to the original + * image dimensions to retain visual quality and appearance. + * + *

    The scaling process follows these steps:

    + *
      + *
    1. Adjusts the provided {@code width} to account for GUI scaling factors + * by using {@link UIUtil#scaleForGUI(int)} to ensure consistent dimensions.
    2. + *
    3. Calculates the new height required to maintain the image's original aspect ratio + * using the formula {@code height = (width * originalHeight) / originalWidth}.
    4. + *
    5. Uses {@link Image#getScaledInstance(int, int, int)} to create a resized image + * with smooth scaling for better visual quality ({@code Image.SCALE_SMOOTH}).
    6. + *
    7. Packages the resulting scaled image into a new {@link ImageIcon} object + * and returns it.
    8. + *
    + * + *

    By dynamically scaling both width and height based on the GUI scaling factor, + * the method ensures that the resized icon adapts seamlessly across high-DPI displays + * and varying screen resolutions.

    + * + * @param icon the {@link ImageIcon} to be resized. This represents the source image + * that requires scaling. + * @param width the desired width (in pixels) to scale the {@link ImageIcon} to. + * The actual applied value will consider GUI scaling factors to ensure + * consistent appearance across different display settings. + * @return a new {@link ImageIcon} instance representing the scaled image while + * preserving the original aspect ratio. + */ + public static ImageIcon scaleImageIconToWidth(ImageIcon icon, int width) { + width = UIUtil.scaleForGUI(width); + int height = (int) Math.ceil((double) width * icon.getIconHeight() / icon.getIconWidth()); + + Image image = icon.getImage(); + Image scaledImage = image.getScaledInstance(width, height, Image.SCALE_SMOOTH); + + return new ImageIcon(scaledImage); + } +} diff --git a/MekHQ/src/mekhq/utilities/MHQInternationalization.java b/MekHQ/src/mekhq/utilities/MHQInternationalization.java new file mode 100644 index 00000000000..400980bba1a --- /dev/null +++ b/MekHQ/src/mekhq/utilities/MHQInternationalization.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 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.utilities; + + +import megamek.MegaMek; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Class to handle MHQInternationalization (you will find online material on that looking for i18n) + * It makes use of some short names to make it easier to use since it is used in many places + */ +public class MHQInternationalization { + + private final String defaultBundle; + private final ConcurrentHashMap resourceBundles = new ConcurrentHashMap<>(); + protected static MHQInternationalization instance; + + static { + instance = new MHQInternationalization("mekhq.resources.GUI"); + } + + public static MHQInternationalization getInstance() { + return instance; + } + + protected MHQInternationalization(String defaultBundle) { + this.defaultBundle = defaultBundle; + } + + private static class UTF8Control extends ResourceBundle.Control { + @Override + public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) + throws IOException { + // The below is one approach; there are multiple ways to do this + String resourceName = toResourceName(toBundleName(baseName, locale), "properties"); + try (InputStream is = loader.getResourceAsStream(resourceName); + InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8)) { + return new PropertyResourceBundle(isr); + } + } + } + + ResourceBundle getResourceBundle(String bundleName) { + return resourceBundles.computeIfAbsent(bundleName, k -> + ResourceBundle.getBundle(bundleName, MegaMek.getMMOptions().getLocale(), new UTF8Control())); + } + + /** + * Get a localized string from a specific bundle + * @param bundleName the name of the bundle + * @param key the key of the string + * @return the localized string + */ + public static String getTextAt(String bundleName, String key) { + if (getInstance().getResourceBundle(bundleName).containsKey(key)) { + return getInstance().getResourceBundle(bundleName).getString(key); + } + return "!" + key + "!"; + } + + /** + * Get a localized string from the default bundle + * @param key the key of the string + * @return the localized string + */ + public static String getText(String key) { + return getTextAt(getInstance().defaultBundle, key); + } + + /** + * Get a formatted localized string from the default bundle + * @param key the key of the string + * @param args the arguments to format the string + * @return the localized string + */ + public static String getFormattedText(String key, Object... args) { + return MessageFormat.format(getFormattedTextAt(getInstance().defaultBundle, key), args); + } + + /** + * Get a formatted localized string from the default bundle + * @param bundleName the name of the bundle + * @param key the key of the string + * @param args the arguments to format the string + * @return the localized string + */ + public static String getFormattedTextAt(String bundleName, String key, Object... args) { + return MessageFormat.format(getTextAt(bundleName, key), args); + } + +} diff --git a/MekHQ/src/mekhq/utilities/PotentialTransportsMap.java b/MekHQ/src/mekhq/utilities/PotentialTransportsMap.java new file mode 100644 index 00000000000..58cd93c74a7 --- /dev/null +++ b/MekHQ/src/mekhq/utilities/PotentialTransportsMap.java @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2025-2025 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.utilities; + +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.unit.Unit; + +import java.util.*; + +import static mekhq.campaign.enums.CampaignTransportType.SHIP_TRANSPORT; +import static mekhq.campaign.enums.CampaignTransportType.TACTICAL_TRANSPORT; + +/** + * We need to determine what units in game are transports, and what units they're transporting. + * This map does most of the work and helps hold what transports are transporting what units + * for a given campaign transport type. + * @see CampaignTransportType + */ +public class PotentialTransportsMap { + + private final HashMap>> hashMap = new HashMap<>(); + + public PotentialTransportsMap(CampaignTransportType[] campaignTransportTypes) { + for (CampaignTransportType campaignTransportType : campaignTransportTypes) { + hashMap.put(campaignTransportType, new HashMap<>()); + } + } + + /** + * For the provided campaign transport type, are there any + * transports in the map? + * @see CampaignTransportType + * @param campaignTransportType type (enum) of campaign transport + * @return true if there are any transports in the map for the corresponding CampaignTransportType + */ + public boolean hasTransports(CampaignTransportType campaignTransportType) { + return hashMap.containsKey(campaignTransportType) && !(hashMap.get(campaignTransportType).isEmpty()); + } + + /** + * For the provided campaign transport type, get the transports + * @see CampaignTransportType + * @param campaignTransportType type (enum) of campaign transport + * @return transports for the given campaign transport type + */ + public Set getTransports(CampaignTransportType campaignTransportType) { + return hashMap.get(campaignTransportType).keySet(); + } + + /** + * For the provided campaign transport type and transport id, get the transported units + * @param campaignTransportType type (enum) of campaign transport + * @param uuid transport id + * @return list of uuids of units on that transport + * @see CampaignTransportType + */ + public List getTransportedUnits(CampaignTransportType campaignTransportType, UUID uuid) { + return hashMap.get(campaignTransportType).get(uuid); + } + + /** + * For the provided campaign transport type, does the provided transport exist in the map? + * @param campaignTransportType type (enum) of campaign transport + * @param key transport id + * @return true if that transport exists, false if not + */ + public boolean containsTransportKey(CampaignTransportType campaignTransportType, UUID key) { + return hashMap.containsKey(campaignTransportType) && hashMap.get(campaignTransportType).containsKey(key); + } + + /** + * For the provided campaign transport type, add a transport + * @param campaignTransportType type (enum) of campaign transport + * @param key transport id + */ + public void putNewTransport(CampaignTransportType campaignTransportType, UUID key) { + hashMap.get(campaignTransportType).put(key, new ArrayList<>()); + } + + /** + * Look through the transport map for this unit's assigned transports + * in priority order (Ship then Tactical Transports). If the transport + * is in the map, add it to the Map for loading later. + * @param unit the Unit we want to transport on its assigned transport, if it has one + */ + public void tryToAddTransportedUnit(Unit unit) { + if (unit.hasTransportShipAssignment()) { + Unit transportShip = unit.getTransportShipAssignment().getTransportShip(); + + if (containsTransportKey(SHIP_TRANSPORT, transportShip.getId())) { + addTransportedUnit(SHIP_TRANSPORT, transportShip.getId(), unit.getId()); + return; + } + } + if ( unit.hasTacticalTransportAssignment()) { + Unit transport = unit.getTacticalTransportAssignment().getTransport(); + + if (containsTransportKey(TACTICAL_TRANSPORT, transport.getId())) { + addTransportedUnit(TACTICAL_TRANSPORT, transport.getId(), unit.getId()); + return; + } + } + } + + /** + * For the provided campaign transport type and transport, add a transported unit + * @param campaignTransportType type (enum) of campaign transport + * @param key transport unit id + * @param value transported unit id + */ + public void addTransportedUnit(CampaignTransportType campaignTransportType, UUID key, UUID value) { + hashMap.get(campaignTransportType).get(key).add(value); + } + + /** + * Removes any transports that are empty from the map so they don't need referenced anymore + */ + public void removeEmptyTransports() { + if (hashMap.isEmpty()) { + return; + } + for (CampaignTransportType campaignTransportType : hashMap.keySet()) { + Set emptyTransports = new HashSet<>(); + if (!(hashMap.get(campaignTransportType).isEmpty())) { + for (UUID transport : hashMap.get(campaignTransportType).keySet()) { + if (hashMap.get(campaignTransportType).get(transport).isEmpty()) { + emptyTransports.add(transport); + } + } + } + for (UUID emptyTransport : emptyTransports) { + hashMap.get(campaignTransportType).remove(emptyTransport); + } + } + } +} diff --git a/MekHQ/unittests/mekhq/campaign/CampaignTest.java b/MekHQ/unittests/mekhq/campaign/CampaignTest.java index 18ae65a9160..724af62f0b5 100644 --- a/MekHQ/unittests/mekhq/campaign/CampaignTest.java +++ b/MekHQ/unittests/mekhq/campaign/CampaignTest.java @@ -1,25 +1,25 @@ /* - * Campaign.java + * Copyright (c) 2009-2025 - The MegaMek Team. All Rights Reserved. * - * Copyright (c) 2009 Jay Lawson (jaylawson39 at yahoo.com). All rights reserved. + * This file is part of MekHQ. * - * 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 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. * - * 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 . + * You should have received a copy of the GNU General Public License + * along with MekHQ. If not, see . */ package mekhq.campaign; + +import static mekhq.campaign.unit.enums.TransporterType.ASF_BAY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -30,10 +30,11 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; +import megamek.common.Bay; +import mekhq.campaign.enums.CampaignTransportType; +import mekhq.campaign.unit.AbstractTransportedUnitsSummary; import org.apache.logging.log4j.LogManager; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -47,12 +48,15 @@ import mekhq.campaign.personnel.ranks.Ranks; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Systems; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; /** * @author Deric Page (dericdotpageatgmaildotcom) * @since 6/10/14 10:23 AM */ public class CampaignTest { + @BeforeAll public static void setup() { EquipmentType.initializeTypes(); @@ -150,39 +154,50 @@ void testGetTechs() { assertEquals(expected, testCampaign.getTechs(true)); } - @Test - void testTransportShips() { + @ParameterizedTest + @EnumSource(value = CampaignTransportType.class) + void testTransportShips(CampaignTransportType campaignTransportType) { Campaign campaign = spy(new Campaign()); // New campaigns have no transports - assertTrue(campaign.getTransportShips().isEmpty()); + assertTrue(campaign.getTransports(campaignTransportType).isEmpty()); + campaign.hasTransports(campaignTransportType); // Create a mock transport Dropship mockTransport = mock(Dropship.class); + UUID mockId = UUID.randomUUID(); Unit mockUnit = mock(Unit.class); when(mockUnit.getId()).thenReturn(mockId); when(mockUnit.getEntity()).thenReturn(mockTransport); + // Create mock transport capacity info for transport + AbstractTransportedUnitsSummary mockTransportedUnitsSummary = mock(campaignTransportType.getTransportedUnitsSummaryType()); + when(mockTransportedUnitsSummary.getTransportCapabilities()).thenReturn(new HashSet<>(List.of(ASF_BAY))); + + when(mockUnit.getTransportedUnitsSummary(campaignTransportType)).thenReturn(mockTransportedUnitsSummary); + // Add our mock transport - campaign.addTransportShip(mockUnit); + campaign.importUnit(mockUnit); + campaign.addCampaignTransport(campaignTransportType, mockUnit); // Ensure our mock transport exists - assertEquals(1, campaign.getTransportShips().size()); - assertTrue(campaign.getTransportShips().contains(mockUnit)); + assertEquals(1, campaign.getTransports(campaignTransportType).size()); + assertTrue(campaign.getTransportsByType(campaignTransportType, ASF_BAY).contains(mockUnit)); // Add our mock transport a second time - campaign.addTransportShip(mockUnit); + campaign.addCampaignTransport(campaignTransportType, mockUnit); // Ensure our mock transport exists only once - assertEquals(1, campaign.getTransportShips().size()); - assertTrue(campaign.getTransportShips().contains(mockUnit)); + assertEquals(1, campaign.getTransports(campaignTransportType).size()); + assertTrue(campaign.getTransportsByType(campaignTransportType, ASF_BAY).contains(mockUnit)); // Remove the mock transport - campaign.removeTransportShip(mockUnit); + campaign.removeCampaignTransporter(campaignTransportType, mockUnit); // Ensure it was removed - assertTrue(campaign.getTransportShips().isEmpty()); + campaign.hasTransports(campaignTransportType); + assertTrue(campaign.getTransports(campaignTransportType).isEmpty()); } @Test diff --git a/MekHQ/unittests/mekhq/campaign/autoresolve/ResolverTest.java b/MekHQ/unittests/mekhq/campaign/autoresolve/ResolverTest.java new file mode 100644 index 00000000000..3aabde9a8db --- /dev/null +++ b/MekHQ/unittests/mekhq/campaign/autoresolve/ResolverTest.java @@ -0,0 +1,407 @@ +/* + * Copyright (c) 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.autoresolve; + +import megamek.client.ui.swing.util.PlayerColour; +import megamek.common.*; +import megamek.common.autoresolve.Resolver; +import megamek.common.autoresolve.acar.SimulationOptions; +import megamek.common.autoresolve.converter.FlattenForces; +import megamek.common.autoresolve.event.AutoResolveConcludedEvent; +import megamek.common.enums.Gender; +import megamek.common.enums.SkillLevel; +import megamek.common.icons.Camouflage; +import megamek.common.planetaryconditions.*; +import mekhq.campaign.Campaign; +import mekhq.campaign.RandomSkillPreferences; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.AtBDynamicScenario; +import mekhq.campaign.mission.AtBScenario; +import mekhq.campaign.mission.BotForce; +import mekhq.campaign.mission.enums.CombatRole; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.SkillType; +import mekhq.campaign.personnel.enums.PersonnelRole; +import mekhq.campaign.personnel.generator.AbstractSkillGenerator; +import mekhq.campaign.personnel.generator.DefaultSkillGenerator; +import mekhq.campaign.personnel.ranks.Ranks; +import mekhq.campaign.rating.CamOpsReputation.ReputationController; +import mekhq.campaign.unit.Unit; +import mekhq.campaign.universe.Systems; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + + +/** + * @author Luana Coppio + */ +@DisabledIfEnvironmentVariable(named = "CI", matches = "true") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class ResolverTest { + + // The order of the things in this file is atypical, but it is set in a way that makes it easy to find the only two tests + // that exists in this file, however those tests do not run since this is an abstract class + // instead, if you click "run test" in one of those functions it will ask which implementation class to run + // and then run the tests in that class + @EnabledIfEnvironmentVariable(named = "mm.test_auto_resolve_multiple_times", matches = "true") + @RepeatedTest(1000) + public void testAutoResolveMultipleTimes() { + autoResolve(this::postAutoResolveAccumulator); + } + + @Test + public void testAutoResolveOnce() { + autoResolve(this::assertGameFinishedWithAWinner); + } + + // Counters for tracking success across multiple runs + private static int totalRuns = 0; + private static int team1 = 0; + private static int team2 = 0; + private static int draws = 0; + + public enum TeamArrangement { + BALANCED, + UNBALANCED, + SAME_BV, + SAME_BV_SAME_SKILL + } + + static double lowerBoundTeam1() { + return 0.35; + } + static double upperBoundTeam1() { + return 0.50; + } + static double lowerBoundTeam2() { + return 0.35; + } + static double upperBoundTeam2() { + return 0.50; + } + static double lowerBoundDraw() { + return 0.10; + } + static double upperBoundDraw() { + return 0.20; + } + + static TeamArrangement getTeamArrangement() { + return TeamArrangement.BALANCED; + } + + @BeforeAll + public static void setupClass() throws IOException { + EquipmentType.initializeTypes(); + Ranks.initializeRankSystems(); + SkillType.initializeTypes(); + Systems.setInstance(Systems.loadDefault()); + } + + @Mock + private BotForce botForce; + + + + @BeforeEach + public void setup() { + MockitoAnnotations.openMocks(this); + } + + static void resetTrackers() { + totalRuns = 0; + team1 = 0; + team2 = 0; + draws = 0; + } + + AtBScenario createScenario(Campaign campaign) { + var contract = mock(AtBContract.class); + when(contract.getEnemySkill()).thenReturn(SkillLevel.REGULAR); + when(contract.getAllySkill()).thenReturn(SkillLevel.REGULAR); + + var scenario = mock(AtBDynamicScenario.class); + when(scenario.getContract(any())).thenReturn(contract); + + // Board setup + when(scenario.getBoardType()).thenReturn(0); + when(scenario.getTerrainType()).thenReturn("Woods"); + when(scenario.getMapX()).thenReturn(30); + when(scenario.getMapY()).thenReturn(30); + when(scenario.getMap()).thenReturn("Woods-deep"); + + // Planetary conditions setup + when(scenario.getLight()).thenReturn(Light.DAY); + when(scenario.getWeather()).thenReturn(Weather.CLEAR); + when(scenario.getWind()).thenReturn(Wind.CALM); + when(scenario.getFog()).thenReturn(Fog.FOG_NONE); + when(scenario.getEMI()).thenReturn(EMI.EMI_NONE); + when(scenario.getBlowingSand()).thenReturn(BlowingSand.BLOWING_SAND_NONE); + when(scenario.getAtmosphere()).thenReturn(Atmosphere.STANDARD); + when(scenario.getGravity()).thenReturn(1.0f); + when(scenario.getModifiedTemperature()).thenReturn(20); + when(scenario.getBlowingSand()).thenReturn(BlowingSand.BLOWING_SAND_NONE); + + // Lance setup + when(scenario.getCombatRole()).thenReturn(CombatRole.MANEUVER); + when(scenario.getId()).thenReturn(11); + + // Bots setup + when(scenario.getBotForce(anyInt())).thenReturn(botForce); + when(scenario.getNumBots()).thenReturn(1); + + for (var force : campaign.getAllForces()) { + force.setScenarioId(11, campaign); + } + + return scenario; + } + + Campaign createCampaign() { + var campaign = new Campaign(); + campaign.setName("Test Player"); + var reputationController = mock(ReputationController.class); + when(reputationController.getAverageSkillLevel()).thenReturn(SkillLevel.REGULAR); + + campaign.setReputation(reputationController); + var force = new Force("Heroes"); + + campaign.addForce(force, campaign.getForce(0)); + return campaign; + } + + static final AbstractSkillGenerator skillGenerator = new DefaultSkillGenerator(new RandomSkillPreferences()); + + Person randomPerson(Crew crew, Campaign campaign) { + var person = new Person(campaign); + person.setPrimaryRole(campaign, PersonnelRole.MEKWARRIOR); + person.addSkill(SkillType.S_GUN_MEK, crew.getGunnery(), 0); + person.addSkill(SkillType.S_PILOT_MEK, crew.getPiloting(), 0); + return person; + } + + private Person randomPersonForPlayer(Crew crew, Campaign campaign) { + var person = randomPerson(crew, campaign); + campaign.importPerson(person); + return person; + } + + private static final String[][] unitNames = { + { // Team 1/2: Team 1 and Team 2 + "Enforcer III ENF-6M", + "Shadow Hawk SHD-5D", + "Hatchetman HCT-6D", + "Osiris OSR-5D" + }, + { // Team 1: Unbalanced Team 1 + "Enforcer III ENF-6M", + "Shadow Hawk SHD-5D", + "Hatchetman HCT-6D", + "Osiris OSR-5D", + "Wasp WSP-1" + }, + { // Team 1: Same BV Team 1 and Team 2 with different meks and crews + "Shadow Hawk SHD-7CS", + "Vulcan VT-7T", + "Crusader CRD-7M", + "Rifleman RFL-9T", + }, + { // Team 2: Same BV Team 1 and Team 2 with different meks and crews + "Hammerhands HMH-5D", + "Spartan SPT-N1", + "Rifleman RFL-9T", + "Champion CHP-3P" + }, + { // Team 1: Same BV and Skills + "Griffin GRF-1E 'Sparky'", + "Flashman FLS-7K", + "Stalker STK-4N", + "Victor VTR-9S", + }, + { // Team 2: Same BV and Skills + "Victor VTR-9S", + "Victor VTR-9B", + "Zeus ZEU-6A", + "Crockett CRK-5003-0", + } + }; + + private static Crew createCrew(int gunnery, int piloting) { + return new Crew(CrewType.SINGLE, "John Doe", 1, gunnery, piloting, Gender.FEMALE, false, null); + } + + private final Crew[][] crews = { + { // Team 1 for same BV + createCrew(4, 5), + createCrew(4, 6), + createCrew(3, 5), + createCrew(3, 4), + }, + { // Team 2 for same BV + createCrew(3, 4), + createCrew(4, 4), + createCrew(4, 6), + createCrew(5, 6), + } + }; + + + List getEntities(TeamArrangement teamArrangement) { + var entities = new ArrayList(); + + var unitFullNames = switch (teamArrangement) { + case BALANCED, UNBALANCED -> unitNames[0]; + case SAME_BV -> unitNames[3]; + case SAME_BV_SAME_SKILL -> unitNames[5]; + }; + + for (var i = 0; i < unitFullNames.length; i++) { + var fullName = unitFullNames[i]; + var entity = MekSummary.loadEntity(fullName); + assert entity != null; + + var crew = switch (teamArrangement) { + case BALANCED, UNBALANCED, SAME_BV_SAME_SKILL -> createCrew(4, 5); + case SAME_BV -> crews[1][i % crews[1].length]; + }; + + entity.setCrew(crew); + entity.calculateBattleValue(); + entities.add(entity); + } + return entities; + } + + List getUnits(Campaign campaign, TeamArrangement teamArrangement) { + var units = new ArrayList(); + + var unitFullNames = switch (teamArrangement) { + case BALANCED -> unitNames[0]; + case UNBALANCED -> unitNames[1]; + case SAME_BV -> unitNames[2]; + case SAME_BV_SAME_SKILL -> unitNames[4]; + }; + + for (var i = 0; i < unitFullNames.length; i++) { + var fullName = unitFullNames[i]; + var unit = new Unit(); + unit.setCampaign(campaign); + var entity = MekSummary.loadEntity(fullName); + assert entity != null; + entity.setOwner(campaign.getPlayer()); + entity.setForceString("Valkiries|1||Third Support Company|31||9th Scout Lance|544||"); + entity.calculateBattleValue(); + unit.setEntity(entity); + unit.setId(UUID.randomUUID()); + + var crew = switch (teamArrangement) { + case BALANCED, UNBALANCED, SAME_BV_SAME_SKILL-> createCrew(4, 5); + case SAME_BV -> crews[0][i % crews[0].length]; + }; + + unit.addPilotOrSoldier(randomPersonForPlayer(crew, campaign)); + units.add(unit); + entity.setCrew(crew); + } + return units; + } + + void autoResolve(Consumer autoResolveConcludedEvent) { + var teamArrangement = getTeamArrangement(); + + var campaign = createCampaign(); + var units = getUnits(campaign, teamArrangement); + var scenario = createScenario(campaign); + var entities = getEntities(teamArrangement); + + when(botForce.getCamouflage()).thenReturn(Camouflage.of(PlayerColour.MAROON)); + when(botForce.getColour()).thenReturn(PlayerColour.MAROON); + when(botForce.getName()).thenReturn("OpFor"); + when(botForce.getTeam()).thenReturn(2); + when(botForce.getFullEntityList(any())).thenReturn(entities); + + var resolver = Resolver.simulationRun(new AtBSetupForces(campaign, units, scenario, new FlattenForces()), SimulationOptions.empty(), new Board(30, 30)); + autoResolveConcludedEvent.accept(resolver.resolveSimulation()); + } + + private void assertGameFinishedWithAWinner(AutoResolveConcludedEvent event) { + var victoryTeam = event.getVictoryResult().getWinningTeam(); + assertTrue((0 <= victoryTeam) && (victoryTeam <= 2), "Victory team is not 1 or 2"); + } + + private void postAutoResolveAccumulator(AutoResolveConcludedEvent event) { + totalRuns++; + var victoryTeam = event.getVictoryResult().getWinningTeam(); + switch (victoryTeam) { + case 1 -> team1++; + case 2 -> team2++; + default -> draws++; + } + // Each individual run asserts that event.controlledScenario() is valid. + // If you want a per-run assertion, you could do so here. But since we + // are aggregating results, it might be better to do final checks later. + } + + @AfterAll + public static void afterAllTests() { + if (totalRuns == 0) { + return; + } + double team1Rate = (double) team1 / totalRuns; + double team2Rate = (double) team2 / totalRuns; + double drawRate = (double) draws / totalRuns; + + System.out.println("Ran " + totalRuns + " times. \n\tTeam 1: " + team1 + " (" + team1Rate*100 + "%) " + + "\n\tTeam 2: " + team2 + " (" + team2Rate*100 + "%) \n\tDraws: " + draws + " (" + drawRate*100 + "%)"); + + assertAll("Distribution check", + () -> assertTrue(team1Rate >= lowerBoundTeam1(), + "Team 1 rate (" + team1Rate + ") is below lower bound " + lowerBoundTeam1() + ". Deviation: " + (lowerBoundTeam1() - team1Rate)), + () -> assertTrue(team1Rate <= upperBoundTeam1(), + "Team 1 rate (" + team1Rate + ") is above upper bound " + upperBoundTeam1() + ". Deviation: " + (team1Rate - upperBoundTeam1())), + + () -> assertTrue(team2Rate >= lowerBoundTeam2(), + "Team 2 rate (" + team2Rate + ") is below lower bound " + lowerBoundTeam2() + ". Deviation: " + (lowerBoundTeam2() - team2Rate)), + () -> assertTrue(team2Rate <= upperBoundTeam2(), + "Team 2 rate (" + team2Rate + ") is above upper bound " + upperBoundTeam2() + ". Deviation: " + (team2Rate - upperBoundTeam2())), + + () -> assertTrue(drawRate >= lowerBoundDraw(), + "Draw rate (" + drawRate + ") is below lower bound " + lowerBoundDraw() + ". Deviation: " + (lowerBoundDraw() - drawRate)), + () -> assertTrue(drawRate <= upperBoundDraw(), + "Draw rate (" + drawRate + ") is above upper bound " + upperBoundDraw() + ". Deviation: " + (drawRate - upperBoundDraw())) + ); + resetTrackers(); + } +} diff --git a/MekHQ/unittests/mekhq/campaign/parts/RefitTest.java b/MekHQ/unittests/mekhq/campaign/parts/RefitTest.java index 951dd1a5db8..d5e1292f7d0 100644 --- a/MekHQ/unittests/mekhq/campaign/parts/RefitTest.java +++ b/MekHQ/unittests/mekhq/campaign/parts/RefitTest.java @@ -39,6 +39,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; @@ -747,6 +749,7 @@ public void testFleaFLE4toFLE15WriteToXml() throws ParserConfigurationException, } @Test + @MockitoSettings(strictness = Strictness.LENIENT) // Allegedly unnecessary stubbing for the mockHanger & mockShoppingList public void heavyTrackedApcMgToStandard() throws EntityLoadingException, IOException { final Hangar mockHangar = mock(Hangar.class); when(mockCampaign.getHangar()).thenReturn(mockHangar); diff --git a/MekHQ/unittests/mekhq/campaign/personnel/marriage/AbstractMarriageTest.java b/MekHQ/unittests/mekhq/campaign/personnel/marriage/AbstractMarriageTest.java index b2bcd479abb..04d5c3eb659 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/marriage/AbstractMarriageTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/marriage/AbstractMarriageTest.java @@ -77,91 +77,291 @@ public void testGettersAndSetters() { //endregion Getters/Setters @Test - public void testCanMarry() { - doCallRealMethod().when(mockMarriage).canMarry(any(), any(), anyBoolean()); + public void testNotMarriageable() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); - final Genealogy mockGenealogy = mock(Genealogy.class); + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); - final Person mockPerson = mock(Person.class); - when(mockPerson.getGenealogy()).thenReturn(mockGenealogy); + when(person.isMarriageable()).thenReturn(false); - // Have to be marriageable - when(mockPerson.isMarriageable()).thenReturn(false); - assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can't be married - when(mockPerson.isMarriageable()).thenReturn(true); - when(mockGenealogy.hasSpouse()).thenReturn(true); - 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(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can't be deployed - when(mockPerson.getStatus()).thenReturn(PersonnelStatus.ACTIVE); - when(mockPerson.isDeployed()).thenReturn(true); - 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.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.isChild(any())).thenReturn(false); - when(mockPerson.isClanPersonnel()).thenReturn(true); - 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); - 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(LocalDate.ofYearDay(3025, 1), mockPerson, false)); + // Act + String result = marriage.canMarry(date, person, false); - // Can't be a Prisoner with Prisoner Marriage Disabled - when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); - assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); + // Assert + assertNotNull(result); + } - // 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(LocalDate.ofYearDay(3025, 1), mockPerson, false)); + @Test + public void testAlreadyMarried() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); - // Can't be Clan Personnel with Random Clan Marriage Disabled - when(mockMarriage.isUseRandomClanPersonnelMarriages()).thenReturn(false); - when(mockMarriage.isUseRandomPrisonerMarriages()).thenReturn(true); - assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); - // Can be Non-Clan Personnel with Random Clan Marriage Disabled - when(mockPerson.isClanPersonnel()).thenReturn(false); - assertNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(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(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + // Act + String result = marriage.canMarry(date, person, false); - // Can't be a Prisoner with Random Prisoner Marriage Disabled - when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); - assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + // Assert + assertNotNull(result); + } + + @Test + public void testInactiveStatus() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); - // 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(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.STUDENT); + + // Act + String result = marriage.canMarry(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testDeployed() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(true); + + // Act + String result = marriage.canMarry(date, person, false); + + // Assert + assertNotNull(result); } + @Test + public void testUnderage() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(true); + + // Act + String result = marriage.canMarry(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testClanPersonnelMarriageDisabled() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); + marriage.setUseClanPersonnelMarriages(false); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.isClanPersonnel()).thenReturn(true); + + // Act + String result = marriage.canMarry(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testPrisonerMarriageDisabled() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); + marriage.setUsePrisonerMarriages(false); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); + + // Act + String result = marriage.canMarry(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testPrisonerMarriageDisabledBondsman() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); + marriage.setUsePrisonerMarriages(false); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.BONDSMAN); + + // Act + String result = marriage.canMarry(date, person, false); + + // Assert + assertNull(result); + } + + @Test + public void testRandomClanMarriageDisabled() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); + marriage.setUseClanPersonnelMarriages(true); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.isClanPersonnel()).thenReturn(true); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + + // Act + String result = marriage.canMarry(date, person, true); + + // Assert + assertNotNull(result); + } + + @Test + public void testRandomPrisonerMarriageDisabled() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); + marriage.setUsePrisonerMarriages(true); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); + + // Act + String result = marriage.canMarry(date, person, true); + + // Assert + assertNotNull(result); + } + + @Test + public void testRandomPrisonerMarriageDisabledBondsman() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); + marriage.setUsePrisonerMarriages(true); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.BONDSMAN); + + // Act + String result = marriage.canMarry(date, person, true); + + // Assert + assertNull(result); + } + + @Test + public void testNoBlockers() { + // Arrange + AbstractMarriage marriage = new RandomMarriage(mockCampaignOptions); + marriage.setUseClanPersonnelMarriages(true); + marriage.setUseRandomClanPersonnelMarriages(true); + marriage.setUsePrisonerMarriages(true); + marriage.setUseRandomPrisonerMarriages(true); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + Genealogy genealogy = mock(Genealogy.class); + + when(person.isMarriageable()).thenReturn(true); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + + // Act + String result = marriage.canMarry(date, person, true); + + // Assert + assertNull(result); + } + + @Test public void testSafeSpouse() { doCallRealMethod().when(mockMarriage).safeSpouse(any(), any(), any(), any(), anyBoolean()); @@ -262,19 +462,19 @@ public void testMarry() { //region New Week @Test public void testProcessNewWeek() { - doCallRealMethod().when(mockMarriage).processNewWeek(any(), any(), any()); + doCallRealMethod().when(mockMarriage).processNewWeek(any(), any(), any(), anyBoolean()); doNothing().when(mockMarriage).marryRandomSpouse(any(), any(), any(), anyBoolean(), anyBoolean(), eq(true)); final Person mockPerson = mock(Person.class); when(mockMarriage.canMarry(any(), any(), anyBoolean())).thenReturn("Married"); - mockMarriage.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); + mockMarriage.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true); 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); + mockMarriage.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true); verify(mockMarriage, times(1)).randomMarriage(); verify(mockMarriage, times(1)).marryRandomSpouse(any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean()); } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/procreation/AbstractProcreationTest.java b/MekHQ/unittests/mekhq/campaign/personnel/procreation/AbstractProcreationTest.java index 7cdafedfae9..44c744f12b7 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/procreation/AbstractProcreationTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/procreation/AbstractProcreationTest.java @@ -142,159 +142,557 @@ public void testDetermineFather() { //endregion Determination Methods @Test - public void testCanProcreate() { - doCallRealMethod().when(mockProcreation).canProcreate(any(), any(), anyBoolean()); + public void testIsMale() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); - final Person mockPerson = mock(Person.class); - final Person mockSpouse = mock(Person.class); - final Genealogy mockGenealogy = mock(Genealogy.class); + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); - when(mockPerson.getGenealogy()).thenReturn(mockGenealogy); + when(person.getGender()).thenReturn(Gender.MALE); - // Males can't procreate - when(mockPerson.getGender()).thenReturn(Gender.MALE); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); + // Act + String result = procreation.canProcreate(date, person, false); - // Have to be trying to conceive - when(mockPerson.getGender()).thenReturn(Gender.FEMALE); - when(mockPerson.isTryingToConceive()).thenReturn(false); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); + // Assert + assertNotNull(result); + } - // Can't already be pregnant - when(mockPerson.isTryingToConceive()).thenReturn(true); - when(mockPerson.isPregnant()).thenReturn(true); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Must be active - when(mockPerson.isPregnant()).thenReturn(false); - when(mockPerson.getStatus()).thenReturn(PersonnelStatus.RETIRED); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can't be deployed - when(mockPerson.getStatus()).thenReturn(PersonnelStatus.ACTIVE); - when(mockPerson.isDeployed()).thenReturn(true); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can't be a child - when(mockPerson.isDeployed()).thenReturn(false); - when(mockPerson.isChild(any())).thenReturn(true); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Must be younger than 51 - when(mockPerson.isChild(any())).thenReturn(false); - when(mockPerson.getAge(any())).thenReturn(51); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can't be Clan Personnel with Clan Procreation Disabled - when(mockPerson.getAge(any())).thenReturn(25); - when(mockPerson.isClanPersonnel()).thenReturn(true); - when(mockProcreation.isUseClanPersonnelProcreation()).thenReturn(false); - when(mockProcreation.isUsePrisonerProcreation()).thenReturn(true); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can be Non-Clan Personnel with Clan Procreation Disabled - when(mockPerson.isClanPersonnel()).thenReturn(false); - assertNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can be a Non-Prisoner with Prisoner Procreation Disabled - when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); - when(mockProcreation.isUsePrisonerProcreation()).thenReturn(false); - assertNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can't be a Prisoner with Prisoner Procreation Disabled - when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can be a Non-Random Clan Prisoner with Clan and Prisoner Procreation Enabled and Random Procreation Disabled - when(mockPerson.isClanPersonnel()).thenReturn(true); - when(mockProcreation.isUseClanPersonnelProcreation()).thenReturn(true); - when(mockProcreation.isUsePrisonerProcreation()).thenReturn(true); - assertNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - - // Can't be Single with Relationshipless Random Procreation Disabled - when(mockGenealogy.hasSpouse()).thenReturn(false); - when(mockProcreation.isUseRelationshiplessProcreation()).thenReturn(false); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); - - // Can't be Clan Personnel with Random Clan Procreation Disabled - when(mockProcreation.isUseRelationshiplessProcreation()).thenReturn(true); - when(mockProcreation.isUseRandomClanPersonnelProcreation()).thenReturn(false); - when(mockProcreation.isUseRandomPrisonerProcreation()).thenReturn(true); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); - - // Can be Non-Clan Personnel with Random Clan Procreation Disabled - when(mockPerson.isClanPersonnel()).thenReturn(false); - assertNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); - - // Can be a Non-Prisoner with Random Prisoner Procreation Disabled - when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); - when(mockProcreation.isUseRandomPrisonerProcreation()).thenReturn(false); - assertNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); - - // Can't be a Prisoner with Random Prisoner Procreation Disabled - when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); - - // Can be a Clan Prisoner with no Spouse with Random Relationshipless, Random Clan, and - // Random Prisoner Procreation Enabled - when(mockPerson.isClanPersonnel()).thenReturn(true); - when(mockProcreation.isUseRandomClanPersonnelProcreation()).thenReturn(true); - when(mockProcreation.isUseRandomPrisonerProcreation()).thenReturn(true); - assertNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); - - // Can't have a Same-sex Spouse - when(mockSpouse.getGender()).thenReturn(Gender.FEMALE); - when(mockGenealogy.hasSpouse()).thenReturn(true); - when(mockGenealogy.getSpouse()).thenReturn(mockSpouse); - when(mockProcreation.isUseRelationshiplessProcreation()).thenReturn(false); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testNotInterestInChildren() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(false); + + // Act + String result = procreation.canProcreate(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testIsAlreadyPregnant() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(true); + + // Act + String result = procreation.canProcreate(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testIsInactive() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.STUDENT); + + // Act + String result = procreation.canProcreate(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testIsDeployed() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(true); + + // Act + String result = procreation.canProcreate(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testIsChild() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(true); + + // Act + String result = procreation.canProcreate(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testIsTooOld() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(356); + + // Act + String result = procreation.canProcreate(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testIsClanAndClanProcreationDisabled() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(true); + + // Act + String result = procreation.canProcreate(date, person, false); + + // Assert + assertNotNull(result); + } - // Spouse must also be trying to conceive - when(mockSpouse.getGender()).thenReturn(Gender.MALE); - when(mockSpouse.isTryingToConceive()).thenReturn(false); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testIsPrisonerAndPrisonerProcreationDisabled() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); + + // Act + String result = procreation.canProcreate(date, person, false); + + // Assert + assertNotNull(result); + } + + @Test + public void testIsPrisonerAndPrisonerProcreationDisabledBondsman() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.BONDSMAN); + + // Act + String result = procreation.canProcreate(date, person, false); + + // Assert + assertNull(result); + } + + @Test + public void testRandomProcreationRelationshiplessProcreationDisabled() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(false); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNotNull(result); + } - // Spouse must be active - when(mockSpouse.getStatus()).thenReturn(PersonnelStatus.RETIRED); - when(mockSpouse.isTryingToConceive()).thenReturn(true); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testRandomProcreationSpouseIsFemale() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Person spouse = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(true); + when(genealogy.getSpouse()).thenReturn(spouse); + when(spouse.getGender()).thenReturn(Gender.FEMALE); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNotNull(result); + } - // Spouse can't be deployed - when(mockSpouse.getStatus()).thenReturn(PersonnelStatus.ACTIVE); - when(mockSpouse.isDeployed()).thenReturn(true); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testRandomProcreationSpouseHasNoInterestInChildren() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Person spouse = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(true); + when(genealogy.getSpouse()).thenReturn(spouse); + when(spouse.getGender()).thenReturn(Gender.MALE); + when(spouse.isTryingToConceive()).thenReturn(false); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNotNull(result); + } - // Spouse can't be a child - when(mockSpouse.isDeployed()).thenReturn(false); - when(mockSpouse.isChild(any())).thenReturn(true); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testRandomProcreationSpouseIsInactive() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Person spouse = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(true); + when(genealogy.getSpouse()).thenReturn(spouse); + when(spouse.getGender()).thenReturn(Gender.MALE); + when(spouse.isTryingToConceive()).thenReturn(true); + when(spouse.getStatus()).thenReturn(PersonnelStatus.STUDENT); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNotNull(result); + } - // Spouse can't be Clan Personnel with Random Clan Procreation Disabled - when(mockPerson.isClanPersonnel()).thenReturn(false); - when(mockSpouse.isClanPersonnel()).thenReturn(true); - when(mockSpouse.isChild(any())).thenReturn(false); - when(mockProcreation.isUseRandomClanPersonnelProcreation()).thenReturn(false); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testRandomProcreationSpouseIsDeployed() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Person spouse = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(true); + when(genealogy.getSpouse()).thenReturn(spouse); + when(spouse.getGender()).thenReturn(Gender.MALE); + when(spouse.isTryingToConceive()).thenReturn(true); + when(spouse.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(spouse.isDeployed()).thenReturn(true); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNotNull(result); + } - // Spouse can be Non-Clan Personnel with Random Clan Procreation Disabled - when(mockSpouse.isClanPersonnel()).thenReturn(false); - assertNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testRandomProcreationSpouseIsChild() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Person spouse = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(true); + when(genealogy.getSpouse()).thenReturn(spouse); + when(spouse.getGender()).thenReturn(Gender.MALE); + when(spouse.isTryingToConceive()).thenReturn(true); + when(spouse.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(spouse.isDeployed()).thenReturn(false); + when(spouse.isChild(date, true)).thenReturn(true); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNotNull(result); + } - // Spouse can be a Non-Prisoner with Random Prisoner Procreation Disabled - when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); - when(mockSpouse.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); - when(mockProcreation.isUseRandomPrisonerProcreation()).thenReturn(false); - assertNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testRandomProcreationSpouseIsClanAndClanRandomProcreationDisabled() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Person spouse = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(true); + when(genealogy.getSpouse()).thenReturn(spouse); + when(spouse.getGender()).thenReturn(Gender.MALE); + when(spouse.isTryingToConceive()).thenReturn(true); + when(spouse.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(spouse.isDeployed()).thenReturn(false); + when(spouse.isChild(date, true)).thenReturn(false); + when(spouse.isClanPersonnel()).thenReturn(true); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNotNull(result); + } - // Spouse can't be a Prisoner with Prisoner Procreation Disabled - when(mockSpouse.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); - assertNotNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testRandomProcreationSpouseIsPrisonAndPrisonerRandomProcreationDisabled() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Person spouse = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(true); + when(genealogy.getSpouse()).thenReturn(spouse); + when(spouse.getGender()).thenReturn(Gender.MALE); + when(spouse.isTryingToConceive()).thenReturn(true); + when(spouse.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(spouse.isDeployed()).thenReturn(false); + when(spouse.isChild(date, true)).thenReturn(false); + when(spouse.isClanPersonnel()).thenReturn(false); + when(spouse.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNotNull(result); + } - // Spouse can be a Prisoner Clan Personnel with Random Clan and Prisoner Procreation Enabled - lenient().when(mockSpouse.isClanPersonnel()).thenReturn(true); - when(mockProcreation.isUseRandomClanPersonnelProcreation()).thenReturn(true); - when(mockProcreation.isUseRandomPrisonerProcreation()).thenReturn(true); - assertNull(mockProcreation.canProcreate(LocalDate.ofYearDay(3025, 1), mockPerson, true)); + @Test + public void testRandomProcreationSpouseIsPrisonAndPrisonerRandomProcreationDisabledBondsman() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Person spouse = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(true); + when(genealogy.getSpouse()).thenReturn(spouse); + when(spouse.getGender()).thenReturn(Gender.MALE); + when(spouse.isTryingToConceive()).thenReturn(true); + when(spouse.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(spouse.isDeployed()).thenReturn(false); + when(spouse.isChild(date, true)).thenReturn(false); + when(spouse.isClanPersonnel()).thenReturn(false); + when(spouse.getPrisonerStatus()).thenReturn(PrisonerStatus.BONDSMAN); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNull(result); + } + + @Test + public void testProcreationNoBlockers() { + // Arrange + AbstractProcreation procreation = new RandomProcreation(mockCampaignOptions); + + Person person = mock(Person.class); + Person spouse = mock(Person.class); + Genealogy genealogy = mock(Genealogy.class); + LocalDate date = LocalDate.of(3025, 1, 1); + + when(person.getGender()).thenReturn(Gender.FEMALE); + when(person.isTryingToConceive()).thenReturn(true); + when(person.isPregnant()).thenReturn(false); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(person.isDeployed()).thenReturn(false); + when(person.isChild(date, true)).thenReturn(false); + when(person.getAge(date)).thenReturn(21); + when(person.isClanPersonnel()).thenReturn(false); + when(person.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + when(person.getGenealogy()).thenReturn(genealogy); + when(genealogy.hasSpouse()).thenReturn(true); + when(genealogy.getSpouse()).thenReturn(spouse); + when(spouse.getGender()).thenReturn(Gender.MALE); + when(spouse.isTryingToConceive()).thenReturn(true); + when(spouse.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + when(spouse.isDeployed()).thenReturn(false); + when(spouse.isChild(date, true)).thenReturn(false); + when(spouse.isClanPersonnel()).thenReturn(false); + when(spouse.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); + + // Act + String result = procreation.canProcreate(date, person, true); + + // Assert + assertNull(result); } @Test @@ -445,27 +843,47 @@ public void testProcessPregnancyComplications() { } //endregion Pregnancy Complications - //region New Day + //region Process New Week + @Test - public void testProcessNewWeek() { + public void testProcessNewWeek_ForNonPregnantMale() { doCallRealMethod().when(mockProcreation).processNewWeek(any(), any(), any()); - doNothing().when(mockProcreation).birth(any(), any(), any()); final Person mockPerson = mock(Person.class); - when(mockPerson.getGender()).thenReturn(Gender.MALE); mockProcreation.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); verify(mockPerson, never()).isPregnant(); verify(mockProcreation, never()).randomlyProcreates(any(), any()); + } + + @Test + public void testProcessNewWeek_ForPregnantFemale() { + doCallRealMethod().when(mockProcreation).processNewWeek(any(), any(), any()); + + final Person mockPerson = mock(Person.class); when(mockPerson.getGender()).thenReturn(Gender.FEMALE); when(mockPerson.isPregnant()).thenReturn(true); when(mockPerson.getDueDate()).thenReturn(LocalDate.ofYearDay(3025, 2)); + mockProcreation.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); + verify(mockProcreation, never()).birth(any(), any(), any()); verify(mockProcreation, never()).randomlyProcreates(any(), any()); + } + + @Test + public void testProcessNewWeek_ForPregnantFemaleWithDueDate() { + doCallRealMethod().when(mockProcreation).processNewWeek(any(), any(), any()); + doNothing().when(mockProcreation).birth(any(), any(), any()); + final Person mockPerson = mock(Person.class); + + // Ensure proper stubbing + when(mockPerson.getGender()).thenReturn(Gender.FEMALE); + when(mockPerson.isPregnant()).thenReturn(true); when(mockPerson.getDueDate()).thenReturn(LocalDate.ofYearDay(3025, 1)); + mockProcreation.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); verify(mockProcreation, times(1)).birth(any(), any(), any()); verify(mockProcreation, never()).randomlyProcreates(any(), any()); diff --git a/MekHQ/unittests/mekhq/campaign/unit/LegacyUnitShipTransportTest.java b/MekHQ/unittests/mekhq/campaign/unit/LegacyUnitShipTransportTest.java new file mode 100644 index 00000000000..6dc15ce035a --- /dev/null +++ b/MekHQ/unittests/mekhq/campaign/unit/LegacyUnitShipTransportTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2020-2025 - 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.unit; + +import megamek.common.*; +import mekhq.campaign.Campaign; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.Vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.*; + +// + +/** + * Psi - Transport has been split into Ship & Tactical transport. + * @see UnitTransportTest + */ +public class LegacyUnitShipTransportTest { + + @BeforeAll + static void before() { + EquipmentType.initializeTypes(); + + + } + + @Test + public void basicTransportedUnits() { + Game mockGame = mock(Game.class); + Campaign mockCampaign = mock(Campaign.class); + + + Unit transport = new Unit(); + when(mockCampaign.getGame()).thenReturn(mockGame); + mockCampaign.importUnit(transport); + + + + // We start with empty transport bays + assertFalse(transport.hasShipTransportedUnits()); + assertNotNull(transport.getShipTransportedUnits()); + assertTrue(transport.getShipTransportedUnits().isEmpty()); + + // Create a fake unit to transport + Unit mockUnit = mock(Unit.class); + when(mockUnit.getId()).thenReturn(UUID.randomUUID()); + + // Add a transported unit + transport.addShipTransportedUnit(mockUnit); + + // Now we should have units + assertTrue(transport.hasShipTransportedUnits()); + assertEquals(1, transport.getShipTransportedUnits().size()); + assertTrue(transport.getShipTransportedUnits().contains(mockUnit)); + + // Adding the same unit again... + transport.addShipTransportedUnit(mockUnit); + + // ... should leave everything the same. + assertTrue(transport.hasShipTransportedUnits()); + assertEquals(1, transport.getShipTransportedUnits().size()); + assertTrue(transport.getShipTransportedUnits().contains(mockUnit)); + + Unit mockOtherUnit = mock(Unit.class); + when(mockOtherUnit.getId()).thenReturn(UUID.randomUUID()); + + // We should not be able to remove an unknown unit + transport.removeShipTransportedUnit(mockOtherUnit); + + // But we can add at least one more unit... + transport.addShipTransportedUnit(mockOtherUnit); + + assertTrue(transport.hasShipTransportedUnits()); + assertEquals(2, transport.getShipTransportedUnits().size()); + assertTrue(transport.getShipTransportedUnits().contains(mockUnit)); + assertTrue(transport.getShipTransportedUnits().contains(mockOtherUnit)); + + // ... and removing the first... + assertTrue(transport.removeShipTransportedUnit(mockUnit)); + + // ... should leave us with just that one other unit. + assertTrue(transport.hasShipTransportedUnits()); + assertEquals(1, transport.getShipTransportedUnits().size()); + assertTrue(transport.getShipTransportedUnits().contains(mockOtherUnit)); + + // ... and clearing out our transport bays... + transport.clearShipTransportedUnits(); + + // ... should leave us empty again. + assertFalse(transport.hasShipTransportedUnits()); + assertNotNull(transport.getShipTransportedUnits()); + assertTrue(transport.getShipTransportedUnits().isEmpty()); + } + + @Test + public void isCarryingAeroAndGround() { + Unit transport = new Unit(); + + // No units? No aeros. + assertFalse(transport.hasShipTransportedUnits()); + assertFalse(transport.isCarryingSmallerAero()); + assertFalse(transport.isCarryingGround()); + + // Add a ground unit + Entity mockGroundEntity = mock(Mek.class); + when(mockGroundEntity.isAero()).thenReturn(false); + Unit mockGroundUnit = mock(Unit.class); + when(mockGroundUnit.getId()).thenReturn(UUID.randomUUID()); + when(mockGroundUnit.getEntity()).thenReturn(mockGroundEntity); + + transport.addShipTransportedUnit(mockGroundUnit); + + // No aeros, just a ground unit + assertTrue(transport.hasShipTransportedUnits()); + assertFalse(transport.isCarryingSmallerAero()); + assertTrue(transport.isCarryingGround()); + + // Add an aero unit + Entity mockAeroEntity = mock(Aero.class); + when(mockAeroEntity.isAero()).thenReturn(true); + Unit mockAeroUnit = mock(Unit.class); + when(mockAeroUnit.getId()).thenReturn(UUID.randomUUID()); + when(mockAeroUnit.getEntity()).thenReturn(mockAeroEntity); + + transport.addShipTransportedUnit(mockAeroUnit); + + // Now we have an areo + assertTrue(transport.hasShipTransportedUnits()); + assertTrue(transport.isCarryingSmallerAero()); + assertTrue(transport.isCarryingGround()); + + // Removing the ground unit should not affect our aero calculation + transport.removeShipTransportedUnit(mockGroundUnit); + + assertTrue(transport.hasShipTransportedUnits()); + assertTrue(transport.isCarryingSmallerAero()); + assertFalse(transport.isCarryingGround()); + } + + @Test + public void testUnitTypeForAerosMatchesAeroBayType() { + Campaign campaign = mock(Campaign.class); + + // Create a fake entity to back the real transport Unit + Dropship mockVengeance = mock(Dropship.class); + Unit transport = new Unit(mockVengeance,campaign); + ASFBay mockASFBay = new ASFBay(100, 1 ,0); + + // Initialize bays + Vector bays = new Vector<>(); + bays.add(mockASFBay); + Vector transporters = new Vector<>(); + transporters.add(mockASFBay); + when(mockVengeance.getTransportBays()).thenReturn(bays); + when(mockVengeance.getTransports()).thenReturn(transporters); + mockASFBay.setGame(mock(Game.class)); + transport.initializeShipTransportSpace(); + + // Add an aero unit + Entity aero = new AeroSpaceFighter(); + Unit mockAeroUnit = mock(Unit.class); + when(mockAeroUnit.getId()).thenReturn(UUID.randomUUID()); + when(mockAeroUnit.getEntity()).thenReturn(aero); + + // Verify the AeroSpaceFighter is recognized as a valid ASFBay occupant + double remainingCap = transport.getCorrectBayCapacity(aero.getUnitType(), 50); + assertEquals(100.0, remainingCap); + } + + @Test + public void unloadFromTransportShipDoesNothingIfNotLoaded() { + Unit transport = spy(new Unit()); + + Unit randomUnit = mock(Unit.class); + when(randomUnit.hasTransportShipAssignment()).thenReturn(false); + + // Try removing a ship that's not on our transport. + transport.removeShipTransportedUnit(randomUnit); + + // The unit should NOT have its assignment changed. + verify(randomUnit, times(0)).setTransportShipAssignment(eq(null)); + + // And we should not have had our bay space recalculated. + verify(transport, times(0)).updateBayCapacity(anyInt(), anyDouble(), + anyBoolean(), anyInt()); + } + + @Test + public void unloadFromTransportShipDoesNothingIfLoadedOnAnotherShip() { + Unit transport0 = spy(new Unit()); + + Unit transport1 = mock(Unit.class); + Unit randomUnit = mock(Unit.class); + TransportShipAssignment transportAssignment = mock(TransportShipAssignment.class); + when(randomUnit.hasTransportShipAssignment()).thenReturn(true); + when(randomUnit.getTransportShipAssignment()).thenReturn(transportAssignment); + when(transportAssignment.getTransport()).thenReturn(transport1); + + // Try removing a ship that's on somebody else's transport + transport0.removeShipTransportedUnit(randomUnit); + + // The unit should NOT have its assignment changed. + verify(randomUnit, times(0)).setTransportShipAssignment(eq(null)); + + // And we should not have had our bay space recalculated. + verify(transport0, times(0)).updateBayCapacity(anyInt(), anyDouble(), + anyBoolean(), anyInt()); + } +} diff --git a/MekHQ/unittests/mekhq/campaign/unit/UnitPersonTest.java b/MekHQ/unittests/mekhq/campaign/unit/UnitPersonTest.java index a4558278adb..c533dea0f76 100644 --- a/MekHQ/unittests/mekhq/campaign/unit/UnitPersonTest.java +++ b/MekHQ/unittests/mekhq/campaign/unit/UnitPersonTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; import java.util.List; +import java.util.Set; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -301,7 +302,7 @@ public void testGunner() { verify(unit, times(1)).resetPilotAndEntity(); // Ensure when getting the gunner that it is the same gunner - List gunners = unit.getGunners(); + Set gunners = unit.getGunners(); assertTrue(gunners.contains(mockGunner)); assertTrue(unit.isGunner(mockGunner)); diff --git a/MekHQ/unittests/mekhq/campaign/unit/UnitTransportTest.java b/MekHQ/unittests/mekhq/campaign/unit/UnitTransportTest.java index dcc273c8abf..f26ad185630 100644 --- a/MekHQ/unittests/mekhq/campaign/unit/UnitTransportTest.java +++ b/MekHQ/unittests/mekhq/campaign/unit/UnitTransportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2025 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -19,16 +19,16 @@ package mekhq.campaign.unit; import megamek.common.*; +import mekhq.campaign.Campaign; +import mekhq.campaign.enums.CampaignTransportType; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import java.util.UUID; import java.util.Vector; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class UnitTransportTest { @@ -36,76 +36,86 @@ public class UnitTransportTest { @BeforeAll static void before() { EquipmentType.initializeTypes(); + + } - @Test - public void basicTransportedUnits() { + @ParameterizedTest + @EnumSource(value = CampaignTransportType.class) + public void basicTransportedUnits(CampaignTransportType campaignTransportType) { + Game mockGame = mock(Game.class); + Campaign mockCampaign = mock(Campaign.class); + + Unit transport = new Unit(); + when(mockCampaign.getGame()).thenReturn(mockGame); + mockCampaign.importUnit(transport); + + // We start with empty transport bays - assertFalse(transport.hasTransportedUnits()); - assertNotNull(transport.getTransportedUnits()); - assertTrue(transport.getTransportedUnits().isEmpty()); + assertFalse(transport.hasTransportedUnits(campaignTransportType)); + assertNotNull(transport.getTransportedUnits(campaignTransportType)); + assertTrue(transport.getTransportedUnits(campaignTransportType).isEmpty()); // Create a fake unit to transport Unit mockUnit = mock(Unit.class); when(mockUnit.getId()).thenReturn(UUID.randomUUID()); // Add a transported unit - transport.addTransportedUnit(mockUnit); + transport.addTransportedUnit(campaignTransportType, mockUnit); // Now we should have units - assertTrue(transport.hasTransportedUnits()); - assertEquals(1, transport.getTransportedUnits().size()); - assertTrue(transport.getTransportedUnits().contains(mockUnit)); + assertTrue(transport.hasTransportedUnits(campaignTransportType)); + assertEquals(1, transport.getTransportedUnits(campaignTransportType).size()); + assertTrue(transport.getTransportedUnits(campaignTransportType).contains(mockUnit)); // Adding the same unit again... - transport.addTransportedUnit(mockUnit); + transport.addTransportedUnit(campaignTransportType, mockUnit); // ... should leave everything the same. - assertTrue(transport.hasTransportedUnits()); - assertEquals(1, transport.getTransportedUnits().size()); - assertTrue(transport.getTransportedUnits().contains(mockUnit)); + assertTrue(transport.hasTransportedUnits(campaignTransportType)); + assertEquals(1, transport.getTransportedUnits(campaignTransportType).size()); + assertTrue(transport.getTransportedUnits(campaignTransportType).contains(mockUnit)); Unit mockOtherUnit = mock(Unit.class); when(mockOtherUnit.getId()).thenReturn(UUID.randomUUID()); // We should not be able to remove an unknown unit - transport.removeTransportedUnit(mockOtherUnit); + transport.removeTransportedUnit(campaignTransportType, mockOtherUnit); // But we can add at least one more unit... - transport.addTransportedUnit(mockOtherUnit); + transport.addTransportedUnit(campaignTransportType, mockOtherUnit); - assertTrue(transport.hasTransportedUnits()); - assertEquals(2, transport.getTransportedUnits().size()); - assertTrue(transport.getTransportedUnits().contains(mockUnit)); - assertTrue(transport.getTransportedUnits().contains(mockOtherUnit)); + assertTrue(transport.hasTransportedUnits(campaignTransportType)); + assertEquals(2, transport.getTransportedUnits(campaignTransportType).size()); + assertTrue(transport.getTransportedUnits(campaignTransportType).contains(mockUnit)); + assertTrue(transport.getTransportedUnits(campaignTransportType).contains(mockOtherUnit)); // ... and removing the first... - assertTrue(transport.removeTransportedUnit(mockUnit)); + assertTrue(transport.removeTransportedUnit(campaignTransportType, mockUnit)); // ... should leave us with just that one other unit. - assertTrue(transport.hasTransportedUnits()); - assertEquals(1, transport.getTransportedUnits().size()); - assertTrue(transport.getTransportedUnits().contains(mockOtherUnit)); + assertTrue(transport.hasTransportedUnits(campaignTransportType)); + assertEquals(1, transport.getTransportedUnits(campaignTransportType).size()); + assertTrue(transport.getTransportedUnits(campaignTransportType).contains(mockOtherUnit)); // ... and clearing out our transport bays... - transport.clearTransportedUnits(); + transport.clearTransportedUnits(campaignTransportType); // ... should leave us empty again. - assertFalse(transport.hasTransportedUnits()); - assertNotNull(transport.getTransportedUnits()); - assertTrue(transport.getTransportedUnits().isEmpty()); + assertFalse(transport.hasTransportedUnits(campaignTransportType)); + assertNotNull(transport.getTransportedUnits(campaignTransportType)); + assertTrue(transport.getTransportedUnits(campaignTransportType).isEmpty()); } - @Test - public void isCarryingAeroAndGround() { + @ParameterizedTest + @EnumSource(value = CampaignTransportType.class) + public void isCarryingAeroAndGround(CampaignTransportType campaignTransportType) { Unit transport = new Unit(); // No units? No aeros. - assertFalse(transport.hasTransportedUnits()); - assertFalse(transport.isCarryingSmallerAero()); - assertFalse(transport.isCarryingGround()); + assertFalse(transport.hasTransportedUnits(campaignTransportType)); // Add a ground unit Entity mockGroundEntity = mock(Mek.class); @@ -114,12 +124,10 @@ public void isCarryingAeroAndGround() { when(mockGroundUnit.getId()).thenReturn(UUID.randomUUID()); when(mockGroundUnit.getEntity()).thenReturn(mockGroundEntity); - transport.addTransportedUnit(mockGroundUnit); + transport.addTransportedUnit(campaignTransportType, mockGroundUnit); // No aeros, just a ground unit - assertTrue(transport.hasTransportedUnits()); - assertFalse(transport.isCarryingSmallerAero()); - assertTrue(transport.isCarryingGround()); + assertTrue(transport.hasTransportedUnits(campaignTransportType)); // Add an aero unit Entity mockAeroEntity = mock(Aero.class); @@ -128,35 +136,36 @@ public void isCarryingAeroAndGround() { when(mockAeroUnit.getId()).thenReturn(UUID.randomUUID()); when(mockAeroUnit.getEntity()).thenReturn(mockAeroEntity); - transport.addTransportedUnit(mockAeroUnit); + transport.addTransportedUnit(campaignTransportType, mockAeroUnit); // Now we have an areo - assertTrue(transport.hasTransportedUnits()); - assertTrue(transport.isCarryingSmallerAero()); - assertTrue(transport.isCarryingGround()); + assertTrue(transport.hasTransportedUnits(campaignTransportType)); // Removing the ground unit should not affect our aero calculation - transport.removeTransportedUnit(mockGroundUnit); + transport.removeTransportedUnit(campaignTransportType, mockGroundUnit); - assertTrue(transport.hasTransportedUnits()); - assertTrue(transport.isCarryingSmallerAero()); - assertFalse(transport.isCarryingGround()); + assertTrue(transport.hasTransportedUnits(campaignTransportType)); } - @Test + @ParameterizedTest + @EnumSource(value = CampaignTransportType.class) public void testUnitTypeForAerosMatchesAeroBayType() { + Campaign campaign = mock(Campaign.class); + // Create a fake entity to back the real transport Unit Dropship mockVengeance = mock(Dropship.class); - Unit transport = new Unit(); - ASFBay mockASFBay = mock(ASFBay.class); - when(mockASFBay.getCapacity()).thenReturn(100.0); - transport.setEntity(mockVengeance); + Unit transport = new Unit(mockVengeance,campaign); + ASFBay mockASFBay = new ASFBay(100, 1 ,0); // Initialize bays Vector bays = new Vector<>(); bays.add(mockASFBay); + Vector transporters = new Vector<>(); + transporters.add(mockASFBay); when(mockVengeance.getTransportBays()).thenReturn(bays); - transport.initializeBaySpace(); + when(mockVengeance.getTransports()).thenReturn(transporters); + mockASFBay.setGame(mock(Game.class)); + transport.initializeShipTransportSpace(); // Add an aero unit Entity aero = new AeroSpaceFighter(); @@ -169,41 +178,43 @@ public void testUnitTypeForAerosMatchesAeroBayType() { assertEquals(100.0, remainingCap); } - @Test - public void unloadFromTransportShipDoesNothingIfNotLoaded() { + @ParameterizedTest + @EnumSource(value = CampaignTransportType.class) + public void unloadFromTransportDoesNothingIfNotLoaded(CampaignTransportType campaignTransportType) { Unit transport = spy(new Unit()); Unit randomUnit = mock(Unit.class); - when(randomUnit.hasTransportShipAssignment()).thenReturn(false); + when(randomUnit.hasTransportAssignment(campaignTransportType)).thenReturn(false); // Try removing a ship that's not on our transport. - transport.removeTransportedUnit(randomUnit); + transport.removeTransportedUnit(campaignTransportType, randomUnit); // The unit should NOT have its assignment changed. - verify(randomUnit, times(0)).setTransportShipAssignment(eq(null)); + verify(randomUnit, times(0)).setTransportAssignment(eq(campaignTransportType), eq(null)); // And we should not have had our bay space recalculated. - verify(transport, times(0)).updateBayCapacity(anyInt(), anyDouble(), - anyBoolean(), anyInt()); + verify(transport, times(0)).initializeTransportSpace(campaignTransportType); } - @Test - public void unloadFromTransportShipDoesNothingIfLoadedOnAnotherShip() { + @ParameterizedTest + @EnumSource(value = CampaignTransportType.class) + public void unloadFromTransportDoesNothingIfLoadedOnAnotherTransport(CampaignTransportType campaignTransportType) { Unit transport0 = spy(new Unit()); Unit transport1 = mock(Unit.class); Unit randomUnit = mock(Unit.class); - when(randomUnit.hasTransportShipAssignment()).thenReturn(true); - when(randomUnit.getTransportShipAssignment()).thenReturn(new TransportShipAssignment(transport1, 0)); + ITransportAssignment transportAssignment = mock(campaignTransportType.getTransportAssignmentType()); + when(randomUnit.hasTransportAssignment(eq(campaignTransportType))).thenReturn(true); + when(randomUnit.getTransportAssignment(eq(campaignTransportType))).thenReturn(transportAssignment); + when(transportAssignment.getTransport()).thenReturn(transport1); // Try removing a ship that's on somebody else's transport - transport0.removeTransportedUnit(randomUnit); + transport0.removeTransportedUnit(campaignTransportType, randomUnit); // The unit should NOT have its assignment changed. - verify(randomUnit, times(0)).setTransportShipAssignment(eq(null)); + verify(randomUnit, times(0)).setTransportAssignment(eq(campaignTransportType), eq(null)); // And we should not have had our bay space recalculated. - verify(transport0, times(0)).updateBayCapacity(anyInt(), anyDouble(), - anyBoolean(), anyInt()); + verify(transport0, times(0)).initializeTransportSpace(campaignTransportType); } } diff --git a/MekHQ/unittests/mekhq/campaign/unit/actions/RestoreUnitActionTest.java b/MekHQ/unittests/mekhq/campaign/unit/actions/RestoreUnitActionTest.java index 8ba7ea7cc51..899b9df34a2 100644 --- a/MekHQ/unittests/mekhq/campaign/unit/actions/RestoreUnitActionTest.java +++ b/MekHQ/unittests/mekhq/campaign/unit/actions/RestoreUnitActionTest.java @@ -20,13 +20,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import java.util.UUID; @@ -89,7 +83,7 @@ public void restoreUnitSwitchesOutEntityAndParts() { verify(unit, times(1)).setEntity(eq(mockNewEntity)); verify(unit, times(1)).removeParts(); - verify(unit, times(1)).initializeBaySpace(); + verify(unit, times(1)).initializeAllTransportSpace(); verify(unit, times(1)).initializeParts(eq(true)); verify(unit, times(1)).runDiagnostic(eq(false)); verify(unit, times(1)).setSalvage(eq(false)); diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialogTest.java deleted file mode 100644 index 084ceb73e01..00000000000 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialogTest.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (c) 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.gui.dialog.nagDialogs; - -import mekhq.campaign.Campaign; -import mekhq.campaign.Hangar; -import mekhq.campaign.unit.Unit; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.stream.Stream; - -import static mekhq.gui.dialog.nagDialogs.InsufficientAstechTimeNagDialog.checkAstechTimeDeficit; -import static mekhq.gui.dialog.nagDialogs.InsufficientAstechTimeNagDialog.getAstechTimeDeficit; -import static org.junit.jupiter.api.Assertions.assertEquals; -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; - -/** - * This class contains test cases for the {@link InsufficientAstechTimeNagDialog} class. - * It tests the different combinations of Astech requirements and verifies the behavior of the - * {@code getAstechTimeDeficit()} and {@code checkAstechTimeDeficit()} methods. - */ -class InsufficientAstechTimeNagDialogTest { - // Mock objects for the tests - private Campaign campaign; - private Hangar hangar; - private Unit unit1, unit2; - private int possibleAstechMinutes; - private int possibleAstechOvertimeMinutes; - - /** - * Test setup for each test, runs before each test. - * Initializes the mock objects and sets up the necessary mock behaviors. - */ - @BeforeEach - void init() { - // Initialize the mock objects - campaign = mock(Campaign.class); - hangar = mock(Hangar.class); - unit1 = mock(Unit.class); - unit2 = mock(Unit.class); - possibleAstechMinutes = 2880; // This is the equivalent of one full team - possibleAstechOvertimeMinutes = 1440; // As is this - - // Stub the getHangar() method to return the hangar mock - when(campaign.getHangar()).thenReturn(hangar); - } - - /** - * Initializes a unit with the given parameters. - * - * @param unit the unit to be initialized - * @param isUnmaintained a boolean indicating if the unit is unmaintained - * @param isPresent a boolean indicating if the unit is present - * @param isSelfCrewed a boolean indicating if the unit is self-crewed - * @param maintenanceTime the maintenance time for the unit - */ - private void initiateUnit(Unit unit, boolean isUnmaintained, boolean isPresent, boolean isSelfCrewed, int maintenanceTime) { - when(unit.isUnmaintained()).thenReturn(isUnmaintained); - when(unit.isPresent()).thenReturn(isPresent); - when(unit.isSelfCrewed()).thenReturn(isSelfCrewed); - when(unit.getMaintenanceTime()).thenReturn(maintenanceTime); - } - - /** - * Processes units and Astech time based on the given parameters. - * - * @param isOvertimeAllowed {@code true} if overtime is allowed, {@code false} otherwise - */ - private void processUnitsAndAstechTime(boolean isOvertimeAllowed) { - // Prepare a stream of the unit mocks - Stream unitStream = Stream.of(unit1, unit2); - - // Stub the getUnitsStream() method to return the stream of unit mocks - when(hangar.getUnitsStream()).thenReturn(unitStream); - - // Calculate possible Astech Minutes - when(campaign.getPossibleAstechPoolMinutes()).thenReturn(possibleAstechMinutes); - - // Calculate overtime minutes - when(campaign.isOvertimeAllowed()).thenReturn(isOvertimeAllowed); - when(campaign.getPossibleAstechPoolOvertime()).thenReturn(possibleAstechOvertimeMinutes); - } - - // In the following tests the getAstechTimeDeficit() method is called, and its response is - // checked against expected behavior - - @Test - void testAstechTimeDeficitCalculationNoOvertime() { - // Initiate Units - initiateUnit(unit1, false, true, false, 60); - initiateUnit(unit2, false, true, false, 60); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertEquals(-4, getAstechTimeDeficit(campaign)); - } - - @Test - void testAstechTimeDeficitCalculationWithOvertime() { - // Initiate Units - initiateUnit(unit1, false, true, false, 60); - initiateUnit(unit2, false, true, false, 60); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(true); - - // Assert results equals expected value - assertEquals(-7, getAstechTimeDeficit(campaign)); - } - - @Test - void testInsufficientAstechTimeDeficitCalculationNoOvertime() { - // Initiate Units - initiateUnit(unit1, false, true, false, 6000); - initiateUnit(unit2, false, true, false, 6000); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertEquals(144, getAstechTimeDeficit(campaign)); - } - - @Test - void testInsufficientAstechTimeDeficitCalculationWithInsufficientOvertime() { - // Initiate Units - initiateUnit(unit1, false, true, false, 6000); - initiateUnit(unit2, false, true, false, 6000); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(true); - - // Assert results equals expected value - assertEquals(141, getAstechTimeDeficit(campaign)); - } - - @Test - void testInsufficientAstechTimeDeficitCalculationWithSufficientOvertime() { - // Initiate Units - initiateUnit(unit1, false, true, false, 300); - initiateUnit(unit2, false, true, false, 300); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(true); - - // Assert results equals expected value - assertEquals(-1, getAstechTimeDeficit(campaign)); - } - - @Test - void testAstechTimeDeficitCalculationOneUnitUnmaintained() { - // Initiate Units - initiateUnit(unit1, true, true, false, 60); - initiateUnit(unit2, false, true, false, 60); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertEquals(-5, getAstechTimeDeficit(campaign)); - } - - @Test - void testAstechTimeDeficitCalculationTwoUnitsUnmaintained() { - // Initiate Units - initiateUnit(unit1, true, true, false, 60); - initiateUnit(unit2, true, true, false, 60); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertEquals(-6, getAstechTimeDeficit(campaign)); - } - - @Test - void testAstechTimeDeficitCalculationOneUnitAbsent() { - // Initiate Units - initiateUnit(unit1, false, false, false, 60); - initiateUnit(unit2, false, true, false, 60); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertEquals(-5, getAstechTimeDeficit(campaign)); - } - - @Test - void testAstechTimeDeficitCalculationTwoUnitsAbsent() { - // Initiate Units - initiateUnit(unit1, false, false, false, 60); - initiateUnit(unit2, false, false, false, 60); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertEquals(-6, getAstechTimeDeficit(campaign)); - } - - @Test - void testAstechTimeDeficitCalculationOneUnitSelfCrewed() { - // Initiate Units - initiateUnit(unit1, false, true, true, 60); - initiateUnit(unit2, false, true, false, 60); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertEquals(-5, getAstechTimeDeficit(campaign)); - } - - @Test - void testAstechTimeDeficitCalculationTwoUnitsSelfCrewed() { - // Initiate Units - initiateUnit(unit1, false, true, true, 60); - initiateUnit(unit2, false, true, true, 60); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertEquals(-6, getAstechTimeDeficit(campaign)); - } - - // In the following tests the checkAstechTimeDeficit() method is called, and its response is - // checked against expected behavior - - @Test - void testAstechTimeDeficitCheckNegativeDeficit() { - // Initiate Units - initiateUnit(unit1, false, true, false, 60); - initiateUnit(unit2, false, true, false, 60); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertFalse(checkAstechTimeDeficit(campaign)); - } - - @Test - void testAstechTimeDeficitCheckPositiveDeficit() { - // Initiate Units - initiateUnit(unit1, false, true, false, 6000); - initiateUnit(unit2, false, true, false, 6000); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertTrue(checkAstechTimeDeficit(campaign)); - } - - @Test - void testAstechTimeDeficitCheckZeroDeficit() { - // Initiate Units - initiateUnit(unit1, false, true, false, 240); - initiateUnit(unit2, false, true, false, 240); - - // Stream Units and process Astech Time - processUnitsAndAstechTime(false); - - // Assert results equals expected value - assertFalse(checkAstechTimeDeficit(campaign)); - } -} \ No newline at end of file diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialogTest.java deleted file mode 100644 index b62254caf84..00000000000 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialogTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 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.gui.dialog.nagDialogs; - -import mekhq.campaign.Campaign; -import mekhq.campaign.CampaignOptions; -import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.stratcon.StratconCampaignState; -import mekhq.campaign.stratcon.StratconCoords; -import mekhq.campaign.stratcon.StratconScenario; -import mekhq.campaign.stratcon.StratconScenario.ScenarioState; -import mekhq.campaign.stratcon.StratconTrackState; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.LocalDate; -import java.util.*; - -import static mekhq.gui.dialog.nagDialogs.UnresolvedStratConContactsNagDialog.nagUnresolvedContacts; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * This class is a test class for the {@link UnresolvedStratConContactsNagDialog} class. - * It contains tests for various scenarios related to the {@code nagUnresolvedContacts} method - */ -public class UnresolvedStratConContactsNagDialogTest { - // Mock objects for the tests - private Campaign campaign; - private LocalDate today; - private CampaignOptions options; - private AtBContract contract; - private StratconCampaignState stratconCampaignState; - private StratconTrackState track; - - private StratconCoords coordinates; - private StratconScenario scenarioNotDue, scenarioDue; - - /** - * Test setup for each test, runs before each test. - * Initializes the mock objects and sets up the necessary mock behaviors. - */ - @BeforeEach - void init() { - // Initialize the mock objects - campaign = mock(Campaign.class); - options = mock(CampaignOptions.class); - today = LocalDate.now(); - - contract = mock(AtBContract.class); - - stratconCampaignState = mock(StratconCampaignState.class); - track = mock(StratconTrackState.class); - - coordinates = mock(StratconCoords.class); - - scenarioNotDue = mock(StratconScenario.class); - scenarioDue = mock(StratconScenario.class); - - // Stubs - when(campaign.getCampaignOptions()).thenReturn(options); - when(campaign.getLocalDate()).thenReturn(today); - - when(contract.getStratconCampaignState()).thenReturn(stratconCampaignState); - when(stratconCampaignState.getTracks()).thenReturn(Collections.singletonList(track)); - - when(options.isUseStratCon()).thenReturn(true); - when(campaign.getActiveAtBContracts()).thenReturn(List.of(contract)); - - when(scenarioDue.getCurrentState()).thenReturn(ScenarioState.UNRESOLVED); - when(scenarioDue.getDeploymentDate()).thenReturn(today); - when(scenarioDue.getName()).thenReturn("Scenario Due"); - - when(scenarioNotDue.getCurrentState()).thenReturn(ScenarioState.UNRESOLVED); - when(scenarioNotDue.getDeploymentDate()).thenReturn(today.plusDays(1)); - when(scenarioNotDue.getName()).thenReturn("Scenario Not Due"); - } - - @Test - void stratConDisabled() { - when(options.isUseStratCon()).thenReturn(false); - - assertEquals("", nagUnresolvedContacts(campaign)); - } - - @Test - void noActiveContract() { - when(campaign.getActiveAtBContracts()).thenReturn(new ArrayList<>()); - - assertEquals("", nagUnresolvedContacts(campaign)); - } - - @Test - void nullStratConState() { - when(contract.getStratconCampaignState()).thenReturn(null); - - assertEquals("", nagUnresolvedContacts(campaign)); - } - - @Test - void noScenarios() { - when(track.getScenarios()).thenReturn(new HashMap<>()); - - assertEquals("", nagUnresolvedContacts(campaign)); - } - - @Test - void noUnresolvedScenarios() { - Map mockMap = new HashMap<>(); - mockMap.put(coordinates, scenarioNotDue); - - when(track.getScenarios()).thenReturn(mockMap); - when(scenarioNotDue.getCurrentState()).thenReturn(ScenarioState.COMPLETED); - - assertEquals("", nagUnresolvedContacts(campaign)); - } - - @Test - void noScenariosDue() { - when(track.getScenarios()).thenReturn(Collections.singletonMap(coordinates, scenarioNotDue)); - when(track.getDisplayableName()).thenReturn("Test Track"); - - assertEquals("", nagUnresolvedContacts(campaign)); - } - - @Test - void scenarioDue() { - when(track.getScenarios()).thenReturn(Collections.singletonMap(coordinates, scenarioDue)); - when(track.getDisplayableName()).thenReturn("Test Track"); - - assertEquals("Scenario Due, Test Track\n", nagUnresolvedContacts(campaign)); - } -} \ No newline at end of file diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/EndContractNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/EndContractNagLogicTest.java similarity index 91% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/EndContractNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/EndContractNagLogicTest.java index 89936462005..dbd0abdd3ec 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/EndContractNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/EndContractNagLogicTest.java @@ -16,10 +16,11 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.mission.Contract; +import mekhq.gui.dialog.nagDialogs.EndContractNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -27,7 +28,7 @@ import java.util.ArrayList; import java.util.List; -import static mekhq.gui.dialog.nagDialogs.EndContractNagDialog.isContractEnded; +import static mekhq.gui.dialog.nagDialogs.nagLogic.EndContractNagLogic.isContractEnded; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -37,7 +38,7 @@ * This class is a test class for the {@link EndContractNagDialog} class. * It contains test methods for various scenarios related to contract expiration. */ -public class EndContractNagDialogTest { +public class EndContractNagLogicTest { // Mock objects for the tests private Campaign campaign; private LocalDate today; @@ -49,13 +50,14 @@ public class EndContractNagDialogTest { */ @BeforeEach void init() { + System.setProperty("java.awt.headless", "true"); + // Initialize the mock objects campaign = mock(Campaign.class); today = LocalDate.now(); contract1 = mock(Contract.class); contract2 = mock(Contract.class); - // When the Campaign mock calls 'getLocalDate()' return today's date when(campaign.getLocalDate()).thenReturn(today); } @@ -96,4 +98,4 @@ void twoActiveContractsBothEndToday() { when(contract1.getEndingDate()).thenReturn(today); assertTrue(isContractEnded(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechsNagLogicTest.java similarity index 74% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechsNagLogicTest.java index 8664a38a28b..b2be355ebc6 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientAstechsNagLogicTest.java @@ -16,13 +16,14 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; +import mekhq.gui.dialog.nagDialogs.InsufficientAstechsNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static mekhq.gui.dialog.nagDialogs.InsufficientAstechsNagDialog.checkAstechsNeededCount; +import static mekhq.gui.dialog.nagDialogs.nagLogic.InsufficientAstechsNagLogic.hasAsTechsNeeded; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -33,8 +34,7 @@ * It tests the different combinations of Astech requirements and verifies the behavior of the * {@code checkAstechsNeededCount()} method. */ -class InsufficientAstechsNagDialogTest { - // Mock objects for the tests +class InsufficientAstechsNagLogicTest { private Campaign campaign; /** @@ -46,24 +46,21 @@ void init() { campaign = mock(Campaign.class); } - // In the following tests the checkAstechsNeededCount() method is called, and its response is - // checked against expected behavior - @Test - void noAstechsNeeded() { + void noAsTechsNeeded() { when(campaign.getAstechNeed()).thenReturn(0); - assertFalse(checkAstechsNeededCount(campaign)); + assertFalse(hasAsTechsNeeded(campaign)); } @Test - void oneAstechNeeded() { + void oneAsTechNeeded() { when(campaign.getAstechNeed()).thenReturn(1); - assertTrue(checkAstechsNeededCount(campaign)); + assertTrue(hasAsTechsNeeded(campaign)); // Updated assertion } @Test - void negativeAstechsNeeded() { + void negativeAsTechsNeeded() { when(campaign.getAstechNeed()).thenReturn(-1); - assertFalse(checkAstechsNeededCount(campaign)); + assertFalse(hasAsTechsNeeded(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientMedicsNagLogicTest.java similarity index 77% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientMedicsNagLogicTest.java index 00a3a9fdbda..f978e1bb017 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/InsufficientMedicsNagLogicTest.java @@ -16,25 +16,24 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static mekhq.gui.dialog.nagDialogs.InsufficientMedicsNagDialog.checkMedicsNeededCount; +import static mekhq.gui.dialog.nagDialogs.nagLogic.InsufficientMedicsNagLogic.hasMedicsNeeded; 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; /** - * This class contains test cases for the {@link InsufficientMedicsNagDialogTest} class. + * This class contains test cases for the {@link InsufficientMedicsNagLogicTest} class. * It tests the different combinations of Medic requirements and verifies the behavior of the * {@code checkMedicsNeededCount()} method. */ -class InsufficientMedicsNagDialogTest { - // Mock objects for the tests +class InsufficientMedicsNagLogicTest { private Campaign campaign; /** @@ -46,24 +45,21 @@ void init() { campaign = mock(Campaign.class); } - // In the following tests the checkMedicsNeededCount() method is called, and its response is - // checked against expected behavior - @Test void noMedicsNeeded() { when(campaign.getMedicsNeed()).thenReturn(0); - assertFalse(checkMedicsNeededCount(campaign)); + assertFalse(hasMedicsNeeded(campaign)); } @Test void oneMedicNeeded() { when(campaign.getMedicsNeed()).thenReturn(1); - assertTrue(checkMedicsNeededCount(campaign)); + assertTrue(hasMedicsNeeded(campaign)); } @Test void negativeMedicsNeeded() { when(campaign.getMedicsNeed()).thenReturn(-1); - assertFalse(checkMedicsNeededCount(campaign)); + assertFalse(hasMedicsNeeded(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/InvalidFactionNagLogicTest.java similarity index 58% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/InvalidFactionNagLogicTest.java index 5316f020ed6..c77728e60f0 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/InvalidFactionNagLogicTest.java @@ -16,18 +16,17 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.universe.Faction; +import mekhq.gui.dialog.nagDialogs.InvalidFactionNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.time.LocalDate; -import static mekhq.gui.dialog.nagDialogs.InvalidFactionNagDialog.federatedSunsSpecialHandler; -import static mekhq.gui.dialog.nagDialogs.InvalidFactionNagDialog.isFactionInvalid; -import static mekhq.gui.dialog.nagDialogs.InvalidFactionNagDialog.lyranAllianceSpecialHandler; +import static mekhq.gui.dialog.nagDialogs.nagLogic.InvalidFactionNagLogic.isFactionInvalid; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -40,7 +39,7 @@ * {@code isFactionInvalid()}, {@code lyranAllianceSpecialHandler()}, and * {@code federatedSunsSpecialHandler()} methods. */ -class InvalidFactionNagDialogTest { +class InvalidFactionNagLogicTest { // Mock objects for the tests private Campaign campaign; private Faction faction; @@ -82,49 +81,4 @@ public void invalidDate() { assertTrue(isFactionInvalid(campaign)); } - - // In the following tests, the lyranAllianceSpecialHandler() method is called, and its response - // is checked against expected behavior - - @Test - public void lyranAllianceSpecialHandlerInvalidDate() { - when(campaign.getLocalDate()).thenReturn(LocalDate.of(3067, 4, 19)); - assertTrue(lyranAllianceSpecialHandler(campaign)); - } - - @Test - public void lyranAllianceSpecialHandlerBeforeActiveDate() { - when(campaign.getLocalDate()).thenReturn(LocalDate.of(3040, 1, 17)); - assertFalse(lyranAllianceSpecialHandler(campaign)); - } - - @Test - public void lyranAllianceSpecialHandlerAfterInactiveDate() { - when(campaign.getLocalDate()).thenReturn(LocalDate.of(3067, 4, 21)); - assertFalse(lyranAllianceSpecialHandler(campaign)); - } - - // In the following tests, the federatedSunsSpecialHandler() method is called, and its response - // is checked against expected behavior - - @Test - public void federatedSunsSpecialHandlerInvalidDate() { - when(campaign.getLocalDate()).thenReturn(LocalDate.of(3057, 9, 17)); - - assertTrue(federatedSunsSpecialHandler(campaign)); - } - - @Test - public void federatedSunsSpecialHandlerBeforeActiveDate() { - when(campaign.getLocalDate()).thenReturn(LocalDate.of(3040, 1, 17)); - - assertFalse(federatedSunsSpecialHandler(campaign)); - } - - @Test - public void federatedSunsSpecialHandlerAfterInactiveDate() { - when(campaign.getLocalDate()).thenReturn(LocalDate.of(3057, 9, 19)); - - assertFalse(federatedSunsSpecialHandler(campaign)); - } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/NoCommanderNagLogicTest.java similarity index 86% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/NoCommanderNagLogicTest.java index 37f725ede81..7dee775f1c8 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/NoCommanderNagLogicTest.java @@ -16,14 +16,15 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.personnel.Person; +import mekhq.gui.dialog.nagDialogs.NoCommanderNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static mekhq.gui.dialog.nagDialogs.NoCommanderNagDialog.isCommanderMissing; +import static mekhq.gui.dialog.nagDialogs.nagLogic.NoCommanderNagLogic.hasNoCommander; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -33,7 +34,7 @@ * This class is a test class for the {@link NoCommanderNagDialog} class. * It contains test methods for various scenarios related to commander assignment. */ -class NoCommanderNagDialogTest { +class NoCommanderNagLogicTest { // Mock objects for the tests private Campaign campaign; private Person commander; @@ -57,12 +58,12 @@ void init() { @Test void commanderPresent() { when(campaign.getFlaggedCommander()).thenReturn(commander); - assertFalse(isCommanderMissing(campaign)); + assertFalse(hasNoCommander(campaign)); } @Test void commanderMissing() { when(campaign.getFlaggedCommander()).thenReturn(commanderNull); - assertTrue(isCommanderMissing(campaign)); + assertTrue(hasNoCommander(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/OutstandingScenariosNagLogicTest.java similarity index 57% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/OutstandingScenariosNagLogicTest.java index a7a5f8e6b5c..edee95e3ecc 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/OutstandingScenariosNagLogicTest.java @@ -16,19 +16,22 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; +import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.AtBScenario; +import mekhq.gui.dialog.nagDialogs.OutstandingScenariosNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; +import java.util.ResourceBundle; -import static mekhq.gui.dialog.nagDialogs.OutstandingScenariosNagDialog.checkForOutstandingScenarios; +import static mekhq.gui.dialog.nagDialogs.nagLogic.OutstandingScenariosNagLogic.hasOutStandingScenarios; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -38,93 +41,64 @@ * This class is a test class for the {@link OutstandingScenariosNagDialog} class. * It contains tests for various scenarios related to the {@code checkForOutstandingScenarios} method */ -class OutstandingScenariosNagDialogTest { - // Mock objects for the tests +class OutstandingScenariosNagLogicTest { private Campaign campaign; + private CampaignOptions campaignOptions; private AtBContract contract; private AtBScenario scenario1, scenario2; private LocalDate today; + protected final transient ResourceBundle resources = ResourceBundle.getBundle( + "mekhq.resources.GUI", MekHQ.getMHQOptions().getLocale()); + /** * Test setup for each test, runs before each test. * Initializes the mock objects and sets up the necessary mock behaviors. */ @BeforeEach void init() { - // Initialize the mock objects campaign = mock(Campaign.class); + campaignOptions = mock(CampaignOptions.class); contract = mock(AtBContract.class); scenario1 = mock(AtBScenario.class); scenario2 = mock(AtBScenario.class); - today = LocalDate.now(); + today = LocalDate.of(3025, 1, 1); - // When the Campaign mock calls 'getLocalDate()' return today's date - when(campaign.getLocalDate()).thenReturn(today); - } + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + when(campaignOptions.isUseStratCon()).thenReturn(false); - /** - * Initializes an {@link AtBContract} containing two instances of {@link AtBScenario}. - */ - private void initializeContractWithTwoScenarios() { when(campaign.getActiveAtBContracts(true)).thenReturn(List.of(contract)); + when(campaign.getLocalDate()).thenReturn(today); when(contract.getCurrentAtBScenarios()).thenReturn(List.of(scenario1, scenario2)); } - // In the following tests the checkForOutstandingScenarios() method is called, and its response - // is checked against expected behavior - - @Test - void noContracts() { - when(campaign.getActiveAtBContracts(true)).thenReturn(new ArrayList<>()); - - assertFalse(checkForOutstandingScenarios(campaign)); - } - - @Test - void noScenarios() { - when(campaign.getActiveAtBContracts(true)).thenReturn(List.of(contract)); - when(contract.getCurrentAtBScenarios()).thenReturn(new ArrayList<>()); - - assertFalse(checkForOutstandingScenarios(campaign)); - } - @Test - void noOutstandingScenarios() { - initializeContractWithTwoScenarios(); - - when(scenario1.getDate()).thenReturn(today.plusDays(1)); - when(scenario2.getDate()).thenReturn(today.plusDays(1)); + public void twoScenariosDueToday() { + when(scenario1.getDate()).thenReturn(today); + when(scenario2.getDate()).thenReturn(today); + when(scenario1.getHasTrack()).thenReturn(false); + when(scenario2.getHasTrack()).thenReturn(false); - assertFalse(checkForOutstandingScenarios(campaign)); + assertTrue(hasOutStandingScenarios(campaign)); } @Test - void oneOutstandingScenarioFirst() { - initializeContractWithTwoScenarios(); - + public void oneScenariosDueToday() { when(scenario1.getDate()).thenReturn(today); when(scenario2.getDate()).thenReturn(today.plusDays(1)); + when(scenario1.getHasTrack()).thenReturn(false); + when(scenario2.getHasTrack()).thenReturn(false); - assertTrue(checkForOutstandingScenarios(campaign)); + assertTrue(hasOutStandingScenarios(campaign)); } @Test - void oneOutstandingScenarioSecond() { - initializeContractWithTwoScenarios(); - + public void noScenariosDueToday() { when(scenario1.getDate()).thenReturn(today.plusDays(1)); - when(scenario2.getDate()).thenReturn(today); - - assertTrue(checkForOutstandingScenarios(campaign)); - } - - @Test - void twoOutstandingScenarios() { - initializeContractWithTwoScenarios(); - - when(scenario1.getDate()).thenReturn(today); - when(scenario2.getDate()).thenReturn(today); + when(scenario2.getDate()).thenReturn(today.plusDays(1)); + when(scenario1.getHasTrack()).thenReturn(false); + when(scenario2.getHasTrack()).thenReturn(false); - assertTrue(checkForOutstandingScenarios(campaign)); + assertFalse(hasOutStandingScenarios(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/PregnantCombatantNagLogicTest.java similarity index 87% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/PregnantCombatantNagLogicTest.java index 343aa7987dc..c8e188d82a2 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/PregnantCombatantNagLogicTest.java @@ -16,20 +16,21 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.force.Force; import mekhq.campaign.mission.Mission; import mekhq.campaign.personnel.Person; import mekhq.campaign.unit.Unit; +import mekhq.gui.dialog.nagDialogs.PregnantCombatantNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -import static mekhq.gui.dialog.nagDialogs.PregnantCombatantNagDialog.isPregnantCombatant; +import static mekhq.gui.dialog.nagDialogs.nagLogic.PregnantCombatantNagLogic.hasActivePregnantCombatant; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -39,7 +40,7 @@ * This class is a test class for the {@link PregnantCombatantNagDialog} class. * It contains tests for various scenarios related to the {@code isPregnantCombatant} method */ -class PregnantCombatantNagDialogTest { +class PregnantCombatantNagLogicTest { // Mock objects for the tests private Campaign campaign; private Mission mission; @@ -60,6 +61,7 @@ void init() { personPregnant = mock(Person.class); unit = mock(Unit.class); + // Stubs when(personNotPregnant.isPregnant()).thenReturn(false); when(personPregnant.isPregnant()).thenReturn(true); @@ -71,7 +73,7 @@ void init() { @Test void noActiveMission() { when(campaign.getActiveMissions(false)).thenReturn(new ArrayList<>()); - assertFalse(isPregnantCombatant(campaign)); + assertFalse(hasActivePregnantCombatant(campaign)); } @Test @@ -79,7 +81,7 @@ void activeMissionsNoPregnancy() { when(campaign.getActiveMissions(false)).thenReturn(List.of(mission)); when(campaign.getActivePersonnel()).thenReturn(List.of(personNotPregnant)); - assertFalse(isPregnantCombatant(campaign)); + assertFalse(hasActivePregnantCombatant(campaign)); } @Test @@ -89,7 +91,7 @@ void activeMissionsPregnancyNoUnit() { when(personPregnant.getUnit()).thenReturn(null); - assertFalse(isPregnantCombatant(campaign)); + assertFalse(hasActivePregnantCombatant(campaign)); } @Test @@ -100,7 +102,7 @@ void activeMissionsPregnancyNoForce() { when(personPregnant.getUnit()).thenReturn(unit); when(unit.getForceId()).thenReturn(Force.FORCE_NONE); - assertFalse(isPregnantCombatant(campaign)); + assertFalse(hasActivePregnantCombatant(campaign)); } @Test @@ -111,6 +113,6 @@ void activeMissionsPregnancyYesUnitYesForce() { when(personPregnant.getUnit()).thenReturn(unit); when(unit.getForceId()).thenReturn(1); - assertTrue(isPregnantCombatant(campaign)); + assertTrue(hasActivePregnantCombatant(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PrisonersNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/PrisonersNagLogicTest.java similarity index 89% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PrisonersNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/PrisonersNagLogicTest.java index 7e8af2e374c..9a2829ae36b 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PrisonersNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/PrisonersNagLogicTest.java @@ -16,17 +16,18 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.personnel.Person; +import mekhq.gui.dialog.nagDialogs.PrisonersNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -import static mekhq.gui.dialog.nagDialogs.PrisonersNagDialog.hasPrisoners; +import static mekhq.gui.dialog.nagDialogs.nagLogic.PrisonersNagLogic.hasPrisoners; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -36,7 +37,7 @@ * This class is a test class for the {@link PrisonersNagDialog} class. * It contains tests for various scenarios related to the {@code hasPrisoners} method */ -class PrisonersNagDialogTest { +class PrisonersNagLogicTest { // Mock objects for the tests private Campaign campaign; @@ -50,9 +51,6 @@ void init() { campaign = mock(Campaign.class); } - // In the following tests the hasPrisoners() method is called, and its response is checked - // against expected behavior - @Test void activeContract() { when(campaign.hasActiveContract()).thenReturn(true); @@ -77,4 +75,4 @@ void noActiveContractPrisoners() { assertTrue(hasPrisoners(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/ShortDeploymentNagLogicTest.java similarity index 81% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/ShortDeploymentNagLogicTest.java index 25ab03cd9b4..553d5735029 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/ShortDeploymentNagLogicTest.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.CurrentLocation; @@ -28,7 +28,7 @@ import java.util.ArrayList; import java.util.List; -import static mekhq.gui.dialog.nagDialogs.ShortDeploymentNagDialog.checkDeploymentRequirementsMet; +import static mekhq.gui.dialog.nagDialogs.nagLogic.DeploymentShortfallNagLogic.hasDeploymentShortfall; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -39,11 +39,9 @@ * It contains tests for various scenarios related to the {@code checkDeploymentRequirementsMet} * method */ -public class ShortDeploymentNagDialogTest { +public class ShortDeploymentNagLogicTest { // Mock objects for the tests private Campaign campaign; - // I know 'location' can be converted to a local variable, but it makes sense to keep all the - // mock objects in one place private CurrentLocation location; private LocalDate monday, sunday; private AtBContract contract; @@ -68,14 +66,11 @@ void init() { when(campaign.getLocation()).thenReturn(location); } - // In the following tests the checkDeploymentRequirementsMet() method is called, and its - // response is checked against expected behavior - @Test void notOnPlanet() { when(campaign.getLocation().isOnPlanet()).thenReturn(false); - assertFalse(checkDeploymentRequirementsMet(campaign)); + assertFalse(hasDeploymentShortfall(campaign)); } @Test @@ -83,7 +78,7 @@ void notSunday() { when(campaign.getLocation().isOnPlanet()).thenReturn(true); when(campaign.getLocalDate()).thenReturn(monday); - assertFalse(checkDeploymentRequirementsMet(campaign)); + assertFalse(hasDeploymentShortfall(campaign)); } @Test @@ -93,7 +88,7 @@ void noContract() { when(campaign.getActiveAtBContracts()).thenReturn(new ArrayList<>()); - assertFalse(checkDeploymentRequirementsMet(campaign)); + assertFalse(hasDeploymentShortfall(campaign)); } @Test @@ -104,7 +99,7 @@ void noDeploymentDeficit() { when(campaign.getActiveAtBContracts()).thenReturn(List.of(contract)); when(campaign.getDeploymentDeficit(contract)).thenReturn(0); - assertFalse(checkDeploymentRequirementsMet(campaign)); + assertFalse(hasDeploymentShortfall(campaign)); } @Test @@ -115,7 +110,7 @@ void negativeDeploymentDeficit() { when(campaign.getActiveAtBContracts()).thenReturn(List.of(contract)); when(campaign.getDeploymentDeficit(contract)).thenReturn(-3); - assertFalse(checkDeploymentRequirementsMet(campaign)); + assertFalse(hasDeploymentShortfall(campaign)); } @Test @@ -126,6 +121,6 @@ void positiveDeploymentDeficit() { when(campaign.getActiveAtBContracts()).thenReturn(List.of(contract)); when(campaign.getDeploymentDeficit(contract)).thenReturn(1); - assertTrue(checkDeploymentRequirementsMet(campaign)); + assertTrue(hasDeploymentShortfall(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordExpensesNagLogicTest.java similarity index 87% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordExpensesNagLogicTest.java index f74dce5be05..399d6318fef 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordExpensesNagLogicTest.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.CampaignOptions; @@ -26,10 +26,11 @@ import mekhq.campaign.finances.FinancialReport; import mekhq.campaign.finances.Money; import mekhq.campaign.unit.Unit; +import mekhq.gui.dialog.nagDialogs.UnableToAffordExpensesNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static mekhq.gui.dialog.nagDialogs.UnableToAffordExpensesNagDialog.isUnableToAffordExpenses; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnableToAffordExpensesNagLogic.unableToAffordExpenses; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -38,7 +39,7 @@ * This class is a test class for the {@link UnableToAffordExpensesNagDialog} class. * It contains tests for various scenarios related to the {@code isUnableToAffordExpenses} method */ -class UnableToAffordExpensesNagDialogTest { +class UnableToAffordExpensesNagLogicTest { // Mock objects for the tests // I know some of these can be converted to a local variable, but it makes sense to keep all the // mock objects in one place @@ -77,15 +78,12 @@ void init() { when(campaign.getCampaignOptions()).thenReturn(campaignOptions); } - // In the following tests the isUnableToAffordExpenses() method is called, and its response is - // checked against expected behavior - @Test void canAffordExpenses() { when(campaign.getFunds()).thenReturn(Money.of(2)); when(report.getMonthlyExpenses()).thenReturn(Money.of(1)); - assertFalse(isUnableToAffordExpenses(campaign)); + assertFalse(unableToAffordExpenses(campaign)); } @Test @@ -93,6 +91,6 @@ void cannotAffordExpenses() { when(campaign.getFunds()).thenReturn(Money.of(1)); when(report.getMonthlyExpenses()).thenReturn(Money.of(2)); - assertFalse(isUnableToAffordExpenses(campaign)); + assertFalse(unableToAffordExpenses(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordJumpNagLogicTest.java similarity index 76% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordJumpNagLogicTest.java index 810ea982e53..42c55a10849 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordJumpNagLogicTest.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.CampaignOptions; @@ -24,11 +24,11 @@ import mekhq.campaign.JumpPath; import mekhq.campaign.finances.Money; import mekhq.campaign.universe.PlanetarySystem; +import mekhq.gui.dialog.nagDialogs.UnableToAffordJumpNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static mekhq.gui.dialog.nagDialogs.UnableToAffordJumpNagDialog.getNextJumpCost; -import static mekhq.gui.dialog.nagDialogs.UnableToAffordJumpNagDialog.isUnableToAffordNextJump; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnableToAffordJumpNagLogic.unableToAffordNextJump; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -38,7 +38,7 @@ * This class is a test class for the {@link UnableToAffordJumpNagDialog} class. * It contains tests for various scenarios related to the {@code isUnableToAffordNextJump} method */ -class UnableToAffordJumpNagDialogTest { +class UnableToAffordJumpNagLogicTest { // Mock objects for the tests private Campaign campaign; @@ -53,36 +53,31 @@ void init() { CampaignOptions options = mock(CampaignOptions.class); CurrentLocation location = mock(CurrentLocation.class); JumpPath jumpPath = mock(JumpPath.class); - - - // Stubs - when(campaign.getCampaignOptions()).thenReturn(options); - PlanetarySystem originSystem = mock(PlanetarySystem.class); PlanetarySystem destinationSystem = mock(PlanetarySystem.class); - jumpPath.addSystem(destinationSystem); + when(campaign.getCampaignOptions()).thenReturn(options); + when(campaign.getFunds()).thenReturn(Money.of(1)); + when(campaign.getLocation()).thenReturn(location); when(location.getCurrentSystem()).thenReturn(originSystem); when(location.getJumpPath()).thenReturn(jumpPath); - } + when(jumpPath.getLastSystem()).thenReturn(destinationSystem); - // In the following tests the canAffordNextJump() method is called, and its response is checked - // against expected behavior + when(options.isEquipmentContractBase()).thenReturn(false); + } @Test void canAffordNextJump() { - when(campaign.getFunds()).thenReturn(Money.of(5)); - when(getNextJumpCost(campaign)).thenReturn(Money.of(1)); + when(campaign.calculateCostPerJump(true, false)).thenReturn(Money.of(0)); - assertFalse(isUnableToAffordNextJump(campaign)); + assertFalse(unableToAffordNextJump(campaign)); } @Test void cannotAffordNextJump() { - when(campaign.getFunds()).thenReturn(Money.of(1)); - when(getNextJumpCost(campaign)).thenReturn(Money.of(5)); + when(campaign.calculateCostPerJump(true, false)).thenReturn(Money.of(2)); - assertTrue(isUnableToAffordNextJump(campaign)); + assertTrue(unableToAffordNextJump(campaign)); } } diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordLoanPaymentNagLogicTest.java similarity index 67% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordLoanPaymentNagLogicTest.java index 0bea2932fa0..53167eb408b 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnableToAffordLoanPaymentNagLogicTest.java @@ -16,22 +16,20 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.finances.Finances; import mekhq.campaign.finances.Loan; import mekhq.campaign.finances.Money; +import mekhq.gui.dialog.nagDialogs.UnableToAffordLoanPaymentNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; -import static mekhq.gui.dialog.nagDialogs.UnableToAffordLoanPaymentNagDialog.getTotalPaymentsDue; -import static mekhq.gui.dialog.nagDialogs.UnableToAffordLoanPaymentNagDialog.isUnableToAffordLoanPayment; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnableToAffordLoanPaymentNag.unableToAffordLoans; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -42,7 +40,7 @@ * It contains tests for various scenarios related to the {@code getTotalPaymentsDue} and * {@code isUnableToAffordLoanPayment} methods */ -class UnableToAffordLoanPaymentNagDialogTest { +class UnableToAffordLoanPaymentNagLogicTest { // Mock objects for the tests private Campaign campaign; private LocalDate today; @@ -74,64 +72,32 @@ void init() { * Initializes the loans with the specified number of days till the next payment. * * @param daysTillFirstLoan The number of days till the next payment for the first loan. - * @param daysTillSecondLoan The number of days till the next payment for the second loan. */ - private void initializeLoans(int daysTillFirstLoan, int daysTillSecondLoan) { + private void initializeLoans(int daysTillFirstLoan) { when(finances.getLoans()).thenReturn(List.of(firstLoan, secondLoan)); when(firstLoan.getNextPayment()).thenReturn(today.plusDays(daysTillFirstLoan)); - when(secondLoan.getNextPayment()).thenReturn(today.plusDays(daysTillSecondLoan)); + when(secondLoan.getNextPayment()).thenReturn(today.plusDays(1)); } // In the following tests the getTotalPaymentsDue() method is called, and its response // is checked against expected behavior - @Test - void noLoans() { - when(finances.getLoans()).thenReturn(new ArrayList<>()); - - assertEquals(Money.zero(), getTotalPaymentsDue(campaign)); - } - - @Test - void noLoanDueTomorrow() { - initializeLoans(2, 2); - - assertEquals(Money.zero(), getTotalPaymentsDue(campaign)); - } - - @Test - void oneLoanDueTomorrow() { - initializeLoans(2, 1); - - assertEquals(Money.of(5), getTotalPaymentsDue(campaign)); - } - - @Test - void twoLoansDueTomorrow() { - initializeLoans(1, 1); - - assertEquals(Money.of(10), getTotalPaymentsDue(campaign)); - } - - // In the following tests the canAffordLoans() method is called, and its response is checked - // against expected behavior - @Test void canAffordLoans() { - initializeLoans(2, 1); + initializeLoans(2); when(campaign.getFunds()).thenReturn(Money.of(10)); - assertFalse(isUnableToAffordLoanPayment(campaign)); + assertFalse(unableToAffordLoans(campaign)); } @Test void cannotAffordLoans() { - initializeLoans(1, 1); + initializeLoans(1); when(campaign.getFunds()).thenReturn(Money.of(5)); - assertTrue(isUnableToAffordLoanPayment(campaign)); + assertTrue(unableToAffordLoans(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnmaintainedUnitsNagLogicTest.java similarity index 81% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnmaintainedUnitsNagLogicTest.java index 63b2911e6ea..bf104dba857 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnmaintainedUnitsNagLogicTest.java @@ -16,17 +16,18 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import mekhq.campaign.Campaign; import mekhq.campaign.Hangar; import mekhq.campaign.unit.Unit; +import mekhq.gui.dialog.nagDialogs.UnmaintainedUnitsNagDialog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.List; -import static mekhq.gui.dialog.nagDialogs.UnmaintainedUnitsNagDialog.checkHanger; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnmaintainedUnitsNagLogic.campaignHasUnmaintainedUnits; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -36,7 +37,7 @@ * This class is a test class for the {@link UnmaintainedUnitsNagDialog} class. * It tests the different combinations of unit states and verifies the behavior of the {@code checkHanger()} method. */ -class UnmaintainedUnitsNagDialogTest { +class UnmaintainedUnitsNagLogicTest { // Mock objects for the tests private Campaign campaign; private Hangar hangar; @@ -66,7 +67,8 @@ void init() { * @param unit2Unmaintained A boolean indicating whether the second unit is unmaintained. * @param unit2Salvage A boolean indicating whether the second unit is salvage. */ - private void initializeUnits(boolean unit1Unmaintained, boolean unit1Salvage, boolean unit2Unmaintained, boolean unit2Salvage) { + private void initializeUnits(boolean unit1Unmaintained, boolean unit1Salvage, + boolean unit2Unmaintained, boolean unit2Salvage) { when(mockUnit1.isUnmaintained()).thenReturn(unit1Unmaintained); when(mockUnit1.isSalvage()).thenReturn(unit1Salvage); @@ -83,60 +85,60 @@ private void initializeUnits(boolean unit1Unmaintained, boolean unit1Salvage, bo @Test void unmaintainedUnitExistsUnit1() { initializeUnits(true, false, false, false); - assertTrue(checkHanger(campaign)); + assertTrue(campaignHasUnmaintainedUnits(campaign)); } @Test void unmaintainedUnitExistsUnit2() { initializeUnits(false, false, true, false); - assertTrue(checkHanger(campaign)); + assertTrue(campaignHasUnmaintainedUnits(campaign)); } @Test void unmaintainedUnitExistsButSalvageUnit1() { initializeUnits(true, true, true, false); - assertTrue(checkHanger(campaign)); + assertTrue(campaignHasUnmaintainedUnits(campaign)); } @Test void unmaintainedUnitExistsButSalvageUnit2() { initializeUnits(true, false, true, true); - assertTrue(checkHanger(campaign)); + assertTrue(campaignHasUnmaintainedUnits(campaign)); } @Test void unmaintainedUnitExistsButSalvageMixed() { initializeUnits(false, true, true, false); - assertTrue(checkHanger(campaign)); + assertTrue(campaignHasUnmaintainedUnits(campaign)); } @Test void noUnmaintainedUnitExistsNoSalvage() { initializeUnits(false, false, false, false); - assertFalse(checkHanger(campaign)); + assertFalse(campaignHasUnmaintainedUnits(campaign)); } @Test void noUnmaintainedUnitExistsAllSalvage() { initializeUnits(false, true, false, true); - assertFalse(checkHanger(campaign)); + assertFalse(campaignHasUnmaintainedUnits(campaign)); } @Test void noUnmaintainedUnitExistsButSalvageUnit1() { initializeUnits(false, true, false, false); - assertFalse(checkHanger(campaign)); + assertFalse(campaignHasUnmaintainedUnits(campaign)); } @Test void noUnmaintainedUnitExistsButSalvageUnit2() { initializeUnits(false, false, false, true); - assertFalse(checkHanger(campaign)); + assertFalse(campaignHasUnmaintainedUnits(campaign)); } @Test void noUnmaintainedUnitExistsButSalvageMixed() { initializeUnits(false, true, false, false); - assertFalse(checkHanger(campaign)); + assertFalse(campaignHasUnmaintainedUnits(campaign)); } } diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnresolvedStratConContactsNagLogicTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnresolvedStratConContactsNagLogicTest.java new file mode 100644 index 00000000000..d8bbaea41cb --- /dev/null +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UnresolvedStratConContactsNagLogicTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 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.gui.dialog.nagDialogs.nagLogic; + +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.stratcon.StratconCampaignState; +import mekhq.campaign.stratcon.StratconCoords; +import mekhq.campaign.stratcon.StratconScenario; +import mekhq.campaign.stratcon.StratconScenario.ScenarioState; +import mekhq.campaign.stratcon.StratconTrackState; +import mekhq.gui.dialog.nagDialogs.UnresolvedStratConContactsNagDialog; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + +import static mekhq.gui.dialog.nagDialogs.nagLogic.UnresolvedStratConContactsNagLogic.hasUnresolvedContacts; +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; + +/** + * This class is a test class for the {@link UnresolvedStratConContactsNagDialog} class. + * It contains tests for various scenarios related to the {@code nagUnresolvedContacts} method + */ +public class UnresolvedStratConContactsNagLogicTest { + private Campaign campaign; + private CampaignOptions campaignOptions; + private AtBContract contract; + private StratconCampaignState stratconCampaignState; + private StratconTrackState track; + private LocalDate today; + private StratconScenario scenario1, scenario2; + + /** + * Test setup for each test, runs before each test. + * Initializes the mock objects and sets up the necessary mock behaviors. + */ + @BeforeEach + void init() { + campaign = mock(Campaign.class); + campaignOptions = mock(CampaignOptions.class); + today = LocalDate.of(3025, 1, 1); + contract = mock(AtBContract.class); + stratconCampaignState = mock(StratconCampaignState.class); + track = mock(StratconTrackState.class); + + StratconCoords mockCoordinates1 = mock(StratconCoords.class); + StratconCoords mockCoordinates2 = mock(StratconCoords.class); + + when(mockCoordinates1.toBTString()).thenReturn("MockCoordinate1"); + when(mockCoordinates2.toBTString()).thenReturn("MockCoordinate2"); + + scenario1 = mock(StratconScenario.class); + scenario2 = mock(StratconScenario.class); + + when(scenario1.getCoords()).thenReturn(mockCoordinates1); + when(scenario2.getCoords()).thenReturn(mockCoordinates2); + + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + when(campaignOptions.isUseStratCon()).thenReturn(true); + + when(campaign.getActiveAtBContracts()).thenReturn(List.of(contract)); + when(contract.getStratconCampaignState()).thenReturn(stratconCampaignState); + when(stratconCampaignState.getTracks()).thenReturn(List.of(track)); + + when(track.getScenarios()).thenReturn(Map.of(mockCoordinates1, scenario1, mockCoordinates2, scenario2)); + when(scenario1.getCurrentState()).thenReturn(ScenarioState.UNRESOLVED); + when(scenario2.getCurrentState()).thenReturn(ScenarioState.UNRESOLVED); + when(campaign.getLocalDate()).thenReturn(today); + } + + @Test + public void noScenariosDue() { + when(scenario1.getDeploymentDate()).thenReturn(today.plusDays(1)); + when(scenario2.getDeploymentDate()).thenReturn(today.plusDays(1)); + + assertFalse(hasUnresolvedContacts(campaign)); + } + + @Test + public void scenariosDue() { + when(scenario1.getDeploymentDate()).thenReturn(today.plusDays(1)); + when(scenario2.getDeploymentDate()).thenReturn(today); + + assertTrue(hasUnresolvedContacts(campaign)); + } +} diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UntreatedPersonnelNagLogicTest.java similarity index 84% rename from MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialogTest.java rename to MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UntreatedPersonnelNagLogicTest.java index f27513b2f11..3dd99a20477 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/nagLogic/UntreatedPersonnelNagLogicTest.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with MekHQ. If not, see . */ -package mekhq.gui.dialog.nagDialogs; +package mekhq.gui.dialog.nagDialogs.nagLogic; import megamek.common.EquipmentType; import megamek.logging.MMLogger; @@ -28,11 +28,12 @@ import mekhq.campaign.personnel.enums.PrisonerStatus; import mekhq.campaign.personnel.ranks.Ranks; import mekhq.campaign.universe.Systems; +import mekhq.gui.dialog.nagDialogs.UntreatedPersonnelNagDialog; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static mekhq.gui.dialog.nagDialogs.UntreatedPersonnelNagDialog.isUntreatedInjury; +import static mekhq.gui.dialog.nagDialogs.nagLogic.UntreatedPersonnelNagLogic.campaignHasUntreatedInjuries; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -41,7 +42,7 @@ * It tests the different combinations of untreated personnel and verifies the behavior of the * {@code isUntreatedInjury()} method. */ -class UntreatedPersonnelNagDialogTest { +class UntreatedPersonnelNagLogicTest { Campaign campaign; Person person; @@ -56,7 +57,7 @@ public static void setup() { try { Systems.setInstance(Systems.loadDefault()); } catch (Exception ex) { - MMLogger.create(UntreatedPersonnelNagDialogTest.class).error("", ex); + MMLogger.create(UntreatedPersonnelNagLogicTest.class).error("", ex); } } @@ -78,34 +79,34 @@ public void init() { public void isUntreatedInjuryIncludesNonPrisonersTest() { person.setPrisonerStatus(campaign, PrisonerStatus.FREE, false); campaign.importPerson(person); - assertTrue(isUntreatedInjury(campaign)); + assertTrue(campaignHasUntreatedInjuries(campaign)); } @Test public void isUntreatedInjuryExcludesPrisonersTest() { person.setPrisonerStatus(campaign, PrisonerStatus.PRISONER, false); campaign.importPerson(person); - assertFalse(isUntreatedInjury(campaign)); + assertFalse(campaignHasUntreatedInjuries(campaign)); } @Test public void isUntreatedInjuryExcludesPrisonerDefectorsTest() { person.setPrisonerStatus(campaign, PrisonerStatus.PRISONER_DEFECTOR, false); campaign.importPerson(person); - assertFalse(isUntreatedInjury(campaign)); + assertFalse(campaignHasUntreatedInjuries(campaign)); } @Test public void isUntreatedInjuryExcludesBondsmenTest() { person.setPrisonerStatus(campaign, PrisonerStatus.BONDSMAN, false); campaign.importPerson(person); - assertTrue(isUntreatedInjury(campaign)); + assertTrue(campaignHasUntreatedInjuries(campaign)); } @Test public void isUntreatedInjuryExcludesInactivePersonnelTest() { person.setStatus(PersonnelStatus.AWOL); campaign.importPerson(person); - assertFalse(isUntreatedInjury(campaign)); + assertFalse(campaignHasUntreatedInjuries(campaign)); } -} \ No newline at end of file +} diff --git a/MekHQ/unittests/mekhq/gui/model/UnitTableModelTest.java b/MekHQ/unittests/mekhq/gui/model/UnitTableModelTest.java index 3da8b24d5e7..966a5311327 100644 --- a/MekHQ/unittests/mekhq/gui/model/UnitTableModelTest.java +++ b/MekHQ/unittests/mekhq/gui/model/UnitTableModelTest.java @@ -28,8 +28,8 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - import java.util.Collections; - import java.util.List; + import java.util.*; + import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -66,7 +66,12 @@ private void setCrew(int driverCount, int totalDriverNeeds, boolean hasNavigator, Entity entity, String expected) { List drivers = Collections.nCopies(driverCount, crewMember); - List gunners = Collections.nCopies(gunnerCount, crewMember); + + Set gunners = new HashSet<>(); + for(int i = 0; i < gunnerCount; i++){ + gunners.add(mock(Person.class)); + } + List crew = Collections.nCopies(crewCount + drivers.size() + gunners.size() + (hasNavigator ? 1 : 0), crewMember); @@ -134,4 +139,4 @@ public void noAssignedCrew() { false, mock(Jumpship.class), "Drivers: 0/1
    Gunners: 0/1
    Crew: 0/1
    Navigator: 0/1"); } -} \ No newline at end of file +} diff --git a/MekHQ/userdata/data/universe/ranks.xml b/MekHQ/userdata/data/universe/ranks.xml index 26b16db0ed5..f8d8b8591d3 100644 --- a/MekHQ/userdata/data/universe/ranks.xml +++ b/MekHQ/userdata/data/universe/ranks.xml @@ -1,3 +1,3 @@ - + diff --git a/build.gradle b/build.gradle index 15c5fa26a9f..660fb90841a 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ allprojects { subprojects { group = 'org.megamek' - version = '0.50.02-SNAPSHOT' + version = '0.50.03-SNAPSHOT' } // A properties_local.gradle file can be used to override any of the above options. For instance, diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c4586c843d1d3e9090525f1898cde..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 3990 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 From d8121c84f4c631b8e7bd488385fd137263f1acad Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 12:05:26 -0700 Subject: [PATCH 02/11] Readded linkedScenarioProcessing to resolution, fixed rebase errors --- MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java | 4 +++- MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 825465a148f..074cb04295f 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -40,6 +40,7 @@ import mekhq.campaign.mission.enums.AtBMoraleLevel; import mekhq.campaign.mission.enums.CombatRole; import mekhq.campaign.mission.enums.ContractCommandRights; +import mekhq.campaign.mission.enums.ScenarioStatus; import mekhq.campaign.mission.resupplyAndCaches.StarLeagueCache; import mekhq.campaign.mission.resupplyAndCaches.StarLeagueCache.CacheType; import mekhq.campaign.personnel.Person; @@ -1627,7 +1628,7 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc ScenarioTemplate scenarioTemplate = getInterceptionScenarioTemplate(force, campaign); - generateReinforcementInterceptionScenario(campaign, contract, track, scenarioTemplate, force); + generateReinforcementInterceptionScenario(campaign, scenario, contract, track, scenarioTemplate, force); return INTERCEPTED; } @@ -2916,6 +2917,7 @@ public static boolean processIgnoredScenario(StratconScenario scenario, Stratcon public void startup() { MekHQ.registerHandler(this); } + /** * Event handler for the new day event. diff --git a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java index 77c98bfef8c..727301a8a0e 100644 --- a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java @@ -1617,6 +1617,12 @@ private void finish() { } StratconRulesManager.processScenarioCompletion(tracker); + + if (reinforcementsSent || tracker.getScenario().getStatus().isVictory()) { + StratconRulesManager.linkedScenerioProcessing(tracker, forces); + } + + aborted = false; this.setVisible(false); From f2aac31646d278ca8bbc40192b52c05acb37ea1a Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 13:04:18 -0700 Subject: [PATCH 03/11] formatting and rebase fixes --- MekHQ/src/mekhq/campaign/Campaign.java | 1330 ++++++----------- .../campaign/ResolveScenarioTracker.java | 5 - .../AtbMonthlyContractMarket.java | 87 +- .../stratcon/StratconRulesManager.java | 613 +++----- 4 files changed, 665 insertions(+), 1370 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index a744a0583ff..4176bcb7f2b 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -306,7 +306,7 @@ public class Campaign implements ITechManager { private BehaviorSettings autoResolveBehaviorSettings; private List automatedMothballUnits; - //options relating to parts in use and restock + //options relating to parts in use and restock private boolean ignoreMothballed; private boolean topUpWeekly; private PartQuality ignoreSparesUnderQuality; @@ -323,8 +323,7 @@ public enum AdministratorSpecialization { COMMAND, LOGISTICS, TRANSPORT, HR } - private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Campaign", - MekHQ.getMHQOptions().getLocale()); + private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Campaign", MekHQ.getMHQOptions().getLocale()); /** * This is used to determine if the player has an active AtB Contract, and is @@ -395,7 +394,7 @@ public Campaign() { autoResolveBehaviorSettings = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; automatedMothballUnits = new ArrayList<>(); topUpWeekly = false; - ignoreMothballed = false; + ignoreMothballed = false; ignoreSparesUnderQuality = QUALITY_A; } @@ -465,9 +464,7 @@ public Era getEra() { } public String getTitle() { - return getName() + " (" + getFaction().getFullName(getGameYear()) + ')' + " - " - + MekHQ.getMHQOptions().getLongDisplayFormattedDate(getLocalDate()) - + " (" + getEra() + ')'; + return getName() + " (" + getFaction().getFullName(getGameYear()) + ')' + " - " + MekHQ.getMHQOptions().getLongDisplayFormattedDate(getLocalDate()) + " (" + getEra() + ')'; } public LocalDate getLocalDate() { @@ -588,9 +585,7 @@ public ArrayList getAllCombatTeams() { // without needing to directly include the code here, too. combatTeams = getCombatTeamsTable(); - return combatTeams.values().stream() - .filter(l -> forceIds.containsKey(l.getForceId())) - .collect(Collectors.toCollection(ArrayList::new)); + return combatTeams.values().stream().filter(l -> forceIds.containsKey(l.getForceId())).collect(Collectors.toCollection(ArrayList::new)); } public void setShoppingList(ShoppingList sl) { @@ -748,6 +743,7 @@ public AtBConfiguration getAtBConfig() { } // region Ship Search + /** * Sets the date a ship search was started, or null if no search is in progress. */ @@ -805,13 +801,10 @@ private void processShipSearch() { } StringBuilder report = new StringBuilder(); - if (getFinances().debit(TransactionType.UNIT_PURCHASE, getLocalDate(), - getAtBConfig().shipSearchCostPerWeek(), "Ship Search")) { - report.append(getAtBConfig().shipSearchCostPerWeek().toAmountAndSymbolString()) - .append(" deducted for ship search."); + if (getFinances().debit(TransactionType.UNIT_PURCHASE, getLocalDate(), getAtBConfig().shipSearchCostPerWeek(), "Ship Search")) { + report.append(getAtBConfig().shipSearchCostPerWeek().toAmountAndSymbolString()).append(" deducted for ship search."); } else { - addReport("Insufficient funds for ship search."); + addReport("Insufficient funds for ship search."); setShipSearchStart(null); return; } @@ -821,14 +814,12 @@ private void processShipSearch() { int roll = Compute.d6(2); TargetRoll target = getAtBConfig().shipSearchTargetRoll(shipSearchType, this); setShipSearchStart(null); - report.append("
    Ship search target: ").append(target.getValueAsString()).append(" roll: ") - .append(roll); + report.append("
    Ship search target: ").append(target.getValueAsString()).append(" roll: ").append(roll); // TODO : mos zero should make ship available on retainer if (roll >= target.getValue()) { report.append("
    Search successful. "); - MekSummary ms = getUnitGenerator().generate(getFactionCode(), shipSearchType, -1, - getGameYear(), getAtBUnitRatingMod()); + MekSummary ms = getUnitGenerator().generate(getFactionCode(), shipSearchType, -1, getGameYear(), getAtBUnitRatingMod()); if (ms == null) { ms = getAtBConfig().findShip(shipSearchType); @@ -837,13 +828,9 @@ private void processShipSearch() { if (ms != null) { setShipSearchResult(ms.getName()); setShipSearchExpiration(getLocalDate().plusDays(31)); - report.append(getShipSearchResult()).append(" is available for purchase for ") - .append(Money.of(ms.getCost()).toAmountAndSymbolString()) - .append(" until ") - .append(MekHQ.getMHQOptions().getDisplayFormattedDate(getShipSearchExpiration())); + report.append(getShipSearchResult()).append(" is available for purchase for ").append(Money.of(ms.getCost()).toAmountAndSymbolString()).append(" until ").append(MekHQ.getMHQOptions().getDisplayFormattedDate(getShipSearchExpiration())); } else { - report.append(" Could not determine ship type."); + report.append(" Could not determine ship type."); } } else { report.append("
    Ship search unsuccessful."); @@ -862,8 +849,7 @@ public void purchaseShipSearchResult() { Money cost = Money.of(ms.getCost()); if (getFunds().isLessThan(cost)) { - addReport(" You cannot afford this unit. Transaction cancelled."); + addReport(" You cannot afford this unit. Transaction cancelled."); return; } @@ -878,8 +864,7 @@ public void purchaseShipSearchResult() { Entity en = mekFileParser.getEntity(); - int transitDays = getCampaignOptions().isInstantUnitMarketDelivery() ? 0 - : calculatePartTransitTime(Compute.d6(2) - 2); + int transitDays = getCampaignOptions().isInstantUnitMarketDelivery() ? 0 : calculatePartTransitTime(Compute.d6(2) - 2); getFinances().debit(TransactionType.UNIT_PURCHASE, getLocalDate(), cost, "Purchased " + en.getShortName()); PartQuality quality = PartQuality.QUALITY_D; @@ -891,8 +876,7 @@ public void purchaseShipSearchResult() { addNewUnit(en, true, transitDays, quality); if (!getCampaignOptions().isInstantUnitMarketDelivery()) { - addReport("Unit will be delivered in " + transitDays + " days."); + addReport("Unit will be delivered in " + transitDays + " days."); } setShipSearchResult(null); setShipSearchExpiration(null); @@ -905,7 +889,7 @@ public void purchaseShipSearchResult() { * @param totalPayout The total retirement payout. * @param unitAssignments List of unit assignments. * @return False if there were payments AND they were unable to be processed, - * true otherwise. + * true otherwise. */ public boolean applyRetirement(Money totalPayout, Map unitAssignments) { turnoverRetirementInformation.clear(); @@ -921,8 +905,7 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment if (!person.getPermanentInjuries().isEmpty()) { person.changeStatus(this, getLocalDate(), PersonnelStatus.RETIRED); } - if (isBreakingContract(person, getLocalDate(), - getCampaignOptions().getServiceContractDuration())) { + if (isBreakingContract(person, getLocalDate(), getCampaignOptions().getServiceContractDuration())) { if (!getActiveContracts().isEmpty()) { int roll = Compute.randomInt(20); @@ -940,8 +923,7 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment } if (!person.getStatus().isActive()) { - turnoverRetirementInformation.add( - String.format(person.getStatus().getReportText(), person.getHyperlinkedFullTitle())); + turnoverRetirementInformation.add(String.format(person.getStatus().getReportText(), person.getHyperlinkedFullTitle())); } if (wasSacked) { @@ -956,12 +938,10 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment Person spouse = person.getGenealogy().getSpouse(); if ((spouse != null) && (spouse.getPrimaryRole().isCivilian())) { - addReport(spouse.getHyperlinkedFullTitle() + ' ' - + resources.getString("turnoverJointDeparture.text")); + addReport(spouse.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDeparture.text")); spouse.changeStatus(this, getLocalDate(), PersonnelStatus.LEFT); - turnoverRetirementInformation.add(spouse.getHyperlinkedFullTitle() + ' ' - + resources.getString("turnoverJointDeparture.text")); + turnoverRetirementInformation.add(spouse.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDeparture.text")); } // non-civilian spouses may divorce the remaining partner @@ -970,8 +950,7 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment if ((person.getStatus().isDefected()) || (Compute.randomInt(6) == 0)) { getDivorce().divorce(this, getLocalDate(), person, SplittingSurnameStyle.WEIGHTED); - turnoverRetirementInformation.add(String.format(resources.getString("divorce.text"), - person.getHyperlinkedFullTitle(), spouse.getHyperlinkedFullTitle())); + turnoverRetirementInformation.add(String.format(resources.getString("divorce.text"), person.getHyperlinkedFullTitle(), spouse.getHyperlinkedFullTitle())); } } } @@ -981,34 +960,27 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment // factored in for (Person child : person.getGenealogy().getChildren()) { if ((child.isChild(getLocalDate())) && (!child.getStatus().isDepartedUnit())) { - boolean hasRemainingParent = child.getGenealogy().getParents().stream() - .anyMatch(parent -> (!parent.getStatus().isDepartedUnit()) - && (!parent.getStatus().isAbsent())); + boolean hasRemainingParent = child.getGenealogy().getParents().stream().anyMatch(parent -> (!parent.getStatus().isDepartedUnit()) && (!parent.getStatus().isAbsent())); // if there is a remaining parent, there is a 50/50 chance the child departs if ((hasRemainingParent) && (Compute.randomInt(2) == 0)) { - addReport(child.getHyperlinkedFullTitle() + ' ' - + resources.getString("turnoverJointDepartureChild.text")); + addReport(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text")); child.changeStatus(this, getLocalDate(), PersonnelStatus.LEFT); - turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' - + resources.getString("turnoverJointDepartureChild.text")); + turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text")); } // if there is no remaining parent, the child will always depart, unless the // parents are dead if ((!hasRemainingParent) && (child.getGenealogy().hasLivingParents())) { - addReport(child.getHyperlinkedFullTitle() + ' ' - + resources.getString("turnoverJointDepartureChild.text")); + addReport(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text")); child.changeStatus(this, getLocalDate(), PersonnelStatus.LEFT); - turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' - + resources.getString("turnoverJointDepartureChild.text")); + turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text")); } else if (!child.getGenealogy().hasLivingParents()) { addReport(child.getHyperlinkedFullTitle() + ' ' + resources.getString("orphaned.text")); - turnoverRetirementInformation.add( - child.getHyperlinkedFullTitle() + ' ' + resources.getString("orphaned.text")); + turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' + resources.getString("orphaned.text")); ServiceLogger.orphaned(person, getLocalDate()); } } @@ -1022,8 +994,7 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment return true; } } else { - addReport("You cannot afford to make the final payments."); + addReport("You cannot afford to make the final payments."); return false; } @@ -1140,8 +1111,7 @@ public void addUnitToForce(@Nullable Unit u, int id) { u.setTech(forceTech); } else { - String cantTech = forceTech.getFullName() + " cannot maintain " + u.getName() + '\n' - + "You will need to assign a tech manually."; + String cantTech = forceTech.getFullName() + " cannot maintain " + u.getName() + '\n' + "You will need to assign a tech manually."; JOptionPane.showMessageDialog(null, cantTech, "Warning", JOptionPane.WARNING_MESSAGE); } } @@ -1166,6 +1136,7 @@ private void addAllCombatTeams(Force force) { } // region Missions/Contracts + /** * Add a mission to the campaign * @@ -1215,39 +1186,26 @@ public Collection getMissions() { * @return missions List sorted with complete missions at the bottom */ public List getSortedMissions() { - return getMissions().stream() - .sorted(Comparator.comparing(Mission::getStatus) - .thenComparing(m -> (m instanceof Contract) ? ((Contract) m).getStartDate() : LocalDate.now())) - .collect(Collectors.toList()); + return getMissions().stream().sorted(Comparator.comparing(Mission::getStatus).thenComparing(m -> (m instanceof Contract) ? ((Contract) m).getStartDate() : LocalDate.now())).collect(Collectors.toList()); } public List getActiveMissions(final boolean excludeEndDateCheck) { - return getMissions().stream() - .filter(m -> m.isActiveOn(getLocalDate(), excludeEndDateCheck)) - .collect(Collectors.toList()); + return getMissions().stream().filter(m -> m.isActiveOn(getLocalDate(), excludeEndDateCheck)).collect(Collectors.toList()); } public List getCompletedMissions() { - return getMissions().stream() - .filter(m -> m.getStatus().isCompleted()) - .collect(Collectors.toList()); + return getMissions().stream().filter(m -> m.getStatus().isCompleted()).collect(Collectors.toList()); } /** * @return a list of all currently active contracts */ public List getActiveContracts() { - return getMissions().stream() - .filter(c -> (c instanceof Contract) && c.isActiveOn(getLocalDate())) - .map(c -> (Contract) c) - .collect(Collectors.toList()); + return getMissions().stream().filter(c -> (c instanceof Contract) && c.isActiveOn(getLocalDate())).map(c -> (Contract) c).collect(Collectors.toList()); } public List getAtBContracts() { - return getMissions().stream() - .filter(c -> c instanceof AtBContract) - .map(c -> (AtBContract) c) - .collect(Collectors.toList()); + return getMissions().stream().filter(c -> c instanceof AtBContract).map(c -> (AtBContract) c).collect(Collectors.toList()); } /** @@ -1260,8 +1218,8 @@ public List getAtBContracts() { * have been accepted but have not yet started * should also be considered as active. * @return {@code true} if there is any currently active AtB contract, or if - * {@code includeFutureContracts} is {@code true} and there are future - * contracts starting after the current date. Otherwise, {@code false}. + * {@code includeFutureContracts} is {@code true} and there are future + * contracts starting after the current date. Otherwise, {@code false}. * @see #hasFutureAtBContract() */ public boolean hasActiveAtBContract(boolean includeFutureContracts) { @@ -1282,7 +1240,7 @@ public boolean hasActiveAtBContract(boolean includeFutureContracts) { * has a start date later than the current day. * * @return true if there is at least one future AtB contract (accepted but - * starting after the current date). Otherwise, false. + * starting after the current date). Otherwise, false. */ public boolean hasFutureAtBContract() { List contracts = getAtBContracts(); @@ -1302,22 +1260,16 @@ public List getActiveAtBContracts() { } public List getActiveAtBContracts(boolean excludeEndDateCheck) { - return getMissions().stream() - .filter(c -> (c instanceof AtBContract) && c.isActiveOn(getLocalDate(), excludeEndDateCheck)) - .map(c -> (AtBContract) c) - .collect(Collectors.toList()); + return getMissions().stream().filter(c -> (c instanceof AtBContract) && c.isActiveOn(getLocalDate(), excludeEndDateCheck)).map(c -> (AtBContract) c).collect(Collectors.toList()); } public List getCompletedAtBContracts() { - return getMissions().stream() - .filter(c -> (c instanceof AtBContract) && c.getStatus().isCompleted()) - .map(c -> (AtBContract) c) - .collect(Collectors.toList()); + return getMissions().stream().filter(c -> (c instanceof AtBContract) && c.getStatus().isCompleted()).map(c -> (AtBContract) c).collect(Collectors.toList()); } /** * @return whether or not the current campaign has an active contract for the - * current date + * current date */ public boolean hasActiveContract() { return hasActiveContract; @@ -1330,8 +1282,7 @@ public boolean hasActiveContract() { * elsewhere */ public void setHasActiveContract() { - hasActiveContract = getMissions().stream() - .anyMatch(c -> (c instanceof Contract) && c.isActiveOn(getLocalDate())); + hasActiveContract = getMissions().stream().anyMatch(c -> (c instanceof Contract) && c.isActiveOn(getLocalDate())); } // endregion Missions/Contracts @@ -1346,7 +1297,7 @@ public void addScenario(Scenario s, Mission m) { * Add scenario to an existing mission. This method will also assign the * scenario an id, provided * that it is a new scenario. It then adds the scenario to the scenarioId hash. - * + *

    * Scenarios with previously set ids can be sent to this mission, allowing one * to remove * and then re-add scenarios if needed. This functionality is used in the @@ -1366,9 +1317,7 @@ public void addScenario(Scenario s, Mission m, boolean suppressReport) { scenarios.put(id, s); if (newScenario && !suppressReport) { - addReport(MessageFormat.format( - resources.getString("newAtBScenario.format"), - s.getName(), MekHQ.getMHQOptions().getDisplayFormattedDate(s.getDate()))); + addReport(MessageFormat.format(resources.getString("newAtBScenario.format"), s.getName(), MekHQ.getMHQOptions().getDisplayFormattedDate(s.getDate()))); } MekHQ.triggerEvent(new ScenarioNewEvent(s)); @@ -1438,9 +1387,10 @@ public void importUnit(Unit unit) { * This transporters map is used to store transports, the kinds of * transporters they have, and their remaining capacity. The * transporters map is meant to be utilized by the GUI. - * @see CampaignTransporterMap + * * @param campaignTransportType Transport Type (enum) we're adding to - * @param unit unit with transport capabilities + * @param unit unit with transport capabilities + * @see CampaignTransporterMap */ public void addCampaignTransport(CampaignTransportType campaignTransportType, Unit unit) { if (campaignTransportType.isShipTransport()) { @@ -1456,8 +1406,9 @@ public void addCampaignTransport(CampaignTransportType campaignTransportType, Un * in the campaign transport map. This method takes the CampaignTransportType and * transport as inputs and updates the map with the current capacities of the * transport. + * * @param campaignTransportType type (Enum) of TransportedUnitsSummary we're interested in - * @param transport Unit + * @param transport Unit */ public void updateTransportInTransports(CampaignTransportType campaignTransportType, Unit transport) { getCampaignTransporterMap(campaignTransportType).updateTransportInTransporterMap(transport); @@ -1467,9 +1418,10 @@ public void updateTransportInTransports(CampaignTransportType campaignTransportT * Deletes an entry from the list of specified list of transports. This gets * updated when the transport should no longer be in the CampaignTransporterMap, * such as when a Transport is mothballed or removed from the campaign. - * @see CampaignTransporterMap + * * @param campaignTransportType Transport Type (enum) we're checking - * @param unit - The ship we want to remove from this Set + * @param unit - The ship we want to remove from this Set + * @see CampaignTransporterMap */ public void removeCampaignTransporter(CampaignTransportType campaignTransportType, Unit unit) { if (campaignTransportType.isShipTransport()) { @@ -1580,8 +1532,7 @@ public Unit addNewUnit(Entity en, boolean allowNewPilots, int days, PartQuality unit.setDaysToArrival(days); if (allowNewPilots) { - Map> newCrew = Utilities - .genRandomCrewWithCombinedSkill(this, unit, getFactionCode()); + Map> newCrew = Utilities.genRandomCrewWithCombinedSkill(this, unit, getFactionCode()); newCrew.forEach((type, personnel) -> personnel.forEach(p -> type.getAddMethod().accept(unit, p))); } @@ -1633,21 +1584,15 @@ public Collection getUnits() { * @return a collection of active units */ public Collection getActiveUnits() { - return getHangar().getUnits().stream() - .filter(unit -> !unit.isMothballed() && !unit.isSalvage()) - .toList(); + return getHangar().getUnits().stream().filter(unit -> !unit.isMothballed() && !unit.isSalvage()).toList(); } public Collection getLargeCraftAndWarShips() { - return getHangar().getUnits().stream() - .filter(unit -> (unit.getEntity().isLargeCraft()) || (unit.getEntity().isWarShip())) - .collect(Collectors.toList()); + return getHangar().getUnits().stream().filter(unit -> (unit.getEntity().isLargeCraft()) || (unit.getEntity().isWarShip())).collect(Collectors.toList()); } public List getEntities() { - return getUnits().stream() - .map(Unit::getEntity) - .collect(Collectors.toList()); + return getUnits().stream().map(Unit::getEntity).collect(Collectors.toList()); } public Unit getUnit(UUID id) { @@ -1656,6 +1601,7 @@ public Unit getUnit(UUID id) { // region Personnel // region Person Creation + /** * Creates a new dependent with given gender. The origin faction and planet are * set to null. @@ -1681,13 +1627,8 @@ public Person newDependent(Gender gender) { * based on campaign options. * @return Return a {@link Person} object representing the new dependent. */ - public Person newDependent(Gender gender, @Nullable Faction originFaction, - @Nullable Planet originPlanet) { - return newPerson(PersonnelRole.DEPENDENT, - PersonnelRole.NONE, - new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), originFaction), - new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions(), originPlanet), - gender); + public Person newDependent(Gender gender, @Nullable Faction originFaction, @Nullable Planet originPlanet) { + return newPerson(PersonnelRole.DEPENDENT, PersonnelRole.NONE, new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), originFaction), new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions(), originPlanet), gender); } /** @@ -1712,8 +1653,7 @@ public Person newPerson(final PersonnelRole role) { * @return A new {@link Person}. */ public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole) { - return newPerson(primaryRole, secondaryRole, getFactionSelector(), getPlanetSelector(), - Gender.RANDOMIZE); + return newPerson(primaryRole, secondaryRole, getFactionSelector(), getPlanetSelector(), Gender.RANDOMIZE); } /** @@ -1728,12 +1668,8 @@ public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole sec * it value * @return A new {@link Person}. */ - public Person newPerson(final PersonnelRole primaryRole, final String factionCode, - final Gender gender) { - return newPerson(primaryRole, PersonnelRole.NONE, - new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), - (factionCode == null) ? null : Factions.getInstance().getFaction(factionCode)), - getPlanetSelector(), gender); + public Person newPerson(final PersonnelRole primaryRole, final String factionCode, final Gender gender) { + return newPerson(primaryRole, PersonnelRole.NONE, new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), (factionCode == null) ? null : Factions.getInstance().getFaction(factionCode)), getPlanetSelector(), gender); } /** @@ -1749,11 +1685,8 @@ public Person newPerson(final PersonnelRole primaryRole, final String factionCod * randomize it value * @return A new {@link Person}. */ - public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole, - final AbstractFactionSelector factionSelector, - final AbstractPlanetSelector planetSelector, final Gender gender) { - return newPerson(primaryRole, secondaryRole, - getPersonnelGenerator(factionSelector, planetSelector), gender); + public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole, final AbstractFactionSelector factionSelector, final AbstractPlanetSelector planetSelector, final Gender gender) { + return newPerson(primaryRole, secondaryRole, getPersonnelGenerator(factionSelector, planetSelector), gender); } /** @@ -1765,8 +1698,7 @@ public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole sec * creating the {@link Person}. * @return A new {@link Person} configured using {@code personnelGenerator}. */ - public Person newPerson(final PersonnelRole primaryRole, - final AbstractPersonnelGenerator personnelGenerator) { + public Person newPerson(final PersonnelRole primaryRole, final AbstractPersonnelGenerator personnelGenerator) { return newPerson(primaryRole, PersonnelRole.NONE, personnelGenerator, Gender.RANDOMIZE); } @@ -1782,9 +1714,7 @@ public Person newPerson(final PersonnelRole primaryRole, * randomize it value * @return A new {@link Person} configured using {@code personnelGenerator}. */ - public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole, - final AbstractPersonnelGenerator personnelGenerator, - final Gender gender) { + public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole, final AbstractPersonnelGenerator personnelGenerator, final Gender gender) { final Person person = personnelGenerator.generate(this, primaryRole, secondaryRole, gender); // Assign a random portrait after we generate a new person @@ -1805,6 +1735,7 @@ public void setFieldKitchenWithinCapacity(final Boolean fieldKitchenWithinCapaci // endregion Person Creation // region Personnel Recruitment + /** * @param p the person being added * @return true, if the person is hired successfully, otherwise false @@ -1826,7 +1757,6 @@ public boolean recruitPerson(Person p, boolean gmAdd) { } /** - * * @param p the person being added * @param prisonerStatus the person's prisoner status upon recruitment * @return true if the person is hired successfully, otherwise false @@ -1836,7 +1766,7 @@ public boolean recruitPerson(Person p, PrisonerStatus prisonerStatus) { } /** - * @param person the person being added + * @param person the person being added * @param prisonerStatus the person's prisoner status upon recruitment * @param gmAdd false means that they need to pay to hire this person, * true means it is added without paying @@ -1849,10 +1779,8 @@ public boolean recruitPerson(Person person, PrisonerStatus prisonerStatus, boole } // Only pay if option set, they weren't GM added, and they aren't a dependent, prisoner or bondsman - if (getCampaignOptions().isPayForRecruitment() && !person.getPrimaryRole().isDependent() - && !gmAdd && prisonerStatus.isFree()) { - if (!getFinances().debit(TransactionType.RECRUITMENT, getLocalDate(), - person.getSalary(this).multipliedBy(2), String.format(resources.getString("personnelRecruitmentFinancesReason.text"),person.getFullName()))) { + if (getCampaignOptions().isPayForRecruitment() && !person.getPrimaryRole().isDependent() && !gmAdd && prisonerStatus.isFree()) { + if (!getFinances().debit(TransactionType.RECRUITMENT, getLocalDate(), person.getSalary(this).multipliedBy(2), String.format(resources.getString("personnelRecruitmentFinancesReason.text"), person.getFullName()))) { addReport(String.format(resources.getString("personnelRecruitmentInsufficientFunds.text"), MekHQ.getMHQOptions().getFontColorNegativeHexColor(), person.getFullName())); return false; } @@ -1883,8 +1811,7 @@ public boolean recruitPerson(Person person, PrisonerStatus prisonerStatus, boole if (log) { formerSurname = person.getSurname().equals(formerSurname) ? "" : ' ' + String.format(resources.getString("personnelRecruitmentFormerSurname.text") + ' ', formerSurname); - String add = !prisonerStatus.isFree() ? (' ' + resources.getString(prisonerStatus.isBondsman() ? "personnelRecruitmentBondsman.text" : "personnelRecruitmentPrisoner.text")) - : ""; + String add = !prisonerStatus.isFree() ? (' ' + resources.getString(prisonerStatus.isBondsman() ? "personnelRecruitmentBondsman.text" : "personnelRecruitmentPrisoner.text")) : ""; addReport(String.format(resources.getString("personnelRecruitmentAddedToRoster.text"), person.getHyperlinkedName(), formerSurname, add)); } @@ -1942,8 +1869,7 @@ private void simulateRelationshipHistory(Person person) { // then we check for children if ((person.getGender().isFemale()) && (!person.isPregnant())) { - getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, - true); + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, true); if (person.isPregnant()) { @@ -1954,8 +1880,7 @@ private void simulateRelationshipHistory(Person person) { } if ((currentSpouse != null) && (currentSpouse.getGender().isFemale()) && (!currentSpouse.isPregnant())) { - getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), - currentSpouse, true); + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), currentSpouse, true); if (currentSpouse.isPregnant()) { @@ -1970,8 +1895,7 @@ private void simulateRelationshipHistory(Person person) { babysFather = null; } - if ((currentSpouse != null) && (currentSpouse.isPregnant()) - && (currentDate.isAfter(currentSpouse.getDueDate()))) { + if ((currentSpouse != null) && (currentSpouse.isPregnant()) && (currentDate.isAfter(currentSpouse.getDueDate()))) { children.addAll(getProcreation().birthHistoric(this, localDate, currentSpouse, spousesBabysFather)); spousesBabysFather = null; } @@ -1982,10 +1906,7 @@ private void simulateRelationshipHistory(Person person) { if (currentSpouse != null) { recruitPerson(currentSpouse, PrisonerStatus.FREE, true, false); - addReport(String.format(resources.getString("relativeJoinsForce.text"), - currentSpouse.getHyperlinkedFullTitle(), - person.getHyperlinkedFullTitle(), - resources.getString("relativeJoinsForceSpouse.text"))); + addReport(String.format(resources.getString("relativeJoinsForce.text"), currentSpouse.getHyperlinkedFullTitle(), person.getHyperlinkedFullTitle(), resources.getString("relativeJoinsForceSpouse.text"))); MekHQ.triggerEvent(new PersonChangedEvent(currentSpouse)); } @@ -2029,10 +1950,7 @@ private void simulateRelationshipHistory(Person person) { recruitPerson(child, PrisonerStatus.FREE, true, false); - addReport(String.format(resources.getString("relativeJoinsForce.text"), - child.getHyperlinkedFullTitle(), - person.getHyperlinkedFullTitle(), - resources.getString("relativeJoinsForceChild.text"))); + addReport(String.format(resources.getString("relativeJoinsForce.text"), child.getHyperlinkedFullTitle(), person.getHyperlinkedFullTitle(), resources.getString("relativeJoinsForceChild.text"))); MekHQ.triggerEvent(new PersonChangedEvent(child)); } @@ -2042,6 +1960,7 @@ private void simulateRelationshipHistory(Person person) { // endregion Personnel Recruitment // region Bloodnames + /** * If the person does not already have a bloodname, assigns a chance of having * one based on @@ -2063,10 +1982,7 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { // keep the // current bloodname or assign a new one if (!person.getBloodname().isEmpty()) { - int result = JOptionPane.showConfirmDialog(null, - person.getFullTitle() + " already has the bloodname " + person.getBloodname() - + "\nDo you wish to remove that bloodname and generate a new one?", - "Already Has Bloodname", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + int result = JOptionPane.showConfirmDialog(null, person.getFullTitle() + " already has the bloodname " + person.getBloodname() + "\nDo you wish to remove that bloodname and generate a new one?", "Already Has Bloodname", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (result == JOptionPane.NO_OPTION) { return; } else { @@ -2079,51 +1995,31 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { if (!ignoreDice) { switch (person.getPhenotype()) { case MEKWARRIOR: { - bloodnameTarget += person.hasSkill(SkillType.S_GUN_MEK) - ? person.getSkill(SkillType.S_GUN_MEK).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_MEK) - ? person.getSkill(SkillType.S_PILOT_MEK).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_GUN_MEK) ? person.getSkill(SkillType.S_GUN_MEK).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_MEK) ? person.getSkill(SkillType.S_PILOT_MEK).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; break; } case AEROSPACE: { - bloodnameTarget += person.hasSkill(SkillType.S_GUN_AERO) - ? person.getSkill(SkillType.S_GUN_AERO).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_AERO) - ? person.getSkill(SkillType.S_PILOT_AERO).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_GUN_AERO) ? person.getSkill(SkillType.S_GUN_AERO).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_AERO) ? person.getSkill(SkillType.S_PILOT_AERO).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; break; } case ELEMENTAL: { - bloodnameTarget += person.hasSkill(SkillType.S_GUN_BA) - ? person.getSkill(SkillType.S_GUN_BA).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; - bloodnameTarget += person.hasSkill(SkillType.S_ANTI_MEK) - ? person.getSkill(SkillType.S_ANTI_MEK).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_GUN_BA) ? person.getSkill(SkillType.S_GUN_BA).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_ANTI_MEK) ? person.getSkill(SkillType.S_ANTI_MEK).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; break; } case VEHICLE: { - bloodnameTarget += person.hasSkill(SkillType.S_GUN_VEE) - ? person.getSkill(SkillType.S_GUN_VEE).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_GUN_VEE) ? person.getSkill(SkillType.S_GUN_VEE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; switch (person.getPrimaryRole()) { case GROUND_VEHICLE_DRIVER: - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_GVEE) - ? person.getSkill(SkillType.S_PILOT_GVEE).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_GVEE) ? person.getSkill(SkillType.S_PILOT_GVEE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; break; case NAVAL_VEHICLE_DRIVER: - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_NVEE) - ? person.getSkill(SkillType.S_PILOT_NVEE).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_NVEE) ? person.getSkill(SkillType.S_PILOT_NVEE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; break; case VTOL_PILOT: - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_VTOL) - ? person.getSkill(SkillType.S_PILOT_VTOL).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_VTOL) ? person.getSkill(SkillType.S_PILOT_VTOL).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; break; default: break; @@ -2131,32 +2027,22 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { break; } case PROTOMEK: { - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_GUN_PROTO) - ? person.getSkill(SkillType.S_GUN_PROTO).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_GUN_PROTO) ? person.getSkill(SkillType.S_GUN_PROTO).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); break; } case NAVAL: { switch (person.getPrimaryRole()) { case VESSEL_PILOT: - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_PILOT_SPACE) - ? person.getSkill(SkillType.S_PILOT_SPACE).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_PILOT_SPACE) ? person.getSkill(SkillType.S_PILOT_SPACE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); break; case VESSEL_GUNNER: - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_GUN_SPACE) - ? person.getSkill(SkillType.S_GUN_SPACE).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_GUN_SPACE) ? person.getSkill(SkillType.S_GUN_SPACE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); break; case VESSEL_CREW: - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_TECH_VESSEL) - ? person.getSkill(SkillType.S_TECH_VESSEL).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_TECH_VESSEL) ? person.getSkill(SkillType.S_TECH_VESSEL).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); break; case VESSEL_NAVIGATOR: - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_NAV) - ? person.getSkill(SkillType.S_NAV).getFinalSkillValue() - : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_NAV) ? person.getSkill(SkillType.S_NAV).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); break; default: break; @@ -2197,9 +2083,7 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { if (ignoreDice || (Compute.d6(2) >= bloodnameTarget)) { final Phenotype phenotype = person.getPhenotype().isNone() ? Phenotype.GENERAL : person.getPhenotype(); - final Bloodname bloodname = Bloodname.randomBloodname( - (getFaction().isClan() ? getFaction() : person.getOriginFaction()).getShortName(), - phenotype, getGameYear()); + final Bloodname bloodname = Bloodname.randomBloodname((getFaction().isClan() ? getFaction() : person.getOriginFaction()).getShortName(), phenotype, getGameYear()); if (bloodname != null) { person.setBloodname(bloodname.getName()); personUpdated(person); @@ -2209,6 +2093,7 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { // endregion Bloodnames // region Other Personnel Methods + /** * Imports a {@link Person} into a campaign. * @@ -2238,9 +2123,7 @@ public Collection getPersonnel() { * @return a {@code List} of {@link Person} objects who have not left the unit */ public List getPersonnelFilteringOutDeparted() { - return getPersonnel().stream() - .filter(person -> !person.getStatus().isDepartedUnit()) - .collect(Collectors.toList()); + return getPersonnel().stream().filter(person -> !person.getStatus().isDepartedUnit()).collect(Collectors.toList()); } /** @@ -2249,9 +2132,7 @@ public List getPersonnelFilteringOutDeparted() { * @return a {@link Person} List containing all active personnel */ public List getActivePersonnel() { - return getPersonnel().stream() - .filter(p -> p.getStatus().isActive()) - .collect(Collectors.toList()); + return getPersonnel().stream().filter(p -> p.getStatus().isActive()).collect(Collectors.toList()); } /** @@ -2264,21 +2145,16 @@ public List getActivePersonnel() { * @return a {@link List} of {@link Person} objects representing combat-capable personnel */ public List getActiveCombatPersonnel() { - return getActivePersonnel().stream() - .filter(p -> p.getPrimaryRole().isCombat() || p.getSecondaryRole().isCombat()) - .collect(Collectors.toList()); + return getActivePersonnel().stream().filter(p -> p.getPrimaryRole().isCombat() || p.getSecondaryRole().isCombat()).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()); + return getPersonnel().stream().filter(person -> person.getPrimaryRole().isDependent()).filter(person -> person.getStatus().isActive()).collect(Collectors.toList()); } /** @@ -2287,9 +2163,7 @@ public List getActiveDependents() { * @return a {@link Person} List containing all active personnel */ public List getCurrentPrisoners() { - return getActivePersonnel().stream() - .filter(person -> person.getPrisonerStatus().isCurrentPrisoner()) - .collect(Collectors.toList()); + return getActivePersonnel().stream().filter(person -> person.getPrisonerStatus().isCurrentPrisoner()).collect(Collectors.toList()); } /** @@ -2298,9 +2172,7 @@ public List getCurrentPrisoners() { * @return a {@link Person} List containing all active personnel */ public List getFriendlyPrisoners() { - return getPersonnel().stream() - .filter(p -> p.getStatus().isPoW()) - .collect(Collectors.toList()); + return getPersonnel().stream().filter(p -> p.getStatus().isPoW()).collect(Collectors.toList()); } /** @@ -2310,9 +2182,7 @@ public List getFriendlyPrisoners() { * @return a {@link Person} List containing all active personnel */ public List getAwolPersonnel() { - return getPersonnel().stream() - .filter(p -> p.getStatus().isAwol()) - .collect(Collectors.toList()); + return getPersonnel().stream().filter(p -> p.getStatus().isAwol()).collect(Collectors.toList()); } /** @@ -2322,18 +2192,17 @@ public List getAwolPersonnel() { * @return a {@link Person} List containing all active personnel */ public List getStudents() { - return getPersonnel().stream() - .filter(p -> p.getStatus().isStudent()) - .collect(Collectors.toList()); + return getPersonnel().stream().filter(p -> p.getStatus().isStudent()).collect(Collectors.toList()); } // endregion Other Personnel Methods // region Personnel Selectors and Generators + /** * Gets the {@link AbstractFactionSelector} to use with this campaign. * * @return An {@link AbstractFactionSelector} to use when selecting a - * {@link Faction}. + * {@link Faction}. */ public AbstractFactionSelector getFactionSelector() { return getFactionSelector(getCampaignOptions().getRandomOriginOptions()); @@ -2344,18 +2213,17 @@ public AbstractFactionSelector getFactionSelector() { * * @param options the random origin options to use * @return An {@link AbstractFactionSelector} to use when selecting a - * {@link Faction}. + * {@link Faction}. */ public AbstractFactionSelector getFactionSelector(final RandomOriginOptions options) { - return options.isRandomizeOrigin() ? new RangedFactionSelector(options) - : new DefaultFactionSelector(options); + return options.isRandomizeOrigin() ? new RangedFactionSelector(options) : new DefaultFactionSelector(options); } /** * Gets the {@link AbstractPlanetSelector} to use with this campaign. * * @return An {@link AbstractPlanetSelector} to use when selecting a - * {@link Planet}. + * {@link Planet}. */ public AbstractPlanetSelector getPlanetSelector() { return getPlanetSelector(getCampaignOptions().getRandomOriginOptions()); @@ -2366,11 +2234,10 @@ public AbstractPlanetSelector getPlanetSelector() { * * @param options the random origin options to use * @return An {@link AbstractPlanetSelector} to use when selecting a - * {@link Planet}. + * {@link Planet}. */ public AbstractPlanetSelector getPlanetSelector(final RandomOriginOptions options) { - return options.isRandomizeOrigin() ? new RangedPlanetSelector(options) - : new DefaultPlanetSelector(options); + return options.isRandomizeOrigin() ? new RangedPlanetSelector(options) : new DefaultPlanetSelector(options); } /** @@ -2381,11 +2248,9 @@ public AbstractPlanetSelector getPlanetSelector(final RandomOriginOptions option * @param planetSelector The {@link AbstractPlanetSelector} to use when * choosing a {@link Planet}. * @return An {@link AbstractPersonnelGenerator} to use when creating new - * personnel. + * personnel. */ - public AbstractPersonnelGenerator getPersonnelGenerator( - final AbstractFactionSelector factionSelector, - final AbstractPlanetSelector planetSelector) { + public AbstractPersonnelGenerator getPersonnelGenerator(final AbstractFactionSelector factionSelector, final AbstractPlanetSelector planetSelector) { final DefaultPersonnelGenerator generator = new DefaultPersonnelGenerator(factionSelector, planetSelector); generator.setNameGenerator(RandomNameGenerator.getInstance()); generator.setSkillPreferences(getRandomSkillPreferences()); @@ -2397,9 +2262,7 @@ public AbstractPersonnelGenerator getPersonnelGenerator( public List getPatients() { List patients = new ArrayList<>(); for (Person p : getPersonnel()) { - if (p.needsFixing() - || (getCampaignOptions().isUseAdvancedMedical() && p.hasInjuries(true) - && p.getStatus().isActive())) { + if (p.needsFixing() || (getCampaignOptions().isUseAdvancedMedical() && p.hasInjuries(true) && p.getStatus().isActive())) { patients.add(p); } } @@ -2491,9 +2354,7 @@ private PartInUse getPartInUse(Part part) { return null; } // Makes no sense buying those separately from the chasis - if ((part instanceof EquipmentPart) - && ((EquipmentPart) part).getType() != null - && (((EquipmentPart) part).getType().hasFlag(MiscType.F_CHASSIS_MODIFICATION))) { + if ((part instanceof EquipmentPart) && ((EquipmentPart) part).getType() != null && (((EquipmentPart) part).getType().hasFlag(MiscType.F_CHASSIS_MODIFICATION))) { return null; } // Replace a "missing" part with a corresponding "new" one. @@ -2515,7 +2376,7 @@ private PartInUse getPartInUse(Part part) { * @param part The {@link Part} for which the default stock percentage is to * be determined. The part must not be {@code null}. * @return An {@code int} representing the default stock percentage for the - * given part type, as defined in the campaign options. + * given part type, as defined in the campaign options. */ private int getDefaultStockPercent(Part part) { if (part instanceof HeatSink) { @@ -2534,7 +2395,7 @@ private int getDefaultStockPercent(Part part) { return campaignOptions.getAutoLogisticsNonRepairableLocation(); } else if (part instanceof AmmoBin) { return campaignOptions.getAutoLogisticsAmmunition(); - } else if (part instanceof Armor ) { + } else if (part instanceof Armor) { return campaignOptions.getAutoLogisticsArmor(); } @@ -2543,7 +2404,7 @@ private int getDefaultStockPercent(Part part) { /** * Add data from an actual part to a PartInUse data element - * + * * @param partInUse part in use record to update * @param incomingPart new part that needs to be added to this * record @@ -2551,8 +2412,7 @@ private int getDefaultStockPercent(Part part) { * @param ignoreSparesUnderQuality don't count spare parts lower than this * quality */ - private void updatePartInUseData(PartInUse partInUse, Part incomingPart, - boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { + private void updatePartInUseData(PartInUse partInUse, Part incomingPart, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { if (ignoreMothballedUnits && (null != incomingPart.getUnit()) && incomingPart.getUnit().isMothballed()) { } else if ((incomingPart.getUnit() != null) || (incomingPart instanceof MissingPart)) { @@ -2572,14 +2432,13 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, /** * Find all the parts that match this PartInUse and update their data - * + * * @param partInUse part in use record to update * @param ignoreMothballedUnits don't count parts in mothballed units * @param ignoreSparesUnderQuality don't count spare parts lower than this * quality */ - public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, - PartQuality ignoreSparesUnderQuality) { + public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { partInUse.setUseCount(0); partInUse.setStoreCount(0); partInUse.setTransferCount(0); @@ -2587,16 +2446,13 @@ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, getWarehouse().forEachPart(incomingPart -> { PartInUse newPartInUse = getPartInUse(incomingPart); if (partInUse.equals(newPartInUse)) { - updatePartInUseData(partInUse, incomingPart, - ignoreMothballedUnits, ignoreSparesUnderQuality); + updatePartInUseData(partInUse, incomingPart, ignoreMothballedUnits, ignoreSparesUnderQuality); } }); for (IAcquisitionWork maybePart : shoppingList.getPartList()) { PartInUse newPartInUse = getPartInUse((Part) maybePart); if (partInUse.equals(newPartInUse)) { - partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() - : (Part) maybePart) * maybePart.getQuantity()); + partInUse.setPlannedCount(partInUse.getPlannedCount() + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); } } } @@ -2604,14 +2460,13 @@ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, /** * Create a data set detailing all the parts being used (or not) and their * warehouse spares - * + * * @param ignoreMothballedUnits don't count parts in mothballed units * @param ignoreSparesUnderQuality don't count spare parts lower than this * quality * @return a Set of PartInUse data for display or inspection */ - public Set getPartsInUse(boolean ignoreMothballedUnits, - PartQuality ignoreSparesUnderQuality) { + public Set getPartsInUse(boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { // java.util.Set doesn't supply a get(Object) method, so we have to use a // java.util.Map Map inUse = new HashMap<>(); @@ -2650,15 +2505,12 @@ public Set getPartsInUse(boolean ignoreMothballedUnits, } inUse.put(partInUse, partInUse); } - partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() - : (Part) maybePart) * maybePart.getQuantity()); + partInUse.setPlannedCount(partInUse.getPlannedCount() + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); } return inUse.keySet().stream() - // Hacky but otherwise we end up with zero lines when filtering things out - .filter(p -> p.getUseCount() != 0 || p.getStoreCount() != 0 || p.getPlannedCount() != 0) - .collect(Collectors.toSet()); + // Hacky but otherwise we end up with zero lines when filtering things out + .filter(p -> p.getUseCount() != 0 || p.getStoreCount() != 0 || p.getPlannedCount() != 0).collect(Collectors.toSet()); } @Deprecated @@ -2699,8 +2551,7 @@ public List fetchAndClearNewReports() { * * @param role One of the PersonnelRole enum values * @param primary The skill to use for comparison. - * @param secondary - * If not null and there is more than one person tied for the + * @param secondary If not null and there is more than one person tied for the * most * the highest, preference will be given to the one with a * higher @@ -2716,16 +2567,16 @@ public Person findBestInRole(PersonnelRole role, String primary, String secondar retVal = p; highest = p.getSkill(primary).getLevel(); } else if (secondary != null && p.getSkill(primary).getLevel() == highest && - /* - * If the skill level of the current person is the same as the previous highest, - * select the current instead under the following conditions: - */ - (retVal == null || // None has been selected yet (current has level 0) - retVal.getSkill(secondary) == null || // Previous selection does not have secondary - // skill - (p.getSkill(secondary) != null // Current has secondary skill and it is higher than the - // previous. - && p.getSkill(secondary).getLevel() > retVal.getSkill(secondary).getLevel()))) { + /* + * If the skill level of the current person is the same as the previous highest, + * select the current instead under the following conditions: + */ + (retVal == null || // None has been selected yet (current has level 0) + retVal.getSkill(secondary) == null || // Previous selection does not have secondary + // skill + (p.getSkill(secondary) != null // Current has secondary skill and it is higher than the + // previous. + && p.getSkill(secondary).getLevel() > retVal.getSkill(secondary).getLevel()))) { retVal = p; } } @@ -2751,7 +2602,7 @@ public Person findBestInRole(PersonnelRole role, String skill) { /** * @return The list of all active {@link Person}s who qualify as technicians - * ({@link Person#isTech()})); + * ({@link Person#isTech()})); */ public List getTechs() { return getTechs(false); @@ -2779,14 +2630,11 @@ public List getTechs(final boolean noZeroMinute, final boolean eliteFirs * @param expanded If TRUE, then include techs with expanded roles (e.g. * Tech/Vessel skill) * @return The list of active {@link Person}s who qualify as technicians - * ({@link Person#isTech()}), or who qualify as expanded technicians - * ({@link Person#isTechExpanded()}). + * ({@link Person#isTech()}), or who qualify as expanded technicians + * ({@link Person#isTechExpanded()}). */ public List getTechsExpanded(final boolean noZeroMinute, final boolean eliteFirst, final boolean expanded) { - final List techs = getActivePersonnel().stream() - .filter(person -> (expanded ? person.isTechExpanded() : person.isTech()) - && (!noZeroMinute || (person.getMinutesLeft() > 0))) - .collect(Collectors.toList()); + final List techs = getActivePersonnel().stream().filter(person -> (expanded ? person.isTechExpanded() : person.isTech()) && (!noZeroMinute || (person.getMinutesLeft() > 0))).collect(Collectors.toList()); // also need to loop through and collect engineers on self-crewed vessels for (final Unit unit : getUnits()) { @@ -2798,13 +2646,10 @@ public List getTechsExpanded(final boolean noZeroMinute, final boolean e // Return the tech collection sorted worst to best Skill Level, or reversed if // we want // elites first - Comparator techSorter = Comparator - .comparingInt(person -> person.getSkillLevel(this, !person.getPrimaryRole().isTech() - && person.getSecondaryRole().isTechSecondary()).ordinal()); + Comparator techSorter = Comparator.comparingInt(person -> person.getSkillLevel(this, !person.getPrimaryRole().isTech() && person.getSecondaryRole().isTechSecondary()).ordinal()); if (eliteFirst) { - techSorter = techSorter.reversed().thenComparing(Comparator - .comparingInt(Person::getDailyAvailableTechTime).reversed()); + techSorter = techSorter.reversed().thenComparing(Comparator.comparingInt(Person::getDailyAvailableTechTime).reversed()); } else { techSorter = techSorter.thenComparing(Comparator.comparingInt(Person::getMinutesLeft).reversed()); } @@ -2860,22 +2705,18 @@ public String healPerson(Person medWork, Person doctor) { return ""; } String report = ""; - report += doctor.getHyperlinkedFullTitle() + " attempts to heal " - + medWork.getFullName(); + report += doctor.getHyperlinkedFullTitle() + " attempts to heal " + medWork.getFullName(); TargetRoll target = getTargetFor(medWork, doctor); int roll = Compute.d6(2); - report = report + ", needs " + target.getValueAsString() - + " and rolls " + roll + ':'; + report = report + ", needs " + target.getValueAsString() + " and rolls " + roll + ':'; int xpGained = 0; // If we get a natural 2 that isn't an automatic success, reroll if Edge is // available and in use. - if (getCampaignOptions().isUseSupportEdge() - && doctor.getOptions().booleanOption(PersonnelOptions.EDGE_MEDICAL)) { + if (getCampaignOptions().isUseSupportEdge() && doctor.getOptions().booleanOption(PersonnelOptions.EDGE_MEDICAL)) { if ((roll == 2) && (doctor.getCurrentEdge() > 0) && (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS)) { doctor.changeCurrentEdge(-1); roll = Compute.d6(2); - report += medWork.fail() + '\n' + doctor.getHyperlinkedFullTitle() + " uses Edge to reroll:" - + " rolls " + roll + ':'; + report += medWork.fail() + '\n' + doctor.getHyperlinkedFullTitle() + " uses Edge to reroll:" + " rolls " + roll + ':'; } } if (roll >= target.getValue()) { @@ -2904,30 +2745,23 @@ public String healPerson(Person medWork, Person doctor) { doctor.awardXP(this, xpGained); report += " (" + xpGained + "XP gained) "; } - medWork.setDaysToWaitForHealing(getCampaignOptions() - .getHealingWaitingPeriod()); + medWork.setDaysToWaitForHealing(getCampaignOptions().getHealingWaitingPeriod()); return report; } public TargetRoll getTargetFor(Person medWork, Person doctor) { Skill skill = doctor.getSkill(SkillType.S_DOCTOR); if (null == skill) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, doctor.getFullName() - + " isn't a doctor, he just plays one on TV."); + return new TargetRoll(TargetRoll.IMPOSSIBLE, doctor.getFullName() + " isn't a doctor, he just plays one on TV."); } - if (medWork.getDoctorId() != null - && !medWork.getDoctorId().equals(doctor.getId())) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, - medWork.getFullName() + " is already being tended by another doctor"); + if (medWork.getDoctorId() != null && !medWork.getDoctorId().equals(doctor.getId())) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, medWork.getFullName() + " is already being tended by another doctor"); } - if (!medWork.needsFixing() - && !(getCampaignOptions().isUseAdvancedMedical() && medWork.needsAMFixing())) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, - medWork.getFullName() + " does not require healing."); + if (!medWork.needsFixing() && !(getCampaignOptions().isUseAdvancedMedical() && medWork.needsAMFixing())) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, medWork.getFullName() + " does not require healing."); } if (getPatientsFor(doctor) > 25) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, doctor.getFullName() - + " already has 25 patients."); + return new TargetRoll(TargetRoll.IMPOSSIBLE, doctor.getFullName() + " already has 25 patients."); } TargetRoll target = new TargetRoll(skill.getFinalSkillValue(), skill.getSkillLevel().toString()); if (target.getValue() == TargetRoll.IMPOSSIBLE) { @@ -2997,9 +2831,8 @@ public TargetRoll getTargetFor(Person medWork, Person doctor) { * * @param type the {@link AdministratorSpecialization} representing the administrative role to check for. * Passing a {@code null} type will result in an {@link IllegalStateException}. - * * @return the most senior {@link Person} with the specified administrative role, or {@code null} - * if no eligible administrator is found. + * if no eligible administrator is found. * *

    Behavior:

    *
      @@ -3011,7 +2844,6 @@ public TargetRoll getTargetFor(Person medWork, Person doctor) { *
    • Seniority is determined by the {@link Person#outRanksUsingSkillTiebreaker} method, * which uses a skill-based tiebreaker when necessary.
    • *
    - * * @throws IllegalStateException if {@code type} is null or an unsupported value. */ public @Nullable Person getSeniorAdminPerson(AdministratorSpecialization type) { @@ -3019,14 +2851,12 @@ public TargetRoll getTargetFor(Person medWork, Person doctor) { for (Person person : getAdmins()) { boolean isEligible = switch (type) { - case COMMAND -> person.getPrimaryRole().isAdministratorCommand() - || person.getSecondaryRole().isAdministratorCommand(); - case LOGISTICS -> person.getPrimaryRole().isAdministratorLogistics() - || person.getSecondaryRole().isAdministratorLogistics(); - case TRANSPORT -> person.getPrimaryRole().isAdministratorTransport() - || person.getSecondaryRole().isAdministratorTransport(); - case HR -> person.getPrimaryRole().isAdministratorHR() - || person.getSecondaryRole().isAdministratorHR(); + case COMMAND -> person.getPrimaryRole().isAdministratorCommand() || person.getSecondaryRole().isAdministratorCommand(); + case LOGISTICS -> + person.getPrimaryRole().isAdministratorLogistics() || person.getSecondaryRole().isAdministratorLogistics(); + case TRANSPORT -> + person.getPrimaryRole().isAdministratorTransport() || person.getSecondaryRole().isAdministratorTransport(); + case HR -> person.getPrimaryRole().isAdministratorHR() || person.getSecondaryRole().isAdministratorHR(); }; if (isEligible) { @@ -3048,7 +2878,7 @@ public TargetRoll getTargetFor(Person medWork, Person doctor) { * if acquisitions automatically succeed. * * @return A List of {@link Person} who can perform logistical - * actions. + * actions. */ public List getLogisticsPersonnel() { String skill = getCampaignOptions().getAcquisitionSkill(); @@ -3117,7 +2947,7 @@ public ShoppingList goShopping(ShoppingList sList) { * * @param sList The shopping list to use when shopping. * @return The new shopping list containing the items that were not - * acquired. + * acquired. */ private ShoppingList goShoppingAutomatically(ShoppingList sList) { List currentList = new ArrayList<>(sList.getShoppingList()); @@ -3146,7 +2976,7 @@ private ShoppingList goShoppingAutomatically(ShoppingList sList) { * * @param sList The shopping list to use when shopping. * @return The new shopping list containing the items that were not - * acquired. + * acquired. */ private ShoppingList goShoppingStandard(ShoppingList sList) { List logisticsPersonnel = getLogisticsPersonnel(); @@ -3189,7 +3019,7 @@ private ShoppingList goShoppingStandard(ShoppingList sList) { * * @param sList The shopping list to use when shopping. * @return The new shopping list containing the items that were not - * acquired. + * acquired. */ private ShoppingList goShoppingByPlanet(ShoppingList sList) { List logisticsPersonnel = getLogisticsPersonnel(); @@ -3208,8 +3038,7 @@ private ShoppingList goShoppingByPlanet(ShoppingList sList) { // find planets within a certain radius - the function will weed out dead // planets - List systems = Systems.getInstance().getShoppingSystems(getCurrentSystem(), - getCampaignOptions().getMaxJumpsPlanetaryAcquisition(), currentDate); + List systems = Systems.getInstance().getShoppingSystems(getCurrentSystem(), getCampaignOptions().getMaxJumpsPlanetaryAcquisition(), currentDate); for (Person person : logisticsPersonnel) { if (currentList.isEmpty()) { @@ -3243,18 +3072,11 @@ private ShoppingList goShoppingByPlanet(ShoppingList sList) { if (result == PartAcquisitionResult.Success) { int transitTime = calculatePartTransitTime(system); int totalQuantity = 0; - while (shoppingItem.getQuantity() > 0 - && canAcquireParts(person) - && acquireEquipment(shoppingItem, person, system, transitTime)) { + while (shoppingItem.getQuantity() > 0 && canAcquireParts(person) && acquireEquipment(shoppingItem, person, system, transitTime)) { totalQuantity++; } if (totalQuantity > 0) { - addReport(personTitle + " found " - + shoppingItem.getQuantityName(totalQuantity) - + " on " - + system.getPrintableName(currentDate) - + ". Delivery in " + transitTime + " days."); + addReport(personTitle + " found " + shoppingItem.getQuantityName(totalQuantity) + " on " + system.getPrintableName(currentDate) + ". Delivery in " + transitTime + " days."); } } else if (result == PartAcquisitionResult.PartInherentFailure) { shelvedItems.add(shoppingItem); @@ -3267,9 +3089,7 @@ && acquireEquipment(shoppingItem, person, system, transitTime)) { // if we can't afford it, then don't keep searching for it on other planets if (!canPayFor(shoppingItem)) { if (!getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("You cannot afford to purchase another " - + shoppingItem.getAcquisitionName() + ""); + addReport("You cannot afford to purchase another " + shoppingItem.getAcquisitionName() + ""); } shelvedItems.add(shoppingItem); } else { @@ -3316,8 +3136,7 @@ public boolean canAcquireParts(@Nullable Person person) { return true; } int maxAcquisitions = getCampaignOptions().getMaxAcquisitions(); - return maxAcquisitions <= 0 - || person.getAcquisitions() < maxAcquisitions; + return maxAcquisitions <= 0 || person.getAcquisitions() < maxAcquisitions; } /*** @@ -3332,8 +3151,7 @@ public boolean canAcquireParts(@Nullable Person person) { */ public boolean canPayFor(IAcquisitionWork acquisition) { // SHOULD we check to see if this acquisition needs to be paid for - if ((acquisition instanceof UnitOrder && getCampaignOptions().isPayForUnits()) - || (acquisition instanceof Part && getCampaignOptions().isPayForParts())) { + if ((acquisition instanceof UnitOrder && getCampaignOptions().isPayForUnits()) || (acquisition instanceof Part && getCampaignOptions().isPayForParts())) { // CAN the acquisition actually be paid for return getFunds().isGreaterOrEqualThan(acquisition.getBuyCost()); } @@ -3353,54 +3171,38 @@ public boolean canPayFor(IAcquisitionWork acquisition) { * user is not using planetary acquisition. * @return true if your target roll succeeded. */ - public PartAcquisitionResult findContactForAcquisition(IAcquisitionWork acquisition, Person person, - PlanetarySystem system) { + public PartAcquisitionResult findContactForAcquisition(IAcquisitionWork acquisition, Person person, PlanetarySystem system) { TargetRoll target = getTargetForAcquisition(acquisition, person); - String impossibleSentencePrefix = person == null ? "Can't search for " - : person.getFullName() + " can't search for "; - String failedSentencePrefix = person == null ? "No contacts available for " - : person.getFullName() + " is unable to find contacts for "; - String succeededSentencePrefix = person == null ? "Possible contact for " - : person.getFullName() + " has found a contact for "; + String impossibleSentencePrefix = person == null ? "Can't search for " : person.getFullName() + " can't search for "; + String failedSentencePrefix = person == null ? "No contacts available for " : person.getFullName() + " is unable to find contacts for "; + String succeededSentencePrefix = person == null ? "Possible contact for " : person.getFullName() + " has found a contact for "; // if it's already impossible, don't bother with the rest if (target.getValue() == TargetRoll.IMPOSSIBLE) { if (getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("" - + impossibleSentencePrefix + acquisition.getAcquisitionName() - + " on " + system.getPrintableName(getLocalDate()) + " because: " - + target.getDesc()); + addReport("" + impossibleSentencePrefix + acquisition.getAcquisitionName() + " on " + system.getPrintableName(getLocalDate()) + " because: " + target.getDesc()); } return PartAcquisitionResult.PartInherentFailure; } - target = system.getPrimaryPlanet().getAcquisitionMods(target, getLocalDate(), getCampaignOptions(), - getFaction(), - acquisition.getTechBase() == Part.T_CLAN); + target = system.getPrimaryPlanet().getAcquisitionMods(target, getLocalDate(), getCampaignOptions(), getFaction(), acquisition.getTechBase() == Part.T_CLAN); if (target.getValue() == TargetRoll.IMPOSSIBLE) { if (getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("" - + impossibleSentencePrefix + acquisition.getAcquisitionName() - + " on " + system.getPrintableName(getLocalDate()) + " because: " - + target.getDesc()); + addReport("" + impossibleSentencePrefix + acquisition.getAcquisitionName() + " on " + system.getPrintableName(getLocalDate()) + " because: " + target.getDesc()); } return PartAcquisitionResult.PlanetSpecificFailure; } if (Compute.d6(2) < target.getValue()) { // no contacts on this planet, move along if (getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("" - + failedSentencePrefix + acquisition.getAcquisitionName() - + " on " + system.getPrintableName(getLocalDate()) + ""); + addReport("" + failedSentencePrefix + acquisition.getAcquisitionName() + " on " + system.getPrintableName(getLocalDate()) + ""); } return PartAcquisitionResult.PlanetSpecificFailure; } else { if (getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("" - + succeededSentencePrefix + acquisition.getAcquisitionName() - + " on " + system.getPrintableName(getLocalDate()) + ""); + addReport("" + succeededSentencePrefix + acquisition.getAcquisitionName() + " on " + system.getPrintableName(getLocalDate()) + ""); } return PartAcquisitionResult.Success; } @@ -3438,8 +3240,7 @@ public boolean acquireEquipment(IAcquisitionWork acquisition, Person person) { * @return a boolean indicating whether the attempt to acquire equipment was * successful. */ - private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, PlanetarySystem system, - int transitDays) { + private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, PlanetarySystem system, int transitDays) { boolean found = false; String report = ""; @@ -3455,17 +3256,14 @@ private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, Pl } if (null != system) { - target = system.getPrimaryPlanet().getAcquisitionMods(target, getLocalDate(), - getCampaignOptions(), getFaction(), acquisition.getTechBase() == Part.T_CLAN); + target = system.getPrimaryPlanet().getAcquisitionMods(target, getLocalDate(), getCampaignOptions(), getFaction(), acquisition.getTechBase() == Part.T_CLAN); } report += "attempts to find " + acquisition.getAcquisitionName(); // if impossible, then return if (target.getValue() == TargetRoll.IMPOSSIBLE) { - report += ": " - + target.getDesc() + ""; - if (!getCampaignOptions().isUsePlanetaryAcquisition() - || getCampaignOptions().isPlanetAcquisitionVerbose()) { + report += ": " + target.getDesc() + ""; + if (!getCampaignOptions().isUsePlanetaryAcquisition() || getCampaignOptions().isPlanetAcquisitionVerbose()) { addReport(report); } return false; @@ -3475,9 +3273,7 @@ private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, Pl report += " needs " + target.getValueAsString(); report += " and rolls " + roll + ':'; // Edge reroll, if applicable - if (getCampaignOptions().isUseSupportEdge() && (roll < target.getValue()) && (person != null) - && person.getOptions().booleanOption(PersonnelOptions.EDGE_ADMIN_ACQUIRE_FAIL) - && (person.getCurrentEdge() > 0)) { + if (getCampaignOptions().isUseSupportEdge() && (roll < target.getValue()) && (person != null) && person.getOptions().booleanOption(PersonnelOptions.EDGE_ADMIN_ACQUIRE_FAIL) && (person.getCurrentEdge() > 0)) { person.changeCurrentEdge(-1); roll = Compute.d6(2); report += " failed! but uses Edge to reroll...getting a " + roll + ": "; @@ -3494,8 +3290,7 @@ private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, Pl report = report + acquisition.find(transitDays); found = true; if (person != null) { - if (roll == 12 - && target.getValue() != TargetRoll.AUTOMATIC_SUCCESS) { + if (roll == 12 && target.getValue() != TargetRoll.AUTOMATIC_SUCCESS) { xpGained += getCampaignOptions().getSuccessXP(); } if (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS) { @@ -3508,8 +3303,7 @@ private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, Pl } } else { report = report + acquisition.failToFind(); - if (person != null && roll == 2 - && target.getValue() != TargetRoll.AUTOMATIC_FAIL) { + if (person != null && roll == 2 && target.getValue() != TargetRoll.AUTOMATIC_FAIL) { xpGained += getCampaignOptions().getMistakeXP(); } } @@ -3578,8 +3372,7 @@ public void mothball(Unit u) { u.setMothballTime(u.getMothballTime() - minutes); - String report = tech.getHyperlinkedFullTitle() + " spent " + minutes + " minutes mothballing " - + u.getHyperlinkedName(); + String report = tech.getHyperlinkedFullTitle() + " spent " + minutes + " minutes mothballing " + u.getHyperlinkedName(); if (!u.isMothballing()) { u.completeMothball(); report += ". Mothballing complete."; @@ -3627,8 +3420,7 @@ public void activate(Unit u) { u.setMothballTime(u.getMothballTime() - minutes); - String report = tech.getHyperlinkedFullTitle() + " spent " + minutes + " minutes activating " - + u.getHyperlinkedName(); + String report = tech.getHyperlinkedFullTitle() + " spent " + minutes + " minutes activating " + u.getHyperlinkedName(); tech.setMinutesLeft(tech.getMinutesLeft() - minutes); if (!u.isSelfCrewed()) { @@ -3646,11 +3438,9 @@ public void activate(Unit u) { } public void refit(Refit theRefit) { - Person tech = (theRefit.getUnit().getEngineer() == null) ? theRefit.getTech() - : theRefit.getUnit().getEngineer(); + Person tech = (theRefit.getUnit().getEngineer() == null) ? theRefit.getTech() : theRefit.getUnit().getEngineer(); if (tech == null) { - addReport("No tech is assigned to refit " + theRefit.getOriginalEntity().getShortName() - + ". Refit cancelled."); + addReport("No tech is assigned to refit " + theRefit.getOriginalEntity().getShortName() + ". Refit cancelled."); theRefit.cancel(); return; } @@ -3687,9 +3477,7 @@ public void refit(Refit theRefit) { wrongType = " Warning: wrong tech type for this refit."; } report = report + ", needs " + target.getValueAsString() + " and rolls " + roll + ": "; - if (getCampaignOptions().isUseSupportEdge() && (roll < target.getValue()) - && tech.getOptions().booleanOption(PersonnelOptions.EDGE_REPAIR_FAILED_REFIT) - && (tech.getCurrentEdge() > 0)) { + if (getCampaignOptions().isUseSupportEdge() && (roll < target.getValue()) && tech.getOptions().booleanOption(PersonnelOptions.EDGE_REPAIR_FAILED_REFIT) && (tech.getCurrentEdge() > 0)) { tech.changeCurrentEdge(-1); roll = tech.isRightTechTypeFor(theRefit) ? Compute.d6(2) : Utilities.roll3d6(); // This is needed to update the edge values of individual crewmen @@ -3749,7 +3537,7 @@ public Part fixWarehousePart(Part part, Person tech) { * @param partWork - the {@link IPartWork} to be fixed * @param tech - the {@link Person} who will attempt to fix the part * @return a String of the report that summarizes the outcome of - * the attempt to fix the part + * the attempt to fix the part */ public String fixPart(IPartWork partWork, Person tech) { TargetRoll target = getTargetFor(partWork, tech); @@ -3796,11 +3584,9 @@ public String fixPart(IPartWork partWork, Person tech) { } if (partWork instanceof SpacecraftCoolingSystem) { // Change the string since we're not working on the part itself - report += tech.getHyperlinkedFullTitle() + " attempts to" + action - + "a heat sink"; + report += tech.getHyperlinkedFullTitle() + " attempts to" + action + "a heat sink"; } else { - report += tech.getHyperlinkedFullTitle() + " attempts to" + action - + partWork.getPartName(); + report += tech.getHyperlinkedFullTitle() + " attempts to" + action + partWork.getPartName(); } if (null != partWork.getUnit()) { report += " on " + partWork.getUnit().getName(); @@ -3832,11 +3618,8 @@ public String fixPart(IPartWork partWork, Person tech) { partWork.addTimeSpent(minutesUsed); tech.setMinutesLeft(0); tech.setOvertimeLeft(tech.getOvertimeLeft() - overtimeUsed); - int helpMod = getShorthandedMod( - getAvailableAstechs(minutesUsed, usedOvertime), false); - if ((null != partWork.getUnit()) - && ((partWork.getUnit().getEntity() instanceof Dropship) - || (partWork.getUnit().getEntity() instanceof Jumpship))) { + int helpMod = getShorthandedMod(getAvailableAstechs(minutesUsed, usedOvertime), false); + if ((null != partWork.getUnit()) && ((partWork.getUnit().getEntity() instanceof Dropship) || (partWork.getUnit().getEntity() instanceof Jumpship))) { helpMod = 0; } if (partWork.getShorthandedMod() < helpMod) { @@ -3849,7 +3632,7 @@ public String fixPart(IPartWork partWork, Person tech) { report += " minutes left. Work"; if ((minutesUsed > 0) && (tech.getDailyAvailableTechTime() > 0)) { report += " will be finished "; - int daysLeft = (int) Math.ceil((double) partWork.getTimeLeft() / (double)tech.getDailyAvailableTechTime()); + int daysLeft = (int) Math.ceil((double) partWork.getTimeLeft() / (double) tech.getDailyAvailableTechTime()); if (daysLeft == 1) { report += " tomorrow.
    "; } else { @@ -3869,8 +3652,7 @@ public String fixPart(IPartWork partWork, Person tech) { } else { tech.setMinutesLeft(tech.getMinutesLeft() - minutes); } - int astechMinutesUsed = minutesUsed - * getAvailableAstechs(minutesUsed, usedOvertime); + int astechMinutesUsed = minutesUsed * getAvailableAstechs(minutesUsed, usedOvertime); if (astechPoolMinutes < astechMinutesUsed) { astechMinutesUsed -= astechPoolMinutes; astechPoolMinutes = 0; @@ -3887,44 +3669,31 @@ public String fixPart(IPartWork partWork, Person tech) { roll = Utilities.roll3d6(); wrongType = " Warning: wrong tech type for this repair."; } - report = report + ", needs " + target.getValueAsString() - + " and rolls " + roll + ':'; + report = report + ", needs " + target.getValueAsString() + " and rolls " + roll + ':'; int xpGained = 0; // if we fail and would break a part, here's a chance to use Edge for a // reroll... - if (getCampaignOptions().isUseSupportEdge() - && tech.getOptions().booleanOption(PersonnelOptions.EDGE_REPAIR_BREAK_PART) - && (tech.getCurrentEdge() > 0) - && (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS)) { - if ((getCampaignOptions().isDestroyByMargin() - && (getCampaignOptions().getDestroyMargin() <= (target.getValue() - roll))) - || (!getCampaignOptions().isDestroyByMargin() - // if an elite, primary tech and destroy by margin is NOT on - && ((tech.getExperienceLevel(this, false) == SkillType.EXP_ELITE) - || tech.getPrimaryRole().isVehicleCrew())) // For vessel crews - && (roll < target.getValue())) { + if (getCampaignOptions().isUseSupportEdge() && tech.getOptions().booleanOption(PersonnelOptions.EDGE_REPAIR_BREAK_PART) && (tech.getCurrentEdge() > 0) && (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS)) { + if ((getCampaignOptions().isDestroyByMargin() && (getCampaignOptions().getDestroyMargin() <= (target.getValue() - roll))) || (!getCampaignOptions().isDestroyByMargin() + // if an elite, primary tech and destroy by margin is NOT on + && ((tech.getExperienceLevel(this, false) == SkillType.EXP_ELITE) || tech.getPrimaryRole().isVehicleCrew())) // For vessel crews + && (roll < target.getValue())) { tech.changeCurrentEdge(-1); roll = tech.isRightTechTypeFor(partWork) ? Compute.d6(2) : Utilities.roll3d6(); // This is needed to update the edge values of individual crewmen if (tech.isEngineer()) { tech.setEdgeUsed(tech.getEdgeUsed() + 1); } - report += " failed! and would destroy the part, but uses Edge to reroll...getting a " + roll - + ':'; + report += " failed! and would destroy the part, but uses Edge to reroll...getting a " + roll + ':'; } } if (roll >= target.getValue()) { report = report + partWork.succeed(); - if (getCampaignOptions().isPayForRepairs() - && action.equals(" fix ") - && !(partWork instanceof Armor)) { + if (getCampaignOptions().isPayForRepairs() && action.equals(" fix ") && !(partWork instanceof Armor)) { Money cost = partWork.getUndamagedValue().multipliedBy(0.2); - report += "
    Repairs cost " + - cost.toAmountAndSymbolString() + - " worth of parts."; - finances.debit(TransactionType.REPAIRS, getLocalDate(), cost, - "Repair of " + partWork.getPartName()); + report += "
    Repairs cost " + cost.toAmountAndSymbolString() + " worth of parts."; + finances.debit(TransactionType.REPAIRS, getLocalDate(), cost, "Repair of " + partWork.getPartName()); } if ((roll == 12) && (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS)) { xpGained += getCampaignOptions().getSuccessXP(); @@ -4049,9 +3818,7 @@ public int getDeploymentDeficit(AtBContract contract) { } if (reportUnderStrength) { - addReport(String.format(resources.getString("understrength.text"), - force.getName(), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), - CLOSING_SPAN_TAG, minimumUnitCount)); + addReport(String.format(resources.getString("understrength.text"), force.getName(), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), CLOSING_SPAN_TAG, minimumUnitCount)); } } @@ -4074,17 +3841,13 @@ private void processNewDayATBScenarios() { * unit is actually on route to the planet in case the user is using a custom * system for transport or splitting the unit, etc. */ - if (!getLocation().isOnPlanet() && !getLocation().getJumpPath().isEmpty() - && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystemId())) { + if (!getLocation().isOnPlanet() && !getLocation().getJumpPath().isEmpty() && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystemId())) { // transitTime is measured in days; so we round up to the next whole day contract.setStartAndEndDate(getLocalDate().plusDays((int) Math.ceil(getLocation().getTransitTime()))); - addReport("The start and end dates of " + contract.getName() - + " have been shifted to reflect the current ETA."); + addReport("The start and end dates of " + contract.getName() + " have been shifted to reflect the current ETA."); if (campaignOptions.isUseStratCon() && contract.getMoraleLevel().isRouted()) { - LocalDate newRoutEndDate = contract.getStartDate() - .plusMonths(max(1, Compute.d6() - 3)) - .minusDays(1); + LocalDate newRoutEndDate = contract.getStartDate().plusMonths(max(1, Compute.d6() - 3)).minusDays(1); contract.setRoutEndDate(newRoutEndDate); } @@ -4100,16 +3863,12 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem StratconCampaignState campaignState = contract.getStratconCampaignState(); if (campaignState != null && deficit > 0) { - addReport(String.format(resources.getString("contractBreach.text"), - contract.getName(), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); + addReport(String.format(resources.getString("contractBreach.text"), contract.getName(), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG)); campaignState.updateVictoryPoints(-1); } else if (deficit > 0) { contract.addPlayerMinorBreaches(deficit); - addReport("Failure to meet " + contract.getName() + " requirements resulted in " + deficit - + ((deficit == 1) ? " minor contract breach" : " minor contract breaches")); + addReport("Failure to meet " + contract.getName() + " requirements resulted in " + deficit + ((deficit == 1) ? " minor contract breach" : " minor contract breaches")); } } @@ -4122,8 +3881,7 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem for (final Scenario scenario : contract.getCurrentAtBScenarios()) { if ((scenario.getDate() != null) && scenario.getDate().isBefore(getLocalDate())) { if (getCampaignOptions().isUseStratCon() && (scenario instanceof AtBDynamicScenario)) { - final boolean stub = StratconRulesManager.processIgnoredScenario( - (AtBDynamicScenario) scenario, contract.getStratconCampaignState()); + final boolean stub = StratconRulesManager.processIgnoredScenario((AtBDynamicScenario) scenario, contract.getStratconCampaignState()); if (stub) { if (scenario.getStratConScenarioType().isResupply()) { @@ -4138,8 +3896,7 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem scenario.convertToStub(this, ScenarioStatus.REFUSED_ENGAGEMENT); contract.addPlayerMinorBreach(); - addReport("Failure to deploy for " + scenario.getName() - + " resulted in a minor contract breach."); + addReport("Failure to deploy for " + scenario.getName() + " resulted in a minor contract breach."); } } } @@ -4174,26 +3931,20 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem forceIds.get(forceId).setScenarioId(atBScenario.getId(), this); atBScenario.addForces(forceId); - addReport(MessageFormat.format( - resources.getString("atbScenarioTodayWithForce.format"), - atBScenario.getName(), forceIds.get(forceId).getName())); + addReport(MessageFormat.format(resources.getString("atbScenarioTodayWithForce.format"), atBScenario.getName(), forceIds.get(forceId).getName())); MekHQ.triggerEvent(new DeploymentChangedEvent(forceIds.get(forceId), atBScenario)); } else { if (atBScenario.getHasTrack()) { - addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), - atBScenario.getName())); + addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), atBScenario.getName())); } else { - addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), - atBScenario.getName())); + addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), atBScenario.getName())); } } } else { if (atBScenario.getHasTrack()) { - addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), - atBScenario.getName())); + addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), atBScenario.getName())); } else { - addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), - atBScenario.getName())); + addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), atBScenario.getName())); } } } @@ -4249,8 +4000,7 @@ private void processNewDayATB() { if (contract.getContractType().isGarrisonDuty()) { report = resources.getString("garrisonDutyRouted.text"); } else if (oldMorale != newMorale) { - report = String.format(resources.getString("contractMoraleReport.text"), - newMorale, contract.getName(), newMorale.getToolTipText()); + report = String.format(resources.getString("contractMoraleReport.text"), newMorale, contract.getName(), newMorale.getToolTipText()); } if (!report.isBlank()) { @@ -4259,8 +4009,7 @@ private void processNewDayATB() { // Resupply if (getCampaignOptions().isUseStratCon()) { - boolean inLocation = location.isOnPlanet() - && location.getCurrentSystem().equals(contract.getSystem()); + boolean inLocation = location.isOnPlanet() && location.getCurrentSystem().equals(contract.getSystem()); if (inLocation) { processResupply(contract); @@ -4278,8 +4027,7 @@ private void processNewDayATB() { for (AtBContract contract : getActiveAtBContracts()) { if (campaignOptions.isUseGenericBattleValue()) { if (contract.getStartDate().equals(getLocalDate())) { - if (getCampaignOptions().isUseGenericBattleValue() - && BatchallFactions.usesBatchalls(contract.getEnemyCode())) { + if (getCampaignOptions().isUseGenericBattleValue() && BatchallFactions.usesBatchalls(contract.getEnemyCode())) { contract.setBatchallAccepted(contract.initiateBatchall(this)); } } @@ -4399,9 +4147,7 @@ public void processNewDayPersonnel() { } if (!personnelWhoAdvancedInXP.isEmpty()) { - addReport(String.format(resources.getString("gainedExperience.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - personnelWhoAdvancedInXP.size(), CLOSING_SPAN_TAG)); + addReport(String.format(resources.getString("gainedExperience.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), personnelWhoAdvancedInXP.size(), CLOSING_SPAN_TAG)); } } @@ -4460,7 +4206,7 @@ private void processWeeklyEdgeResets(Person person) { * Note: Bondsmen are exempt from this restriction and are eligible for vocational XP. * * - * @param person the {@link Person} whose monthly vocational XP is to be processed + * @param person the {@link Person} whose monthly vocational XP is to be processed * @param vocationalXpRate the amount of XP awarded on a successful roll * @return {@code true} if XP was successfully awarded during the process, {@code false} otherwise */ @@ -4523,14 +4269,8 @@ private void processWeeklyRelationshipEvents(Person person) { */ private void processAnniversaries(Person person) { if ((person.getRank().isOfficer()) || (!getCampaignOptions().isAnnounceOfficersOnly())) { - if ((person.getBirthday(getGameYear()).isEqual(getLocalDate())) - && (campaignOptions.isAnnounceBirthdays())) { - addReport(String.format(resources.getString("anniversaryBirthday.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities - .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - person.getAge(getLocalDate()), - CLOSING_SPAN_TAG)); + if ((person.getBirthday(getGameYear()).isEqual(getLocalDate())) && (campaignOptions.isAnnounceBirthdays())) { + addReport(String.format(resources.getString("anniversaryBirthday.text"), person.getHyperlinkedFullTitle(), ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), person.getAge(getLocalDate()), CLOSING_SPAN_TAG)); } LocalDate recruitmentDate = person.getRecruitment(); @@ -4538,23 +4278,13 @@ private void processAnniversaries(Person person) { LocalDate recruitmentAnniversary = recruitmentDate.withYear(getGameYear()); int yearsOfEmployment = (int) ChronoUnit.YEARS.between(recruitmentDate, currentDay); - if ((recruitmentAnniversary.isEqual(getLocalDate())) - && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { - addReport(String.format(resources.getString("anniversaryRecruitment.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities - .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - yearsOfEmployment, CLOSING_SPAN_TAG, name)); + if ((recruitmentAnniversary.isEqual(getLocalDate())) && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { + addReport(String.format(resources.getString("anniversaryRecruitment.text"), person.getHyperlinkedFullTitle(), ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), yearsOfEmployment, CLOSING_SPAN_TAG, name)); } } } else if ((person.getAge(getLocalDate()) == 18) && (campaignOptions.isAnnounceChildBirthdays())) { if (person.getBirthday(getGameYear()).isEqual(getLocalDate())) { - addReport(String.format(resources.getString("anniversaryBirthday.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities - .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - person.getAge(getLocalDate()), - CLOSING_SPAN_TAG)); + addReport(String.format(resources.getString("anniversaryBirthday.text"), person.getHyperlinkedFullTitle(), ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), person.getAge(getLocalDate()), CLOSING_SPAN_TAG)); } } } @@ -4630,11 +4360,8 @@ public void processNewDayUnits() { doMaintenance(u); } catch (Exception e) { - logger.error(String.format( - "Unable to perform maintenance on %s (%s) due to an error", - u.getName(), u.getId().toString()), e); - addReport(String.format("ERROR: An error occurred performing maintenance on %s, check the log", - u.getName())); + logger.error(String.format("Unable to perform maintenance on %s (%s) due to an error", u.getName(), u.getId().toString()), e); + addReport(String.format("ERROR: An error occurred performing maintenance on %s, check the log", u.getName())); } } @@ -4682,24 +4409,15 @@ public void processNewDayUnits() { try { fixPart(part, tech); } catch (Exception e) { - logger.error(String.format( - "Could not perform overnight maintenance on %s (%d) due to an error", - part.getName(), part.getId()), e); - addReport(String.format( - "ERROR: an error occurred performing overnight maintenance on %s, check the log", - part.getName())); + logger.error(String.format("Could not perform overnight maintenance on %s (%d) due to an error", part.getName(), part.getId()), e); + addReport(String.format("ERROR: an error occurred performing overnight maintenance on %s, check the log", part.getName())); } } else { - addReport(String.format( - "%s looks at %s, recalls his total lack of skill for working with such technology, then slowly puts the tools down before anybody gets hurt.", - tech.getHyperlinkedFullTitle(), part.getName())); + addReport(String.format("%s looks at %s, recalls his total lack of skill for working with such technology, then slowly puts the tools down before anybody gets hurt.", tech.getHyperlinkedFullTitle(), part.getName())); part.setTech(null); } } else { - JOptionPane.showMessageDialog(null, - "Could not find tech for part: " + part.getName() + " on unit: " - + part.getUnit().getHyperlinkedName(), - "Invalid Auto-continue", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "Could not find tech for part: " + part.getName() + " on unit: " + part.getUnit().getHyperlinkedName(), "Invalid Auto-continue", JOptionPane.ERROR_MESSAGE); } // check to see if this part can now be combined with other spare parts @@ -4785,7 +4503,7 @@ public boolean newDay() { // Clear Reports currentReport.clear(); - currentReportHTML=""; + currentReportHTML = ""; newReports.clear(); personnelWhoAdvancedInXP.clear(); beginReport("" + MekHQ.getMHQOptions().getLongDisplayFormattedDate(getLocalDate()) + ""); @@ -4848,8 +4566,7 @@ public boolean newDay() { finances.newDay(this, yesterday, getLocalDate()); // process removal of old personnel data on the last day of each month - if ((campaignOptions.isUsePersonnelRemoval()) - && (currentDay.getMonth().length(false) == currentDay.getDayOfMonth())) { + if ((campaignOptions.isUsePersonnelRemoval()) && (currentDay.getMonth().length(false) == currentDay.getDayOfMonth())) { processPersonnelRemoval(); } @@ -5000,7 +4717,7 @@ private void processRandomDependents() { * random * dependent removal. * - * @param dependents The list of dependents. + * @param dependents The list of dependents. * @param dependentCapacity The maximum number of dependents allowed. * @return The updated number of dependents. */ @@ -5020,8 +4737,7 @@ int dependentsRollForRemoval(List dependents, int dependentCapacity) { int targetNumber = 5 - getAtBUnitRatingMod(); if (roll <= targetNumber) { - addReport(String.format(resources.getString("dependentLeavesForce.text"), - dependent.getFullTitle())); + addReport(String.format(resources.getString("dependentLeavesForce.text"), dependent.getFullTitle())); removePerson(dependent, false); } @@ -5032,11 +4748,10 @@ int dependentsRollForRemoval(List dependents, int dependentCapacity) { } /** - * @return The lower integer value between the given input and the randomly - * generated integer. - * * @param firstRoll The input integer to compare with the randomly generated * integer. + * @return The lower integer value between the given input and the randomly + * generated integer. */ private int getLowerRandomInt(int firstRoll) { int secondRoll = Compute.randomInt(100); @@ -5049,7 +4764,7 @@ private int getLowerRandomInt(int firstRoll) { * @param dependent the person to check * @param currentDate the current date * @return {@code true} if the person is eligible for removal, {@code false} - * otherwise + * otherwise */ boolean isRemovalEligible(Person dependent, LocalDate currentDate) { boolean hasNonAdultChildren = dependent.getGenealogy().hasNonAdultChildren(currentDate); @@ -5082,8 +4797,7 @@ void dependentsAddNew(int dependentCount, int dependentCapacity) { recruitPerson(dependent, PrisonerStatus.FREE, true, false); - addReport(String.format(resources.getString("dependentJoinsForce.text"), - dependent.getHyperlinkedFullTitle())); + addReport(String.format(resources.getString("dependentJoinsForce.text"), dependent.getHyperlinkedFullTitle())); dependentCount++; } @@ -5130,9 +4844,7 @@ private boolean shouldRemovePerson(Person person, PersonnelStatus status) { // with at least one member still present in the campaign Map> family = person.getGenealogy().getFamily(); - if (family.keySet().stream() - .flatMap(relationshipType -> family.get(relationshipType).stream()) - .anyMatch(relation -> relation.getStatus().isDepartedUnit())) { + if (family.keySet().stream().flatMap(relationshipType -> family.get(relationshipType).stream()).anyMatch(relation -> relation.getStatus().isDepartedUnit())) { return false; } @@ -5149,9 +4861,7 @@ private boolean shouldRemovePerson(Person person, PersonnelStatus status) { // *AND* // is dead (and we're not exempting the cemetery) *OR* is retired (and we're not // exempting retirees) - return (retirementMonthValue < (getLocalDate().getMonthValue() + 1)) && - (((status.isDead()) && (!campaignOptions.isUseRemovalExemptCemetery())) - || ((status.isRetired()) && (!campaignOptions.isUseRemovalExemptRetirees()))); + return (retirementMonthValue < (getLocalDate().getMonthValue() + 1)) && (((status.isDead()) && (!campaignOptions.isUseRemovalExemptCemetery())) || ((status.isRetired()) && (!campaignOptions.isUseRemovalExemptRetirees()))); } /** @@ -5218,10 +4928,7 @@ private void processFatigueNewDay() { } public @Nullable Person getFlaggedCommander() { - return getPersonnel().stream() - .filter(Person::isCommander) - .findFirst() - .orElse(null); + return getPersonnel().stream().filter(Person::isCommander).findFirst().orElse(null); } /** @@ -5288,9 +4995,7 @@ public void removeUnit(UUID id) { // If this unit was assigned to a transport ship, remove it from the transport if (unit.hasTransportShipAssignment()) { - unit.getTransportShipAssignment() - .getTransportShip() - .unloadFromTransportShip(unit); + unit.getTransportShipAssignment().getTransportShip().unloadFromTransportShip(unit); } // remove from automatic mothballing @@ -5348,10 +5053,8 @@ public void removePerson(final @Nullable Person person, final boolean log) { public void removeAllPatientsFor(Person doctor) { for (Person p : getPersonnel()) { - if (null != p.getDoctorId() - && p.getDoctorId().equals(doctor.getId())) { - p.setDoctorId(null, getCampaignOptions() - .getNaturalHealingWaitingPeriod()); + if (null != p.getDoctorId() && p.getDoctorId().equals(doctor.getId())) { + p.setDoctorId(null, getCampaignOptions().getNaturalHealingWaitingPeriod()); } } } @@ -5364,9 +5067,7 @@ public void removeScenario(final Scenario scenario) { // run through the stratcon campaign state where applicable and remove the // "parent" scenario as well - if ((mission instanceof AtBContract) && - (((AtBContract) mission).getStratconCampaignState() != null) && - (scenario instanceof AtBDynamicScenario)) { + if ((mission instanceof AtBContract) && (((AtBContract) mission).getStratconCampaignState() != null) && (scenario instanceof AtBDynamicScenario)) { ((AtBContract) mission).getStratconCampaignState().removeStratconScenario(scenario.getId()); } } @@ -5443,16 +5144,14 @@ public void removeUnitFromForce(Unit u) { force.removeUnit(this, u.getId(), true); u.setForceId(Force.FORCE_NONE); u.setScenarioId(-1); - if (u.getEntity().hasNavalC3() - && u.getEntity().calculateFreeC3Nodes() < 5) { + if (u.getEntity().hasNavalC3() && u.getEntity().calculateFreeC3Nodes() < 5) { Vector removedUnits = new Vector<>(); removedUnits.add(u); removeUnitsFromNetwork(removedUnits); u.getEntity().setC3MasterIsUUIDAsString(null); u.getEntity().setC3Master(null, true); refreshNetworks(); - } else if (u.getEntity().hasC3i() - && u.getEntity().calculateFreeC3Nodes() < 5) { + } else if (u.getEntity().hasC3i() && u.getEntity().calculateFreeC3Nodes() < 5) { Vector removedUnits = new Vector<>(); removedUnits.add(u); removeUnitsFromNetwork(removedUnits); @@ -5481,9 +5180,7 @@ public void removeUnitFromForce(Unit u) { if (unit != null) { return getForceFor(unit); } else if (person.isTech()) { - return forceIds.values().stream() - .filter(force -> person.getId().equals(force.getTechID())) - .findFirst().orElse(null); + return forceIds.values().stream().filter(force -> person.getId().equals(force.getTechID())).findFirst().orElse(null); } return null; @@ -5526,8 +5223,7 @@ public void restore() { // Aerospace parts have changed after 0.45.4. Reinitialize parts for Small Craft // and up - if (unit.getEntity().hasETypeFlag(Entity.ETYPE_JUMPSHIP) - || unit.getEntity().hasETypeFlag(Entity.ETYPE_SMALL_CRAFT)) { + if (unit.getEntity().hasETypeFlag(Entity.ETYPE_JUMPSHIP) || unit.getEntity().hasETypeFlag(Entity.ETYPE_SMALL_CRAFT)) { unitsToCheck.add(unit); } } @@ -5568,8 +5264,7 @@ public void cleanUp() { if (p.getGenealogy().hasSpouse()) { if (!personnel.containsKey(p.getGenealogy().getSpouse().getId())) { p.getGenealogy().setSpouse(null); - if (!getCampaignOptions().isKeepMarriedNameUponSpouseDeath() - && (p.getMaidenName() != null)) { + if (!getCampaignOptions().isKeepMarriedNameUponSpouseDeath() && (p.getMaidenName() != null)) { p.setSurname(p.getMaidenName()); } p.setMaidenName(null); @@ -5725,8 +5420,7 @@ public void setReputation(ReputationController reputation) { private void addInMemoryLogHistory(LogEntry le) { if (!inMemoryLogHistory.isEmpty()) { - while (ChronoUnit.DAYS.between(inMemoryLogHistory.get(0).getDate(), - le.getDate()) > MHQConstants.MAX_HISTORICAL_LOG_DAYS) { + while (ChronoUnit.DAYS.between(inMemoryLogHistory.get(0).getDate(), le.getDate()) > MHQConstants.MAX_HISTORICAL_LOG_DAYS) { // we've hit the max size for the in-memory based on the UI display limit prune // the oldest entry inMemoryLogHistory.remove(0); @@ -5810,8 +5504,7 @@ public void addFunds(final Money quantity) { addFunds(TransactionType.MISCELLANEOUS, quantity, null); } - public void addFunds(final TransactionType type, final Money quantity, - @Nullable String description) { + public void addFunds(final TransactionType type, final Money quantity, @Nullable String description) { if ((description == null) || description.isEmpty()) { description = "Rich Uncle"; } @@ -5821,8 +5514,7 @@ public void addFunds(final TransactionType type, final Money quantity, addReport("Funds added : " + quantityString + " (" + description + ')'); } - public void removeFunds(final TransactionType type, final Money quantity, - @Nullable String description) { + public void removeFunds(final TransactionType type, final Money quantity, @Nullable String description) { if ((description == null) || description.isEmpty()) { description = "Rich Uncle"; } @@ -5837,14 +5529,14 @@ public void removeFunds(final TransactionType type, final Money quantity, * Generic method for paying Personnel (Person) in the company. * Debits money from the campaign and if the campaign tracks * total earnings it will account for that. - * @param type TransactionType being debited - * @param quantity total money - it's usually displayed outside of this method - * @param description String displayed in the ledger and report + * + * @param type TransactionType being debited + * @param quantity total money - it's usually displayed outside of this method + * @param description String displayed in the ledger and report * @param individualPayouts Map of Person to the Money they're owed */ public void payPersonnel(TransactionType type, Money quantity, String description, Map individualPayouts) { - getFinances().debit(type, getLocalDate(), quantity, description, - individualPayouts, getCampaignOptions().isTrackTotalEarnings()); + getFinances().debit(type, getLocalDate(), quantity, description, individualPayouts, getCampaignOptions().isTrackTotalEarnings()); String quantityString = quantity.toAmountAndSymbolString(); addReport("Funds removed : " + quantityString + " (" + description + ')'); @@ -5895,7 +5587,7 @@ public FameAndInfamyController getFameAndInfamy() { * and operational demands over time.

    * * @return A {@link List} of {@link Unit} objects that are set for automated - * mothballing. Returns an empty list if no units are configured. + * mothballing. Returns an empty list if no units are configured. */ public List getAutomatedMothballUnits() { return automatedMothballUnits; @@ -6092,8 +5784,7 @@ public void writeToXML(final PrintWriter pw) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchType", shipSearchType); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchResult", shipSearchResult); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchExpiration", getShipSearchExpiration()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveBehaviorSettings", - autoResolveBehaviorSettings.getDescription()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveBehaviorSettings", autoResolveBehaviorSettings.getDescription()); } retirementDefectionTracker.writeToXML(pw, indent); @@ -6270,8 +5961,7 @@ public void setRankSystem(final @Nullable RankSystem rankSystem) { setRankSystemDirect(rankSystem); // Finally, we fix all personnel ranks and ensure they are properly set - getPersonnel().stream().filter(person -> person.getRankSystem().equals(oldRankSystem)) - .forEach(person -> person.setRankSystem(rankValidator, rankSystem)); + getPersonnel().stream().filter(person -> person.getRankSystem().equals(oldRankSystem)).forEach(person -> person.setRankSystem(rankValidator, rankSystem)); } public void setRankSystemDirect(final RankSystem rankSystem) { @@ -6288,7 +5978,7 @@ public void setRankSystemDirect(final RankSystem rankSystem) { * (true - use skill tiebreaker, false - do not use * skill tiebreaker) * @return the highest ranked person from the list, or null if the provided - * personnel list is empty + * personnel list is empty */ public Person getHighestRankedPerson(List personnel, boolean useSkillTiebreaker) { Person highestRankedPerson = null; @@ -6369,8 +6059,7 @@ public Accountant getAccountant() { while (!found && jumps < 10000) { jumps++; - double currentG = scoreG.get(current) - + Systems.getInstance().getSystemById(current).getRechargeTime(getLocalDate()); + double currentG = scoreG.get(current) + Systems.getInstance().getSystemById(current).getRechargeTime(getLocalDate()); final String localCurrent = current; Systems.getInstance().visitNearbySystems(Systems.getInstance().getSystemById(current), 30, p -> { @@ -6443,7 +6132,7 @@ public Accountant getAccountant() { * fairly hacky, but * improves slightly on the prior implementation as far as following the * rulebooks goes. - * + *

    * It can be used to calculate total travel costs in the style of FM:Mercs * (excludeOwnTransports * and campaignOpsCosts set to false), to calculate leased/rented travel costs @@ -6494,8 +6183,7 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign int noASF = max(nAero - stats.getOccupiedBays(Entity.ETYPE_AEROSPACEFIGHTER), 0); int nolv = max(nLVee - stats.getOccupiedBays(Entity.ETYPE_TANK, true), 0); int nohv = max(nHVee - stats.getOccupiedBays(Entity.ETYPE_TANK), 0); - int noinf = max( - stats.getNumberOfUnitsByType(Entity.ETYPE_INFANTRY) - stats.getOccupiedBays(Entity.ETYPE_INFANTRY), 0); + int noinf = max(stats.getNumberOfUnitsByType(Entity.ETYPE_INFANTRY) - stats.getOccupiedBays(Entity.ETYPE_INFANTRY), 0); int noBA = max(nBA - stats.getOccupiedBays(Entity.ETYPE_BATTLEARMOR), 0); int noProto = max(nProto - stats.getOccupiedBays(Entity.ETYPE_PROTOMEK), 0); int freehv = max(stats.getTotalHeavyVehicleBays() - stats.getOccupiedBays(Entity.ETYPE_TANK), 0); @@ -6738,15 +6426,10 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign if (!u.isMothballed()) { Entity e = u.getEntity(); if ((e.getEntityType() & Entity.ETYPE_DROPSHIP) != 0) { - ownDropshipCost = ownDropshipCost.plus( - mekDropshipCost.multipliedBy(u.getMekCapacity()).dividedBy(averageDropshipMekCapacity)); - ownDropshipCost = ownDropshipCost.plus( - asfDropshipCost.multipliedBy(u.getASFCapacity()).dividedBy(averageDropshipASFCapacity)); - ownDropshipCost = ownDropshipCost.plus(vehicleDropshipCost - .multipliedBy(u.getHeavyVehicleCapacity() + u.getLightVehicleCapacity()) - .dividedBy(averageDropshipVehicleCapacity)); - ownDropshipCost = ownDropshipCost.plus(cargoDropshipCost.multipliedBy(u.getCargoCapacity()) - .dividedBy(averageDropshipCargoCapacity)); + ownDropshipCost = ownDropshipCost.plus(mekDropshipCost.multipliedBy(u.getMekCapacity()).dividedBy(averageDropshipMekCapacity)); + ownDropshipCost = ownDropshipCost.plus(asfDropshipCost.multipliedBy(u.getASFCapacity()).dividedBy(averageDropshipASFCapacity)); + ownDropshipCost = ownDropshipCost.plus(vehicleDropshipCost.multipliedBy(u.getHeavyVehicleCapacity() + u.getLightVehicleCapacity()).dividedBy(averageDropshipVehicleCapacity)); + ownDropshipCost = ownDropshipCost.plus(cargoDropshipCost.multipliedBy(u.getCargoCapacity()).dividedBy(averageDropshipCargoCapacity)); } else if ((e.getEntityType() & Entity.ETYPE_JUMPSHIP) != 0) { ownJumpshipCost = ownDropshipCost.plus(collarCost.multipliedBy(e.getDockingCollars().size())); } @@ -6802,20 +6485,17 @@ public TargetRoll getTargetFor(final IPartWork partWork, final Person tech) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Already being worked on by another team"); } else if (skill == null) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Assigned tech does not have the right skills"); - } else if (!getCampaignOptions().isDestroyByMargin() - && (partWork.getSkillMin() > (skill.getExperienceLevel() - modePenalty))) { + } else if (!getCampaignOptions().isDestroyByMargin() && (partWork.getSkillMin() > (skill.getExperienceLevel() - modePenalty))) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Task is beyond this tech's skill level"); } else if (partWork.getSkillMin() > SkillType.EXP_ELITE) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Task is impossible."); } else if (!partWork.needsFixing() && !partWork.isSalvaging()) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Task is not needed."); - } else if ((partWork instanceof MissingPart) - && (((MissingPart) partWork).findReplacement(false) == null)) { + } else if ((partWork instanceof MissingPart) && (((MissingPart) partWork).findReplacement(false) == null)) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Replacement part not available."); } - final int techTime = isOvertimeAllowed() ? tech.getMinutesLeft() + tech.getOvertimeLeft() - : tech.getMinutesLeft(); + final int techTime = isOvertimeAllowed() ? tech.getMinutesLeft() + tech.getOvertimeLeft() : tech.getMinutesLeft(); if (!(partWork instanceof Refit) && (techTime <= 0)) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "The tech has no time left."); } @@ -6826,8 +6506,7 @@ public TargetRoll getTargetFor(final IPartWork partWork, final Person tech) { } // if this is an infantry refit, then automatic success - if ((partWork instanceof Refit) && (partWork.getUnit() != null) - && partWork.getUnit().isConventionalInfantry()) { + if ((partWork instanceof Refit) && (partWork.getUnit() != null) && partWork.getUnit().isConventionalInfantry()) { return new TargetRoll(TargetRoll.AUTOMATIC_SUCCESS, "infantry refit"); } @@ -6840,12 +6519,10 @@ public TargetRoll getTargetFor(final IPartWork partWork, final Person tech) { // this is ugly, if the mode penalty drops you to green, you drop two // levels instead of two int value = skill.getFinalSkillValue() + modePenalty; - if ((modePenalty > 0) - && (SkillType.EXP_GREEN == (skill.getExperienceLevel() - modePenalty))) { + if ((modePenalty > 0) && (SkillType.EXP_GREEN == (skill.getExperienceLevel() - modePenalty))) { value++; } - final TargetRoll target = new TargetRoll(value, - SkillType.getExperienceLevelName(skill.getExperienceLevel() - modePenalty)); + final TargetRoll target = new TargetRoll(value, SkillType.getExperienceLevelName(skill.getExperienceLevel() - modePenalty)); if (target.getValue() == TargetRoll.IMPOSSIBLE) { return target; } @@ -6857,8 +6534,7 @@ public TargetRoll getTargetFor(final IPartWork partWork, final Person tech) { } final boolean isOvertime; - if (isOvertimeAllowed() - && (tech.isTaskOvertime(partWork) || partWork.hasWorkedOvertime())) { + if (isOvertimeAllowed() && (tech.isTaskOvertime(partWork) || partWork.hasWorkedOvertime())) { target.addModifier(3, "overtime"); isOvertime = true; } else { @@ -6919,8 +6595,7 @@ public TargetRoll getTargetForMaintenance(IPartWork partWork, Person tech) { if (getLocation().isOnPlanet() && campaignOptions.isUsePlanetaryModifiers()) { Planet planet = getLocation().getPlanet(); Atmosphere atmosphere = planet.getAtmosphere(getLocalDate()); - megamek.common.planetaryconditions.Atmosphere planetaryConditions = megamek.common.planetaryconditions.Atmosphere - .getAtmosphere(planet.getPressure(getLocalDate())); + megamek.common.planetaryconditions.Atmosphere planetaryConditions = megamek.common.planetaryconditions.Atmosphere.getAtmosphere(planet.getPressure(getLocalDate())); int temperature = planet.getTemperature(getLocalDate()); if (planet.getGravity() < 0.8) { @@ -6992,58 +6667,38 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition) { return getTargetForAcquisition(acquisition, getLogisticsPerson()); } - public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, - final @Nullable Person person) { + public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, final @Nullable Person person) { return getTargetForAcquisition(acquisition, person, false); } - public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, - final @Nullable Person person, - final boolean checkDaysToWait) { + public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, final @Nullable Person person, final boolean checkDaysToWait) { if (getCampaignOptions().getAcquisitionSkill().equals(CampaignOptions.S_AUTO)) { return new TargetRoll(TargetRoll.AUTOMATIC_SUCCESS, "Automatic Success"); } if (null == person) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, - "No one on your force is capable of acquiring parts"); + return new TargetRoll(TargetRoll.IMPOSSIBLE, "No one on your force is capable of acquiring parts"); } final Skill skill = person.getSkillForWorkingOn(getCampaignOptions().getAcquisitionSkill()); - if (null != getShoppingList().getShoppingItem( - acquisition.getNewEquipment()) - && checkDaysToWait) { - return new TargetRoll( - TargetRoll.AUTOMATIC_FAIL, - "You must wait until the new cycle to check for this part. Further attempts will be added to the shopping list."); - } - if (acquisition.getTechBase() == Part.T_CLAN - && !getCampaignOptions().isAllowClanPurchases()) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, - "You cannot acquire clan parts"); - } - if (acquisition.getTechBase() == Part.T_IS - && !getCampaignOptions().isAllowISPurchases()) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, - "You cannot acquire inner sphere parts"); - } - if (getCampaignOptions().getTechLevel() < Utilities - .getSimpleTechLevel(acquisition.getTechLevel())) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, - "You cannot acquire parts of this tech level"); - } - if (getCampaignOptions().isLimitByYear() - && !acquisition.isIntroducedBy(getGameYear(), useClanTechBase(), getTechFaction())) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, - "It has not been invented yet!"); - } - if (getCampaignOptions().isDisallowExtinctStuff() && - (acquisition.isExtinctIn(getGameYear(), useClanTechBase(), getTechFaction()) - || acquisition.getAvailability() == EquipmentType.RATING_X)) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, - "It is extinct!"); - } - if (getCampaignOptions().isUseAtB() && - getCampaignOptions().isRestrictPartsByMission() && acquisition instanceof Part) { + if (null != getShoppingList().getShoppingItem(acquisition.getNewEquipment()) && checkDaysToWait) { + return new TargetRoll(TargetRoll.AUTOMATIC_FAIL, "You must wait until the new cycle to check for this part. Further attempts will be added to the shopping list."); + } + if (acquisition.getTechBase() == Part.T_CLAN && !getCampaignOptions().isAllowClanPurchases()) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, "You cannot acquire clan parts"); + } + if (acquisition.getTechBase() == Part.T_IS && !getCampaignOptions().isAllowISPurchases()) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, "You cannot acquire inner sphere parts"); + } + if (getCampaignOptions().getTechLevel() < Utilities.getSimpleTechLevel(acquisition.getTechLevel())) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, "You cannot acquire parts of this tech level"); + } + if (getCampaignOptions().isLimitByYear() && !acquisition.isIntroducedBy(getGameYear(), useClanTechBase(), getTechFaction())) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, "It has not been invented yet!"); + } + if (getCampaignOptions().isDisallowExtinctStuff() && (acquisition.isExtinctIn(getGameYear(), useClanTechBase(), getTechFaction()) || acquisition.getAvailability() == EquipmentType.RATING_X)) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, "It is extinct!"); + } + if (getCampaignOptions().isUseAtB() && getCampaignOptions().isRestrictPartsByMission() && acquisition instanceof Part) { int partAvailability = ((Part) acquisition).getAvailability(); EquipmentType et = null; if (acquisition instanceof EquipmentPart) { @@ -7053,8 +6708,7 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, } StringBuilder partAvailabilityLog = new StringBuilder(""); - partAvailabilityLog.append("Part Rating Level: ").append(partAvailability) - .append(" (").append(EquipmentType.ratingNames[partAvailability]).append(')'); + partAvailabilityLog.append("Part Rating Level: ").append(partAvailability).append(" (").append(EquipmentType.ratingNames[partAvailability]).append(')'); /* * Even if we can acquire Clan parts, they have a minimum availability of F for @@ -7072,9 +6726,7 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, * a minimum for non-flamer energy weapons, which was the reason this rule was * included in AtB to begin with. */ - if (et instanceof EnergyWeapon - && !(et instanceof FlamerWeapon) - && partAvailability < EquipmentType.RATING_C) { + if (et instanceof EnergyWeapon && !(et instanceof FlamerWeapon) && partAvailability < EquipmentType.RATING_C) { partAvailability = EquipmentType.RATING_C; partAvailabilityLog.append("
    [Non-Flamer Lasers]"); } @@ -7082,8 +6734,7 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, partAvailability -= 2; partAvailabilityLog.append("
    Autocannon: -2"); } - if (et instanceof GaussWeapon - || et instanceof FlamerWeapon) { + if (et instanceof GaussWeapon || et instanceof FlamerWeapon) { partAvailability--; partAvailabilityLog.append("
    Gauss Rifle or Flamer: -1"); } @@ -7098,28 +6749,20 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, partAvailabilityLog.append("
    Gauss Ammo: -1"); break; } - if (EnumSet.of(Munitions.M_STANDARD).containsAll( - ((AmmoType) et).getMunitionType())) { + if (EnumSet.of(Munitions.M_STANDARD).containsAll(((AmmoType) et).getMunitionType())) { partAvailability--; partAvailabilityLog.append("
    Standard Ammo: -1"); } } } - if (((getGameYear() < 2950) || (getGameYear() > 3040)) - && (acquisition instanceof Armor || acquisition instanceof MissingMekActuator - || acquisition instanceof MissingMekCockpit - || acquisition instanceof MissingMekLifeSupport - || acquisition instanceof MissingMekLocation - || acquisition instanceof MissingMekSensor)) { + if (((getGameYear() < 2950) || (getGameYear() > 3040)) && (acquisition instanceof Armor || acquisition instanceof MissingMekActuator || acquisition instanceof MissingMekCockpit || acquisition instanceof MissingMekLifeSupport || acquisition instanceof MissingMekLocation || acquisition instanceof MissingMekSensor)) { partAvailability--; partAvailabilityLog.append("
    Mek part prior to 2950 or after 3040: - 1"); } int AtBPartsAvailability = findAtBPartsAvailabilityLevel(acquisition, null); - partAvailabilityLog.append("
    Total part availability: ").append(partAvailability) - .append("
    Current campaign availability: ").append(AtBPartsAvailability) - .append(""); + partAvailabilityLog.append("
    Total part availability: ").append(partAvailability).append("
    Current campaign availability: ").append(AtBPartsAvailability).append(""); if (partAvailability > AtBPartsAvailability) { return new TargetRoll(TargetRoll.IMPOSSIBLE, partAvailabilityLog.toString()); } @@ -7146,8 +6789,7 @@ public int findAtBPartsAvailabilityLevel(IAcquisitionWork acquisition, StringBui */ if (hasActiveContract()) { for (final AtBContract c : getActiveAtBContracts()) { - if ((contract == null) - || (c.getPartsAvailabilityLevel() > contract.getPartsAvailabilityLevel())) { + if ((contract == null) || (c.getPartsAvailabilityLevel() > contract.getPartsAvailabilityLevel())) { contract = c; } } @@ -7156,8 +6798,7 @@ public int findAtBPartsAvailabilityLevel(IAcquisitionWork acquisition, StringBui // if we have a contract and it has started if ((null != contract) && contract.isActiveOn(getLocalDate(), true)) { if (reportBuilder != null) { - reportBuilder.append(contract.getPartsAvailabilityLevel()).append(" (").append(contract.getType()) - .append(')'); + reportBuilder.append(contract.getPartsAvailabilityLevel()).append(" (").append(contract.getType()).append(')'); } return contract.getPartsAvailabilityLevel(); } @@ -7188,10 +6829,8 @@ public int findAtBPartsAvailabilityLevel(IAcquisitionWork acquisition, StringBui } public void resetAstechMinutes() { - astechPoolMinutes = Person.PRIMARY_ROLE_SUPPORT_TIME * getNumberPrimaryAstechs() - + Person.PRIMARY_ROLE_OVERTIME_SUPPORT_TIME * getNumberSecondaryAstechs(); - astechPoolOvertime = Person.SECONDARY_ROLE_SUPPORT_TIME * getNumberPrimaryAstechs() - + Person.SECONDARY_ROLE_OVERTIME_SUPPORT_TIME * getNumberSecondaryAstechs(); + astechPoolMinutes = Person.PRIMARY_ROLE_SUPPORT_TIME * getNumberPrimaryAstechs() + Person.PRIMARY_ROLE_OVERTIME_SUPPORT_TIME * getNumberSecondaryAstechs(); + astechPoolOvertime = Person.SECONDARY_ROLE_SUPPORT_TIME * getNumberPrimaryAstechs() + Person.SECONDARY_ROLE_OVERTIME_SUPPORT_TIME * getNumberSecondaryAstechs(); } public void setAstechPoolMinutes(int minutes) { @@ -7239,8 +6878,7 @@ public boolean requiresAdditionalAstechs() { } public int getAstechNeed() { - return (Math.toIntExact(getActivePersonnel().stream().filter(Person::isTech).count()) * 6) - - getNumberAstechs(); + return (Math.toIntExact(getActivePersonnel().stream().filter(Person::isTech).count()) * 6) - getNumberAstechs(); } public void increaseAstechPool(int i) { @@ -7360,14 +6998,10 @@ public int getMedicsPerDoctor() { /** * @return the number of medics in the campaign including any in the temporary - * medic pool + * medic pool */ public int getNumberMedics() { - return getMedicPool() - + Math.toIntExact(getActivePersonnel().stream() - .filter(p -> (p.getPrimaryRole().isMedic() || p.getSecondaryRole().isMedic()) - && !p.isDeployed()) - .count()); + return getMedicPool() + Math.toIntExact(getActivePersonnel().stream().filter(p -> (p.getPrimaryRole().isMedic() || p.getSecondaryRole().isMedic()) && !p.isDeployed()).count()); } public boolean requiresAdditionalMedics() { @@ -7401,9 +7035,9 @@ public GameOptions getGameOptions() { public Vector getGameOptionsVector() { Vector options = new Vector<>(); - for (Enumeration i = gameOptions.getGroups(); i.hasMoreElements();) { + for (Enumeration i = gameOptions.getGroups(); i.hasMoreElements(); ) { IOptionGroup group = i.nextElement(); - for (Enumeration j = group.getOptions(); j.hasMoreElements();) { + for (Enumeration j = group.getOptions(); j.hasMoreElements(); ) { IOption option = j.nextElement(); options.add(option); } @@ -7423,24 +7057,16 @@ public void setGameOptions(final Vector options) { } public void updateCampaignOptionsFromGameOptions() { - getCampaignOptions() - .setUseTactics(getGameOptions().getOption(OptionsConstants.RPG_COMMAND_INIT).booleanValue()); - getCampaignOptions().setUseInitiativeBonus( - getGameOptions().getOption(OptionsConstants.RPG_INDIVIDUAL_INITIATIVE).booleanValue()); + getCampaignOptions().setUseTactics(getGameOptions().getOption(OptionsConstants.RPG_COMMAND_INIT).booleanValue()); + getCampaignOptions().setUseInitiativeBonus(getGameOptions().getOption(OptionsConstants.RPG_INDIVIDUAL_INITIATIVE).booleanValue()); getCampaignOptions().setUseToughness(getGameOptions().getOption(OptionsConstants.RPG_TOUGHNESS).booleanValue()); - getCampaignOptions() - .setUseArtillery(getGameOptions().getOption(OptionsConstants.RPG_ARTILLERY_SKILL).booleanValue()); - getCampaignOptions() - .setUseAbilities(getGameOptions().getOption(OptionsConstants.RPG_PILOT_ADVANTAGES).booleanValue()); + getCampaignOptions().setUseArtillery(getGameOptions().getOption(OptionsConstants.RPG_ARTILLERY_SKILL).booleanValue()); + getCampaignOptions().setUseAbilities(getGameOptions().getOption(OptionsConstants.RPG_PILOT_ADVANTAGES).booleanValue()); getCampaignOptions().setUseEdge(getGameOptions().getOption(OptionsConstants.EDGE).booleanValue()); - getCampaignOptions() - .setUseImplants(getGameOptions().getOption(OptionsConstants.RPG_MANEI_DOMINI).booleanValue()); - getCampaignOptions() - .setQuirks(getGameOptions().getOption(OptionsConstants.ADVANCED_STRATOPS_QUIRKS).booleanValue()); - getCampaignOptions() - .setAllowCanonOnly(getGameOptions().getOption(OptionsConstants.ALLOWED_CANON_ONLY).booleanValue()); - getCampaignOptions().setTechLevel(TechConstants - .getSimpleLevel(getGameOptions().getOption(OptionsConstants.ALLOWED_TECHLEVEL).stringValue())); + getCampaignOptions().setUseImplants(getGameOptions().getOption(OptionsConstants.RPG_MANEI_DOMINI).booleanValue()); + getCampaignOptions().setQuirks(getGameOptions().getOption(OptionsConstants.ADVANCED_STRATOPS_QUIRKS).booleanValue()); + getCampaignOptions().setAllowCanonOnly(getGameOptions().getOption(OptionsConstants.ALLOWED_CANON_ONLY).booleanValue()); + getCampaignOptions().setTechLevel(TechConstants.getSimpleLevel(getGameOptions().getOption(OptionsConstants.ALLOWED_TECHLEVEL).stringValue())); MekHQ.triggerEvent(new OptionsChangedEvent(this)); } @@ -7560,20 +7186,18 @@ public int getAtBUnitRatingMod() { return IUnitRating.DRAGOON_C; } - return getCampaignOptions().getUnitRatingMethod().isFMMR() ? getUnitRating().getUnitRatingAsInteger() - : reputation.getAtbModifier(); + return getCampaignOptions().getUnitRatingMethod().isFMMR() ? getUnitRating().getUnitRatingAsInteger() : reputation.getAtbModifier(); } /** * Returns the Strategy skill of the designated commander in the campaign. * * @return The value of the commander's strategy skill if a commander exists, - * otherwise 0. + * otherwise 0. */ public int getCommanderStrategy() { int cmdrStrategy = 0; - if (getFlaggedCommander() != null && - getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) { + if (getFlaggedCommander() != null && getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) { cmdrStrategy = getFlaggedCommander().getSkill(SkillType.S_STRATEGY).getLevel(); } return cmdrStrategy; @@ -7602,11 +7226,9 @@ public void setStartingSystem(final @Nullable Planet planet) { startingSystem = systemList.get(getFaction().getStartingPlanet(getLocalDate())); if (startingSystem == null) { - startingSystem = systemList.get(JOptionPane.showInputDialog( - "This faction does not have a starting planet for this era. Please choose a planet.")); + startingSystem = systemList.get(JOptionPane.showInputDialog("This faction does not have a starting planet for this era. Please choose a planet.")); while (startingSystem == null) { - startingSystem = systemList.get(JOptionPane.showInputDialog( - "This planet you entered does not exist. Please choose a valid planet.")); + startingSystem = systemList.get(JOptionPane.showInputDialog("This planet you entered does not exist. Please choose a valid planet.")); } } } else { @@ -7760,8 +7382,7 @@ public void refreshNetworks() { for (Entity e : game.getEntitiesVector()) { // C3 Checks if (entity.hasC3()) { - if ((entity.getC3MasterIsUUIDAsString() != null) - && entity.getC3MasterIsUUIDAsString().equals(e.getC3UUIDAsString())) { + if ((entity.getC3MasterIsUUIDAsString() != null) && entity.getC3MasterIsUUIDAsString().equals(e.getC3UUIDAsString())) { entity.setC3Master(e, false); break; } @@ -7773,9 +7394,7 @@ public void refreshNetworks() { // Well, they're the same value of 6... while (pos < Entity.MAX_C3i_NODES) { // We've found a network, join it. - if ((entity.getNC3NextUUIDAsString(pos) != null) - && (e.getC3UUIDAsString() != null) - && entity.getNC3NextUUIDAsString(pos).equals(e.getC3UUIDAsString())) { + if ((entity.getNC3NextUUIDAsString(pos) != null) && (e.getC3UUIDAsString() != null) && entity.getNC3NextUUIDAsString(pos).equals(e.getC3UUIDAsString())) { entity.setC3NetId(e); NC3Set = true; break; @@ -7790,9 +7409,7 @@ public void refreshNetworks() { int pos = 0; while (pos < Entity.MAX_C3i_NODES) { // We've found a network, join it. - if ((entity.getC3iNextUUIDAsString(pos) != null) - && (e.getC3UUIDAsString() != null) - && entity.getC3iNextUUIDAsString(pos).equals(e.getC3UUIDAsString())) { + if ((entity.getC3iNextUUIDAsString(pos) != null) && (e.getC3UUIDAsString() != null) && entity.getC3iNextUUIDAsString(pos).equals(e.getC3UUIDAsString())) { entity.setC3NetId(e); C3iSet = true; break; @@ -7810,8 +7427,7 @@ public void disbandNetworkOf(Unit u) { // collect all of the other units on this network to rebuild the uuids Vector networkedUnits = new Vector<>(); for (Unit unit : getUnits()) { - if (null != unit.getEntity().getC3NetId() - && unit.getEntity().getC3NetId().equals(u.getEntity().getC3NetId())) { + if (null != unit.getEntity().getC3NetId() && unit.getEntity().getC3NetId().equals(u.getEntity().getC3NetId())) { networkedUnits.add(unit); } } @@ -7837,8 +7453,7 @@ public void removeUnitsFromNetwork(Vector removedUnits) { if (removedUnits.contains(unit)) { continue; } - if (null != unit.getEntity().getC3NetId() - && unit.getEntity().getC3NetId().equals(network)) { + if (null != unit.getEntity().getC3NetId() && unit.getEntity().getC3NetId().equals(network)) { networkedUnits.add(unit); uuids.add(unit.getEntity().getC3UUIDAsString()); } @@ -7854,11 +7469,9 @@ public void removeUnitsFromNetwork(Vector removedUnits) { for (Unit nUnit : networkedUnits) { if (pos < uuids.size()) { if (nUnit.getEntity().hasNavalC3()) { - nUnit.getEntity().setNC3NextUUIDAsString(pos, - uuids.get(pos)); + nUnit.getEntity().setNC3NextUUIDAsString(pos, uuids.get(pos)); } else { - nUnit.getEntity().setC3iNextUUIDAsString(pos, - uuids.get(pos)); + nUnit.getEntity().setC3iNextUUIDAsString(pos, uuids.get(pos)); } } else { if (nUnit.getEntity().hasNavalC3()) { @@ -7884,8 +7497,7 @@ public void addUnitsToNetwork(Vector addedUnits, String netid) { if (addedUnits.contains(unit)) { continue; } - if (null != unit.getEntity().getC3NetId() - && unit.getEntity().getC3NetId().equals(netid)) { + if (null != unit.getEntity().getC3NetId() && unit.getEntity().getC3NetId().equals(netid)) { networkedUnits.add(unit); uuids.add(unit.getEntity().getC3UUIDAsString()); } @@ -7894,11 +7506,9 @@ public void addUnitsToNetwork(Vector addedUnits, String netid) { for (Unit nUnit : networkedUnits) { if (pos < uuids.size()) { if (nUnit.getEntity().hasNavalC3()) { - nUnit.getEntity().setNC3NextUUIDAsString(pos, - uuids.get(pos)); + nUnit.getEntity().setNC3NextUUIDAsString(pos, uuids.get(pos)); } else { - nUnit.getEntity().setC3iNextUUIDAsString(pos, - uuids.get(pos)); + nUnit.getEntity().setC3iNextUUIDAsString(pos, uuids.get(pos)); } } else { if (nUnit.getEntity().hasNavalC3()) { @@ -7927,8 +7537,7 @@ public Vector getAvailableC3iNetworks() { if (null == en) { continue; } - if (en.hasC3i() && en.calculateFreeC3Nodes() < 5 - && en.calculateFreeC3Nodes() > 0) { + if (en.hasC3i() && en.calculateFreeC3Nodes() < 5 && en.calculateFreeC3Nodes() > 0) { String[] network = new String[2]; network[0] = en.getC3NetId(); network[1] = "" + en.calculateFreeC3Nodes(); @@ -7963,8 +7572,7 @@ public Vector getAvailableNC3Networks() { if (null == en) { continue; } - if (en.hasNavalC3() && en.calculateFreeC3Nodes() < 5 - && en.calculateFreeC3Nodes() > 0) { + if (en.hasNavalC3() && en.calculateFreeC3Nodes() < 5 && en.calculateFreeC3Nodes() > 0) { String[] network = new String[2]; network[0] = en.getC3NetId(); network[1] = "" + en.calculateFreeC3Nodes(); @@ -8043,8 +7651,7 @@ public Vector getAvailableC3MastersForMasters() { public void removeUnitsFromC3Master(Unit master) { List removed = new ArrayList<>(); for (Unit unit : getUnits()) { - if (null != unit.getEntity().getC3MasterIsUUIDAsString() - && unit.getEntity().getC3MasterIsUUIDAsString().equals(master.getEntity().getC3UUIDAsString())) { + if (null != unit.getEntity().getC3MasterIsUUIDAsString() && unit.getEntity().getC3MasterIsUUIDAsString().equals(master.getEntity().getC3UUIDAsString())) { unit.getEntity().setC3MasterIsUUIDAsString(null); unit.getEntity().setC3Master(null, true); removed.add(unit); @@ -8080,8 +7687,7 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { // According to FMM(r) pg 179, both failure and breach lead to no // further payment even though this seems foolish if (contract.getStatus().isSuccess()) { - remainingMoney = contract.getMonthlyPayOut() - .multipliedBy(contract.getMonthsLeft(getLocalDate())); + remainingMoney = contract.getMonthlyPayOut().multipliedBy(contract.getMonthsLeft(getLocalDate())); if (contract instanceof AtBContract) { Money routedPayout = ((AtBContract) contract).getRoutedPayout(); @@ -8096,11 +7702,9 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { // Then, we check if the salvage percent is less than the percent salvaged by // the // unit in question. If it is, then they owe the assigner some cash - if (getCampaignOptions().isOverageRepaymentInFinalPayment() - && (contract.getSalvagePct() < 100.0)) { + if (getCampaignOptions().isOverageRepaymentInFinalPayment() && (contract.getSalvagePct() < 100.0)) { final double salvagePercent = contract.getSalvagePct() / 100.0; - final Money maxSalvage = contract.getSalvagedByEmployer() - .multipliedBy(salvagePercent / (1 - salvagePercent)); + final Money maxSalvage = contract.getSalvagedByEmployer().multipliedBy(salvagePercent / (1 - salvagePercent)); if (contract.getSalvagedByUnit().isGreaterThan(maxSalvage)) { final Money amountToRepay = contract.getSalvagedByUnit().minus(maxSalvage); remainingMoney = remainingMoney.minus(amountToRepay); @@ -8109,15 +7713,13 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { } if (getCampaignOptions().isUseShareSystem()) { - ResourceBundle financeResources = ResourceBundle.getBundle("mekhq.resources.Finances", - MekHQ.getMHQOptions().getLocale()); + ResourceBundle financeResources = ResourceBundle.getBundle("mekhq.resources.Finances", MekHQ.getMHQOptions().getLocale()); if (remainingMoney.isGreaterThan(Money.zero())) { Money shares = remainingMoney.multipliedBy(contract.getSharesPercent()).dividedBy(100); remainingMoney = remainingMoney.minus(shares); - if (getFinances().debit(TransactionType.SALARIES, getLocalDate(), shares, - String.format(financeResources.getString("ContractSharePayment.text"), contract.getName()))) { + if (getFinances().debit(TransactionType.SALARIES, getLocalDate(), shares, String.format(financeResources.getString("ContractSharePayment.text"), contract.getName()))) { addReport(financeResources.getString("DistributedShares.text"), shares.toAmountAndSymbolString()); getFinances().payOutSharesToPersonnel(this, shares); @@ -8126,15 +7728,11 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { } if (remainingMoney.isPositive()) { - getFinances().credit(TransactionType.CONTRACT_PAYMENT, getLocalDate(), remainingMoney, - "Remaining payment for " + contract.getName()); - addReport("Your account has been credited for " + remainingMoney.toAmountAndSymbolString() - + " for the remaining payout from contract " + contract.getName()); + getFinances().credit(TransactionType.CONTRACT_PAYMENT, getLocalDate(), remainingMoney, "Remaining payment for " + contract.getName()); + addReport("Your account has been credited for " + remainingMoney.toAmountAndSymbolString() + " for the remaining payout from contract " + contract.getName()); } else if (remainingMoney.isNegative()) { - getFinances().credit(TransactionType.CONTRACT_PAYMENT, getLocalDate(), remainingMoney, - "Repaying payment overages for " + contract.getName()); - addReport("Your account has been debited for " + remainingMoney.absolute().toAmountAndSymbolString() - + " to repay payment overages occurred during the contract " + contract.getName()); + getFinances().credit(TransactionType.CONTRACT_PAYMENT, getLocalDate(), remainingMoney, "Repaying payment overages for " + contract.getName()); + addReport("Your account has been debited for " + remainingMoney.absolute().toAmountAndSymbolString() + " to repay payment overages occurred during the contract " + contract.getName()); } // This relies on the mission being a Contract, and AtB to be on @@ -8213,10 +7811,8 @@ public int calculatePartTransitTime(int mos) { // now establish minimum date and if this is before LocalDate minimumDate = getLocalDate(); minimumDate = switch (getCampaignOptions().getAcquireMinimumTimeUnit()) { - case CampaignOptions.TRANSIT_UNIT_MONTH -> - minimumDate.plusMonths(getCampaignOptions().getAcquireMinimumTime()); - case CampaignOptions.TRANSIT_UNIT_WEEK -> - minimumDate.plusWeeks(getCampaignOptions().getAcquireMinimumTime()); + case CampaignOptions.TRANSIT_UNIT_MONTH -> minimumDate.plusMonths(getCampaignOptions().getAcquireMinimumTime()); + case CampaignOptions.TRANSIT_UNIT_WEEK -> minimumDate.plusWeeks(getCampaignOptions().getAcquireMinimumTime()); default -> minimumDate.plusDays(getCampaignOptions().getAcquireMinimumTime()); }; @@ -8233,7 +7829,7 @@ public int calculatePartTransitTime(int mos) { * * @param part A part to lookup its current inventory. * @return A PartInventory object detailing the current counts of - * the part on hand, in transit, and ordered. + * the part on hand, in transit, and ordered. * @see PartInventory */ public PartInventory getPartInventory(Part part) { @@ -8296,27 +7892,19 @@ public PartInventory getPartInventory(Part part) { } public void addLoan(Loan loan) { - addReport("You have taken out loan " + loan - + ". Your account has been credited " - + loan.getPrincipal().toAmountAndSymbolString() - + " for the principal amount."); + addReport("You have taken out loan " + loan + ". Your account has been credited " + loan.getPrincipal().toAmountAndSymbolString() + " for the principal amount."); finances.addLoan(loan); MekHQ.triggerEvent(new LoanNewEvent(loan)); - finances.credit(TransactionType.LOAN_PRINCIPAL, getLocalDate(), loan.getPrincipal(), - "Loan principal for " + loan); + finances.credit(TransactionType.LOAN_PRINCIPAL, getLocalDate(), loan.getPrincipal(), "Loan principal for " + loan); } public void payOffLoan(Loan loan) { - if (finances.debit(TransactionType.LOAN_PAYMENT, getLocalDate(), loan.determineRemainingValue(), - "Loan payoff for " + loan)) { - addReport("You have paid off the remaining loan balance of " - + loan.determineRemainingValue().toAmountAndSymbolString() - + " on " + loan); + if (finances.debit(TransactionType.LOAN_PAYMENT, getLocalDate(), loan.determineRemainingValue(), "Loan payoff for " + loan)) { + addReport("You have paid off the remaining loan balance of " + loan.determineRemainingValue().toAmountAndSymbolString() + " on " + loan); finances.removeLoan(loan); MekHQ.triggerEvent(new LoanPaidEvent(loan)); } else { - addReport("You do not have enough funds to pay off " + loan + ""); + addReport("You do not have enough funds to pay off " + loan + ""); } } @@ -8325,8 +7913,7 @@ public void setHealingTimeOptions(int newHeal, int newNaturalHeal) { // times for all // personnel, giving them credit for their current waiting time int currentHeal = getCampaignOptions().getHealingWaitingPeriod(); - int currentNaturalHeal = getCampaignOptions() - .getNaturalHealingWaitingPeriod(); + int currentNaturalHeal = getCampaignOptions().getNaturalHealingWaitingPeriod(); getCampaignOptions().setHealingWaitingPeriod(newHeal); getCampaignOptions().setNaturalHealingWaitingPeriod(newNaturalHeal); @@ -8337,11 +7924,9 @@ public void setHealingTimeOptions(int newHeal, int newNaturalHeal) { if (healDiff != 0 || naturalDiff != 0) { for (Person p : getPersonnel()) { if (p.getDoctorId() != null) { - p.setDaysToWaitForHealing(max( - p.getDaysToWaitForHealing() + healDiff, 1)); + p.setDaysToWaitForHealing(max(p.getDaysToWaitForHealing() + healDiff, 1)); } else { - p.setDaysToWaitForHealing(max( - p.getDaysToWaitForHealing() + naturalDiff, 1)); + p.setDaysToWaitForHealing(max(p.getDaysToWaitForHealing() + naturalDiff, 1)); } } } @@ -8350,8 +7935,7 @@ public void setHealingTimeOptions(int newHeal, int newNaturalHeal) { private CampaignTransporterMap getCampaignTransporterMap(CampaignTransportType campaignTransportType) { if (campaignTransportType.isTacticalTransport()) { return tacticalTransporters; - } - else if (campaignTransportType.isShipTransport()) { + } else if (campaignTransportType.isShipTransport()) { return shipTransporters; } return null; @@ -8375,7 +7959,7 @@ public Map>> getTransports(CampaignTransp * TransporterType and CampaignTransportType * * @param campaignTransportType type of campaign transport - * @param transporterType type of Transporter + * @param transporterType type of Transporter * @return units that have that transport type */ public Set getTransportsByType(CampaignTransportType campaignTransportType, TransporterType transporterType) { @@ -8393,8 +7977,8 @@ public Set getTransportsByType(CampaignTransportType campaignTransportType * open for the SHIP_TRANSPORT type of assignment. * * @param campaignTransportType type (Enum) of TransportedUnitSummary - * @param transporterType type (Enum) of Transporter - * @param unitSize capacity that the transport must be capable of + * @param transporterType type (Enum) of Transporter + * @param unitSize capacity that the transport must be capable of * @return units that have that transport type */ public Set getTransportsByType(CampaignTransportType campaignTransportType, TransporterType transporterType, double unitSize) { @@ -8411,14 +7995,14 @@ private boolean hasShipTransports() { /** * Do we have transports for the kind of transport? + * * @param campaignTransportType class of the TransportDetail * @return true if it has transporters, false otherwise */ public boolean hasTransports(CampaignTransportType campaignTransportType) { if (campaignTransportType.isTacticalTransport()) { return hasTacticalTransports(); - } - else if (campaignTransportType.isShipTransport()) { + } else if (campaignTransportType.isShipTransport()) { return hasShipTransports(); } return false; @@ -8432,8 +8016,7 @@ public void doMaintenance(Unit u) { Person tech = u.getTech(); int minutesUsed = u.getMaintenanceTime(); int astechsUsed = getAvailableAstechs(minutesUsed, false); - boolean maintained = ((tech != null) && (tech.getMinutesLeft() >= minutesUsed) - && !tech.isMothballing()); + boolean maintained = ((tech != null) && (tech.getMinutesLeft() >= minutesUsed) && !tech.isMothballing()); boolean paidMaintenance = true; if (maintained) { // use the time @@ -8454,11 +8037,8 @@ public void doMaintenance(Unit u) { if (u.getDaysSinceMaintenance() >= (getCampaignOptions().getMaintenanceCycleDays() * ruggedMultiplier)) { // maybe use the money if (campaignOptions.isPayForMaintain()) { - if (!(finances.debit(TransactionType.MAINTENANCE, getLocalDate(), u.getMaintenanceCost(), - "Maintenance for " + u.getName()))) { - addReport("You cannot afford to pay maintenance costs for " - + u.getHyperlinkedName() + "!"); + if (!(finances.debit(TransactionType.MAINTENANCE, getLocalDate(), u.getMaintenanceCost(), "Maintenance for " + u.getName()))) { + addReport("You cannot afford to pay maintenance costs for " + u.getHyperlinkedName() + "!"); paidMaintenance = false; } } @@ -8474,8 +8054,7 @@ public void doMaintenance(Unit u) { // concurrent mod problems // put it into a hash - 4 points of damage will mean destruction Map partsToDamage = new HashMap<>(); - StringBuilder maintenanceReport = new StringBuilder( - "" + techName + " performing maintenance

    "); + StringBuilder maintenanceReport = new StringBuilder("" + techName + " performing maintenance

    "); for (Part p : u.getParts()) { try { String partReport = doMaintenanceOnUnitPart(u, p, partsToDamage, paidMaintenance); @@ -8483,12 +8062,8 @@ public void doMaintenance(Unit u) { maintenanceReport.append(partReport).append("
    "); } } catch (Exception e) { - logger.error(String.format( - "Could not perform maintenance on part %s (%d) for %s (%s) due to an error", - p.getName(), p.getId(), u.getName(), u.getId().toString()), e); - addReport(String.format( - "ERROR: An error occurred performing maintenance on %s for unit %s, check the log", - p.getName(), u.getName())); + logger.error(String.format("Could not perform maintenance on part %s (%d) for %s (%s) due to an error", p.getName(), p.getId(), u.getName(), u.getId().toString()), e); + addReport(String.format("ERROR: An error occurred performing maintenance on %s for unit %s, check the log", p.getName(), u.getName())); } } @@ -8515,15 +8090,9 @@ public void doMaintenance(Unit u) { String qualityString; boolean reverse = getCampaignOptions().isReverseQualityNames(); if (quality.toNumeric() > qualityOrig.toNumeric()) { - qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorPositiveHexColor(), - "Overall quality improves from " + qualityOrig.toName(reverse) - + " to " + quality.toName(reverse)); + qualityString = ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor(), "Overall quality improves from " + qualityOrig.toName(reverse) + " to " + quality.toName(reverse)); } else if (quality.toNumeric() < qualityOrig.toNumeric()) { - qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorNegativeHexColor(), - "Overall quality declines from " + qualityOrig.toName(reverse) - + " to " + quality.toName(reverse)); + qualityString = ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "Overall quality declines from " + qualityOrig.toName(reverse) + " to " + quality.toName(reverse)); } else { qualityString = "Overall quality remains " + quality.toName(reverse); } @@ -8535,18 +8104,13 @@ public void doMaintenance(Unit u) { damageString += nDestroy + " parts were destroyed."; } if (!damageString.isEmpty()) { - damageString = "" - + damageString + " [Repair bay]"; + damageString = "" + damageString + " [Repair bay]"; } String paidString = ""; if (!paidMaintenance) { - paidString = "Could not afford maintenance costs, so check is at a penalty."; + paidString = "Could not afford maintenance costs, so check is at a penalty."; } - addReport(techNameLinked + " performs maintenance on " + u.getHyperlinkedName() + ". " + paidString - + qualityString + ". " + damageString + " [Get details]"); + addReport(techNameLinked + " performs maintenance on " + u.getHyperlinkedName() + ". " + paidString + qualityString + ". " + damageString + " [Get details]"); u.resetDaysSinceMaintenance(); } @@ -8655,25 +8219,17 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT break; } if (p.getQuality().toNumeric() > oldQuality.toNumeric()) { - partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorPositiveHexColor(), - "new quality is " + p.getQualityName()); + partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor(), "new quality is " + p.getQualityName()); } else if (p.getQuality().toNumeric() < oldQuality.toNumeric()) { - partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorNegativeHexColor(), - "new quality is " + p.getQualityName()); + partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "new quality is " + p.getQualityName()); } else { partReport += ": quality remains " + p.getQualityName(); } if (null != partsToDamage.get(p)) { if (partsToDamage.get(p) > 3) { - partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorNegativeHexColor(), - "part destroyed"); + partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "part destroyed"); } else { - partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorNegativeHexColor(), - "part damaged"); + partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "part damaged"); } } @@ -8711,8 +8267,7 @@ public void initTimeInRank() { join = e.getDate(); } - if (e.getDesc().startsWith("Joined ") || e.getDesc().startsWith("Freed ") - || e.getDesc().startsWith("Promoted ") || e.getDesc().startsWith("Demoted ")) { + if (e.getDesc().startsWith("Joined ") || e.getDesc().startsWith("Freed ") || e.getDesc().startsWith("Promoted ") || e.getDesc().startsWith("Demoted ")) { join = e.getDate(); } } @@ -8759,23 +8314,16 @@ public void initAtB(boolean newCampaign) { * that 'Mek (which is a less certain assumption) */ for (Person p : getPersonnel()) { - LocalDate join = p.getPersonnelLog().stream() - .filter(e -> e.getDesc().startsWith("Joined ")) - .findFirst() - .map(LogEntry::getDate) - .orElse(null); + LocalDate join = p.getPersonnelLog().stream().filter(e -> e.getDesc().startsWith("Joined ")).findFirst().map(LogEntry::getDate).orElse(null); if ((join != null) && join.equals(founding)) { p.setFounder(true); } - if (p.getPrimaryRole().isMekWarrior() - || (p.getPrimaryRole().isAerospacePilot() && getCampaignOptions().isAeroRecruitsHaveUnits()) - || p.getPrimaryRole().isProtoMekPilot()) { + if (p.getPrimaryRole().isMekWarrior() || (p.getPrimaryRole().isAerospacePilot() && getCampaignOptions().isAeroRecruitsHaveUnits()) || p.getPrimaryRole().isProtoMekPilot()) { for (LogEntry e : p.getPersonnelLog()) { if (e.getDate().equals(join) && e.getDesc().startsWith("Assigned to ")) { String mek = e.getDesc().substring(12); MekSummary ms = MekSummaryCache.getInstance().getMek(mek); - if (null != ms && (p.isFounder() - || ms.getWeightClass() < EntityWeightClass.WEIGHT_ASSAULT)) { + if (null != ms && (p.isFounder() || ms.getWeightClass() < EntityWeightClass.WEIGHT_ASSAULT)) { p.setOriginalUnitWeight(ms.getWeightClass()); if (ms.isClan()) { p.setOriginalUnitTech(Person.TECH_CLAN); @@ -8783,8 +8331,7 @@ public void initAtB(boolean newCampaign) { // TODO : Fix this so we aren't using a hack that just assumes IS2 p.setOriginalUnitTech(Person.TECH_IS2); } - if ((null != p.getUnit()) - && ms.getName().equals(p.getUnit().getEntity().getShortNameRaw())) { + if ((null != p.getUnit()) && ms.getName().equals(p.getUnit().getEntity().getShortNameRaw())) { p.setOriginalUnitId(p.getUnit().getId()); } } @@ -8817,13 +8364,7 @@ public boolean checkOverDueLoans() { Money overdueAmount = getFinances().checkOverdueLoanPayments(this); if (overdueAmount.isPositive()) { // FIXME : Localize - JOptionPane.showMessageDialog( - null, - "You have overdue loan payments totaling " - + overdueAmount.toAmountAndSymbolString() - + "\nYou must deal with these payments before advancing the day.\nHere are some options:\n - Sell off equipment to generate funds.\n - Pay off the collateral on the loan.\n - Default on the loan.\n - Just cheat and remove the loan via GM mode.", - "Overdue Loan Payments", - JOptionPane.WARNING_MESSAGE); + JOptionPane.showMessageDialog(null, "You have overdue loan payments totaling " + overdueAmount.toAmountAndSymbolString() + "\nYou must deal with these payments before advancing the day.\nHere are some options:\n - Sell off equipment to generate funds.\n - Pay off the collateral on the loan.\n - Default on the loan.\n - Just cheat and remove the loan via GM mode.", "Overdue Loan Payments", JOptionPane.WARNING_MESSAGE); return true; } return false; @@ -8834,10 +8375,10 @@ public boolean checkOverDueLoans() { * current date. * * @return An integer representing the user's choice: - * -1 if turnover prompt should not be displayed. - * 0 to indicate the user selected "Employee Turnover". - * 1 to indicate the user selected "Advance Day Regardless". - * 2 to indicate the user selected "Cancel Advance Day". + * -1 if turnover prompt should not be displayed. + * 0 to indicate the user selected "Employee Turnover". + * 1 to indicate the user selected "Advance Day Regardless". + * 2 to indicate the user selected "Cancel Advance Day". */ public int checkTurnoverPrompt() { if (getLocalDate().isBefore(getCampaignStartDate().plusDays(6))) { @@ -8853,9 +8394,7 @@ public int checkTurnoverPrompt() { triggerTurnoverPrompt = getLocalDate().getDayOfMonth() == getLocalDate().lengthOfMonth(); break; case QUARTERLY: - triggerTurnoverPrompt = (getLocalDate().getDayOfMonth() == getLocalDate().lengthOfMonth()) - && (List.of(Month.MARCH, Month.JUNE, Month.SEPTEMBER, Month.DECEMBER) - .contains(getLocalDate().getMonth())); + triggerTurnoverPrompt = (getLocalDate().getDayOfMonth() == getLocalDate().lengthOfMonth()) && (List.of(Month.MARCH, Month.JUNE, Month.SEPTEMBER, Month.DECEMBER).contains(getLocalDate().getMonth())); break; case ANNUALLY: triggerTurnoverPrompt = getLocalDate().getDayOfYear() == getLocalDate().lengthOfYear(); @@ -8879,21 +8418,9 @@ public int checkTurnoverPrompt() { dialogBody = resources.getString("turnoverPersonnelKilled.text"); } - Object[] options = { - resources.getString("turnoverEmployeeTurnoverDialog.text"), - resources.getString("turnoverAdvanceRegardless"), - resources.getString("turnoverCancel.text") - }; + Object[] options = { resources.getString("turnoverEmployeeTurnoverDialog.text"), resources.getString("turnoverAdvanceRegardless"), resources.getString("turnoverCancel.text") }; - return JOptionPane.showOptionDialog( - null, - dialogBody, - dialogTitle, - JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.INFORMATION_MESSAGE, - null, - options, - options[0]); + return JOptionPane.showOptionDialog(null, dialogBody, dialogTitle, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); } /** @@ -8902,10 +8429,7 @@ public int checkTurnoverPrompt() { * @return {@code true} if there are scenarios due, {@code false} otherwise */ public boolean checkScenariosDue() { - return getActiveMissions(true).stream() - .flatMap(m -> m.getCurrentScenarios().stream()) - .anyMatch(s -> (s.getDate() != null) && !(s instanceof AtBScenario) - && !getLocalDate().isBefore(s.getDate())); + return getActiveMissions(true).stream().flatMap(m -> m.getCurrentScenarios().stream()).anyMatch(s -> (s.getDate() != null) && !(s instanceof AtBScenario) && !getLocalDate().isBefore(s.getDate())); } /** @@ -9039,7 +8563,7 @@ public void setAutoResolveBehaviorSettings(BehaviorSettings settings) { * @param isInformal A boolean flag indicating whether the address should be informal * (true for informal, false for formal). * @return A {@link String} representing the appropriate address for the commander, - * either formal or informal. + * either formal or informal. */ public String getCommanderAddress(boolean isInformal) { Person commander = getFlaggedCommander(); @@ -9058,25 +8582,22 @@ public String getCommanderAddress(boolean isInformal) { return switch (commanderGender) { case MALE -> resources.getString("informalAddressMale.text"); case FEMALE -> resources.getString("informalAddressFemale.text"); - case OTHER_MALE, OTHER_FEMALE, RANDOMIZE -> - resources.getString("generalFallbackAddressInformal.text"); + case OTHER_MALE, OTHER_FEMALE, RANDOMIZE -> resources.getString("generalFallbackAddressInformal.text"); }; } String commanderRank = commander.getRankName(); - if (commanderRank.equalsIgnoreCase("None") - || commanderRank.equalsIgnoreCase("-") - || commanderRank.isBlank()) { + if (commanderRank.equalsIgnoreCase("None") || commanderRank.equalsIgnoreCase("-") || commanderRank.isBlank()) { return resources.getString("generalFallbackAddress.text"); } return commanderRank; } - public int stockUpPartsInUse(Set partsInUse) { + public int stockUpPartsInUse(Set partsInUse) { int bought = 0; - for(PartInUse partInUse : partsInUse) { + for (PartInUse partInUse : partsInUse) { int toBuy = findStockUpAmount(partInUse); if (toBuy > 0) { IAcquisitionWork partToBuy = partInUse.getPartToBuy(); @@ -9087,13 +8608,13 @@ public int stockUpPartsInUse(Set partsInUse) { return bought; } - public void stockUpPartsInUseGM(Set partsInUse) { - for(PartInUse partInUse : partsInUse) { + public void stockUpPartsInUseGM(Set partsInUse) { + for (PartInUse partInUse : partsInUse) { int toBuy = findStockUpAmount(partInUse); while (toBuy > 0) { IAcquisitionWork partToBuy = partInUse.getPartToBuy(); getQuartermaster().addPart((Part) partToBuy.getNewEquipment(), 0); - -- toBuy; + --toBuy; } } } @@ -9101,11 +8622,11 @@ public void stockUpPartsInUseGM(Set partsInUse) { private int findStockUpAmount(PartInUse PartInUse) { IAcquisitionWork partToBuy = PartInUse.getPartToBuy(); int inventory = PartInUse.getStoreCount() + PartInUse.getTransferCount() + PartInUse.getPlannedCount(); - int needed = (int)Math.ceil(PartInUse.getRequestedStock()/100.0 * PartInUse.getUseCount()); - int toBuy = needed-inventory; + int needed = (int) Math.ceil(PartInUse.getRequestedStock() / 100.0 * PartInUse.getUseCount()); + int toBuy = needed - inventory; if (PartInUse.getIsBundle() == true) { - toBuy = (int)Math.ceil((float)toBuy * PartInUse.getTonnagePerItem() / 5); + toBuy = (int) Math.ceil((float) toBuy * PartInUse.getTonnagePerItem() / 5); //special case for armor only, as it's bought in 5 ton blocks. Armor is the only kind of item that's assigned isBundle() } @@ -9113,7 +8634,7 @@ private int findStockUpAmount(PartInUse PartInUse) { } //Simple getters and setters for our stock map - public Map getPartsInUseRequestedStockMap() { + public Map getPartsInUseRequestedStockMap() { return partsInUseRequestedStockMap; } @@ -9124,6 +8645,7 @@ public void setPartsInUseRequestedStockMap(Map partsInUseRequest public boolean getIgnoreMothballed() { return ignoreMothballed; } + public void setIgnoreMothballed(boolean ignoreMothballed) { this.ignoreMothballed = ignoreMothballed; } @@ -9131,6 +8653,7 @@ public void setIgnoreMothballed(boolean ignoreMothballed) { public boolean getTopUpWeekly() { return topUpWeekly; } + public void setTopUpWeekly(boolean topUpWeekly) { this.topUpWeekly = topUpWeekly; } @@ -9138,6 +8661,7 @@ public void setTopUpWeekly(boolean topUpWeekly) { public PartQuality getIgnoreSparesUnderQuality() { return ignoreSparesUnderQuality; } + public void setIgnoreSparesUnderQuality(PartQuality ignoreSparesUnderQuality) { this.ignoreSparesUnderQuality = ignoreSparesUnderQuality; } @@ -9146,22 +8670,21 @@ public void setIgnoreSparesUnderQuality(PartQuality ignoreSparesUnderQuality) { public void writePartInUseToXML(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ignoreMothBalled", ignoreMothballed); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "topUpWeekly", topUpWeekly); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ignoreSparesUnderQuality", - ignoreSparesUnderQuality.name()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ignoreSparesUnderQuality", ignoreSparesUnderQuality.name()); MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "partInUseMap"); writePartInUseMapToXML(pw, indent); MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "partInUseMap"); } public void writePartInUseMapToXML(final PrintWriter pw, int indent) { - for(String key : partsInUseRequestedStockMap.keySet()) { + for (String key : partsInUseRequestedStockMap.keySet()) { MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "partInUseMapEntry"); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partInUseMapKey", key); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partInUseMapVal", - partsInUseRequestedStockMap.get(key)); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partInUseMapVal", partsInUseRequestedStockMap.get(key)); MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "partInUseMapEntry"); } } + /** * Retrieves the campaign faction icon for the specified {@link Campaign}. * If a custom icon is defined in the campaign's unit icon configuration, that icon is used. @@ -9174,8 +8697,7 @@ public ImageIcon getCampaignFactionIcon() { StandardForceIcon campaignIcon = getUnitIcon(); if (campaignIcon.getFilename() == null) { - icon = getFactionLogo(this, getFaction().getShortName(), - true); + icon = getFactionLogo(this, getFaction().getShortName(), true); } else { icon = new ImageIcon(campaignIcon.getFilename()); } diff --git a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java index b6e3b4cebb1..f0c13dae0fb 100644 --- a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java +++ b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java @@ -48,13 +48,10 @@ import mekhq.campaign.unit.actions.AdjustLargeCraftAmmoAction; import mekhq.gui.FileDialogs; -import java.awt.Frame; import java.io.File; import java.util.*; import java.util.stream.Collectors; -import javax.swing.JFrame; - /** * This object will be the main workhorse for the scenario * resolution wizard. It will keep track of information and be @@ -450,7 +447,6 @@ private UnitStatus processAlliedUnit(Entity e) { return us; } - /** * This checks whether an entity has any blown off limbs. If the battlefield * was not controlled it marks the limb as destroyed. if the battlefield was @@ -2164,5 +2160,4 @@ public void setEvent(PostGameResolution gve) { public boolean playerHasBattlefieldControl() { return control; } - } diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java index afacf0dd193..db76dfc6717 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java @@ -90,8 +90,7 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) { boolean inBackwater = true; if (currentFactions.size() > 1) { - // More than one faction, if any is *not* periphery, we're not in backwater - // either + // More than one faction, if any is *not* periphery, we're not in backwater either for (Faction f : currentFactions) { if (!f.isPeriphery()) { inBackwater = false; @@ -101,8 +100,7 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) { // Just one faction. Are there any others nearby? Faction onlyFaction = currentFactions.iterator().next(); if (!onlyFaction.isPeriphery()) { - for (PlanetarySystem key : Systems.getInstance().getNearbySystems(campaign.getCurrentSystem(), - 30)) { + for (PlanetarySystem key : Systems.getInstance().getNearbySystems(campaign.getCurrentSystem(), 30)) { for (Faction f : key.getFactionSet(campaign.getLocalDate())) { if (!onlyFaction.equals(f)) { inBackwater = false; @@ -129,11 +127,8 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) { if (campaign.getFaction().isMercenary() || campaign.getFaction().isPirate()) { if (campaign.getCurrentSystem().isHiringHall(campaign.getLocalDate())) { numContracts++; - /* - * Though the rules do not state these modifiers are mutually exclusive, the - * fact that the - * distance of Galatea from a border means that it has no advantage for Mercs - * over border + /* Though the rules do not state these modifiers are mutually exclusive, the fact that the + * distance of Galatea from a border means that it has no advantage for Mercs over border * worlds. Common sense dictates that worlds with hiring halls should not be * subject to the -1 for backwater/interior. */ @@ -142,16 +137,12 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) { } } } else { - /* - * Per IOps Beta, government units determine number of contracts as on a system - * with a great hall - */ + /* Per IOps Beta, government units determine number of contracts as on a system with a great hall */ numContracts++; } /* - * If located on a faction's capital (interpreted as the starting planet for - * that faction), + * If located on a faction's capital (interpreted as the starting planet for that faction), * generate one contract offer for that faction. */ for (Faction f : campaign.getCurrentSystem().getFactionSet(campaign.getLocalDate())) { @@ -231,8 +222,7 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u return generateAtBContract(campaign, employer, unitRatingMod, MAXIMUM_GENERATION_RETRIES); } - private @Nullable AtBContract generateAtBContract(Campaign campaign, @Nullable String employer, int unitRatingMod, - int retries) { + private @Nullable AtBContract generateAtBContract(Campaign campaign, @Nullable String employer, int unitRatingMod, int retries) { if (employer == null) { logger.warn("Could not generate an AtB Contract because there was no employer!"); return null; @@ -272,10 +262,8 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u * (ComStar, Mercs not under contract) are more likely to have garrison-type * contracts and less likely to have battle-type contracts unless at war. */ - if (RandomFactionGenerator.getInstance().getFactionHints() - .isNeutral(Factions.getInstance().getFaction(employer)) && - !RandomFactionGenerator.getInstance().getFactionHints().isAtWarWith( - Factions.getInstance().getFaction(employer), + if (RandomFactionGenerator.getInstance().getFactionHints().isNeutral(Factions.getInstance().getFaction(employer)) && + !RandomFactionGenerator.getInstance().getFactionHints().isAtWarWith(Factions.getInstance().getFaction(employer), Factions.getInstance().getFaction(contract.getEnemyCode()), campaign.getLocalDate())) { if (contract.getContractType().isPlanetaryAssault()) { contract.setContractType(AtBContractType.GARRISON_DUTY); @@ -323,9 +311,8 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u contract.setName(String.format("%s - %s - %s %s", contract.getStartDate().format(DateTimeFormatter.ofPattern("yyyy") - .withLocale(MekHQ.getMHQOptions().getDateLocale())), - employer, - contract.getSystem().getName(contract.getStartDate()), contract.getContractType())); + .withLocale(MekHQ.getMHQOptions().getDateLocale())), employer, + contract.getSystem().getName(contract.getStartDate()), contract.getContractType())); return contract; } @@ -415,8 +402,7 @@ protected AtBContract generateAtBSubcontract(Campaign campaign, contract.setName(String.format("%s - %s - %s Subcontract %s", contract.getStartDate().format(DateTimeFormatter.ofPattern("yyyy") - .withLocale(MekHQ.getMHQOptions().getDateLocale())), - contract.getEmployer(), + .withLocale(MekHQ.getMHQOptions().getDateLocale())), contract.getEmployer(), contract.getSystem().getName(parent.getStartDate()), contract.getContractType())); return contract; @@ -425,30 +411,22 @@ protected AtBContract generateAtBSubcontract(Campaign campaign, /** * Creates and adds a follow-up contract based on the just concluded contract. *

    - * This method generates a new contract (`AtBContract`) as a follow-up to the - * provided `contract`. - * Certain properties of the original contract, such as employer, enemy, skill, - * system location, - * and other details, are carried over or modified as necessary based on the - * contract type. - * The method ensures that the follow-up contract contains all necessary details - * and is + * This method generates a new contract (`AtBContract`) as a follow-up to the provided `contract`. + * Certain properties of the original contract, such as employer, enemy, skill, system location, + * and other details, are carried over or modified as necessary based on the contract type. + * The method ensures that the follow-up contract contains all necessary details and is * correctly initialized. *

    * *

    - * Order Dependency: The operations in this method must match the order - * specified in + * Order Dependency: The operations in this method must match the order specified in * `generateAtBContract` to maintain compatibility and consistency. *

    * * @param campaign the {@link Campaign} to which the follow-up contract belongs. - * This is used for retrieving campaign-wide settings and - * applying modifiers. - * @param contract the {@link AtBContract} that serves as the base for - * generating the follow-up contract. - * Key details from this contract are reused or adapted for the - * follow-up. + * This is used for retrieving campaign-wide settings and applying modifiers. + * @param contract the {@link AtBContract} that serves as the base for generating the follow-up contract. + * Key details from this contract are reused or adapted for the follow-up. */ private void addFollowup(Campaign campaign, AtBContract contract) { @@ -546,8 +524,7 @@ public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract private static double getUnofficialMultiplier(Campaign campaign, AtBContract contract) { double modifier = 0; // we apply these modifiers all together to avoid spiking the final pay - // Adjust pay based on the percentage of the players' forces required by the - // contract + // Adjust pay based on the percentage of the players' forces required by the contract int maximumLanceCount = campaign.getAllCombatTeams().size(); int reserveLanceCount = (int) floor(maximumLanceCount / COMBAT_FORCE_DIVIDER); int requiredCombatTeams = contract.getRequiredCombatTeams(); @@ -577,12 +554,12 @@ private static double getUnofficialMultiplier(Campaign campaign, AtBContract con public void checkForFollowup(Campaign campaign, AtBContract contract) { AtBContractType type = contract.getContractType(); if (type.isDiversionaryRaid() || type.isReconRaid() - || type.isRiotDuty()) { + || type.isRiotDuty()) { int roll = Compute.d6(); if (roll == 6) { addFollowup(campaign, contract); campaign.addReport( - "Your employer has offered a follow-up contract (available on the contract market)."); + "Your employer has offered a follow-up contract (available on the contract market)."); } } } @@ -599,18 +576,12 @@ private void setContractClauses(AtBContract contract, int unitRatingMod, Campaig * the highest admin skill, or higher negotiation if the admin * skills are equal. */ - Person adminCommand = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_COMMAND, SkillType.S_ADMIN, - SkillType.S_NEG); - Person adminTransport = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_TRANSPORT, SkillType.S_ADMIN, - SkillType.S_NEG); - Person adminLogistics = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_LOGISTICS, SkillType.S_ADMIN, - SkillType.S_NEG); - int adminCommandExp = (adminCommand == null) ? SkillType.EXP_ULTRA_GREEN - : adminCommand.getSkill(SkillType.S_ADMIN).getExperienceLevel(); - int adminTransportExp = (adminTransport == null) ? SkillType.EXP_ULTRA_GREEN - : adminTransport.getSkill(SkillType.S_ADMIN).getExperienceLevel(); - int adminLogisticsExp = (adminLogistics == null) ? SkillType.EXP_ULTRA_GREEN - : adminLogistics.getSkill(SkillType.S_ADMIN).getExperienceLevel(); + Person adminCommand = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_COMMAND, SkillType.S_ADMIN, SkillType.S_NEG); + Person adminTransport = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_TRANSPORT, SkillType.S_ADMIN, SkillType.S_NEG); + Person adminLogistics = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_LOGISTICS, SkillType.S_ADMIN, SkillType.S_NEG); + int adminCommandExp = (adminCommand == null) ? SkillType.EXP_ULTRA_GREEN : adminCommand.getSkill(SkillType.S_ADMIN).getExperienceLevel(); + int adminTransportExp = (adminTransport == null) ? SkillType.EXP_ULTRA_GREEN : adminTransport.getSkill(SkillType.S_ADMIN).getExperienceLevel(); + int adminLogisticsExp = (adminLogistics == null) ? SkillType.EXP_ULTRA_GREEN : adminLogistics.getSkill(SkillType.S_ADMIN).getExperienceLevel(); /* Treat government units like merc units that have a retainer contract */ if ((!campaign.getFaction().isMercenary() && !campaign.getFaction().isPirate()) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 074cb04295f..e41367180ac 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -171,8 +171,7 @@ public enum ReinforcementResultsType { * @param contract The AtBContract for the campaign. * @param track The StratCon campaign track. */ - public static void generateScenariosDatesForWeek(Campaign campaign, StratconCampaignState campaignState, - AtBContract contract, StratconTrackState track) { + public static void generateScenariosDatesForWeek(Campaign campaign, StratconCampaignState campaignState, AtBContract contract, StratconTrackState track) { // maps scenarios to force IDs final boolean autoAssignLances = contract.getCommandRights().isIntegrated(); List availableForceIDs = getAvailableForceIDs(campaign, contract); @@ -185,7 +184,8 @@ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCamp case ADVANCING -> scenarioRolls = (int) round(scenarioRolls * 1.33); case DOMINATING -> scenarioRolls = (int) round(scenarioRolls * 1.66); case OVERWHELMING -> scenarioRolls = scenarioRolls * 2; - default -> {} + default -> { + } } for (int scenarioIndex = 0; scenarioIndex < scenarioRolls; scenarioIndex++) { @@ -203,8 +203,7 @@ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCamp if (roll < targetNum) { LocalDate scenarioDate = campaign.getLocalDate().plusDays(randomInt(7)); campaignState.addWeeklyScenario(scenarioDate); - logger.info( - String.format("StratCon Weekly Scenario Roll: %s vs. %s (%s)", roll, targetNum, scenarioDate)); + logger.info(String.format("StratCon Weekly Scenario Roll: %s vs. %s (%s)", roll, targetNum, scenarioDate)); } else { logger.info(String.format("StratCon Weekly Scenario Roll: %s vs. %s", roll, targetNum)); } @@ -238,8 +237,7 @@ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCamp * @param contract The relevant contract. * @param scenarioCount The number of scenarios to generate. */ - public static void generateDailyScenariosForTrack(Campaign campaign, StratconCampaignState campaignState, - AtBContract contract, int scenarioCount) { + public static void generateDailyScenariosForTrack(Campaign campaign, StratconCampaignState campaignState, AtBContract contract, int scenarioCount) { final boolean autoAssignLances = contract.getCommandRights().isIntegrated(); // get this list just so we have it available @@ -275,8 +273,7 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam } } - int calculatedOperationStatus = (int) round( - Math.pow((3 - (double) operationalStatus / unitCount), 2.0)); + int calculatedOperationStatus = (int) round(Math.pow((3 - (double) operationalStatus / unitCount), 2.0)); for (int i = 0; i < calculatedOperationStatus; i++) { availableForcePool.add(forceId); @@ -317,8 +314,7 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam // selected ones StratconScenario scenario; if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { - scenario = generateScenarioForExistingForces(scenarioCoords, - track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track); + scenario = generateScenarioForExistingForces(scenarioCoords, track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track); // otherwise, pick a random force from the avail } else { int randomForceIndex = randomInt(availableForceIDs.size()); @@ -339,8 +335,7 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam // two scenarios on the same coordinates wind up increasing in size if (track.getScenarios().containsKey(scenarioCoords)) { track.getScenarios().get(scenarioCoords).incrementRequiredPlayerLances(); - assignAppropriateExtraForceToScenario(track.getScenarios().get(scenarioCoords), - sortedAvailableForceIDs); + assignAppropriateExtraForceToScenario(track.getScenarios().get(scenarioCoords), sortedAvailableForceIDs); continue; } @@ -362,11 +357,10 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam * @param campaign The current campaign. * @param contract The contract associated with the scenario. * @return A newly generated {@link StratconScenario}, or {@code null} if - * scenario creation fails. + * scenario creation fails. */ public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract) { - return generateExternalScenario(campaign, contract, null, null, - null, false, null); + return generateExternalScenario(campaign, contract, null, null, null, false, null); } /** @@ -375,30 +369,25 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam * This is meant for scenario control on a higher level than the overloading * methods. * - * @param campaign The current campaign. - * @param contract The contract associated with the scenario. - * @param track The {@link StratconTrackState} the scenario should be assigned to, or - * {@code null} to select a random track. - * @param scenarioCoords The {@link StratconCoords} where in the track to place the scenario, or - * {@code null} to select a random hex. If populated, {@code track} cannot be - * {@code null} - * @param template A specific {@link ScenarioTemplate} to use for scenario generation, - * or {@code null} to select scenario template randomly. + * @param campaign The current campaign. + * @param contract The contract associated with the scenario. + * @param track The {@link StratconTrackState} the scenario should be assigned to, or + * {@code null} to select a random track. + * @param scenarioCoords The {@link StratconCoords} where in the track to place the scenario, or + * {@code null} to select a random hex. If populated, {@code track} cannot be + * {@code null} + * @param template A specific {@link ScenarioTemplate} to use for scenario generation, + * or {@code null} to select scenario template randomly. * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top of - * player-allied facilities. - * @param daysTilDeployment How many days beyond current date until the scenario, or {@code null} - * to pick a random date within the next 7 days. + * player-allied facilities. + * @param daysTilDeployment How many days beyond current date until the scenario, or {@code null} + * to pick a random date within the next 7 days. * @return A newly generated {@link StratconScenario}, or {@code null} if scenario creation fails. */ - public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract, - @Nullable StratconTrackState track, - @Nullable StratconCoords scenarioCoords, - @Nullable ScenarioTemplate template, - boolean allowPlayerFacilities, - @Nullable Integer daysTilDeployment) { - // If we're not generating for a specific track, randomly pick one. - if (track == null) { - track = getRandomTrack(contract); + public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract, @Nullable StratconTrackState track, @Nullable StratconCoords scenarioCoords, @Nullable ScenarioTemplate template, boolean allowPlayerFacilities, @Nullable Integer daysTilDeployment) { + // If we're not generating for a specific track, randomly pick one. + if (track == null) { + track = getRandomTrack(contract); if (track == null) { logger.error("Failed to generate a random track, aborting scenario generation."); @@ -409,9 +398,9 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam // Are we automatically assigning lances? boolean autoAssignLances = contract.getCommandRights().isIntegrated(); - // Grab the available lances and sort them by map type - List availableForceIDs = getAvailableForceIDs(campaign, contract); - Map> sortedAvailableForceIDs = sortForcesByMapType(availableForceIDs, campaign); + // Grab the available lances and sort them by map type + List availableForceIDs = getAvailableForceIDs(campaign, contract); + Map> sortedAvailableForceIDs = sortForcesByMapType(availableForceIDs, campaign); // Select the target coords. if (scenarioCoords == null) { @@ -423,14 +412,12 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam return null; } - // If forces are already assigned to the target coordinates, use those instead of randomly - // selected a new force - StratconScenario scenario = null; - if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { - scenario = generateScenarioForExistingForces(scenarioCoords, - track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track, - template, daysTilDeployment); - } + // If forces are already assigned to the target coordinates, use those instead of randomly + // selected a new force + StratconScenario scenario = null; + if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { + scenario = generateScenarioForExistingForces(scenarioCoords, track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track, template, daysTilDeployment); + } // Otherwise, pick a random force from those available // If a template has been specified, remove forces that aren't appropriate for @@ -466,9 +453,8 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam randomForceID = availableForceIDs.get(randomForceIndex); } - scenario = setupScenario(scenarioCoords, randomForceID, campaign, contract, track, - template, false, daysTilDeployment); - } + scenario = setupScenario(scenarioCoords, randomForceID, campaign, contract, track, template, false, daysTilDeployment); + } if (scenario == null) { return null; @@ -499,13 +485,10 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam * @param interceptedForce the {@link Force} that's being intercepted in the * scenario */ - public static @Nullable void generateReinforcementInterceptionScenario( - Campaign campaign, StratconScenario linkedScenario, AtBContract contract, - StratconTrackState track, ScenarioTemplate template, Force interceptedForce) { + public static @Nullable void generateReinforcementInterceptionScenario(Campaign campaign, StratconScenario linkedScenario, AtBContract contract, StratconTrackState track, ScenarioTemplate template, Force interceptedForce) { StratconCoords scenarioCoords = getUnoccupiedCoords(track, false); - StratconScenario scenario = setupScenario(scenarioCoords, interceptedForce.getId(), campaign, - contract, track, template, true, 0); + StratconScenario scenario = setupScenario(scenarioCoords, interceptedForce.getId(), campaign, contract, track, template, true, 0); if (scenario == null) { logger.error("Failed to generate a random interception scenario, aborting scenario generation."); @@ -527,29 +510,23 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam * random one will be * picked. * - * @param campaign The current campaign. - * @param contract The {@link AtBContract} associated with the scenario. - * @param trackState The {@link StratconTrackState} in which the scenario occurs. - * If {@code null}, a random trackState is selected. - * @param template The {@link ScenarioTemplate} for the scenario. - * If {@code null}, the default template is used. + * @param campaign The current campaign. + * @param contract The {@link AtBContract} associated with the scenario. + * @param trackState The {@link StratconTrackState} in which the scenario occurs. + * If {@code null}, a random trackState is selected. + * @param template The {@link ScenarioTemplate} for the scenario. + * If {@code null}, the default template is used. * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top of - * player-allied facilities. - * @param daysTilDeployment How many days until the scenario takes place, or {@code null} to - * pick a random day within the next 7 days. - * + * player-allied facilities. + * @param daysTilDeployment How many days until the scenario takes place, or {@code null} to + * pick a random day within the next 7 days. * @return The created {@link StratconScenario} or @code null}, - * if no {@link ScenarioTemplate} is found or if all coordinates in the - * provided - * {@link StratconTrackState} are occupied (and therefore, scenario - * placement is not possible). + * if no {@link ScenarioTemplate} is found or if all coordinates in the + * provided + * {@link StratconTrackState} are occupied (and therefore, scenario + * placement is not possible). */ - public static @Nullable StratconScenario addHiddenExternalScenario(Campaign campaign, - AtBContract contract, - @Nullable StratconTrackState trackState, - @Nullable ScenarioTemplate template, - boolean allowPlayerFacilities, - @Nullable Integer daysTilDeployment) { + public static @Nullable StratconScenario addHiddenExternalScenario(Campaign campaign, AtBContract contract, @Nullable StratconTrackState trackState, @Nullable ScenarioTemplate template, boolean allowPlayerFacilities, @Nullable Integer daysTilDeployment) { // If we're not generating for a specific track, randomly pick one. if (trackState == null) { trackState = getRandomTrack(contract); @@ -563,15 +540,12 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam StratconCoords coords = getUnoccupiedCoords(trackState, allowPlayerFacilities); if (coords == null) { - logger.error(String.format("Unable to place objective scenario on track %s," + - " as all coords were occupied. Aborting.", - trackState.getDisplayableName())); + logger.error(String.format("Unable to place objective scenario on track %s," + " as all coords were occupied. Aborting.", trackState.getDisplayableName())); return null; } // create scenario - don't assign a force yet - StratconScenario scenario = StratconRulesManager.generateScenario(campaign, contract, - trackState, FORCE_NONE, coords, template, daysTilDeployment); + StratconScenario scenario = StratconRulesManager.generateScenario(campaign, contract, trackState, FORCE_NONE, coords, template, daysTilDeployment); if (scenario == null) { return null; @@ -598,7 +572,7 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam * @param contract The {@link AtBContract} from which the track state will be * fetched. * @return The randomly chosen {@link StratconTrackState}, or {@code null} if no - * tracks are available. + * tracks are available. */ public static @Nullable StratconTrackState getRandomTrack(AtBContract contract) { List tracks = contract.getStratconCampaignState().getTracks(); @@ -625,9 +599,7 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam * @param scenario The {@link StratconScenario} scenario to be * finalized. */ - private static void finalizeBackingScenario(Campaign campaign, AtBContract contract, - @Nullable StratconTrackState track, boolean autoAssignLances, - StratconScenario scenario) { + private static void finalizeBackingScenario(Campaign campaign, AtBContract contract, @Nullable StratconTrackState track, boolean autoAssignLances, StratconScenario scenario) { final AtBDynamicScenario backingScenario = scenario.getBackingScenario(); // First determine if the scenario is a Turning Point (that win/lose will affect CVP) @@ -686,11 +658,9 @@ private static void addCadreDutyTrainees(AtBDynamicScenario backingScenario) { boolean isAirBattle = (mapLocation == LowAtmosphere) || (mapLocation == Space); if (isAirBattle) { - backingScenario.addScenarioModifier( - AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_AIR)); + backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_AIR)); } else { - backingScenario.addScenarioModifier( - AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_GROUND)); + backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_GROUND)); } } @@ -764,8 +734,7 @@ public static void setScenarioParametersFromBiome(StratconTrackState track, Stra StratconBiomeManifest biomeManifest = StratconBiomeManifest.getInstance(); // for non-surface scenarios, we will skip the temperature update - if (backingScenario.getBoardType() != Scenario.T_SPACE && - backingScenario.getBoardType() != Scenario.T_ATMOSPHERE) { + if (backingScenario.getBoardType() != Scenario.T_SPACE && backingScenario.getBoardType() != Scenario.T_ATMOSPHERE) { backingScenario.setTemperature(track.getTemperature()); } @@ -780,13 +749,11 @@ public static void setScenarioParametersFromBiome(StratconTrackState track, Stra // if facility doesn't have a biome temp map or no entry for the current // temperature, use the default one if (facility.getBiomes().isEmpty() || (facility.getBiomeTempMap().floorEntry(kelvinTemp) == null)) { - facilityBiome = biomeManifest.getTempMap(StratconBiomeManifest.TERRAN_FACILITY_BIOME) - .floorEntry(kelvinTemp).getValue(); + facilityBiome = biomeManifest.getTempMap(StratconBiomeManifest.TERRAN_FACILITY_BIOME).floorEntry(kelvinTemp).getValue(); } else { facilityBiome = facility.getBiomeTempMap().floorEntry(kelvinTemp).getValue(); } - terrainType = facilityBiome.allowedTerrainTypes - .get(randomInt(facilityBiome.allowedTerrainTypes.size())); + terrainType = facilityBiome.allowedTerrainTypes.get(randomInt(facilityBiome.allowedTerrainTypes.size())); } else { terrainType = track.getTerrainTile(coords); } @@ -822,17 +789,12 @@ public static void setScenarioParametersFromBiome(StratconTrackState track, Stra private static void swapInPlayerUnits(StratconScenario scenario, Campaign campaign, int explicitForceID) { for (ScenarioForceTemplate sft : scenario.getScenarioTemplate().getAllScenarioForces()) { if (sft.getGenerationMethod() == ForceGenerationMethod.PlayerOrFixedUnitCount.ordinal()) { - int unitCount = (int) scenario.getBackingScenario().getBotUnitTemplates().values().stream() - .filter(template -> template.getForceName().equals(sft.getForceName())) - .count(); + int unitCount = (int) scenario.getBackingScenario().getBotUnitTemplates().values().stream().filter(template -> template.getForceName().equals(sft.getForceName())).count(); // get all the units that have been generated for this template // or the units embedded in bot forces - unitCount += scenario.getBackingScenario().getBotForceTemplates().entrySet().stream() - .filter(tuple -> tuple.getValue().getForceName().equals(sft.getForceName())) - .mapToInt(tuple -> tuple.getKey().getFullEntityList(campaign).size()) - .sum(); + unitCount += scenario.getBackingScenario().getBotForceTemplates().entrySet().stream().filter(tuple -> tuple.getValue().getForceName().equals(sft.getForceName())).mapToInt(tuple -> tuple.getKey().getFullEntityList(campaign).size()).sum(); // now we have a unit count. Don't bother with the next step if we don't have // any substitutions to make @@ -849,8 +811,7 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai try { potentialUnits.add(campaign.getUnit(unitId)); } catch (Exception exception) { - logger.error(String.format("Error retrieving unit (%s): %s", - unitId, exception.getMessage())); + logger.error(String.format("Error retrieving unit (%s): %s", unitId, exception.getMessage())); } } // if we're using a seed force, then units transporting this force are eligible @@ -869,15 +830,12 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai } // if it's the right type of unit and is around - if (forceCompositionMatchesDeclaredUnitType(unit.getEntity().getUnitType(), - sft.getAllowedUnitType()) && - unit.isAvailable() && unit.isFunctional()) { + if (forceCompositionMatchesDeclaredUnitType(unit.getEntity().getUnitType(), sft.getAllowedUnitType()) && unit.isAvailable() && unit.isFunctional()) { // add the unit to the scenario and bench the appropriate bot unit if one is // present scenario.addUnit(unit, sft.getForceName(), false); - AtBDynamicScenarioFactory.benchAllyUnit(unit.getId(), sft.getForceName(), - scenario.getBackingScenario()); + AtBDynamicScenarioFactory.benchAllyUnit(unit.getId(), sft.getForceName(), scenario.getBackingScenario()); unitCount--; // once we've supplied enough units, end the process @@ -905,11 +863,8 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai * @param track The relevant StratCon track. * @return The newly generated {@link StratconScenario}. */ - public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, - Set forceIDs, AtBContract contract, Campaign campaign, - StratconTrackState track) { - return generateScenarioForExistingForces(scenarioCoords, forceIDs, contract, campaign, - track, null, null); + public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, Set forceIDs, AtBContract contract, Campaign campaign, StratconTrackState track) { + return generateScenarioForExistingForces(scenarioCoords, forceIDs, contract, campaign, track, null, null); } /** @@ -930,23 +885,16 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai * {@code null} to * select a random template. * @param daysTilDeployment How many days until the scenario takes place, or {@code null} to - * pick a random day within the next 7 days. + * pick a random day within the next 7 days. * @return The newly generated {@link StratconScenario}. */ - public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, - Set forceIDs, - AtBContract contract, - Campaign campaign, - StratconTrackState track, - @Nullable ScenarioTemplate template, - @Nullable Integer daysTilDeployment) { + public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, Set forceIDs, AtBContract contract, Campaign campaign, StratconTrackState track, @Nullable ScenarioTemplate template, @Nullable Integer daysTilDeployment) { boolean firstForce = true; StratconScenario scenario = null; for (int forceID : forceIDs) { if (firstForce) { - scenario = setupScenario(scenarioCoords, forceID, campaign, contract, track, - template, false, daysTilDeployment); + scenario = setupScenario(scenarioCoords, forceID, campaign, contract, track, template, false, daysTilDeployment); firstForce = false; if (scenario == null) { @@ -1007,8 +955,7 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai * the forces remain at the deployment location without automatically updating * their position. */ - public static void deployForceToCoords(StratconCoords coords, int forceID, Campaign campaign, - AtBContract contract, StratconTrackState track, boolean sticky) { + public static void deployForceToCoords(StratconCoords coords, int forceID, Campaign campaign, AtBContract contract, StratconTrackState track, boolean sticky) { CombatTeam combatTeam = campaign.getCombatTeamsTable().get(forceID); // This shouldn't be possible, but never hurts to have a little insurance @@ -1065,9 +1012,8 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa Set preDeployedForce = track.getAssignedCoordForces().get(coords); if (preDeployedForce != null && !preDeployedForce.isEmpty()) { - scenario = generateScenarioForExistingForces(coords, - track.getAssignedCoordForces().get(coords), contract, campaign, track); - // Otherwise, pick a random force from those available + scenario = generateScenarioForExistingForces(coords, track.getAssignedCoordForces().get(coords), contract, campaign, track); + // Otherwise, pick a random force from those available } else { List availableForceIDs = getAvailableForceIDs(campaign, contract); Collections.shuffle(availableForceIDs); @@ -1111,8 +1057,7 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa * @param trackState the state of the track containing information about scenarios, facilities, and forces * @return a randomly selected unoccupied adjacent coordinate, or {@code null} if none are available */ - private static @Nullable StratconCoords getUnoccupiedAdjacentCoords(StratconCoords originCoords, - StratconTrackState trackState) { + private static @Nullable StratconCoords getUnoccupiedAdjacentCoords(StratconCoords originCoords, StratconTrackState trackState) { // We need to reduce width/height by one because coordinates index from 0, not 1 final int trackWidth = trackState.getWidth() - 1; final int trackHeight = trackState.getHeight() - 1; @@ -1134,10 +1079,7 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa } // This is to ensure we're not trying to place a scenario off the map - if ((newCoords.getX() < 0) - || (newCoords.getX() > trackWidth) - || (newCoords.getY() < 0) - || (newCoords.getY() > trackHeight)) { + if ((newCoords.getX() < 0) || (newCoords.getX() > trackWidth) || (newCoords.getY() < 0) || (newCoords.getY() > trackHeight)) { continue; } @@ -1162,8 +1104,7 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa * @param track The relevant StratCon track. * @return The newly set up {@link StratconScenario}. */ - private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, - AtBContract contract, StratconTrackState track) { + private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, AtBContract contract, StratconTrackState track) { return setupScenario(coords, forceID, campaign, contract, track, null, false, null); } @@ -1180,24 +1121,19 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa * facility will be added to the track at * the provided coordinates and setup for that facility. * - * @param coords The coordinates where the scenario is to be placed on the track. - * @param forceID The ID of the forces involved in the scenario. - * @param campaign The current campaign. - * @param contract The contract associated with the current scenario. - * @param track The relevant StratCon track. - * @param template A specific {@link ScenarioTemplate} to use for scenario setup, or - * {@code null} to select the scenario template randomly. + * @param coords The coordinates where the scenario is to be placed on the track. + * @param forceID The ID of the forces involved in the scenario. + * @param campaign The current campaign. + * @param contract The contract associated with the current scenario. + * @param track The relevant StratCon track. + * @param template A specific {@link ScenarioTemplate} to use for scenario setup, or + * {@code null} to select the scenario template randomly. * @param ignoreFacilities Whether we should ignore any facilities at the selected location * @param daysTilDeployment How many days until the scenario takes place, or {@code null} to - * pick a random day within the next 7 days. + * pick a random day within the next 7 days. * @return The newly set up {@link StratconScenario}. */ - private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, - Campaign campaign, AtBContract contract, - StratconTrackState track, - @Nullable ScenarioTemplate template, - boolean ignoreFacilities, - @Nullable Integer daysTilDeployment) { + private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, AtBContract contract, StratconTrackState track, @Nullable ScenarioTemplate template, boolean ignoreFacilities, @Nullable Integer daysTilDeployment) { StratconScenario scenario; if (track.getFacilities().containsKey(coords) && !ignoreFacilities) { @@ -1220,9 +1156,7 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa // we may generate a facility scenario randomly - if so, do the facility-related // stuff and add a new facility to the track if (scenario.getBackingScenario().getTemplate().isFacilityScenario()) { - StratconFacility facility = scenario.getBackingScenario().getTemplate().isHostileFacility() - ? StratconFacilityFactory.getRandomHostileFacility() - : StratconFacilityFactory.getRandomAlliedFacility(); + StratconFacility facility = scenario.getBackingScenario().getTemplate().isHostileFacility() ? StratconFacilityFactory.getRandomHostileFacility() : StratconFacilityFactory.getRandomAlliedFacility(); facility.setVisible(true); track.addFacility(coords, facility); setupFacilityScenario(scenario, facility); @@ -1249,13 +1183,11 @@ private static void setupFacilityScenario(StratconScenario scenario, StratconFac AtBScenarioModifier objectiveModifier = null; boolean alliedFacility = facility.getOwner() == Allied; - objectiveModifier = alliedFacility ? AtBScenarioModifier.getRandomAlliedFacilityModifier() - : AtBScenarioModifier.getRandomHostileFacilityModifier(); + objectiveModifier = alliedFacility ? AtBScenarioModifier.getRandomAlliedFacilityModifier() : AtBScenarioModifier.getRandomHostileFacilityModifier(); if (objectiveModifier != null) { scenario.getBackingScenario().addScenarioModifier(objectiveModifier); - scenario.getBackingScenario().setName(String.format("%s - %s - %s", facility.getFacilityType(), - alliedFacility ? "Allied" : "Hostile", objectiveModifier.getModifierName())); + scenario.getBackingScenario().setName(String.format("%s - %s - %s", facility.getFacilityType(), alliedFacility ? "Allied" : "Hostile", objectiveModifier.getModifierName())); } // add the "fixed" hostile facility modifiers after the primary ones @@ -1271,8 +1203,7 @@ private static void setupFacilityScenario(StratconScenario scenario, StratconFac /** * Applies time-sensitive facility effects. */ - private static void processFacilityEffects(StratconTrackState track, - StratconCampaignState campaignState, boolean isStartOfMonth) { + private static void processFacilityEffects(StratconTrackState track, StratconCampaignState campaignState, boolean isStartOfMonth) { for (StratconFacility facility : track.getFacilities().values()) { if (isStartOfMonth) { campaignState.addSupportPoints(facility.getMonthlySPModifier()); @@ -1304,16 +1235,14 @@ private static void processFacilityEffects(StratconTrackState track, * within the scan range efficiently, avoiding redundant processing using a visited set. * * - * @param coords The coordinates where the force is being deployed. - * @param forceID The ID of the force being deployed. - * @param campaign The current campaign context, used to retrieve combat teams and update game events. - * @param track The current track state where the deployment is happening. - * @param sticky Whether the force should be persistently assigned to the track. - * + * @param coords The coordinates where the force is being deployed. + * @param forceID The ID of the force being deployed. + * @param campaign The current campaign context, used to retrieve combat teams and update game events. + * @param track The current track state where the deployment is happening. + * @param sticky Whether the force should be persistently assigned to the track. * @throws IllegalStateException if the force or the associated combat team is missing or invalid. */ - public static void processForceDeployment(StratconCoords coords, int forceID, Campaign campaign, - StratconTrackState track, boolean sticky) { + public static void processForceDeployment(StratconCoords coords, int forceID, Campaign campaign, StratconTrackState track, boolean sticky) { // we want to ensure we only increase Fatigue once boolean hasFatigueIncreased = false; @@ -1451,7 +1380,7 @@ private static void increaseFatigue(int forceID, Campaign campaign) { * @param campaign the overarching campaign instance managing the scenario * @param reinforcementTargetNumber the target number that the reinforcement roll must meet or exceed * @param isGMReinforcement {@code true} if the player is using GM powers to bypass the - * reinforcement check, {@code false} otherwise. + * reinforcement check, {@code false} otherwise. * @return a {@link ReinforcementResultsType} indicating the result of the reinforcement deployment: *
      *
    • {@link ReinforcementResultsType#SUCCESS} - The reinforcement is deployed successfully.
    • @@ -1461,15 +1390,8 @@ private static void increaseFatigue(int forceID, Campaign campaign) { * possibly resulting in a new scenario. *
    */ - public static ReinforcementResultsType processReinforcementDeployment(Force force, - ReinforcementEligibilityType reinforcementType, - StratconCampaignState campaignState, - StratconScenario scenario, - Campaign campaign, - int reinforcementTargetNumber, - boolean isGMReinforcement) { - final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", - MekHQ.getMHQOptions().getLocale()); + public static ReinforcementResultsType processReinforcementDeployment(Force force, ReinforcementEligibilityType reinforcementType, StratconCampaignState campaignState, StratconScenario scenario, Campaign campaign, int reinforcementTargetNumber, boolean isGMReinforcement) { + final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", MekHQ.getMHQOptions().getLocale()); if (reinforcementType.equals(ReinforcementEligibilityType.CHAINED_SCENARIO)) { return SUCCESS; @@ -1500,25 +1422,19 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc StringBuilder reportStatus = new StringBuilder(); if (isGMReinforcement) { - reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text.gm"), - scenario.getName())); + reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text.gm"), scenario.getName())); reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsAutomaticSuccess.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsAutomaticSuccess.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return SUCCESS; } else { - reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text"), - scenario.getName(), roll, maneuverRoleReport, reinforcementTargetNumber)); + reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text"), scenario.getName(), roll, maneuverRoleReport, reinforcementTargetNumber)); } // Critical Failure if (roll == 2) { reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsCriticalFailure.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsCriticalFailure.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return FAILED; } @@ -1526,9 +1442,7 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc // Reinforcement successful if (roll >= reinforcementTargetNumber) { reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsSuccess.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsSuccess.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return SUCCESS; } @@ -1540,9 +1454,7 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc // Check passed if (interceptionRoll >= interceptionOdds) { reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsCommandFailure.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), - CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsCommandFailure.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return DELAYED; } @@ -1550,18 +1462,14 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc // Check failed, but enemy is routed if (contract.getMoraleLevel().isRouted()) { reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsSuccessRouted.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsSuccessRouted.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return SUCCESS; } // Check failed, enemy attempt interception reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsInterceptionAttempt.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), - CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsInterceptionAttempt.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), CLOSING_SPAN_TAG)); UUID commanderId = force.getForceCommanderID(); @@ -1569,9 +1477,7 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc logger.error("Force Commander ID is null."); reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsErrorNoCommander.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsErrorNoCommander.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return FAILED; } @@ -1582,9 +1488,7 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc logger.error("Failed to fetch commander from ID."); reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsErrorUnableToFetchCommander.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsErrorUnableToFetchCommander.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return FAILED; } @@ -1603,12 +1507,8 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc if (roll >= targetNumber) { reportStatus.append(' '); - String reportString = tactics != null - ? resources.getString("reinforcementEvasionSuccessful.text") - : resources.getString("reinforcementEvasionSuccessful.noSkill"); - reportStatus.append(String.format(reportString, - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - CLOSING_SPAN_TAG, roll, targetNumber)); + String reportString = tactics != null ? resources.getString("reinforcementEvasionSuccessful.text") : resources.getString("reinforcementEvasionSuccessful.noSkill"); + reportStatus.append(String.format(reportString, spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), CLOSING_SPAN_TAG, roll, targetNumber)); campaign.addReport(reportStatus.toString()); @@ -1621,9 +1521,7 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc } reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementEvasionUnsuccessful.text"), - spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), - CLOSING_SPAN_TAG, roll, targetNumber)); + reportStatus.append(String.format(resources.getString("reinforcementEvasionUnsuccessful.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG, roll, targetNumber)); campaign.addReport(reportStatus.toString()); ScenarioTemplate scenarioTemplate = getInterceptionScenarioTemplate(force, campaign); @@ -1655,7 +1553,7 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc * @param campaign The {@link Campaign} in which the interception is taking place. * Provides context for evaluating the {@link Force}. * @return A {@link ScenarioTemplate} instance based on the template file matching the logic above, - * or a default template if no specific case is matched. + * or a default template if no specific case is matched. * @see ScenarioTemplate#Deserialize(String) */ private static ScenarioTemplate getInterceptionScenarioTemplate(Force force, Campaign campaign) { @@ -1665,8 +1563,7 @@ private static ScenarioTemplate getInterceptionScenarioTemplate(Force force, Cam int primaryUnitType = force.getPrimaryUnitType(campaign); - if ((primaryUnitType == CONV_FIGHTER) - || (primaryUnitType == AEROSPACEFIGHTER) && (randomInt(3) == 0)) { + if ((primaryUnitType == CONV_FIGHTER) || (primaryUnitType == AEROSPACEFIGHTER) && (randomInt(3) == 0)) { scenarioTemplate = ScenarioTemplate.Deserialize(String.format(templateString, "Low-Atmosphere ")); } else if (primaryUnitType >= AEROSPACEFIGHTER) { scenarioTemplate = ScenarioTemplate.Deserialize(String.format(templateString, "Space ")); @@ -1704,19 +1601,15 @@ private static ScenarioTemplate getInterceptionScenarioTemplate(Force force, Cam *
  105. -- If command rights indicate that a liaison is required, the modifier is adjusted.
  106. *
* - * @param campaign the {@link Campaign} instance representing the current operational campaign. - * @param scenario the {@link StratconScenario} for which reinforcement details are being determined. - * @param commandLiaison the {@link Person} acting as the command liaison, or {@code null} if no liaison exists. - * @param campaignState the {@link StratconCampaignState} representing the state of the overarching campaign. - * @param contract the {@link AtBContract} defining the terms of the contract for this scenario. - * @return a {@link TargetRoll} object representing the calculated reinforcement target number, - * with appropriate modifiers applied. + * @param campaign the {@link Campaign} instance representing the current operational campaign. + * @param scenario the {@link StratconScenario} for which reinforcement details are being determined. + * @param commandLiaison the {@link Person} acting as the command liaison, or {@code null} if no liaison exists. + * @param campaignState the {@link StratconCampaignState} representing the state of the overarching campaign. + * @param contract the {@link AtBContract} defining the terms of the contract for this scenario. + * @return a {@link TargetRoll} object representing the calculated reinforcement target number, + * with appropriate modifiers applied. */ - public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, - StratconScenario scenario, - @Nullable Person commandLiaison, - StratconCampaignState campaignState, - AtBContract contract) { + public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, StratconScenario scenario, @Nullable Person commandLiaison, StratconCampaignState campaignState, AtBContract contract) { // Create Target Roll TargetRoll reinforcementTargetNumber = new TargetRoll(); @@ -1734,11 +1627,9 @@ public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, skillTargetNumber = skill.getFinalSkillValue(); } - reinforcementTargetNumber.addModifier(skillTargetNumber, - "Administration (" + commandLiaison.getFullTitle() +')'); + reinforcementTargetNumber.addModifier(skillTargetNumber, "Administration (" + commandLiaison.getFullTitle() + ')'); } else { - reinforcementTargetNumber.addModifier(skillTargetNumber, - "Administration (Unskilled)"); + reinforcementTargetNumber.addModifier(skillTargetNumber, "Administration (Unskilled)"); } // Facilities Modifier @@ -1772,7 +1663,7 @@ public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, reinforcementTargetNumber.addModifier(skillModifier, "Skill Modifier"); // Liaison Modifier - if (commandRights.isLiaison()) { + if (commandRights.isLiaison()) { int liaisonModifier = -1; reinforcementTargetNumber.addModifier(liaisonModifier, "Liaison Command Rights"); } @@ -1785,8 +1676,7 @@ public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, * Assigns a force to the scenario such that the majority of the force can be * deployed */ - private static void assignAppropriateExtraForceToScenario(StratconScenario scenario, - Map> sortedAvailableForceIDs) { + private static void assignAppropriateExtraForceToScenario(StratconScenario scenario, Map> sortedAvailableForceIDs) { // the goal of this function is to avoid assigning ground units to air battles // and ground units/conventional fighters to space battle @@ -1799,8 +1689,7 @@ private static void assignAppropriateExtraForceToScenario(StratconScenario scena mapLocations.add(LowAtmosphere); // can add conventional fighters to ground or low atmo battles } - if ((scenarioMapLocation == AllGroundTerrain) - || (scenarioMapLocation == SpecificGroundTerrain)) { + if ((scenarioMapLocation == AllGroundTerrain) || (scenarioMapLocation == SpecificGroundTerrain)) { mapLocations.add(AllGroundTerrain); // can only add ground units to ground battles } @@ -1818,8 +1707,7 @@ private static void assignAppropriateExtraForceToScenario(StratconScenario scena * visible in the * briefing room, adds it to the track */ - public static void commitPrimaryForces(Campaign campaign, StratconScenario scenario, - StratconTrackState trackState) { + public static void commitPrimaryForces(Campaign campaign, StratconScenario scenario, StratconTrackState trackState) { trackState.addScenario(scenario); // set up dates for the scenario if doesn't have them already @@ -1919,19 +1807,16 @@ private static Map> sortForcesByMapType(List * then delegates the scenario creation and configuration to another overloaded {@code generateScenario} method * which handles specific template-based scenario generation.

* - * @param campaign the {@link Campaign} managing the overall gameplay state - * @param contract the {@link AtBContract} governing the StratCon campaign - * @param track the {@link StratconTrackState} where the scenario is placed - * @param forceID the ID of the force for which the scenario is generated - * @param coords the {@link StratconCoords} specifying where the scenario will be generated - * @param daysTilDeployment the number of days until the scenario is deployed; if {@code null}, + * @param campaign the {@link Campaign} managing the overall gameplay state + * @param contract the {@link AtBContract} governing the StratCon campaign + * @param track the {@link StratconTrackState} where the scenario is placed + * @param forceID the ID of the force for which the scenario is generated + * @param coords the {@link StratconCoords} specifying where the scenario will be generated + * @param daysTilDeployment the number of days until the scenario is deployed; if {@code null}, * deployment dates are determined dynamically * @return the generated {@link StratconScenario}, or {@code null} if scenario generation fails */ - private static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, - StratconTrackState track, int forceID, - StratconCoords coords, - @Nullable Integer daysTilDeployment) { + private static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, StratconTrackState track, int forceID, StratconCoords coords, @Nullable Integer daysTilDeployment) { int unitType = campaign.getForce(forceID).getPrimaryUnitType(campaign); ScenarioTemplate template = StratconScenarioFactory.getRandomScenario(unitType); // useful for debugging specific scenario types @@ -1959,22 +1844,19 @@ private static Map> sortForcesByMapType(List *
  • If no force is provided, the scenario is treated as part of contract initialization (e.g., allied forces).
  • * * - * @param campaign the {@link Campaign} managing the gameplay state - * @param contract the {@link AtBContract} governing the StratCon campaign - * @param track the {@link StratconTrackState} to which the scenario belongs - * @param forceID the ID of the force for which the scenario is generated, or - * {@link Force#FORCE_NONE} if none - * @param coords the {@link StratconCoords} specifying where the scenario will be placed - * @param template the {@link ScenarioTemplate} to use for scenario generation; if - * {@code null}, a random one is selected - * @param daysTilDeployment the number of days until the scenario is deployed; if {@code null}, + * @param campaign the {@link Campaign} managing the gameplay state + * @param contract the {@link AtBContract} governing the StratCon campaign + * @param track the {@link StratconTrackState} to which the scenario belongs + * @param forceID the ID of the force for which the scenario is generated, or + * {@link Force#FORCE_NONE} if none + * @param coords the {@link StratconCoords} specifying where the scenario will be placed + * @param template the {@link ScenarioTemplate} to use for scenario generation; if + * {@code null}, a random one is selected + * @param daysTilDeployment the number of days until the scenario is deployed; if {@code null}, * dates will be dynamically set * @return the generated {@link StratconScenario}, or {@code null} if scenario generation failed */ - static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, - StratconTrackState track, int forceID, - StratconCoords coords, ScenarioTemplate template, - @Nullable Integer daysTilDeployment) { + static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, StratconTrackState track, int forceID, StratconCoords coords, ScenarioTemplate template, @Nullable Integer daysTilDeployment) { StratconScenario scenario = new StratconScenario(); if (template == null) { @@ -1994,8 +1876,7 @@ private static Map> sortForcesByMapType(List return null; } - AtBDynamicScenario backingScenario = AtBDynamicScenarioFactory.initializeScenarioFromTemplate(template, - contract, campaign); + AtBDynamicScenario backingScenario = AtBDynamicScenarioFactory.initializeScenarioFromTemplate(template, contract, campaign); scenario.setBackingScenario(backingScenario); scenario.setCoords(coords); @@ -2006,8 +1887,7 @@ private static Map> sortForcesByMapType(List applyFacilityModifiers(scenario, track, coords); applyGlobalModifiers(scenario, contract.getStratconCampaignState()); - AtBDynamicScenarioFactory.setScenarioModifiers(campaign.getCampaignOptions(), - scenario.getBackingScenario()); + AtBDynamicScenarioFactory.setScenarioModifiers(campaign.getCampaignOptions(), scenario.getBackingScenario()); scenario.setCurrentState(ScenarioState.UNRESOLVED); if (daysTilDeployment == null) { @@ -2053,8 +1933,7 @@ private static void applyGlobalModifiers(StratconScenario scenario, StratconCamp /** * Applies scenario modifiers from the current track to the given scenario. */ - private static void applyFacilityModifiers(StratconScenario scenario, StratconTrackState track, - StratconCoords coords) { + private static void applyFacilityModifiers(StratconScenario scenario, StratconTrackState track, StratconCoords coords) { // loop through all the facilities on the track // if a facility has been revealed, then it has a 100% chance to apply its // effect @@ -2077,13 +1956,11 @@ private static void applyFacilityModifiers(StratconScenario scenario, StratconTr for (String modifierID : modifierIDs) { AtBScenarioModifier modifier = AtBScenarioModifier.getScenarioModifier(modifierID); if (modifier == null) { - logger.error(String.format("Modifier %s not found for facility %s", modifierID, - facility.getFormattedDisplayableName())); + logger.error(String.format("Modifier %s not found for facility %s", modifierID, facility.getFormattedDisplayableName())); continue; } - modifier.setAdditionalBriefingText('(' + facility.getDisplayableName() + ") " - + modifier.getAdditionalBriefingText()); + modifier.setAdditionalBriefingText('(' + facility.getDisplayableName() + ") " + modifier.getAdditionalBriefingText()); scenario.getBackingScenario().addScenarioModifier(modifier); } } @@ -2098,26 +1975,18 @@ private static void applyFacilityModifiers(StratconScenario scenario, StratconTr */ public static void setAttachedUnitsModifier(StratconScenario scenario, AtBContract contract) { AtBDynamicScenario backingScenario = scenario.getBackingScenario(); - boolean airBattle = (backingScenario.getTemplate().mapParameters.getMapLocation() == LowAtmosphere) - || (backingScenario.getTemplate().mapParameters.getMapLocation() == Space); + boolean airBattle = (backingScenario.getTemplate().mapParameters.getMapLocation() == LowAtmosphere) || (backingScenario.getTemplate().mapParameters.getMapLocation() == Space); // if we're under non-independent command rights, a supervisor may come along switch (contract.getCommandRights()) { case INTEGRATED: - backingScenario.addScenarioModifier(AtBScenarioModifier - .getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_INTEGRATED_UNITS_AIR - : MHQConstants.SCENARIO_MODIFIER_INTEGRATED_UNITS_GROUND)); + backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_INTEGRATED_UNITS_AIR : MHQConstants.SCENARIO_MODIFIER_INTEGRATED_UNITS_GROUND)); break; case HOUSE: - backingScenario.addScenarioModifier( - AtBScenarioModifier.getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_HOUSE_CO_AIR - : MHQConstants.SCENARIO_MODIFIER_HOUSE_CO_GROUND)); + backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_HOUSE_CO_AIR : MHQConstants.SCENARIO_MODIFIER_HOUSE_CO_GROUND)); break; case LIAISON: if (scenario.isTurningPoint()) { - backingScenario.addScenarioModifier( - AtBScenarioModifier - .getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_LIAISON_AIR - : MHQConstants.SCENARIO_MODIFIER_LIAISON_GROUND)); + backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_LIAISON_AIR : MHQConstants.SCENARIO_MODIFIER_LIAISON_GROUND)); } break; default: @@ -2140,13 +2009,11 @@ private static void setScenarioDates(StratconTrackState track, Campaign campaign * X days from * campaign's today date. */ - private static void setScenarioDates(int deploymentDay, StratconTrackState track, Campaign campaign, - StratconScenario scenario) { + private static void setScenarioDates(int deploymentDay, StratconTrackState track, Campaign campaign, StratconScenario scenario) { // set up deployment day, battle day, return day here // safety code to prevent attempts to generate random int with upper bound of 0 // which is apparently illegal - int battleDay = deploymentDay - + (track.getDeploymentTime() > 0 ? randomInt(track.getDeploymentTime()) : 0); + int battleDay = deploymentDay + (track.getDeploymentTime() > 0 ? randomInt(track.getDeploymentTime()) : 0); int returnDay = deploymentDay + track.getDeploymentTime(); LocalDate deploymentDate = campaign.getLocalDate().plusDays(deploymentDay); @@ -2166,11 +2033,7 @@ private static void setScenarioDates(int deploymentDay, StratconTrackState track private static boolean unitTypeIsAirborne(ScenarioForceTemplate template) { int unitType = template.getAllowedUnitType(); - return ((unitType == AEROSPACEFIGHTER) || - (unitType == CONV_FIGHTER) || - (unitType == DROPSHIP) || - (unitType == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX)) && - (template.getStartingAltitude() > 0); + return ((unitType == AEROSPACEFIGHTER) || (unitType == CONV_FIGHTER) || (unitType == DROPSHIP) || (unitType == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX)) && (template.getStartingAltitude() > 0); } /** @@ -2253,29 +2116,24 @@ public static List getAvailableForceIDs(Campaign campaign, AtBContract * * Forces that meet all conditions are returned as a list of unique force IDs. * - * @param unitType the desired type of unit to evaluate for deployment eligibility. - * @param campaign the {@link Campaign} containing the forces to evaluate. - * @param currentTrack the {@link StratconTrackState} representing the current track, used to - * filter eligible forces. - * @param reinforcements {@code true} if the forces are being deployed as reinforcements; - * otherwise {@code false}. + * @param unitType the desired type of unit to evaluate for deployment eligibility. + * @param campaign the {@link Campaign} containing the forces to evaluate. + * @param currentTrack the {@link StratconTrackState} representing the current track, used to + * filter eligible forces. + * @param reinforcements {@code true} if the forces are being deployed as reinforcements; + * otherwise {@code false}. * @param currentScenario the current {@link StratconScenario}, if any, used to exclude failed - * reinforcements. Can be {@code null}. - * @param campaignState the current {@link StratconCampaignState} representing the campaign - * state for further filtering of eligible forces. + * reinforcements. Can be {@code null}. + * @param campaignState the current {@link StratconCampaignState} representing the campaign + * state for further filtering of eligible forces. * @return a {@link List} of unique force IDs that meet all deployment criteria. */ - public static List getAvailableForceIDs(int unitType, Campaign campaign, StratconTrackState currentTrack, - boolean reinforcements, @Nullable StratconScenario currentScenario, StratconCampaignState campaignState) { + public static List getAvailableForceIDs(int unitType, Campaign campaign, StratconTrackState currentTrack, boolean reinforcements, @Nullable StratconScenario currentScenario, StratconCampaignState campaignState) { List retVal = new ArrayList<>(); // assemble a set of all force IDs that are currently assigned to tracks that // are not this one - Set forcesInTracks = campaign.getActiveAtBContracts().stream() - .flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()) - .filter(track -> (!Objects.equals(track, currentTrack)) || !reinforcements) - .flatMap(track -> track.getAssignedForceCoords().keySet().stream()) - .collect(Collectors.toSet()); + Set forcesInTracks = campaign.getActiveAtBContracts().stream().flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()).filter(track -> (!Objects.equals(track, currentTrack)) || !reinforcements).flatMap(track -> track.getAssignedForceCoords().keySet().stream()).collect(Collectors.toSet()); // if there's an existing scenario, and we're doing reinforcements, // prevent forces that failed to deploy from trying to deploy again @@ -2303,16 +2161,9 @@ public static List getAvailableForceIDs(int unitType, Campaign campaign } int primaryUnitType = force.getPrimaryUnitType(campaign); - boolean noReinforcementRestriction = !reinforcements || - (getReinforcementType(force.getId(), currentTrack, campaign, - campaignState) != ReinforcementEligibilityType.NONE); + boolean noReinforcementRestriction = !reinforcements || (getReinforcementType(force.getId(), currentTrack, campaign, campaignState) != ReinforcementEligibilityType.NONE); - if ((force.getScenarioId() <= 0) - && !force.getAllUnits(true).isEmpty() - && !forcesInTracks.contains(force.getId()) - && forceCompositionMatchesDeclaredUnitType(primaryUnitType, unitType) - && noReinforcementRestriction - && !subElementsOrSelfDeployed(force, campaign)) { + if ((force.getScenarioId() <= 0) && !force.getAllUnits(true).isEmpty() && !forcesInTracks.contains(force.getId()) && forceCompositionMatchesDeclaredUnitType(primaryUnitType, unitType) && noReinforcementRestriction && !subElementsOrSelfDeployed(force, campaign)) { retVal.add(force.getId()); } @@ -2330,14 +2181,11 @@ private static boolean subElementsOrSelfDeployed(Force force, Campaign campaign) return true; } - if (force.getUnits().stream() - .map(campaign::getUnit) - .anyMatch(Unit::isDeployed)) { + if (force.getUnits().stream().map(campaign::getUnit).anyMatch(Unit::isDeployed)) { return true; } - return force.getSubForces().stream() - .anyMatch(child -> subElementsOrSelfDeployed(child, campaign)); + return force.getSubForces().stream().anyMatch(child -> subElementsOrSelfDeployed(child, campaign)); } /** @@ -2353,16 +2201,11 @@ public static List getEligibleDefensiveUnits(Campaign campaign) { // "defensive" units are infantry, battle armor and (Weisman help you) gun // emplacements // and also said unit should be intact/alive/etc - boolean isEligibleInfantry = ((u.getEntity().getUnitType() == INFANTRY) - || (u.getEntity().getUnitType() == BATTLE_ARMOR)) && !u.isUnmanned(); + boolean isEligibleInfantry = ((u.getEntity().getUnitType() == INFANTRY) || (u.getEntity().getUnitType() == BATTLE_ARMOR)) && !u.isUnmanned(); boolean isEligibleGunEmplacement = u.getEntity().getUnitType() == GUN_EMPLACEMENT; - if ((isEligibleInfantry || isEligibleGunEmplacement) - && !u.isDeployed() - && !u.isMothballed() - && (u.checkDeployment() == null) - && !isUnitDeployedToStratCon(u)) { + if ((isEligibleInfantry || isEligibleGunEmplacement) && !u.isDeployed() && !u.isMothballed() && (u.checkDeployment() == null) && !isUnitDeployedToStratCon(u)) { // this is a little inefficient, but probably there aren't too many active AtB // contracts at a time @@ -2386,8 +2229,7 @@ public static List getEligibleDefensiveUnits(Campaign campaign) { * * @return List of unit IDs. */ - public static List getEligibleLeadershipUnits(Campaign campaign, ArrayList forceIDs, - int leadershipSkill) { + public static List getEligibleLeadershipUnits(Campaign campaign, ArrayList forceIDs, int leadershipSkill) { List eligibleUnits = new ArrayList<>(); // If there is no leadership skill, we shouldn't continue @@ -2419,15 +2261,9 @@ public static List getEligibleLeadershipUnits(Campaign campaign, ArrayList // the general idea is that we want something that can be deployed to the // scenario - // e.g., no infantry on air scenarios etc. - boolean validUnitType = (forceCompositionMatchesDeclaredUnitType(unit.getEntity().getUnitType(), - generalUnitType)); - - if (validUnitType - && !unit.isDeployed() - && !unit.isMothballed() - && (unit.getEntity().calculateBattleValue(true, true) <= totalBudget) - && (unit.checkDeployment() == null) - && !isUnitDeployedToStratCon(unit)) { + boolean validUnitType = (forceCompositionMatchesDeclaredUnitType(unit.getEntity().getUnitType(), generalUnitType)); + + if (validUnitType && !unit.isDeployed() && !unit.isMothballed() && (unit.getEntity().calculateBattleValue(true, true) <= totalBudget) && (unit.checkDeployment() == null) && !isUnitDeployedToStratCon(unit)) { eligibleUnits.add(unit); } } @@ -2446,9 +2282,7 @@ public static boolean isUnitDeployedToStratCon(Unit u) { // this is a little inefficient, but probably there aren't too many active AtB // contracts at a time - return u.getCampaign().getActiveAtBContracts().stream() - .anyMatch(contract -> (contract.getStratconCampaignState() != null) && - contract.getStratconCampaignState().isForceDeployedHere(u.getForceId())); + return u.getCampaign().getActiveAtBContracts().stream().anyMatch(contract -> (contract.getStratconCampaignState() != null) && contract.getStratconCampaignState().isForceDeployedHere(u.getForceId())); } /** @@ -2489,13 +2323,9 @@ private static int getPrimaryUnitType(Campaign campaign, ArrayList forc * Determines what rules to use when deploying a force for reinforcements to the * given track. */ - public static ReinforcementEligibilityType getReinforcementType(int forceID, StratconTrackState trackState, - Campaign campaign, StratconCampaignState campaignState) { + public static ReinforcementEligibilityType getReinforcementType(int forceID, StratconTrackState trackState, Campaign campaign, StratconCampaignState campaignState) { // if the force is deployed elsewhere, it cannot be deployed as reinforcements - if (campaign.getActiveAtBContracts().stream() - .flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()) - .anyMatch(track -> !Objects.equals(track, trackState) - && track.getAssignedForceCoords().containsKey(forceID))) { + if (campaign.getActiveAtBContracts().stream().flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()).anyMatch(track -> !Objects.equals(track, trackState) && track.getAssignedForceCoords().containsKey(forceID))) { return ReinforcementEligibilityType.NONE; } @@ -2528,8 +2358,7 @@ public static ReinforcementEligibilityType getReinforcementType(int forceID, Str * track * for the given contract? */ - public static boolean canManuallyDeployAnyForce(StratconCoords coords, - StratconTrackState track, AtBContract contract) { + public static boolean canManuallyDeployAnyForce(StratconCoords coords, StratconTrackState track, AtBContract contract) { // Rules: can't manually deploy under integrated command // can't manually deploy if there's already a force deployed there // exception: on allied facilities @@ -2553,8 +2382,7 @@ public static boolean canManuallyDeployAnyForce(StratconCoords coords, * a force or not, * figure out the odds of a scenario occurring. */ - public static int calculateScenarioOdds(StratconTrackState track, AtBContract contract, - boolean isReinforcements) { + public static int calculateScenarioOdds(StratconTrackState track, AtBContract contract, boolean isReinforcements) { if (contract.getMoraleLevel().isRouted()) { return -1; } @@ -2595,8 +2423,7 @@ public static int calculateScenarioOdds(StratconTrackState track, AtBContract co * Removes the facility associated with the given scenario from the relevant * track */ - public static void updateFacilityForScenario(AtBScenario scenario, AtBContract contract, boolean destroy, - boolean capture) { + public static void updateFacilityForScenario(AtBScenario scenario, AtBContract contract, boolean destroy, boolean capture) { if (contract.getStratconCampaignState() == null) { return; } @@ -2685,17 +2512,16 @@ public static void processScenarioCompletion(ResolveScenarioTracker tracker) { if (backingScenario.getStratConScenarioType().isLosTech()) { if (victory) { int roll = randomInt(10); - StarLeagueCache cache = new StarLeagueCache(campaign, ((AtBContract) mission), - CacheType.TRASH_CACHE.ordinal()); + StarLeagueCache cache = new StarLeagueCache(campaign, ((AtBContract) mission), CacheType.TRASH_CACHE.ordinal()); // The rumor is a dud -// if (false) { // TODO replace placeholder value -// cache.createDudDialog(track, scenario); -// } else { -// if (Objects.equals(cache.getFaction().getShortName(), "SL")) { -// cache.createProposalDialog(); -// } -// } + // if (false) { // TODO replace placeholder value + // cache.createDudDialog(track, scenario); + // } else { + // if (Objects.equals(cache.getFaction().getShortName(), "SL")) { + // cache.createProposalDialog(); + // } + // } } } break; @@ -2706,12 +2532,12 @@ public static void processScenarioCompletion(ResolveScenarioTracker tracker) { public static void linkedScenerioProcessing(ResolveScenarioTracker tracker, List forces) { Scenario nextScenario = tracker.getCampaign().getScenario(tracker.getScenario().getLinkedScenario()); - + if (nextScenario instanceof AtBScenario) { - StratconCampaignState campaignState = ((AtBScenario)nextScenario).getContract(tracker.getCampaign()).getStratconCampaignState(); - if (campaignState == null) { + StratconCampaignState campaignState = ((AtBScenario) nextScenario).getContract(tracker.getCampaign()).getStratconCampaignState(); + if (campaignState == null) { return; - } + } for (StratconTrackState track : campaignState.getTracks()) { if (track.getBackingScenariosMap().containsKey(nextScenario.getId())) { StratconScenario scenario = track.getBackingScenariosMap().get(nextScenario.getId()); @@ -2732,14 +2558,12 @@ public static void linkedScenerioProcessing(ResolveScenarioTracker tracker, List * scenario, track and campaign state. For example, "win scenario A" or "win X * scenarios". */ - private static void updateStrategicObjectives(boolean victory, StratconScenario scenario, - StratconTrackState track) { + private static void updateStrategicObjectives(boolean victory, StratconScenario scenario, StratconTrackState track) { // first, we check if this scenario is associated with any specific scenario // objectives StratconStrategicObjective specificObjective = track.getObjectivesByCoords().get(scenario.getCoords()); - if ((specificObjective != null) && - (specificObjective.getObjectiveType() == StrategicObjectiveType.SpecificScenarioVictory)) { + if ((specificObjective != null) && (specificObjective.getObjectiveType() == StrategicObjectiveType.SpecificScenarioVictory)) { if (victory) { specificObjective.incrementCurrentObjectiveCount(); @@ -2787,8 +2611,7 @@ public static void switchFacilityOwner(StratconFacility facility) { * return date is on or before the given date. */ public static void processTrackForceReturnDates(StratconTrackState track, Campaign campaign) { - final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", - MekHQ.getMHQOptions().getLocale()); + final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", MekHQ.getMHQOptions().getLocale()); List forcesToUndeploy = new ArrayList<>(); LocalDate date = campaign.getLocalDate(); @@ -2799,14 +2622,10 @@ public static void processTrackForceReturnDates(StratconTrackState track, Campai for (int forceID : track.getAssignedForceReturnDates().keySet()) { Force force = campaign.getForce(forceID); - if ((track.getAssignedForceReturnDates().get(forceID).equals(date) - || track.getAssignedForceReturnDates().get(forceID).isBefore(date)) - && (force != null) && !track.getBackingScenariosMap().containsKey(force.getScenarioId()) - && !track.getStickyForces().contains(forceID)) { + if ((track.getAssignedForceReturnDates().get(forceID).equals(date) || track.getAssignedForceReturnDates().get(forceID).isBefore(date)) && (force != null) && !track.getBackingScenariosMap().containsKey(force.getScenarioId()) && !track.getStickyForces().contains(forceID)) { forcesToUndeploy.add(forceID); - campaign.addReport(String.format(resources.getString("force.undeployed"), - force.getName())); + campaign.addReport(String.format(resources.getString("force.undeployed"), force.getName())); } } @@ -2821,15 +2640,10 @@ public static void processTrackForceReturnDates(StratconTrackState track, Campai * 'ignored scenario' routine. * * @return Whether or not we also need to get rid of the backing scenario from - * the campaign + * the campaign */ public static boolean processIgnoredScenario(AtBDynamicScenario scenario, StratconCampaignState campaignState) { - return campaignState.getTracks().stream() - .filter(track -> track.getBackingScenariosMap().containsKey(scenario.getId())) - .findFirst() - .map(track -> processIgnoredScenario(track.getBackingScenariosMap().get(scenario.getId()), - campaignState)) - .orElse(true); + return campaignState.getTracks().stream().filter(track -> track.getBackingScenariosMap().containsKey(scenario.getId())).findFirst().map(track -> processIgnoredScenario(track.getBackingScenariosMap().get(scenario.getId()), campaignState)).orElse(true); } @@ -2837,7 +2651,7 @@ public static boolean processIgnoredScenario(AtBDynamicScenario scenario, Stratc * Processes an ignored Stratcon scenario * * @return Whether or not we also need to get rid of the backing scenario from - * the campaign + * the campaign */ public static boolean processIgnoredScenario(StratconScenario scenario, StratconCampaignState campaignState) { for (StratconTrackState track : campaignState.getTracks()) { @@ -2865,12 +2679,10 @@ public static boolean processIgnoredScenario(StratconScenario scenario, Stratcon } else { // if it's an open-field // move scenario towards nearest allied facility - StratconCoords closestAlliedFacilityCoords = track - .findClosestAlliedFacilityCoords(scenario.getCoords()); + StratconCoords closestAlliedFacilityCoords = track.findClosestAlliedFacilityCoords(scenario.getCoords()); if (closestAlliedFacilityCoords != null) { - StratconCoords newCoords = scenario.getCoords() - .translate(scenario.getCoords().direction(closestAlliedFacilityCoords)); + StratconCoords newCoords = scenario.getCoords().translate(scenario.getCoords().direction(closestAlliedFacilityCoords)); boolean objectiveMoved = track.moveObjective(scenario.getCoords(), newCoords); if (!objectiveMoved) { @@ -2917,7 +2729,7 @@ public static boolean processIgnoredScenario(StratconScenario scenario, Stratcon public void startup() { MekHQ.registerHandler(this); } - + /** * Event handler for the new day event. @@ -2955,9 +2767,7 @@ public void handleNewDay(NewDayEvent ev) { // loop through scenarios - if we haven't deployed in time, // fail it and apply consequences for (StratconScenario scenario : track.getScenarios().values()) { - if ((scenario.getDeploymentDate() != null) && - scenario.getDeploymentDate().isBefore(campaign.getLocalDate()) && - scenario.getPrimaryForceIDs().isEmpty()) { + if ((scenario.getDeploymentDate() != null) && scenario.getDeploymentDate().isBefore(campaign.getLocalDate()) && scenario.getPrimaryForceIDs().isEmpty()) { processIgnoredScenario(scenario, campaignState); } } @@ -2990,10 +2800,7 @@ public void handleNewDay(NewDayEvent ev) { * required data */ private void cleanupPhantomScenarios(StratconTrackState track) { - List cleanupList = track.getScenarios().values().stream() - .filter(scenario -> (scenario.getDeploymentDate() == null) - && !scenario.isStrategicObjective()) - .collect(Collectors.toList()); + List cleanupList = track.getScenarios().values().stream().filter(scenario -> (scenario.getDeploymentDate() == null) && !scenario.isStrategicObjective()).collect(Collectors.toList()); for (StratconScenario scenario : cleanupList) { track.removeScenario(scenario); From 2294e370fb480133a06ba15cc47ff072825e3756 Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 13:16:33 -0700 Subject: [PATCH 04/11] last format fix --- MekHQ/src/mekhq/campaign/Campaign.java | 1330 ++++++++++++++++-------- 1 file changed, 904 insertions(+), 426 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 4176bcb7f2b..a744a0583ff 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -306,7 +306,7 @@ public class Campaign implements ITechManager { private BehaviorSettings autoResolveBehaviorSettings; private List automatedMothballUnits; - //options relating to parts in use and restock + //options relating to parts in use and restock private boolean ignoreMothballed; private boolean topUpWeekly; private PartQuality ignoreSparesUnderQuality; @@ -323,7 +323,8 @@ public enum AdministratorSpecialization { COMMAND, LOGISTICS, TRANSPORT, HR } - private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Campaign", MekHQ.getMHQOptions().getLocale()); + private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Campaign", + MekHQ.getMHQOptions().getLocale()); /** * This is used to determine if the player has an active AtB Contract, and is @@ -394,7 +395,7 @@ public Campaign() { autoResolveBehaviorSettings = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; automatedMothballUnits = new ArrayList<>(); topUpWeekly = false; - ignoreMothballed = false; + ignoreMothballed = false; ignoreSparesUnderQuality = QUALITY_A; } @@ -464,7 +465,9 @@ public Era getEra() { } public String getTitle() { - return getName() + " (" + getFaction().getFullName(getGameYear()) + ')' + " - " + MekHQ.getMHQOptions().getLongDisplayFormattedDate(getLocalDate()) + " (" + getEra() + ')'; + return getName() + " (" + getFaction().getFullName(getGameYear()) + ')' + " - " + + MekHQ.getMHQOptions().getLongDisplayFormattedDate(getLocalDate()) + + " (" + getEra() + ')'; } public LocalDate getLocalDate() { @@ -585,7 +588,9 @@ public ArrayList getAllCombatTeams() { // without needing to directly include the code here, too. combatTeams = getCombatTeamsTable(); - return combatTeams.values().stream().filter(l -> forceIds.containsKey(l.getForceId())).collect(Collectors.toCollection(ArrayList::new)); + return combatTeams.values().stream() + .filter(l -> forceIds.containsKey(l.getForceId())) + .collect(Collectors.toCollection(ArrayList::new)); } public void setShoppingList(ShoppingList sl) { @@ -743,7 +748,6 @@ public AtBConfiguration getAtBConfig() { } // region Ship Search - /** * Sets the date a ship search was started, or null if no search is in progress. */ @@ -801,10 +805,13 @@ private void processShipSearch() { } StringBuilder report = new StringBuilder(); - if (getFinances().debit(TransactionType.UNIT_PURCHASE, getLocalDate(), getAtBConfig().shipSearchCostPerWeek(), "Ship Search")) { - report.append(getAtBConfig().shipSearchCostPerWeek().toAmountAndSymbolString()).append(" deducted for ship search."); + if (getFinances().debit(TransactionType.UNIT_PURCHASE, getLocalDate(), + getAtBConfig().shipSearchCostPerWeek(), "Ship Search")) { + report.append(getAtBConfig().shipSearchCostPerWeek().toAmountAndSymbolString()) + .append(" deducted for ship search."); } else { - addReport("Insufficient funds for ship search."); + addReport("Insufficient funds for ship search."); setShipSearchStart(null); return; } @@ -814,12 +821,14 @@ private void processShipSearch() { int roll = Compute.d6(2); TargetRoll target = getAtBConfig().shipSearchTargetRoll(shipSearchType, this); setShipSearchStart(null); - report.append("
    Ship search target: ").append(target.getValueAsString()).append(" roll: ").append(roll); + report.append("
    Ship search target: ").append(target.getValueAsString()).append(" roll: ") + .append(roll); // TODO : mos zero should make ship available on retainer if (roll >= target.getValue()) { report.append("
    Search successful. "); - MekSummary ms = getUnitGenerator().generate(getFactionCode(), shipSearchType, -1, getGameYear(), getAtBUnitRatingMod()); + MekSummary ms = getUnitGenerator().generate(getFactionCode(), shipSearchType, -1, + getGameYear(), getAtBUnitRatingMod()); if (ms == null) { ms = getAtBConfig().findShip(shipSearchType); @@ -828,9 +837,13 @@ private void processShipSearch() { if (ms != null) { setShipSearchResult(ms.getName()); setShipSearchExpiration(getLocalDate().plusDays(31)); - report.append(getShipSearchResult()).append(" is available for purchase for ").append(Money.of(ms.getCost()).toAmountAndSymbolString()).append(" until ").append(MekHQ.getMHQOptions().getDisplayFormattedDate(getShipSearchExpiration())); + report.append(getShipSearchResult()).append(" is available for purchase for ") + .append(Money.of(ms.getCost()).toAmountAndSymbolString()) + .append(" until ") + .append(MekHQ.getMHQOptions().getDisplayFormattedDate(getShipSearchExpiration())); } else { - report.append(" Could not determine ship type."); + report.append(" Could not determine ship type."); } } else { report.append("
    Ship search unsuccessful."); @@ -849,7 +862,8 @@ public void purchaseShipSearchResult() { Money cost = Money.of(ms.getCost()); if (getFunds().isLessThan(cost)) { - addReport(" You cannot afford this unit. Transaction cancelled."); + addReport(" You cannot afford this unit. Transaction cancelled."); return; } @@ -864,7 +878,8 @@ public void purchaseShipSearchResult() { Entity en = mekFileParser.getEntity(); - int transitDays = getCampaignOptions().isInstantUnitMarketDelivery() ? 0 : calculatePartTransitTime(Compute.d6(2) - 2); + int transitDays = getCampaignOptions().isInstantUnitMarketDelivery() ? 0 + : calculatePartTransitTime(Compute.d6(2) - 2); getFinances().debit(TransactionType.UNIT_PURCHASE, getLocalDate(), cost, "Purchased " + en.getShortName()); PartQuality quality = PartQuality.QUALITY_D; @@ -876,7 +891,8 @@ public void purchaseShipSearchResult() { addNewUnit(en, true, transitDays, quality); if (!getCampaignOptions().isInstantUnitMarketDelivery()) { - addReport("Unit will be delivered in " + transitDays + " days."); + addReport("Unit will be delivered in " + transitDays + " days."); } setShipSearchResult(null); setShipSearchExpiration(null); @@ -889,7 +905,7 @@ public void purchaseShipSearchResult() { * @param totalPayout The total retirement payout. * @param unitAssignments List of unit assignments. * @return False if there were payments AND they were unable to be processed, - * true otherwise. + * true otherwise. */ public boolean applyRetirement(Money totalPayout, Map unitAssignments) { turnoverRetirementInformation.clear(); @@ -905,7 +921,8 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment if (!person.getPermanentInjuries().isEmpty()) { person.changeStatus(this, getLocalDate(), PersonnelStatus.RETIRED); } - if (isBreakingContract(person, getLocalDate(), getCampaignOptions().getServiceContractDuration())) { + if (isBreakingContract(person, getLocalDate(), + getCampaignOptions().getServiceContractDuration())) { if (!getActiveContracts().isEmpty()) { int roll = Compute.randomInt(20); @@ -923,7 +940,8 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment } if (!person.getStatus().isActive()) { - turnoverRetirementInformation.add(String.format(person.getStatus().getReportText(), person.getHyperlinkedFullTitle())); + turnoverRetirementInformation.add( + String.format(person.getStatus().getReportText(), person.getHyperlinkedFullTitle())); } if (wasSacked) { @@ -938,10 +956,12 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment Person spouse = person.getGenealogy().getSpouse(); if ((spouse != null) && (spouse.getPrimaryRole().isCivilian())) { - addReport(spouse.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDeparture.text")); + addReport(spouse.getHyperlinkedFullTitle() + ' ' + + resources.getString("turnoverJointDeparture.text")); spouse.changeStatus(this, getLocalDate(), PersonnelStatus.LEFT); - turnoverRetirementInformation.add(spouse.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDeparture.text")); + turnoverRetirementInformation.add(spouse.getHyperlinkedFullTitle() + ' ' + + resources.getString("turnoverJointDeparture.text")); } // non-civilian spouses may divorce the remaining partner @@ -950,7 +970,8 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment if ((person.getStatus().isDefected()) || (Compute.randomInt(6) == 0)) { getDivorce().divorce(this, getLocalDate(), person, SplittingSurnameStyle.WEIGHTED); - turnoverRetirementInformation.add(String.format(resources.getString("divorce.text"), person.getHyperlinkedFullTitle(), spouse.getHyperlinkedFullTitle())); + turnoverRetirementInformation.add(String.format(resources.getString("divorce.text"), + person.getHyperlinkedFullTitle(), spouse.getHyperlinkedFullTitle())); } } } @@ -960,27 +981,34 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment // factored in for (Person child : person.getGenealogy().getChildren()) { if ((child.isChild(getLocalDate())) && (!child.getStatus().isDepartedUnit())) { - boolean hasRemainingParent = child.getGenealogy().getParents().stream().anyMatch(parent -> (!parent.getStatus().isDepartedUnit()) && (!parent.getStatus().isAbsent())); + boolean hasRemainingParent = child.getGenealogy().getParents().stream() + .anyMatch(parent -> (!parent.getStatus().isDepartedUnit()) + && (!parent.getStatus().isAbsent())); // if there is a remaining parent, there is a 50/50 chance the child departs if ((hasRemainingParent) && (Compute.randomInt(2) == 0)) { - addReport(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text")); + addReport(child.getHyperlinkedFullTitle() + ' ' + + resources.getString("turnoverJointDepartureChild.text")); child.changeStatus(this, getLocalDate(), PersonnelStatus.LEFT); - turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text")); + turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' + + resources.getString("turnoverJointDepartureChild.text")); } // if there is no remaining parent, the child will always depart, unless the // parents are dead if ((!hasRemainingParent) && (child.getGenealogy().hasLivingParents())) { - addReport(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text")); + addReport(child.getHyperlinkedFullTitle() + ' ' + + resources.getString("turnoverJointDepartureChild.text")); child.changeStatus(this, getLocalDate(), PersonnelStatus.LEFT); - turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text")); + turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' + + resources.getString("turnoverJointDepartureChild.text")); } else if (!child.getGenealogy().hasLivingParents()) { addReport(child.getHyperlinkedFullTitle() + ' ' + resources.getString("orphaned.text")); - turnoverRetirementInformation.add(child.getHyperlinkedFullTitle() + ' ' + resources.getString("orphaned.text")); + turnoverRetirementInformation.add( + child.getHyperlinkedFullTitle() + ' ' + resources.getString("orphaned.text")); ServiceLogger.orphaned(person, getLocalDate()); } } @@ -994,7 +1022,8 @@ public boolean applyRetirement(Money totalPayout, Map unitAssignment return true; } } else { - addReport("You cannot afford to make the final payments."); + addReport("You cannot afford to make the final payments."); return false; } @@ -1111,7 +1140,8 @@ public void addUnitToForce(@Nullable Unit u, int id) { u.setTech(forceTech); } else { - String cantTech = forceTech.getFullName() + " cannot maintain " + u.getName() + '\n' + "You will need to assign a tech manually."; + String cantTech = forceTech.getFullName() + " cannot maintain " + u.getName() + '\n' + + "You will need to assign a tech manually."; JOptionPane.showMessageDialog(null, cantTech, "Warning", JOptionPane.WARNING_MESSAGE); } } @@ -1136,7 +1166,6 @@ private void addAllCombatTeams(Force force) { } // region Missions/Contracts - /** * Add a mission to the campaign * @@ -1186,26 +1215,39 @@ public Collection getMissions() { * @return missions List sorted with complete missions at the bottom */ public List getSortedMissions() { - return getMissions().stream().sorted(Comparator.comparing(Mission::getStatus).thenComparing(m -> (m instanceof Contract) ? ((Contract) m).getStartDate() : LocalDate.now())).collect(Collectors.toList()); + return getMissions().stream() + .sorted(Comparator.comparing(Mission::getStatus) + .thenComparing(m -> (m instanceof Contract) ? ((Contract) m).getStartDate() : LocalDate.now())) + .collect(Collectors.toList()); } public List getActiveMissions(final boolean excludeEndDateCheck) { - return getMissions().stream().filter(m -> m.isActiveOn(getLocalDate(), excludeEndDateCheck)).collect(Collectors.toList()); + return getMissions().stream() + .filter(m -> m.isActiveOn(getLocalDate(), excludeEndDateCheck)) + .collect(Collectors.toList()); } public List getCompletedMissions() { - return getMissions().stream().filter(m -> m.getStatus().isCompleted()).collect(Collectors.toList()); + return getMissions().stream() + .filter(m -> m.getStatus().isCompleted()) + .collect(Collectors.toList()); } /** * @return a list of all currently active contracts */ public List getActiveContracts() { - return getMissions().stream().filter(c -> (c instanceof Contract) && c.isActiveOn(getLocalDate())).map(c -> (Contract) c).collect(Collectors.toList()); + return getMissions().stream() + .filter(c -> (c instanceof Contract) && c.isActiveOn(getLocalDate())) + .map(c -> (Contract) c) + .collect(Collectors.toList()); } public List getAtBContracts() { - return getMissions().stream().filter(c -> c instanceof AtBContract).map(c -> (AtBContract) c).collect(Collectors.toList()); + return getMissions().stream() + .filter(c -> c instanceof AtBContract) + .map(c -> (AtBContract) c) + .collect(Collectors.toList()); } /** @@ -1218,8 +1260,8 @@ public List getAtBContracts() { * have been accepted but have not yet started * should also be considered as active. * @return {@code true} if there is any currently active AtB contract, or if - * {@code includeFutureContracts} is {@code true} and there are future - * contracts starting after the current date. Otherwise, {@code false}. + * {@code includeFutureContracts} is {@code true} and there are future + * contracts starting after the current date. Otherwise, {@code false}. * @see #hasFutureAtBContract() */ public boolean hasActiveAtBContract(boolean includeFutureContracts) { @@ -1240,7 +1282,7 @@ public boolean hasActiveAtBContract(boolean includeFutureContracts) { * has a start date later than the current day. * * @return true if there is at least one future AtB contract (accepted but - * starting after the current date). Otherwise, false. + * starting after the current date). Otherwise, false. */ public boolean hasFutureAtBContract() { List contracts = getAtBContracts(); @@ -1260,16 +1302,22 @@ public List getActiveAtBContracts() { } public List getActiveAtBContracts(boolean excludeEndDateCheck) { - return getMissions().stream().filter(c -> (c instanceof AtBContract) && c.isActiveOn(getLocalDate(), excludeEndDateCheck)).map(c -> (AtBContract) c).collect(Collectors.toList()); + return getMissions().stream() + .filter(c -> (c instanceof AtBContract) && c.isActiveOn(getLocalDate(), excludeEndDateCheck)) + .map(c -> (AtBContract) c) + .collect(Collectors.toList()); } public List getCompletedAtBContracts() { - return getMissions().stream().filter(c -> (c instanceof AtBContract) && c.getStatus().isCompleted()).map(c -> (AtBContract) c).collect(Collectors.toList()); + return getMissions().stream() + .filter(c -> (c instanceof AtBContract) && c.getStatus().isCompleted()) + .map(c -> (AtBContract) c) + .collect(Collectors.toList()); } /** * @return whether or not the current campaign has an active contract for the - * current date + * current date */ public boolean hasActiveContract() { return hasActiveContract; @@ -1282,7 +1330,8 @@ public boolean hasActiveContract() { * elsewhere */ public void setHasActiveContract() { - hasActiveContract = getMissions().stream().anyMatch(c -> (c instanceof Contract) && c.isActiveOn(getLocalDate())); + hasActiveContract = getMissions().stream() + .anyMatch(c -> (c instanceof Contract) && c.isActiveOn(getLocalDate())); } // endregion Missions/Contracts @@ -1297,7 +1346,7 @@ public void addScenario(Scenario s, Mission m) { * Add scenario to an existing mission. This method will also assign the * scenario an id, provided * that it is a new scenario. It then adds the scenario to the scenarioId hash. - *

    + * * Scenarios with previously set ids can be sent to this mission, allowing one * to remove * and then re-add scenarios if needed. This functionality is used in the @@ -1317,7 +1366,9 @@ public void addScenario(Scenario s, Mission m, boolean suppressReport) { scenarios.put(id, s); if (newScenario && !suppressReport) { - addReport(MessageFormat.format(resources.getString("newAtBScenario.format"), s.getName(), MekHQ.getMHQOptions().getDisplayFormattedDate(s.getDate()))); + addReport(MessageFormat.format( + resources.getString("newAtBScenario.format"), + s.getName(), MekHQ.getMHQOptions().getDisplayFormattedDate(s.getDate()))); } MekHQ.triggerEvent(new ScenarioNewEvent(s)); @@ -1387,10 +1438,9 @@ public void importUnit(Unit unit) { * This transporters map is used to store transports, the kinds of * transporters they have, and their remaining capacity. The * transporters map is meant to be utilized by the GUI. - * - * @param campaignTransportType Transport Type (enum) we're adding to - * @param unit unit with transport capabilities * @see CampaignTransporterMap + * @param campaignTransportType Transport Type (enum) we're adding to + * @param unit unit with transport capabilities */ public void addCampaignTransport(CampaignTransportType campaignTransportType, Unit unit) { if (campaignTransportType.isShipTransport()) { @@ -1406,9 +1456,8 @@ public void addCampaignTransport(CampaignTransportType campaignTransportType, Un * in the campaign transport map. This method takes the CampaignTransportType and * transport as inputs and updates the map with the current capacities of the * transport. - * * @param campaignTransportType type (Enum) of TransportedUnitsSummary we're interested in - * @param transport Unit + * @param transport Unit */ public void updateTransportInTransports(CampaignTransportType campaignTransportType, Unit transport) { getCampaignTransporterMap(campaignTransportType).updateTransportInTransporterMap(transport); @@ -1418,10 +1467,9 @@ public void updateTransportInTransports(CampaignTransportType campaignTransportT * Deletes an entry from the list of specified list of transports. This gets * updated when the transport should no longer be in the CampaignTransporterMap, * such as when a Transport is mothballed or removed from the campaign. - * - * @param campaignTransportType Transport Type (enum) we're checking - * @param unit - The ship we want to remove from this Set * @see CampaignTransporterMap + * @param campaignTransportType Transport Type (enum) we're checking + * @param unit - The ship we want to remove from this Set */ public void removeCampaignTransporter(CampaignTransportType campaignTransportType, Unit unit) { if (campaignTransportType.isShipTransport()) { @@ -1532,7 +1580,8 @@ public Unit addNewUnit(Entity en, boolean allowNewPilots, int days, PartQuality unit.setDaysToArrival(days); if (allowNewPilots) { - Map> newCrew = Utilities.genRandomCrewWithCombinedSkill(this, unit, getFactionCode()); + Map> newCrew = Utilities + .genRandomCrewWithCombinedSkill(this, unit, getFactionCode()); newCrew.forEach((type, personnel) -> personnel.forEach(p -> type.getAddMethod().accept(unit, p))); } @@ -1584,15 +1633,21 @@ public Collection getUnits() { * @return a collection of active units */ public Collection getActiveUnits() { - return getHangar().getUnits().stream().filter(unit -> !unit.isMothballed() && !unit.isSalvage()).toList(); + return getHangar().getUnits().stream() + .filter(unit -> !unit.isMothballed() && !unit.isSalvage()) + .toList(); } public Collection getLargeCraftAndWarShips() { - return getHangar().getUnits().stream().filter(unit -> (unit.getEntity().isLargeCraft()) || (unit.getEntity().isWarShip())).collect(Collectors.toList()); + return getHangar().getUnits().stream() + .filter(unit -> (unit.getEntity().isLargeCraft()) || (unit.getEntity().isWarShip())) + .collect(Collectors.toList()); } public List getEntities() { - return getUnits().stream().map(Unit::getEntity).collect(Collectors.toList()); + return getUnits().stream() + .map(Unit::getEntity) + .collect(Collectors.toList()); } public Unit getUnit(UUID id) { @@ -1601,7 +1656,6 @@ public Unit getUnit(UUID id) { // region Personnel // region Person Creation - /** * Creates a new dependent with given gender. The origin faction and planet are * set to null. @@ -1627,8 +1681,13 @@ public Person newDependent(Gender gender) { * based on campaign options. * @return Return a {@link Person} object representing the new dependent. */ - public Person newDependent(Gender gender, @Nullable Faction originFaction, @Nullable Planet originPlanet) { - return newPerson(PersonnelRole.DEPENDENT, PersonnelRole.NONE, new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), originFaction), new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions(), originPlanet), gender); + public Person newDependent(Gender gender, @Nullable Faction originFaction, + @Nullable Planet originPlanet) { + return newPerson(PersonnelRole.DEPENDENT, + PersonnelRole.NONE, + new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), originFaction), + new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions(), originPlanet), + gender); } /** @@ -1653,7 +1712,8 @@ public Person newPerson(final PersonnelRole role) { * @return A new {@link Person}. */ public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole) { - return newPerson(primaryRole, secondaryRole, getFactionSelector(), getPlanetSelector(), Gender.RANDOMIZE); + return newPerson(primaryRole, secondaryRole, getFactionSelector(), getPlanetSelector(), + Gender.RANDOMIZE); } /** @@ -1668,8 +1728,12 @@ public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole sec * it value * @return A new {@link Person}. */ - public Person newPerson(final PersonnelRole primaryRole, final String factionCode, final Gender gender) { - return newPerson(primaryRole, PersonnelRole.NONE, new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), (factionCode == null) ? null : Factions.getInstance().getFaction(factionCode)), getPlanetSelector(), gender); + public Person newPerson(final PersonnelRole primaryRole, final String factionCode, + final Gender gender) { + return newPerson(primaryRole, PersonnelRole.NONE, + new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), + (factionCode == null) ? null : Factions.getInstance().getFaction(factionCode)), + getPlanetSelector(), gender); } /** @@ -1685,8 +1749,11 @@ public Person newPerson(final PersonnelRole primaryRole, final String factionCod * randomize it value * @return A new {@link Person}. */ - public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole, final AbstractFactionSelector factionSelector, final AbstractPlanetSelector planetSelector, final Gender gender) { - return newPerson(primaryRole, secondaryRole, getPersonnelGenerator(factionSelector, planetSelector), gender); + public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole, + final AbstractFactionSelector factionSelector, + final AbstractPlanetSelector planetSelector, final Gender gender) { + return newPerson(primaryRole, secondaryRole, + getPersonnelGenerator(factionSelector, planetSelector), gender); } /** @@ -1698,7 +1765,8 @@ public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole sec * creating the {@link Person}. * @return A new {@link Person} configured using {@code personnelGenerator}. */ - public Person newPerson(final PersonnelRole primaryRole, final AbstractPersonnelGenerator personnelGenerator) { + public Person newPerson(final PersonnelRole primaryRole, + final AbstractPersonnelGenerator personnelGenerator) { return newPerson(primaryRole, PersonnelRole.NONE, personnelGenerator, Gender.RANDOMIZE); } @@ -1714,7 +1782,9 @@ public Person newPerson(final PersonnelRole primaryRole, final AbstractPersonnel * randomize it value * @return A new {@link Person} configured using {@code personnelGenerator}. */ - public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole, final AbstractPersonnelGenerator personnelGenerator, final Gender gender) { + public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole secondaryRole, + final AbstractPersonnelGenerator personnelGenerator, + final Gender gender) { final Person person = personnelGenerator.generate(this, primaryRole, secondaryRole, gender); // Assign a random portrait after we generate a new person @@ -1735,7 +1805,6 @@ public void setFieldKitchenWithinCapacity(final Boolean fieldKitchenWithinCapaci // endregion Person Creation // region Personnel Recruitment - /** * @param p the person being added * @return true, if the person is hired successfully, otherwise false @@ -1757,6 +1826,7 @@ public boolean recruitPerson(Person p, boolean gmAdd) { } /** + * * @param p the person being added * @param prisonerStatus the person's prisoner status upon recruitment * @return true if the person is hired successfully, otherwise false @@ -1766,7 +1836,7 @@ public boolean recruitPerson(Person p, PrisonerStatus prisonerStatus) { } /** - * @param person the person being added + * @param person the person being added * @param prisonerStatus the person's prisoner status upon recruitment * @param gmAdd false means that they need to pay to hire this person, * true means it is added without paying @@ -1779,8 +1849,10 @@ public boolean recruitPerson(Person person, PrisonerStatus prisonerStatus, boole } // Only pay if option set, they weren't GM added, and they aren't a dependent, prisoner or bondsman - if (getCampaignOptions().isPayForRecruitment() && !person.getPrimaryRole().isDependent() && !gmAdd && prisonerStatus.isFree()) { - if (!getFinances().debit(TransactionType.RECRUITMENT, getLocalDate(), person.getSalary(this).multipliedBy(2), String.format(resources.getString("personnelRecruitmentFinancesReason.text"), person.getFullName()))) { + if (getCampaignOptions().isPayForRecruitment() && !person.getPrimaryRole().isDependent() + && !gmAdd && prisonerStatus.isFree()) { + if (!getFinances().debit(TransactionType.RECRUITMENT, getLocalDate(), + person.getSalary(this).multipliedBy(2), String.format(resources.getString("personnelRecruitmentFinancesReason.text"),person.getFullName()))) { addReport(String.format(resources.getString("personnelRecruitmentInsufficientFunds.text"), MekHQ.getMHQOptions().getFontColorNegativeHexColor(), person.getFullName())); return false; } @@ -1811,7 +1883,8 @@ public boolean recruitPerson(Person person, PrisonerStatus prisonerStatus, boole if (log) { formerSurname = person.getSurname().equals(formerSurname) ? "" : ' ' + String.format(resources.getString("personnelRecruitmentFormerSurname.text") + ' ', formerSurname); - String add = !prisonerStatus.isFree() ? (' ' + resources.getString(prisonerStatus.isBondsman() ? "personnelRecruitmentBondsman.text" : "personnelRecruitmentPrisoner.text")) : ""; + String add = !prisonerStatus.isFree() ? (' ' + resources.getString(prisonerStatus.isBondsman() ? "personnelRecruitmentBondsman.text" : "personnelRecruitmentPrisoner.text")) + : ""; addReport(String.format(resources.getString("personnelRecruitmentAddedToRoster.text"), person.getHyperlinkedName(), formerSurname, add)); } @@ -1869,7 +1942,8 @@ private void simulateRelationshipHistory(Person person) { // then we check for children if ((person.getGender().isFemale()) && (!person.isPregnant())) { - getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, true); + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, + true); if (person.isPregnant()) { @@ -1880,7 +1954,8 @@ private void simulateRelationshipHistory(Person person) { } if ((currentSpouse != null) && (currentSpouse.getGender().isFemale()) && (!currentSpouse.isPregnant())) { - getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), currentSpouse, true); + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), + currentSpouse, true); if (currentSpouse.isPregnant()) { @@ -1895,7 +1970,8 @@ private void simulateRelationshipHistory(Person person) { babysFather = null; } - if ((currentSpouse != null) && (currentSpouse.isPregnant()) && (currentDate.isAfter(currentSpouse.getDueDate()))) { + if ((currentSpouse != null) && (currentSpouse.isPregnant()) + && (currentDate.isAfter(currentSpouse.getDueDate()))) { children.addAll(getProcreation().birthHistoric(this, localDate, currentSpouse, spousesBabysFather)); spousesBabysFather = null; } @@ -1906,7 +1982,10 @@ private void simulateRelationshipHistory(Person person) { if (currentSpouse != null) { recruitPerson(currentSpouse, PrisonerStatus.FREE, true, false); - addReport(String.format(resources.getString("relativeJoinsForce.text"), currentSpouse.getHyperlinkedFullTitle(), person.getHyperlinkedFullTitle(), resources.getString("relativeJoinsForceSpouse.text"))); + addReport(String.format(resources.getString("relativeJoinsForce.text"), + currentSpouse.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), + resources.getString("relativeJoinsForceSpouse.text"))); MekHQ.triggerEvent(new PersonChangedEvent(currentSpouse)); } @@ -1950,7 +2029,10 @@ private void simulateRelationshipHistory(Person person) { recruitPerson(child, PrisonerStatus.FREE, true, false); - addReport(String.format(resources.getString("relativeJoinsForce.text"), child.getHyperlinkedFullTitle(), person.getHyperlinkedFullTitle(), resources.getString("relativeJoinsForceChild.text"))); + addReport(String.format(resources.getString("relativeJoinsForce.text"), + child.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), + resources.getString("relativeJoinsForceChild.text"))); MekHQ.triggerEvent(new PersonChangedEvent(child)); } @@ -1960,7 +2042,6 @@ private void simulateRelationshipHistory(Person person) { // endregion Personnel Recruitment // region Bloodnames - /** * If the person does not already have a bloodname, assigns a chance of having * one based on @@ -1982,7 +2063,10 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { // keep the // current bloodname or assign a new one if (!person.getBloodname().isEmpty()) { - int result = JOptionPane.showConfirmDialog(null, person.getFullTitle() + " already has the bloodname " + person.getBloodname() + "\nDo you wish to remove that bloodname and generate a new one?", "Already Has Bloodname", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + int result = JOptionPane.showConfirmDialog(null, + person.getFullTitle() + " already has the bloodname " + person.getBloodname() + + "\nDo you wish to remove that bloodname and generate a new one?", + "Already Has Bloodname", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (result == JOptionPane.NO_OPTION) { return; } else { @@ -1995,31 +2079,51 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { if (!ignoreDice) { switch (person.getPhenotype()) { case MEKWARRIOR: { - bloodnameTarget += person.hasSkill(SkillType.S_GUN_MEK) ? person.getSkill(SkillType.S_GUN_MEK).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_MEK) ? person.getSkill(SkillType.S_PILOT_MEK).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_GUN_MEK) + ? person.getSkill(SkillType.S_GUN_MEK).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_MEK) + ? person.getSkill(SkillType.S_PILOT_MEK).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; break; } case AEROSPACE: { - bloodnameTarget += person.hasSkill(SkillType.S_GUN_AERO) ? person.getSkill(SkillType.S_GUN_AERO).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_AERO) ? person.getSkill(SkillType.S_PILOT_AERO).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_GUN_AERO) + ? person.getSkill(SkillType.S_GUN_AERO).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_AERO) + ? person.getSkill(SkillType.S_PILOT_AERO).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; break; } case ELEMENTAL: { - bloodnameTarget += person.hasSkill(SkillType.S_GUN_BA) ? person.getSkill(SkillType.S_GUN_BA).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; - bloodnameTarget += person.hasSkill(SkillType.S_ANTI_MEK) ? person.getSkill(SkillType.S_ANTI_MEK).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_GUN_BA) + ? person.getSkill(SkillType.S_GUN_BA).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_ANTI_MEK) + ? person.getSkill(SkillType.S_ANTI_MEK).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; break; } case VEHICLE: { - bloodnameTarget += person.hasSkill(SkillType.S_GUN_VEE) ? person.getSkill(SkillType.S_GUN_VEE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_GUN_VEE) + ? person.getSkill(SkillType.S_GUN_VEE).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; switch (person.getPrimaryRole()) { case GROUND_VEHICLE_DRIVER: - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_GVEE) ? person.getSkill(SkillType.S_PILOT_GVEE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_GVEE) + ? person.getSkill(SkillType.S_PILOT_GVEE).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; break; case NAVAL_VEHICLE_DRIVER: - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_NVEE) ? person.getSkill(SkillType.S_PILOT_NVEE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_NVEE) + ? person.getSkill(SkillType.S_PILOT_NVEE).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; break; case VTOL_PILOT: - bloodnameTarget += person.hasSkill(SkillType.S_PILOT_VTOL) ? person.getSkill(SkillType.S_PILOT_VTOL).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL; + bloodnameTarget += person.hasSkill(SkillType.S_PILOT_VTOL) + ? person.getSkill(SkillType.S_PILOT_VTOL).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL; break; default: break; @@ -2027,22 +2131,32 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { break; } case PROTOMEK: { - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_GUN_PROTO) ? person.getSkill(SkillType.S_GUN_PROTO).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_GUN_PROTO) + ? person.getSkill(SkillType.S_GUN_PROTO).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL); break; } case NAVAL: { switch (person.getPrimaryRole()) { case VESSEL_PILOT: - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_PILOT_SPACE) ? person.getSkill(SkillType.S_PILOT_SPACE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_PILOT_SPACE) + ? person.getSkill(SkillType.S_PILOT_SPACE).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL); break; case VESSEL_GUNNER: - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_GUN_SPACE) ? person.getSkill(SkillType.S_GUN_SPACE).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_GUN_SPACE) + ? person.getSkill(SkillType.S_GUN_SPACE).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL); break; case VESSEL_CREW: - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_TECH_VESSEL) ? person.getSkill(SkillType.S_TECH_VESSEL).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_TECH_VESSEL) + ? person.getSkill(SkillType.S_TECH_VESSEL).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL); break; case VESSEL_NAVIGATOR: - bloodnameTarget += 2 * (person.hasSkill(SkillType.S_NAV) ? person.getSkill(SkillType.S_NAV).getFinalSkillValue() : TargetRoll.AUTOMATIC_FAIL); + bloodnameTarget += 2 * (person.hasSkill(SkillType.S_NAV) + ? person.getSkill(SkillType.S_NAV).getFinalSkillValue() + : TargetRoll.AUTOMATIC_FAIL); break; default: break; @@ -2083,7 +2197,9 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { if (ignoreDice || (Compute.d6(2) >= bloodnameTarget)) { final Phenotype phenotype = person.getPhenotype().isNone() ? Phenotype.GENERAL : person.getPhenotype(); - final Bloodname bloodname = Bloodname.randomBloodname((getFaction().isClan() ? getFaction() : person.getOriginFaction()).getShortName(), phenotype, getGameYear()); + final Bloodname bloodname = Bloodname.randomBloodname( + (getFaction().isClan() ? getFaction() : person.getOriginFaction()).getShortName(), + phenotype, getGameYear()); if (bloodname != null) { person.setBloodname(bloodname.getName()); personUpdated(person); @@ -2093,7 +2209,6 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { // endregion Bloodnames // region Other Personnel Methods - /** * Imports a {@link Person} into a campaign. * @@ -2123,7 +2238,9 @@ public Collection getPersonnel() { * @return a {@code List} of {@link Person} objects who have not left the unit */ public List getPersonnelFilteringOutDeparted() { - return getPersonnel().stream().filter(person -> !person.getStatus().isDepartedUnit()).collect(Collectors.toList()); + return getPersonnel().stream() + .filter(person -> !person.getStatus().isDepartedUnit()) + .collect(Collectors.toList()); } /** @@ -2132,7 +2249,9 @@ public List getPersonnelFilteringOutDeparted() { * @return a {@link Person} List containing all active personnel */ public List getActivePersonnel() { - return getPersonnel().stream().filter(p -> p.getStatus().isActive()).collect(Collectors.toList()); + return getPersonnel().stream() + .filter(p -> p.getStatus().isActive()) + .collect(Collectors.toList()); } /** @@ -2145,16 +2264,21 @@ public List getActivePersonnel() { * @return a {@link List} of {@link Person} objects representing combat-capable personnel */ public List getActiveCombatPersonnel() { - return getActivePersonnel().stream().filter(p -> p.getPrimaryRole().isCombat() || p.getSecondaryRole().isCombat()).collect(Collectors.toList()); + return getActivePersonnel().stream() + .filter(p -> p.getPrimaryRole().isCombat() || p.getSecondaryRole().isCombat()) + .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()); + return getPersonnel().stream() + .filter(person -> person.getPrimaryRole().isDependent()) + .filter(person -> person.getStatus().isActive()) + .collect(Collectors.toList()); } /** @@ -2163,7 +2287,9 @@ public List getActiveDependents() { * @return a {@link Person} List containing all active personnel */ public List getCurrentPrisoners() { - return getActivePersonnel().stream().filter(person -> person.getPrisonerStatus().isCurrentPrisoner()).collect(Collectors.toList()); + return getActivePersonnel().stream() + .filter(person -> person.getPrisonerStatus().isCurrentPrisoner()) + .collect(Collectors.toList()); } /** @@ -2172,7 +2298,9 @@ public List getCurrentPrisoners() { * @return a {@link Person} List containing all active personnel */ public List getFriendlyPrisoners() { - return getPersonnel().stream().filter(p -> p.getStatus().isPoW()).collect(Collectors.toList()); + return getPersonnel().stream() + .filter(p -> p.getStatus().isPoW()) + .collect(Collectors.toList()); } /** @@ -2182,7 +2310,9 @@ public List getFriendlyPrisoners() { * @return a {@link Person} List containing all active personnel */ public List getAwolPersonnel() { - return getPersonnel().stream().filter(p -> p.getStatus().isAwol()).collect(Collectors.toList()); + return getPersonnel().stream() + .filter(p -> p.getStatus().isAwol()) + .collect(Collectors.toList()); } /** @@ -2192,17 +2322,18 @@ public List getAwolPersonnel() { * @return a {@link Person} List containing all active personnel */ public List getStudents() { - return getPersonnel().stream().filter(p -> p.getStatus().isStudent()).collect(Collectors.toList()); + return getPersonnel().stream() + .filter(p -> p.getStatus().isStudent()) + .collect(Collectors.toList()); } // endregion Other Personnel Methods // region Personnel Selectors and Generators - /** * Gets the {@link AbstractFactionSelector} to use with this campaign. * * @return An {@link AbstractFactionSelector} to use when selecting a - * {@link Faction}. + * {@link Faction}. */ public AbstractFactionSelector getFactionSelector() { return getFactionSelector(getCampaignOptions().getRandomOriginOptions()); @@ -2213,17 +2344,18 @@ public AbstractFactionSelector getFactionSelector() { * * @param options the random origin options to use * @return An {@link AbstractFactionSelector} to use when selecting a - * {@link Faction}. + * {@link Faction}. */ public AbstractFactionSelector getFactionSelector(final RandomOriginOptions options) { - return options.isRandomizeOrigin() ? new RangedFactionSelector(options) : new DefaultFactionSelector(options); + return options.isRandomizeOrigin() ? new RangedFactionSelector(options) + : new DefaultFactionSelector(options); } /** * Gets the {@link AbstractPlanetSelector} to use with this campaign. * * @return An {@link AbstractPlanetSelector} to use when selecting a - * {@link Planet}. + * {@link Planet}. */ public AbstractPlanetSelector getPlanetSelector() { return getPlanetSelector(getCampaignOptions().getRandomOriginOptions()); @@ -2234,10 +2366,11 @@ public AbstractPlanetSelector getPlanetSelector() { * * @param options the random origin options to use * @return An {@link AbstractPlanetSelector} to use when selecting a - * {@link Planet}. + * {@link Planet}. */ public AbstractPlanetSelector getPlanetSelector(final RandomOriginOptions options) { - return options.isRandomizeOrigin() ? new RangedPlanetSelector(options) : new DefaultPlanetSelector(options); + return options.isRandomizeOrigin() ? new RangedPlanetSelector(options) + : new DefaultPlanetSelector(options); } /** @@ -2248,9 +2381,11 @@ public AbstractPlanetSelector getPlanetSelector(final RandomOriginOptions option * @param planetSelector The {@link AbstractPlanetSelector} to use when * choosing a {@link Planet}. * @return An {@link AbstractPersonnelGenerator} to use when creating new - * personnel. + * personnel. */ - public AbstractPersonnelGenerator getPersonnelGenerator(final AbstractFactionSelector factionSelector, final AbstractPlanetSelector planetSelector) { + public AbstractPersonnelGenerator getPersonnelGenerator( + final AbstractFactionSelector factionSelector, + final AbstractPlanetSelector planetSelector) { final DefaultPersonnelGenerator generator = new DefaultPersonnelGenerator(factionSelector, planetSelector); generator.setNameGenerator(RandomNameGenerator.getInstance()); generator.setSkillPreferences(getRandomSkillPreferences()); @@ -2262,7 +2397,9 @@ public AbstractPersonnelGenerator getPersonnelGenerator(final AbstractFactionSel public List getPatients() { List patients = new ArrayList<>(); for (Person p : getPersonnel()) { - if (p.needsFixing() || (getCampaignOptions().isUseAdvancedMedical() && p.hasInjuries(true) && p.getStatus().isActive())) { + if (p.needsFixing() + || (getCampaignOptions().isUseAdvancedMedical() && p.hasInjuries(true) + && p.getStatus().isActive())) { patients.add(p); } } @@ -2354,7 +2491,9 @@ private PartInUse getPartInUse(Part part) { return null; } // Makes no sense buying those separately from the chasis - if ((part instanceof EquipmentPart) && ((EquipmentPart) part).getType() != null && (((EquipmentPart) part).getType().hasFlag(MiscType.F_CHASSIS_MODIFICATION))) { + if ((part instanceof EquipmentPart) + && ((EquipmentPart) part).getType() != null + && (((EquipmentPart) part).getType().hasFlag(MiscType.F_CHASSIS_MODIFICATION))) { return null; } // Replace a "missing" part with a corresponding "new" one. @@ -2376,7 +2515,7 @@ private PartInUse getPartInUse(Part part) { * @param part The {@link Part} for which the default stock percentage is to * be determined. The part must not be {@code null}. * @return An {@code int} representing the default stock percentage for the - * given part type, as defined in the campaign options. + * given part type, as defined in the campaign options. */ private int getDefaultStockPercent(Part part) { if (part instanceof HeatSink) { @@ -2395,7 +2534,7 @@ private int getDefaultStockPercent(Part part) { return campaignOptions.getAutoLogisticsNonRepairableLocation(); } else if (part instanceof AmmoBin) { return campaignOptions.getAutoLogisticsAmmunition(); - } else if (part instanceof Armor) { + } else if (part instanceof Armor ) { return campaignOptions.getAutoLogisticsArmor(); } @@ -2404,7 +2543,7 @@ private int getDefaultStockPercent(Part part) { /** * Add data from an actual part to a PartInUse data element - * + * * @param partInUse part in use record to update * @param incomingPart new part that needs to be added to this * record @@ -2412,7 +2551,8 @@ private int getDefaultStockPercent(Part part) { * @param ignoreSparesUnderQuality don't count spare parts lower than this * quality */ - private void updatePartInUseData(PartInUse partInUse, Part incomingPart, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { + private void updatePartInUseData(PartInUse partInUse, Part incomingPart, + boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { if (ignoreMothballedUnits && (null != incomingPart.getUnit()) && incomingPart.getUnit().isMothballed()) { } else if ((incomingPart.getUnit() != null) || (incomingPart instanceof MissingPart)) { @@ -2432,13 +2572,14 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, boolean /** * Find all the parts that match this PartInUse and update their data - * + * * @param partInUse part in use record to update * @param ignoreMothballedUnits don't count parts in mothballed units * @param ignoreSparesUnderQuality don't count spare parts lower than this * quality */ - public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { + public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, + PartQuality ignoreSparesUnderQuality) { partInUse.setUseCount(0); partInUse.setStoreCount(0); partInUse.setTransferCount(0); @@ -2446,13 +2587,16 @@ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, getWarehouse().forEachPart(incomingPart -> { PartInUse newPartInUse = getPartInUse(incomingPart); if (partInUse.equals(newPartInUse)) { - updatePartInUseData(partInUse, incomingPart, ignoreMothballedUnits, ignoreSparesUnderQuality); + updatePartInUseData(partInUse, incomingPart, + ignoreMothballedUnits, ignoreSparesUnderQuality); } }); for (IAcquisitionWork maybePart : shoppingList.getPartList()) { PartInUse newPartInUse = getPartInUse((Part) maybePart); if (partInUse.equals(newPartInUse)) { - partInUse.setPlannedCount(partInUse.getPlannedCount() + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); + partInUse.setPlannedCount(partInUse.getPlannedCount() + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() + : (Part) maybePart) * maybePart.getQuantity()); } } } @@ -2460,13 +2604,14 @@ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, /** * Create a data set detailing all the parts being used (or not) and their * warehouse spares - * + * * @param ignoreMothballedUnits don't count parts in mothballed units * @param ignoreSparesUnderQuality don't count spare parts lower than this * quality * @return a Set of PartInUse data for display or inspection */ - public Set getPartsInUse(boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { + public Set getPartsInUse(boolean ignoreMothballedUnits, + PartQuality ignoreSparesUnderQuality) { // java.util.Set doesn't supply a get(Object) method, so we have to use a // java.util.Map Map inUse = new HashMap<>(); @@ -2505,12 +2650,15 @@ public Set getPartsInUse(boolean ignoreMothballedUnits, PartQuality i } inUse.put(partInUse, partInUse); } - partInUse.setPlannedCount(partInUse.getPlannedCount() + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); + partInUse.setPlannedCount(partInUse.getPlannedCount() + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() + : (Part) maybePart) * maybePart.getQuantity()); } return inUse.keySet().stream() - // Hacky but otherwise we end up with zero lines when filtering things out - .filter(p -> p.getUseCount() != 0 || p.getStoreCount() != 0 || p.getPlannedCount() != 0).collect(Collectors.toSet()); + // Hacky but otherwise we end up with zero lines when filtering things out + .filter(p -> p.getUseCount() != 0 || p.getStoreCount() != 0 || p.getPlannedCount() != 0) + .collect(Collectors.toSet()); } @Deprecated @@ -2551,7 +2699,8 @@ public List fetchAndClearNewReports() { * * @param role One of the PersonnelRole enum values * @param primary The skill to use for comparison. - * @param secondary If not null and there is more than one person tied for the + * @param secondary + * If not null and there is more than one person tied for the * most * the highest, preference will be given to the one with a * higher @@ -2567,16 +2716,16 @@ public Person findBestInRole(PersonnelRole role, String primary, String secondar retVal = p; highest = p.getSkill(primary).getLevel(); } else if (secondary != null && p.getSkill(primary).getLevel() == highest && - /* - * If the skill level of the current person is the same as the previous highest, - * select the current instead under the following conditions: - */ - (retVal == null || // None has been selected yet (current has level 0) - retVal.getSkill(secondary) == null || // Previous selection does not have secondary - // skill - (p.getSkill(secondary) != null // Current has secondary skill and it is higher than the - // previous. - && p.getSkill(secondary).getLevel() > retVal.getSkill(secondary).getLevel()))) { + /* + * If the skill level of the current person is the same as the previous highest, + * select the current instead under the following conditions: + */ + (retVal == null || // None has been selected yet (current has level 0) + retVal.getSkill(secondary) == null || // Previous selection does not have secondary + // skill + (p.getSkill(secondary) != null // Current has secondary skill and it is higher than the + // previous. + && p.getSkill(secondary).getLevel() > retVal.getSkill(secondary).getLevel()))) { retVal = p; } } @@ -2602,7 +2751,7 @@ public Person findBestInRole(PersonnelRole role, String skill) { /** * @return The list of all active {@link Person}s who qualify as technicians - * ({@link Person#isTech()})); + * ({@link Person#isTech()})); */ public List getTechs() { return getTechs(false); @@ -2630,11 +2779,14 @@ public List getTechs(final boolean noZeroMinute, final boolean eliteFirs * @param expanded If TRUE, then include techs with expanded roles (e.g. * Tech/Vessel skill) * @return The list of active {@link Person}s who qualify as technicians - * ({@link Person#isTech()}), or who qualify as expanded technicians - * ({@link Person#isTechExpanded()}). + * ({@link Person#isTech()}), or who qualify as expanded technicians + * ({@link Person#isTechExpanded()}). */ public List getTechsExpanded(final boolean noZeroMinute, final boolean eliteFirst, final boolean expanded) { - final List techs = getActivePersonnel().stream().filter(person -> (expanded ? person.isTechExpanded() : person.isTech()) && (!noZeroMinute || (person.getMinutesLeft() > 0))).collect(Collectors.toList()); + final List techs = getActivePersonnel().stream() + .filter(person -> (expanded ? person.isTechExpanded() : person.isTech()) + && (!noZeroMinute || (person.getMinutesLeft() > 0))) + .collect(Collectors.toList()); // also need to loop through and collect engineers on self-crewed vessels for (final Unit unit : getUnits()) { @@ -2646,10 +2798,13 @@ public List getTechsExpanded(final boolean noZeroMinute, final boolean e // Return the tech collection sorted worst to best Skill Level, or reversed if // we want // elites first - Comparator techSorter = Comparator.comparingInt(person -> person.getSkillLevel(this, !person.getPrimaryRole().isTech() && person.getSecondaryRole().isTechSecondary()).ordinal()); + Comparator techSorter = Comparator + .comparingInt(person -> person.getSkillLevel(this, !person.getPrimaryRole().isTech() + && person.getSecondaryRole().isTechSecondary()).ordinal()); if (eliteFirst) { - techSorter = techSorter.reversed().thenComparing(Comparator.comparingInt(Person::getDailyAvailableTechTime).reversed()); + techSorter = techSorter.reversed().thenComparing(Comparator + .comparingInt(Person::getDailyAvailableTechTime).reversed()); } else { techSorter = techSorter.thenComparing(Comparator.comparingInt(Person::getMinutesLeft).reversed()); } @@ -2705,18 +2860,22 @@ public String healPerson(Person medWork, Person doctor) { return ""; } String report = ""; - report += doctor.getHyperlinkedFullTitle() + " attempts to heal " + medWork.getFullName(); + report += doctor.getHyperlinkedFullTitle() + " attempts to heal " + + medWork.getFullName(); TargetRoll target = getTargetFor(medWork, doctor); int roll = Compute.d6(2); - report = report + ", needs " + target.getValueAsString() + " and rolls " + roll + ':'; + report = report + ", needs " + target.getValueAsString() + + " and rolls " + roll + ':'; int xpGained = 0; // If we get a natural 2 that isn't an automatic success, reroll if Edge is // available and in use. - if (getCampaignOptions().isUseSupportEdge() && doctor.getOptions().booleanOption(PersonnelOptions.EDGE_MEDICAL)) { + if (getCampaignOptions().isUseSupportEdge() + && doctor.getOptions().booleanOption(PersonnelOptions.EDGE_MEDICAL)) { if ((roll == 2) && (doctor.getCurrentEdge() > 0) && (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS)) { doctor.changeCurrentEdge(-1); roll = Compute.d6(2); - report += medWork.fail() + '\n' + doctor.getHyperlinkedFullTitle() + " uses Edge to reroll:" + " rolls " + roll + ':'; + report += medWork.fail() + '\n' + doctor.getHyperlinkedFullTitle() + " uses Edge to reroll:" + + " rolls " + roll + ':'; } } if (roll >= target.getValue()) { @@ -2745,23 +2904,30 @@ public String healPerson(Person medWork, Person doctor) { doctor.awardXP(this, xpGained); report += " (" + xpGained + "XP gained) "; } - medWork.setDaysToWaitForHealing(getCampaignOptions().getHealingWaitingPeriod()); + medWork.setDaysToWaitForHealing(getCampaignOptions() + .getHealingWaitingPeriod()); return report; } public TargetRoll getTargetFor(Person medWork, Person doctor) { Skill skill = doctor.getSkill(SkillType.S_DOCTOR); if (null == skill) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, doctor.getFullName() + " isn't a doctor, he just plays one on TV."); + return new TargetRoll(TargetRoll.IMPOSSIBLE, doctor.getFullName() + + " isn't a doctor, he just plays one on TV."); } - if (medWork.getDoctorId() != null && !medWork.getDoctorId().equals(doctor.getId())) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, medWork.getFullName() + " is already being tended by another doctor"); + if (medWork.getDoctorId() != null + && !medWork.getDoctorId().equals(doctor.getId())) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, + medWork.getFullName() + " is already being tended by another doctor"); } - if (!medWork.needsFixing() && !(getCampaignOptions().isUseAdvancedMedical() && medWork.needsAMFixing())) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, medWork.getFullName() + " does not require healing."); + if (!medWork.needsFixing() + && !(getCampaignOptions().isUseAdvancedMedical() && medWork.needsAMFixing())) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, + medWork.getFullName() + " does not require healing."); } if (getPatientsFor(doctor) > 25) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, doctor.getFullName() + " already has 25 patients."); + return new TargetRoll(TargetRoll.IMPOSSIBLE, doctor.getFullName() + + " already has 25 patients."); } TargetRoll target = new TargetRoll(skill.getFinalSkillValue(), skill.getSkillLevel().toString()); if (target.getValue() == TargetRoll.IMPOSSIBLE) { @@ -2831,8 +2997,9 @@ public TargetRoll getTargetFor(Person medWork, Person doctor) { * * @param type the {@link AdministratorSpecialization} representing the administrative role to check for. * Passing a {@code null} type will result in an {@link IllegalStateException}. + * * @return the most senior {@link Person} with the specified administrative role, or {@code null} - * if no eligible administrator is found. + * if no eligible administrator is found. * *

    Behavior:

    *
      @@ -2844,6 +3011,7 @@ public TargetRoll getTargetFor(Person medWork, Person doctor) { *
    • Seniority is determined by the {@link Person#outRanksUsingSkillTiebreaker} method, * which uses a skill-based tiebreaker when necessary.
    • *
    + * * @throws IllegalStateException if {@code type} is null or an unsupported value. */ public @Nullable Person getSeniorAdminPerson(AdministratorSpecialization type) { @@ -2851,12 +3019,14 @@ public TargetRoll getTargetFor(Person medWork, Person doctor) { for (Person person : getAdmins()) { boolean isEligible = switch (type) { - case COMMAND -> person.getPrimaryRole().isAdministratorCommand() || person.getSecondaryRole().isAdministratorCommand(); - case LOGISTICS -> - person.getPrimaryRole().isAdministratorLogistics() || person.getSecondaryRole().isAdministratorLogistics(); - case TRANSPORT -> - person.getPrimaryRole().isAdministratorTransport() || person.getSecondaryRole().isAdministratorTransport(); - case HR -> person.getPrimaryRole().isAdministratorHR() || person.getSecondaryRole().isAdministratorHR(); + case COMMAND -> person.getPrimaryRole().isAdministratorCommand() + || person.getSecondaryRole().isAdministratorCommand(); + case LOGISTICS -> person.getPrimaryRole().isAdministratorLogistics() + || person.getSecondaryRole().isAdministratorLogistics(); + case TRANSPORT -> person.getPrimaryRole().isAdministratorTransport() + || person.getSecondaryRole().isAdministratorTransport(); + case HR -> person.getPrimaryRole().isAdministratorHR() + || person.getSecondaryRole().isAdministratorHR(); }; if (isEligible) { @@ -2878,7 +3048,7 @@ public TargetRoll getTargetFor(Person medWork, Person doctor) { * if acquisitions automatically succeed. * * @return A List of {@link Person} who can perform logistical - * actions. + * actions. */ public List getLogisticsPersonnel() { String skill = getCampaignOptions().getAcquisitionSkill(); @@ -2947,7 +3117,7 @@ public ShoppingList goShopping(ShoppingList sList) { * * @param sList The shopping list to use when shopping. * @return The new shopping list containing the items that were not - * acquired. + * acquired. */ private ShoppingList goShoppingAutomatically(ShoppingList sList) { List currentList = new ArrayList<>(sList.getShoppingList()); @@ -2976,7 +3146,7 @@ private ShoppingList goShoppingAutomatically(ShoppingList sList) { * * @param sList The shopping list to use when shopping. * @return The new shopping list containing the items that were not - * acquired. + * acquired. */ private ShoppingList goShoppingStandard(ShoppingList sList) { List logisticsPersonnel = getLogisticsPersonnel(); @@ -3019,7 +3189,7 @@ private ShoppingList goShoppingStandard(ShoppingList sList) { * * @param sList The shopping list to use when shopping. * @return The new shopping list containing the items that were not - * acquired. + * acquired. */ private ShoppingList goShoppingByPlanet(ShoppingList sList) { List logisticsPersonnel = getLogisticsPersonnel(); @@ -3038,7 +3208,8 @@ private ShoppingList goShoppingByPlanet(ShoppingList sList) { // find planets within a certain radius - the function will weed out dead // planets - List systems = Systems.getInstance().getShoppingSystems(getCurrentSystem(), getCampaignOptions().getMaxJumpsPlanetaryAcquisition(), currentDate); + List systems = Systems.getInstance().getShoppingSystems(getCurrentSystem(), + getCampaignOptions().getMaxJumpsPlanetaryAcquisition(), currentDate); for (Person person : logisticsPersonnel) { if (currentList.isEmpty()) { @@ -3072,11 +3243,18 @@ private ShoppingList goShoppingByPlanet(ShoppingList sList) { if (result == PartAcquisitionResult.Success) { int transitTime = calculatePartTransitTime(system); int totalQuantity = 0; - while (shoppingItem.getQuantity() > 0 && canAcquireParts(person) && acquireEquipment(shoppingItem, person, system, transitTime)) { + while (shoppingItem.getQuantity() > 0 + && canAcquireParts(person) + && acquireEquipment(shoppingItem, person, system, transitTime)) { totalQuantity++; } if (totalQuantity > 0) { - addReport(personTitle + " found " + shoppingItem.getQuantityName(totalQuantity) + " on " + system.getPrintableName(currentDate) + ". Delivery in " + transitTime + " days."); + addReport(personTitle + " found " + + shoppingItem.getQuantityName(totalQuantity) + + " on " + + system.getPrintableName(currentDate) + + ". Delivery in " + transitTime + " days."); } } else if (result == PartAcquisitionResult.PartInherentFailure) { shelvedItems.add(shoppingItem); @@ -3089,7 +3267,9 @@ private ShoppingList goShoppingByPlanet(ShoppingList sList) { // if we can't afford it, then don't keep searching for it on other planets if (!canPayFor(shoppingItem)) { if (!getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("You cannot afford to purchase another " + shoppingItem.getAcquisitionName() + ""); + addReport("You cannot afford to purchase another " + + shoppingItem.getAcquisitionName() + ""); } shelvedItems.add(shoppingItem); } else { @@ -3136,7 +3316,8 @@ public boolean canAcquireParts(@Nullable Person person) { return true; } int maxAcquisitions = getCampaignOptions().getMaxAcquisitions(); - return maxAcquisitions <= 0 || person.getAcquisitions() < maxAcquisitions; + return maxAcquisitions <= 0 + || person.getAcquisitions() < maxAcquisitions; } /*** @@ -3151,7 +3332,8 @@ public boolean canAcquireParts(@Nullable Person person) { */ public boolean canPayFor(IAcquisitionWork acquisition) { // SHOULD we check to see if this acquisition needs to be paid for - if ((acquisition instanceof UnitOrder && getCampaignOptions().isPayForUnits()) || (acquisition instanceof Part && getCampaignOptions().isPayForParts())) { + if ((acquisition instanceof UnitOrder && getCampaignOptions().isPayForUnits()) + || (acquisition instanceof Part && getCampaignOptions().isPayForParts())) { // CAN the acquisition actually be paid for return getFunds().isGreaterOrEqualThan(acquisition.getBuyCost()); } @@ -3171,38 +3353,54 @@ public boolean canPayFor(IAcquisitionWork acquisition) { * user is not using planetary acquisition. * @return true if your target roll succeeded. */ - public PartAcquisitionResult findContactForAcquisition(IAcquisitionWork acquisition, Person person, PlanetarySystem system) { + public PartAcquisitionResult findContactForAcquisition(IAcquisitionWork acquisition, Person person, + PlanetarySystem system) { TargetRoll target = getTargetForAcquisition(acquisition, person); - String impossibleSentencePrefix = person == null ? "Can't search for " : person.getFullName() + " can't search for "; - String failedSentencePrefix = person == null ? "No contacts available for " : person.getFullName() + " is unable to find contacts for "; - String succeededSentencePrefix = person == null ? "Possible contact for " : person.getFullName() + " has found a contact for "; + String impossibleSentencePrefix = person == null ? "Can't search for " + : person.getFullName() + " can't search for "; + String failedSentencePrefix = person == null ? "No contacts available for " + : person.getFullName() + " is unable to find contacts for "; + String succeededSentencePrefix = person == null ? "Possible contact for " + : person.getFullName() + " has found a contact for "; // if it's already impossible, don't bother with the rest if (target.getValue() == TargetRoll.IMPOSSIBLE) { if (getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("" + impossibleSentencePrefix + acquisition.getAcquisitionName() + " on " + system.getPrintableName(getLocalDate()) + " because: " + target.getDesc()); + addReport("" + + impossibleSentencePrefix + acquisition.getAcquisitionName() + + " on " + system.getPrintableName(getLocalDate()) + " because: " + + target.getDesc()); } return PartAcquisitionResult.PartInherentFailure; } - target = system.getPrimaryPlanet().getAcquisitionMods(target, getLocalDate(), getCampaignOptions(), getFaction(), acquisition.getTechBase() == Part.T_CLAN); + target = system.getPrimaryPlanet().getAcquisitionMods(target, getLocalDate(), getCampaignOptions(), + getFaction(), + acquisition.getTechBase() == Part.T_CLAN); if (target.getValue() == TargetRoll.IMPOSSIBLE) { if (getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("" + impossibleSentencePrefix + acquisition.getAcquisitionName() + " on " + system.getPrintableName(getLocalDate()) + " because: " + target.getDesc()); + addReport("" + + impossibleSentencePrefix + acquisition.getAcquisitionName() + + " on " + system.getPrintableName(getLocalDate()) + " because: " + + target.getDesc()); } return PartAcquisitionResult.PlanetSpecificFailure; } if (Compute.d6(2) < target.getValue()) { // no contacts on this planet, move along if (getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("" + failedSentencePrefix + acquisition.getAcquisitionName() + " on " + system.getPrintableName(getLocalDate()) + ""); + addReport("" + + failedSentencePrefix + acquisition.getAcquisitionName() + + " on " + system.getPrintableName(getLocalDate()) + ""); } return PartAcquisitionResult.PlanetSpecificFailure; } else { if (getCampaignOptions().isPlanetAcquisitionVerbose()) { - addReport("" + succeededSentencePrefix + acquisition.getAcquisitionName() + " on " + system.getPrintableName(getLocalDate()) + ""); + addReport("" + + succeededSentencePrefix + acquisition.getAcquisitionName() + + " on " + system.getPrintableName(getLocalDate()) + ""); } return PartAcquisitionResult.Success; } @@ -3240,7 +3438,8 @@ public boolean acquireEquipment(IAcquisitionWork acquisition, Person person) { * @return a boolean indicating whether the attempt to acquire equipment was * successful. */ - private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, PlanetarySystem system, int transitDays) { + private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, PlanetarySystem system, + int transitDays) { boolean found = false; String report = ""; @@ -3256,14 +3455,17 @@ private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, Pl } if (null != system) { - target = system.getPrimaryPlanet().getAcquisitionMods(target, getLocalDate(), getCampaignOptions(), getFaction(), acquisition.getTechBase() == Part.T_CLAN); + target = system.getPrimaryPlanet().getAcquisitionMods(target, getLocalDate(), + getCampaignOptions(), getFaction(), acquisition.getTechBase() == Part.T_CLAN); } report += "attempts to find " + acquisition.getAcquisitionName(); // if impossible, then return if (target.getValue() == TargetRoll.IMPOSSIBLE) { - report += ": " + target.getDesc() + ""; - if (!getCampaignOptions().isUsePlanetaryAcquisition() || getCampaignOptions().isPlanetAcquisitionVerbose()) { + report += ": " + + target.getDesc() + ""; + if (!getCampaignOptions().isUsePlanetaryAcquisition() + || getCampaignOptions().isPlanetAcquisitionVerbose()) { addReport(report); } return false; @@ -3273,7 +3475,9 @@ private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, Pl report += " needs " + target.getValueAsString(); report += " and rolls " + roll + ':'; // Edge reroll, if applicable - if (getCampaignOptions().isUseSupportEdge() && (roll < target.getValue()) && (person != null) && person.getOptions().booleanOption(PersonnelOptions.EDGE_ADMIN_ACQUIRE_FAIL) && (person.getCurrentEdge() > 0)) { + if (getCampaignOptions().isUseSupportEdge() && (roll < target.getValue()) && (person != null) + && person.getOptions().booleanOption(PersonnelOptions.EDGE_ADMIN_ACQUIRE_FAIL) + && (person.getCurrentEdge() > 0)) { person.changeCurrentEdge(-1); roll = Compute.d6(2); report += " failed! but uses Edge to reroll...getting a " + roll + ": "; @@ -3290,7 +3494,8 @@ private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, Pl report = report + acquisition.find(transitDays); found = true; if (person != null) { - if (roll == 12 && target.getValue() != TargetRoll.AUTOMATIC_SUCCESS) { + if (roll == 12 + && target.getValue() != TargetRoll.AUTOMATIC_SUCCESS) { xpGained += getCampaignOptions().getSuccessXP(); } if (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS) { @@ -3303,7 +3508,8 @@ private boolean acquireEquipment(IAcquisitionWork acquisition, Person person, Pl } } else { report = report + acquisition.failToFind(); - if (person != null && roll == 2 && target.getValue() != TargetRoll.AUTOMATIC_FAIL) { + if (person != null && roll == 2 + && target.getValue() != TargetRoll.AUTOMATIC_FAIL) { xpGained += getCampaignOptions().getMistakeXP(); } } @@ -3372,7 +3578,8 @@ public void mothball(Unit u) { u.setMothballTime(u.getMothballTime() - minutes); - String report = tech.getHyperlinkedFullTitle() + " spent " + minutes + " minutes mothballing " + u.getHyperlinkedName(); + String report = tech.getHyperlinkedFullTitle() + " spent " + minutes + " minutes mothballing " + + u.getHyperlinkedName(); if (!u.isMothballing()) { u.completeMothball(); report += ". Mothballing complete."; @@ -3420,7 +3627,8 @@ public void activate(Unit u) { u.setMothballTime(u.getMothballTime() - minutes); - String report = tech.getHyperlinkedFullTitle() + " spent " + minutes + " minutes activating " + u.getHyperlinkedName(); + String report = tech.getHyperlinkedFullTitle() + " spent " + minutes + " minutes activating " + + u.getHyperlinkedName(); tech.setMinutesLeft(tech.getMinutesLeft() - minutes); if (!u.isSelfCrewed()) { @@ -3438,9 +3646,11 @@ public void activate(Unit u) { } public void refit(Refit theRefit) { - Person tech = (theRefit.getUnit().getEngineer() == null) ? theRefit.getTech() : theRefit.getUnit().getEngineer(); + Person tech = (theRefit.getUnit().getEngineer() == null) ? theRefit.getTech() + : theRefit.getUnit().getEngineer(); if (tech == null) { - addReport("No tech is assigned to refit " + theRefit.getOriginalEntity().getShortName() + ". Refit cancelled."); + addReport("No tech is assigned to refit " + theRefit.getOriginalEntity().getShortName() + + ". Refit cancelled."); theRefit.cancel(); return; } @@ -3477,7 +3687,9 @@ public void refit(Refit theRefit) { wrongType = " Warning: wrong tech type for this refit."; } report = report + ", needs " + target.getValueAsString() + " and rolls " + roll + ": "; - if (getCampaignOptions().isUseSupportEdge() && (roll < target.getValue()) && tech.getOptions().booleanOption(PersonnelOptions.EDGE_REPAIR_FAILED_REFIT) && (tech.getCurrentEdge() > 0)) { + if (getCampaignOptions().isUseSupportEdge() && (roll < target.getValue()) + && tech.getOptions().booleanOption(PersonnelOptions.EDGE_REPAIR_FAILED_REFIT) + && (tech.getCurrentEdge() > 0)) { tech.changeCurrentEdge(-1); roll = tech.isRightTechTypeFor(theRefit) ? Compute.d6(2) : Utilities.roll3d6(); // This is needed to update the edge values of individual crewmen @@ -3537,7 +3749,7 @@ public Part fixWarehousePart(Part part, Person tech) { * @param partWork - the {@link IPartWork} to be fixed * @param tech - the {@link Person} who will attempt to fix the part * @return a String of the report that summarizes the outcome of - * the attempt to fix the part + * the attempt to fix the part */ public String fixPart(IPartWork partWork, Person tech) { TargetRoll target = getTargetFor(partWork, tech); @@ -3584,9 +3796,11 @@ public String fixPart(IPartWork partWork, Person tech) { } if (partWork instanceof SpacecraftCoolingSystem) { // Change the string since we're not working on the part itself - report += tech.getHyperlinkedFullTitle() + " attempts to" + action + "a heat sink"; + report += tech.getHyperlinkedFullTitle() + " attempts to" + action + + "a heat sink"; } else { - report += tech.getHyperlinkedFullTitle() + " attempts to" + action + partWork.getPartName(); + report += tech.getHyperlinkedFullTitle() + " attempts to" + action + + partWork.getPartName(); } if (null != partWork.getUnit()) { report += " on " + partWork.getUnit().getName(); @@ -3618,8 +3832,11 @@ public String fixPart(IPartWork partWork, Person tech) { partWork.addTimeSpent(minutesUsed); tech.setMinutesLeft(0); tech.setOvertimeLeft(tech.getOvertimeLeft() - overtimeUsed); - int helpMod = getShorthandedMod(getAvailableAstechs(minutesUsed, usedOvertime), false); - if ((null != partWork.getUnit()) && ((partWork.getUnit().getEntity() instanceof Dropship) || (partWork.getUnit().getEntity() instanceof Jumpship))) { + int helpMod = getShorthandedMod( + getAvailableAstechs(minutesUsed, usedOvertime), false); + if ((null != partWork.getUnit()) + && ((partWork.getUnit().getEntity() instanceof Dropship) + || (partWork.getUnit().getEntity() instanceof Jumpship))) { helpMod = 0; } if (partWork.getShorthandedMod() < helpMod) { @@ -3632,7 +3849,7 @@ public String fixPart(IPartWork partWork, Person tech) { report += " minutes left. Work"; if ((minutesUsed > 0) && (tech.getDailyAvailableTechTime() > 0)) { report += " will be finished "; - int daysLeft = (int) Math.ceil((double) partWork.getTimeLeft() / (double) tech.getDailyAvailableTechTime()); + int daysLeft = (int) Math.ceil((double) partWork.getTimeLeft() / (double)tech.getDailyAvailableTechTime()); if (daysLeft == 1) { report += " tomorrow.
    "; } else { @@ -3652,7 +3869,8 @@ public String fixPart(IPartWork partWork, Person tech) { } else { tech.setMinutesLeft(tech.getMinutesLeft() - minutes); } - int astechMinutesUsed = minutesUsed * getAvailableAstechs(minutesUsed, usedOvertime); + int astechMinutesUsed = minutesUsed + * getAvailableAstechs(minutesUsed, usedOvertime); if (astechPoolMinutes < astechMinutesUsed) { astechMinutesUsed -= astechPoolMinutes; astechPoolMinutes = 0; @@ -3669,31 +3887,44 @@ public String fixPart(IPartWork partWork, Person tech) { roll = Utilities.roll3d6(); wrongType = " Warning: wrong tech type for this repair."; } - report = report + ", needs " + target.getValueAsString() + " and rolls " + roll + ':'; + report = report + ", needs " + target.getValueAsString() + + " and rolls " + roll + ':'; int xpGained = 0; // if we fail and would break a part, here's a chance to use Edge for a // reroll... - if (getCampaignOptions().isUseSupportEdge() && tech.getOptions().booleanOption(PersonnelOptions.EDGE_REPAIR_BREAK_PART) && (tech.getCurrentEdge() > 0) && (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS)) { - if ((getCampaignOptions().isDestroyByMargin() && (getCampaignOptions().getDestroyMargin() <= (target.getValue() - roll))) || (!getCampaignOptions().isDestroyByMargin() - // if an elite, primary tech and destroy by margin is NOT on - && ((tech.getExperienceLevel(this, false) == SkillType.EXP_ELITE) || tech.getPrimaryRole().isVehicleCrew())) // For vessel crews - && (roll < target.getValue())) { + if (getCampaignOptions().isUseSupportEdge() + && tech.getOptions().booleanOption(PersonnelOptions.EDGE_REPAIR_BREAK_PART) + && (tech.getCurrentEdge() > 0) + && (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS)) { + if ((getCampaignOptions().isDestroyByMargin() + && (getCampaignOptions().getDestroyMargin() <= (target.getValue() - roll))) + || (!getCampaignOptions().isDestroyByMargin() + // if an elite, primary tech and destroy by margin is NOT on + && ((tech.getExperienceLevel(this, false) == SkillType.EXP_ELITE) + || tech.getPrimaryRole().isVehicleCrew())) // For vessel crews + && (roll < target.getValue())) { tech.changeCurrentEdge(-1); roll = tech.isRightTechTypeFor(partWork) ? Compute.d6(2) : Utilities.roll3d6(); // This is needed to update the edge values of individual crewmen if (tech.isEngineer()) { tech.setEdgeUsed(tech.getEdgeUsed() + 1); } - report += " failed! and would destroy the part, but uses Edge to reroll...getting a " + roll + ':'; + report += " failed! and would destroy the part, but uses Edge to reroll...getting a " + roll + + ':'; } } if (roll >= target.getValue()) { report = report + partWork.succeed(); - if (getCampaignOptions().isPayForRepairs() && action.equals(" fix ") && !(partWork instanceof Armor)) { + if (getCampaignOptions().isPayForRepairs() + && action.equals(" fix ") + && !(partWork instanceof Armor)) { Money cost = partWork.getUndamagedValue().multipliedBy(0.2); - report += "
    Repairs cost " + cost.toAmountAndSymbolString() + " worth of parts."; - finances.debit(TransactionType.REPAIRS, getLocalDate(), cost, "Repair of " + partWork.getPartName()); + report += "
    Repairs cost " + + cost.toAmountAndSymbolString() + + " worth of parts."; + finances.debit(TransactionType.REPAIRS, getLocalDate(), cost, + "Repair of " + partWork.getPartName()); } if ((roll == 12) && (target.getValue() != TargetRoll.AUTOMATIC_SUCCESS)) { xpGained += getCampaignOptions().getSuccessXP(); @@ -3818,7 +4049,9 @@ public int getDeploymentDeficit(AtBContract contract) { } if (reportUnderStrength) { - addReport(String.format(resources.getString("understrength.text"), force.getName(), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), CLOSING_SPAN_TAG, minimumUnitCount)); + addReport(String.format(resources.getString("understrength.text"), + force.getName(), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), + CLOSING_SPAN_TAG, minimumUnitCount)); } } @@ -3841,13 +4074,17 @@ private void processNewDayATBScenarios() { * unit is actually on route to the planet in case the user is using a custom * system for transport or splitting the unit, etc. */ - if (!getLocation().isOnPlanet() && !getLocation().getJumpPath().isEmpty() && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystemId())) { + if (!getLocation().isOnPlanet() && !getLocation().getJumpPath().isEmpty() + && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystemId())) { // transitTime is measured in days; so we round up to the next whole day contract.setStartAndEndDate(getLocalDate().plusDays((int) Math.ceil(getLocation().getTransitTime()))); - addReport("The start and end dates of " + contract.getName() + " have been shifted to reflect the current ETA."); + addReport("The start and end dates of " + contract.getName() + + " have been shifted to reflect the current ETA."); if (campaignOptions.isUseStratCon() && contract.getMoraleLevel().isRouted()) { - LocalDate newRoutEndDate = contract.getStartDate().plusMonths(max(1, Compute.d6() - 3)).minusDays(1); + LocalDate newRoutEndDate = contract.getStartDate() + .plusMonths(max(1, Compute.d6() - 3)) + .minusDays(1); contract.setRoutEndDate(newRoutEndDate); } @@ -3863,12 +4100,16 @@ private void processNewDayATBScenarios() { StratconCampaignState campaignState = contract.getStratconCampaignState(); if (campaignState != null && deficit > 0) { - addReport(String.format(resources.getString("contractBreach.text"), contract.getName(), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG)); + addReport(String.format(resources.getString("contractBreach.text"), + contract.getName(), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); campaignState.updateVictoryPoints(-1); } else if (deficit > 0) { contract.addPlayerMinorBreaches(deficit); - addReport("Failure to meet " + contract.getName() + " requirements resulted in " + deficit + ((deficit == 1) ? " minor contract breach" : " minor contract breaches")); + addReport("Failure to meet " + contract.getName() + " requirements resulted in " + deficit + + ((deficit == 1) ? " minor contract breach" : " minor contract breaches")); } } @@ -3881,7 +4122,8 @@ private void processNewDayATBScenarios() { for (final Scenario scenario : contract.getCurrentAtBScenarios()) { if ((scenario.getDate() != null) && scenario.getDate().isBefore(getLocalDate())) { if (getCampaignOptions().isUseStratCon() && (scenario instanceof AtBDynamicScenario)) { - final boolean stub = StratconRulesManager.processIgnoredScenario((AtBDynamicScenario) scenario, contract.getStratconCampaignState()); + final boolean stub = StratconRulesManager.processIgnoredScenario( + (AtBDynamicScenario) scenario, contract.getStratconCampaignState()); if (stub) { if (scenario.getStratConScenarioType().isResupply()) { @@ -3896,7 +4138,8 @@ private void processNewDayATBScenarios() { scenario.convertToStub(this, ScenarioStatus.REFUSED_ENGAGEMENT); contract.addPlayerMinorBreach(); - addReport("Failure to deploy for " + scenario.getName() + " resulted in a minor contract breach."); + addReport("Failure to deploy for " + scenario.getName() + + " resulted in a minor contract breach."); } } } @@ -3931,20 +4174,26 @@ private void processNewDayATBScenarios() { forceIds.get(forceId).setScenarioId(atBScenario.getId(), this); atBScenario.addForces(forceId); - addReport(MessageFormat.format(resources.getString("atbScenarioTodayWithForce.format"), atBScenario.getName(), forceIds.get(forceId).getName())); + addReport(MessageFormat.format( + resources.getString("atbScenarioTodayWithForce.format"), + atBScenario.getName(), forceIds.get(forceId).getName())); MekHQ.triggerEvent(new DeploymentChangedEvent(forceIds.get(forceId), atBScenario)); } else { if (atBScenario.getHasTrack()) { - addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), atBScenario.getName())); + addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), + atBScenario.getName())); } else { - addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), atBScenario.getName())); + addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), + atBScenario.getName())); } } } else { if (atBScenario.getHasTrack()) { - addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), atBScenario.getName())); + addReport(MessageFormat.format(resources.getString("atbScenarioToday.stratCon"), + atBScenario.getName())); } else { - addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), atBScenario.getName())); + addReport(MessageFormat.format(resources.getString("atbScenarioToday.atb"), + atBScenario.getName())); } } } @@ -4000,7 +4249,8 @@ private void processNewDayATB() { if (contract.getContractType().isGarrisonDuty()) { report = resources.getString("garrisonDutyRouted.text"); } else if (oldMorale != newMorale) { - report = String.format(resources.getString("contractMoraleReport.text"), newMorale, contract.getName(), newMorale.getToolTipText()); + report = String.format(resources.getString("contractMoraleReport.text"), + newMorale, contract.getName(), newMorale.getToolTipText()); } if (!report.isBlank()) { @@ -4009,7 +4259,8 @@ private void processNewDayATB() { // Resupply if (getCampaignOptions().isUseStratCon()) { - boolean inLocation = location.isOnPlanet() && location.getCurrentSystem().equals(contract.getSystem()); + boolean inLocation = location.isOnPlanet() + && location.getCurrentSystem().equals(contract.getSystem()); if (inLocation) { processResupply(contract); @@ -4027,7 +4278,8 @@ private void processNewDayATB() { for (AtBContract contract : getActiveAtBContracts()) { if (campaignOptions.isUseGenericBattleValue()) { if (contract.getStartDate().equals(getLocalDate())) { - if (getCampaignOptions().isUseGenericBattleValue() && BatchallFactions.usesBatchalls(contract.getEnemyCode())) { + if (getCampaignOptions().isUseGenericBattleValue() + && BatchallFactions.usesBatchalls(contract.getEnemyCode())) { contract.setBatchallAccepted(contract.initiateBatchall(this)); } } @@ -4147,7 +4399,9 @@ public void processNewDayPersonnel() { } if (!personnelWhoAdvancedInXP.isEmpty()) { - addReport(String.format(resources.getString("gainedExperience.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), personnelWhoAdvancedInXP.size(), CLOSING_SPAN_TAG)); + addReport(String.format(resources.getString("gainedExperience.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + personnelWhoAdvancedInXP.size(), CLOSING_SPAN_TAG)); } } @@ -4206,7 +4460,7 @@ private void processWeeklyEdgeResets(Person person) { * Note: Bondsmen are exempt from this restriction and are eligible for vocational XP. * * - * @param person the {@link Person} whose monthly vocational XP is to be processed + * @param person the {@link Person} whose monthly vocational XP is to be processed * @param vocationalXpRate the amount of XP awarded on a successful roll * @return {@code true} if XP was successfully awarded during the process, {@code false} otherwise */ @@ -4269,8 +4523,14 @@ private void processWeeklyRelationshipEvents(Person person) { */ private void processAnniversaries(Person person) { if ((person.getRank().isOfficer()) || (!getCampaignOptions().isAnnounceOfficersOnly())) { - if ((person.getBirthday(getGameYear()).isEqual(getLocalDate())) && (campaignOptions.isAnnounceBirthdays())) { - addReport(String.format(resources.getString("anniversaryBirthday.text"), person.getHyperlinkedFullTitle(), ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), person.getAge(getLocalDate()), CLOSING_SPAN_TAG)); + if ((person.getBirthday(getGameYear()).isEqual(getLocalDate())) + && (campaignOptions.isAnnounceBirthdays())) { + addReport(String.format(resources.getString("anniversaryBirthday.text"), + person.getHyperlinkedFullTitle(), + ReportingUtilities + .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + person.getAge(getLocalDate()), + CLOSING_SPAN_TAG)); } LocalDate recruitmentDate = person.getRecruitment(); @@ -4278,13 +4538,23 @@ private void processAnniversaries(Person person) { LocalDate recruitmentAnniversary = recruitmentDate.withYear(getGameYear()); int yearsOfEmployment = (int) ChronoUnit.YEARS.between(recruitmentDate, currentDay); - if ((recruitmentAnniversary.isEqual(getLocalDate())) && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { - addReport(String.format(resources.getString("anniversaryRecruitment.text"), person.getHyperlinkedFullTitle(), ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), yearsOfEmployment, CLOSING_SPAN_TAG, name)); + if ((recruitmentAnniversary.isEqual(getLocalDate())) + && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { + addReport(String.format(resources.getString("anniversaryRecruitment.text"), + person.getHyperlinkedFullTitle(), + ReportingUtilities + .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + yearsOfEmployment, CLOSING_SPAN_TAG, name)); } } } else if ((person.getAge(getLocalDate()) == 18) && (campaignOptions.isAnnounceChildBirthdays())) { if (person.getBirthday(getGameYear()).isEqual(getLocalDate())) { - addReport(String.format(resources.getString("anniversaryBirthday.text"), person.getHyperlinkedFullTitle(), ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), person.getAge(getLocalDate()), CLOSING_SPAN_TAG)); + addReport(String.format(resources.getString("anniversaryBirthday.text"), + person.getHyperlinkedFullTitle(), + ReportingUtilities + .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + person.getAge(getLocalDate()), + CLOSING_SPAN_TAG)); } } } @@ -4360,8 +4630,11 @@ public void processNewDayUnits() { doMaintenance(u); } catch (Exception e) { - logger.error(String.format("Unable to perform maintenance on %s (%s) due to an error", u.getName(), u.getId().toString()), e); - addReport(String.format("ERROR: An error occurred performing maintenance on %s, check the log", u.getName())); + logger.error(String.format( + "Unable to perform maintenance on %s (%s) due to an error", + u.getName(), u.getId().toString()), e); + addReport(String.format("ERROR: An error occurred performing maintenance on %s, check the log", + u.getName())); } } @@ -4409,15 +4682,24 @@ public void processNewDayUnits() { try { fixPart(part, tech); } catch (Exception e) { - logger.error(String.format("Could not perform overnight maintenance on %s (%d) due to an error", part.getName(), part.getId()), e); - addReport(String.format("ERROR: an error occurred performing overnight maintenance on %s, check the log", part.getName())); + logger.error(String.format( + "Could not perform overnight maintenance on %s (%d) due to an error", + part.getName(), part.getId()), e); + addReport(String.format( + "ERROR: an error occurred performing overnight maintenance on %s, check the log", + part.getName())); } } else { - addReport(String.format("%s looks at %s, recalls his total lack of skill for working with such technology, then slowly puts the tools down before anybody gets hurt.", tech.getHyperlinkedFullTitle(), part.getName())); + addReport(String.format( + "%s looks at %s, recalls his total lack of skill for working with such technology, then slowly puts the tools down before anybody gets hurt.", + tech.getHyperlinkedFullTitle(), part.getName())); part.setTech(null); } } else { - JOptionPane.showMessageDialog(null, "Could not find tech for part: " + part.getName() + " on unit: " + part.getUnit().getHyperlinkedName(), "Invalid Auto-continue", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, + "Could not find tech for part: " + part.getName() + " on unit: " + + part.getUnit().getHyperlinkedName(), + "Invalid Auto-continue", JOptionPane.ERROR_MESSAGE); } // check to see if this part can now be combined with other spare parts @@ -4503,7 +4785,7 @@ public boolean newDay() { // Clear Reports currentReport.clear(); - currentReportHTML = ""; + currentReportHTML=""; newReports.clear(); personnelWhoAdvancedInXP.clear(); beginReport("" + MekHQ.getMHQOptions().getLongDisplayFormattedDate(getLocalDate()) + ""); @@ -4566,7 +4848,8 @@ public boolean newDay() { finances.newDay(this, yesterday, getLocalDate()); // process removal of old personnel data on the last day of each month - if ((campaignOptions.isUsePersonnelRemoval()) && (currentDay.getMonth().length(false) == currentDay.getDayOfMonth())) { + if ((campaignOptions.isUsePersonnelRemoval()) + && (currentDay.getMonth().length(false) == currentDay.getDayOfMonth())) { processPersonnelRemoval(); } @@ -4717,7 +5000,7 @@ private void processRandomDependents() { * random * dependent removal. * - * @param dependents The list of dependents. + * @param dependents The list of dependents. * @param dependentCapacity The maximum number of dependents allowed. * @return The updated number of dependents. */ @@ -4737,7 +5020,8 @@ int dependentsRollForRemoval(List dependents, int dependentCapacity) { int targetNumber = 5 - getAtBUnitRatingMod(); if (roll <= targetNumber) { - addReport(String.format(resources.getString("dependentLeavesForce.text"), dependent.getFullTitle())); + addReport(String.format(resources.getString("dependentLeavesForce.text"), + dependent.getFullTitle())); removePerson(dependent, false); } @@ -4748,10 +5032,11 @@ int dependentsRollForRemoval(List dependents, int dependentCapacity) { } /** + * @return The lower integer value between the given input and the randomly + * generated integer. + * * @param firstRoll The input integer to compare with the randomly generated * integer. - * @return The lower integer value between the given input and the randomly - * generated integer. */ private int getLowerRandomInt(int firstRoll) { int secondRoll = Compute.randomInt(100); @@ -4764,7 +5049,7 @@ private int getLowerRandomInt(int firstRoll) { * @param dependent the person to check * @param currentDate the current date * @return {@code true} if the person is eligible for removal, {@code false} - * otherwise + * otherwise */ boolean isRemovalEligible(Person dependent, LocalDate currentDate) { boolean hasNonAdultChildren = dependent.getGenealogy().hasNonAdultChildren(currentDate); @@ -4797,7 +5082,8 @@ void dependentsAddNew(int dependentCount, int dependentCapacity) { recruitPerson(dependent, PrisonerStatus.FREE, true, false); - addReport(String.format(resources.getString("dependentJoinsForce.text"), dependent.getHyperlinkedFullTitle())); + addReport(String.format(resources.getString("dependentJoinsForce.text"), + dependent.getHyperlinkedFullTitle())); dependentCount++; } @@ -4844,7 +5130,9 @@ private boolean shouldRemovePerson(Person person, PersonnelStatus status) { // with at least one member still present in the campaign Map> family = person.getGenealogy().getFamily(); - if (family.keySet().stream().flatMap(relationshipType -> family.get(relationshipType).stream()).anyMatch(relation -> relation.getStatus().isDepartedUnit())) { + if (family.keySet().stream() + .flatMap(relationshipType -> family.get(relationshipType).stream()) + .anyMatch(relation -> relation.getStatus().isDepartedUnit())) { return false; } @@ -4861,7 +5149,9 @@ private boolean shouldRemovePerson(Person person, PersonnelStatus status) { // *AND* // is dead (and we're not exempting the cemetery) *OR* is retired (and we're not // exempting retirees) - return (retirementMonthValue < (getLocalDate().getMonthValue() + 1)) && (((status.isDead()) && (!campaignOptions.isUseRemovalExemptCemetery())) || ((status.isRetired()) && (!campaignOptions.isUseRemovalExemptRetirees()))); + return (retirementMonthValue < (getLocalDate().getMonthValue() + 1)) && + (((status.isDead()) && (!campaignOptions.isUseRemovalExemptCemetery())) + || ((status.isRetired()) && (!campaignOptions.isUseRemovalExemptRetirees()))); } /** @@ -4928,7 +5218,10 @@ private void processFatigueNewDay() { } public @Nullable Person getFlaggedCommander() { - return getPersonnel().stream().filter(Person::isCommander).findFirst().orElse(null); + return getPersonnel().stream() + .filter(Person::isCommander) + .findFirst() + .orElse(null); } /** @@ -4995,7 +5288,9 @@ public void removeUnit(UUID id) { // If this unit was assigned to a transport ship, remove it from the transport if (unit.hasTransportShipAssignment()) { - unit.getTransportShipAssignment().getTransportShip().unloadFromTransportShip(unit); + unit.getTransportShipAssignment() + .getTransportShip() + .unloadFromTransportShip(unit); } // remove from automatic mothballing @@ -5053,8 +5348,10 @@ public void removePerson(final @Nullable Person person, final boolean log) { public void removeAllPatientsFor(Person doctor) { for (Person p : getPersonnel()) { - if (null != p.getDoctorId() && p.getDoctorId().equals(doctor.getId())) { - p.setDoctorId(null, getCampaignOptions().getNaturalHealingWaitingPeriod()); + if (null != p.getDoctorId() + && p.getDoctorId().equals(doctor.getId())) { + p.setDoctorId(null, getCampaignOptions() + .getNaturalHealingWaitingPeriod()); } } } @@ -5067,7 +5364,9 @@ public void removeScenario(final Scenario scenario) { // run through the stratcon campaign state where applicable and remove the // "parent" scenario as well - if ((mission instanceof AtBContract) && (((AtBContract) mission).getStratconCampaignState() != null) && (scenario instanceof AtBDynamicScenario)) { + if ((mission instanceof AtBContract) && + (((AtBContract) mission).getStratconCampaignState() != null) && + (scenario instanceof AtBDynamicScenario)) { ((AtBContract) mission).getStratconCampaignState().removeStratconScenario(scenario.getId()); } } @@ -5144,14 +5443,16 @@ public void removeUnitFromForce(Unit u) { force.removeUnit(this, u.getId(), true); u.setForceId(Force.FORCE_NONE); u.setScenarioId(-1); - if (u.getEntity().hasNavalC3() && u.getEntity().calculateFreeC3Nodes() < 5) { + if (u.getEntity().hasNavalC3() + && u.getEntity().calculateFreeC3Nodes() < 5) { Vector removedUnits = new Vector<>(); removedUnits.add(u); removeUnitsFromNetwork(removedUnits); u.getEntity().setC3MasterIsUUIDAsString(null); u.getEntity().setC3Master(null, true); refreshNetworks(); - } else if (u.getEntity().hasC3i() && u.getEntity().calculateFreeC3Nodes() < 5) { + } else if (u.getEntity().hasC3i() + && u.getEntity().calculateFreeC3Nodes() < 5) { Vector removedUnits = new Vector<>(); removedUnits.add(u); removeUnitsFromNetwork(removedUnits); @@ -5180,7 +5481,9 @@ public void removeUnitFromForce(Unit u) { if (unit != null) { return getForceFor(unit); } else if (person.isTech()) { - return forceIds.values().stream().filter(force -> person.getId().equals(force.getTechID())).findFirst().orElse(null); + return forceIds.values().stream() + .filter(force -> person.getId().equals(force.getTechID())) + .findFirst().orElse(null); } return null; @@ -5223,7 +5526,8 @@ public void restore() { // Aerospace parts have changed after 0.45.4. Reinitialize parts for Small Craft // and up - if (unit.getEntity().hasETypeFlag(Entity.ETYPE_JUMPSHIP) || unit.getEntity().hasETypeFlag(Entity.ETYPE_SMALL_CRAFT)) { + if (unit.getEntity().hasETypeFlag(Entity.ETYPE_JUMPSHIP) + || unit.getEntity().hasETypeFlag(Entity.ETYPE_SMALL_CRAFT)) { unitsToCheck.add(unit); } } @@ -5264,7 +5568,8 @@ public void cleanUp() { if (p.getGenealogy().hasSpouse()) { if (!personnel.containsKey(p.getGenealogy().getSpouse().getId())) { p.getGenealogy().setSpouse(null); - if (!getCampaignOptions().isKeepMarriedNameUponSpouseDeath() && (p.getMaidenName() != null)) { + if (!getCampaignOptions().isKeepMarriedNameUponSpouseDeath() + && (p.getMaidenName() != null)) { p.setSurname(p.getMaidenName()); } p.setMaidenName(null); @@ -5420,7 +5725,8 @@ public void setReputation(ReputationController reputation) { private void addInMemoryLogHistory(LogEntry le) { if (!inMemoryLogHistory.isEmpty()) { - while (ChronoUnit.DAYS.between(inMemoryLogHistory.get(0).getDate(), le.getDate()) > MHQConstants.MAX_HISTORICAL_LOG_DAYS) { + while (ChronoUnit.DAYS.between(inMemoryLogHistory.get(0).getDate(), + le.getDate()) > MHQConstants.MAX_HISTORICAL_LOG_DAYS) { // we've hit the max size for the in-memory based on the UI display limit prune // the oldest entry inMemoryLogHistory.remove(0); @@ -5504,7 +5810,8 @@ public void addFunds(final Money quantity) { addFunds(TransactionType.MISCELLANEOUS, quantity, null); } - public void addFunds(final TransactionType type, final Money quantity, @Nullable String description) { + public void addFunds(final TransactionType type, final Money quantity, + @Nullable String description) { if ((description == null) || description.isEmpty()) { description = "Rich Uncle"; } @@ -5514,7 +5821,8 @@ public void addFunds(final TransactionType type, final Money quantity, @Nullable addReport("Funds added : " + quantityString + " (" + description + ')'); } - public void removeFunds(final TransactionType type, final Money quantity, @Nullable String description) { + public void removeFunds(final TransactionType type, final Money quantity, + @Nullable String description) { if ((description == null) || description.isEmpty()) { description = "Rich Uncle"; } @@ -5529,14 +5837,14 @@ public void removeFunds(final TransactionType type, final Money quantity, @Nulla * Generic method for paying Personnel (Person) in the company. * Debits money from the campaign and if the campaign tracks * total earnings it will account for that. - * - * @param type TransactionType being debited - * @param quantity total money - it's usually displayed outside of this method - * @param description String displayed in the ledger and report + * @param type TransactionType being debited + * @param quantity total money - it's usually displayed outside of this method + * @param description String displayed in the ledger and report * @param individualPayouts Map of Person to the Money they're owed */ public void payPersonnel(TransactionType type, Money quantity, String description, Map individualPayouts) { - getFinances().debit(type, getLocalDate(), quantity, description, individualPayouts, getCampaignOptions().isTrackTotalEarnings()); + getFinances().debit(type, getLocalDate(), quantity, description, + individualPayouts, getCampaignOptions().isTrackTotalEarnings()); String quantityString = quantity.toAmountAndSymbolString(); addReport("Funds removed : " + quantityString + " (" + description + ')'); @@ -5587,7 +5895,7 @@ public FameAndInfamyController getFameAndInfamy() { * and operational demands over time.

    * * @return A {@link List} of {@link Unit} objects that are set for automated - * mothballing. Returns an empty list if no units are configured. + * mothballing. Returns an empty list if no units are configured. */ public List getAutomatedMothballUnits() { return automatedMothballUnits; @@ -5784,7 +6092,8 @@ public void writeToXML(final PrintWriter pw) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchType", shipSearchType); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchResult", shipSearchResult); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchExpiration", getShipSearchExpiration()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveBehaviorSettings", autoResolveBehaviorSettings.getDescription()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveBehaviorSettings", + autoResolveBehaviorSettings.getDescription()); } retirementDefectionTracker.writeToXML(pw, indent); @@ -5961,7 +6270,8 @@ public void setRankSystem(final @Nullable RankSystem rankSystem) { setRankSystemDirect(rankSystem); // Finally, we fix all personnel ranks and ensure they are properly set - getPersonnel().stream().filter(person -> person.getRankSystem().equals(oldRankSystem)).forEach(person -> person.setRankSystem(rankValidator, rankSystem)); + getPersonnel().stream().filter(person -> person.getRankSystem().equals(oldRankSystem)) + .forEach(person -> person.setRankSystem(rankValidator, rankSystem)); } public void setRankSystemDirect(final RankSystem rankSystem) { @@ -5978,7 +6288,7 @@ public void setRankSystemDirect(final RankSystem rankSystem) { * (true - use skill tiebreaker, false - do not use * skill tiebreaker) * @return the highest ranked person from the list, or null if the provided - * personnel list is empty + * personnel list is empty */ public Person getHighestRankedPerson(List personnel, boolean useSkillTiebreaker) { Person highestRankedPerson = null; @@ -6059,7 +6369,8 @@ public Accountant getAccountant() { while (!found && jumps < 10000) { jumps++; - double currentG = scoreG.get(current) + Systems.getInstance().getSystemById(current).getRechargeTime(getLocalDate()); + double currentG = scoreG.get(current) + + Systems.getInstance().getSystemById(current).getRechargeTime(getLocalDate()); final String localCurrent = current; Systems.getInstance().visitNearbySystems(Systems.getInstance().getSystemById(current), 30, p -> { @@ -6132,7 +6443,7 @@ public Accountant getAccountant() { * fairly hacky, but * improves slightly on the prior implementation as far as following the * rulebooks goes. - *

    + * * It can be used to calculate total travel costs in the style of FM:Mercs * (excludeOwnTransports * and campaignOpsCosts set to false), to calculate leased/rented travel costs @@ -6183,7 +6494,8 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign int noASF = max(nAero - stats.getOccupiedBays(Entity.ETYPE_AEROSPACEFIGHTER), 0); int nolv = max(nLVee - stats.getOccupiedBays(Entity.ETYPE_TANK, true), 0); int nohv = max(nHVee - stats.getOccupiedBays(Entity.ETYPE_TANK), 0); - int noinf = max(stats.getNumberOfUnitsByType(Entity.ETYPE_INFANTRY) - stats.getOccupiedBays(Entity.ETYPE_INFANTRY), 0); + int noinf = max( + stats.getNumberOfUnitsByType(Entity.ETYPE_INFANTRY) - stats.getOccupiedBays(Entity.ETYPE_INFANTRY), 0); int noBA = max(nBA - stats.getOccupiedBays(Entity.ETYPE_BATTLEARMOR), 0); int noProto = max(nProto - stats.getOccupiedBays(Entity.ETYPE_PROTOMEK), 0); int freehv = max(stats.getTotalHeavyVehicleBays() - stats.getOccupiedBays(Entity.ETYPE_TANK), 0); @@ -6426,10 +6738,15 @@ public Money calculateCostPerJump(boolean excludeOwnTransports, boolean campaign if (!u.isMothballed()) { Entity e = u.getEntity(); if ((e.getEntityType() & Entity.ETYPE_DROPSHIP) != 0) { - ownDropshipCost = ownDropshipCost.plus(mekDropshipCost.multipliedBy(u.getMekCapacity()).dividedBy(averageDropshipMekCapacity)); - ownDropshipCost = ownDropshipCost.plus(asfDropshipCost.multipliedBy(u.getASFCapacity()).dividedBy(averageDropshipASFCapacity)); - ownDropshipCost = ownDropshipCost.plus(vehicleDropshipCost.multipliedBy(u.getHeavyVehicleCapacity() + u.getLightVehicleCapacity()).dividedBy(averageDropshipVehicleCapacity)); - ownDropshipCost = ownDropshipCost.plus(cargoDropshipCost.multipliedBy(u.getCargoCapacity()).dividedBy(averageDropshipCargoCapacity)); + ownDropshipCost = ownDropshipCost.plus( + mekDropshipCost.multipliedBy(u.getMekCapacity()).dividedBy(averageDropshipMekCapacity)); + ownDropshipCost = ownDropshipCost.plus( + asfDropshipCost.multipliedBy(u.getASFCapacity()).dividedBy(averageDropshipASFCapacity)); + ownDropshipCost = ownDropshipCost.plus(vehicleDropshipCost + .multipliedBy(u.getHeavyVehicleCapacity() + u.getLightVehicleCapacity()) + .dividedBy(averageDropshipVehicleCapacity)); + ownDropshipCost = ownDropshipCost.plus(cargoDropshipCost.multipliedBy(u.getCargoCapacity()) + .dividedBy(averageDropshipCargoCapacity)); } else if ((e.getEntityType() & Entity.ETYPE_JUMPSHIP) != 0) { ownJumpshipCost = ownDropshipCost.plus(collarCost.multipliedBy(e.getDockingCollars().size())); } @@ -6485,17 +6802,20 @@ public TargetRoll getTargetFor(final IPartWork partWork, final Person tech) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Already being worked on by another team"); } else if (skill == null) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Assigned tech does not have the right skills"); - } else if (!getCampaignOptions().isDestroyByMargin() && (partWork.getSkillMin() > (skill.getExperienceLevel() - modePenalty))) { + } else if (!getCampaignOptions().isDestroyByMargin() + && (partWork.getSkillMin() > (skill.getExperienceLevel() - modePenalty))) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Task is beyond this tech's skill level"); } else if (partWork.getSkillMin() > SkillType.EXP_ELITE) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Task is impossible."); } else if (!partWork.needsFixing() && !partWork.isSalvaging()) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Task is not needed."); - } else if ((partWork instanceof MissingPart) && (((MissingPart) partWork).findReplacement(false) == null)) { + } else if ((partWork instanceof MissingPart) + && (((MissingPart) partWork).findReplacement(false) == null)) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "Replacement part not available."); } - final int techTime = isOvertimeAllowed() ? tech.getMinutesLeft() + tech.getOvertimeLeft() : tech.getMinutesLeft(); + final int techTime = isOvertimeAllowed() ? tech.getMinutesLeft() + tech.getOvertimeLeft() + : tech.getMinutesLeft(); if (!(partWork instanceof Refit) && (techTime <= 0)) { return new TargetRoll(TargetRoll.IMPOSSIBLE, "The tech has no time left."); } @@ -6506,7 +6826,8 @@ public TargetRoll getTargetFor(final IPartWork partWork, final Person tech) { } // if this is an infantry refit, then automatic success - if ((partWork instanceof Refit) && (partWork.getUnit() != null) && partWork.getUnit().isConventionalInfantry()) { + if ((partWork instanceof Refit) && (partWork.getUnit() != null) + && partWork.getUnit().isConventionalInfantry()) { return new TargetRoll(TargetRoll.AUTOMATIC_SUCCESS, "infantry refit"); } @@ -6519,10 +6840,12 @@ public TargetRoll getTargetFor(final IPartWork partWork, final Person tech) { // this is ugly, if the mode penalty drops you to green, you drop two // levels instead of two int value = skill.getFinalSkillValue() + modePenalty; - if ((modePenalty > 0) && (SkillType.EXP_GREEN == (skill.getExperienceLevel() - modePenalty))) { + if ((modePenalty > 0) + && (SkillType.EXP_GREEN == (skill.getExperienceLevel() - modePenalty))) { value++; } - final TargetRoll target = new TargetRoll(value, SkillType.getExperienceLevelName(skill.getExperienceLevel() - modePenalty)); + final TargetRoll target = new TargetRoll(value, + SkillType.getExperienceLevelName(skill.getExperienceLevel() - modePenalty)); if (target.getValue() == TargetRoll.IMPOSSIBLE) { return target; } @@ -6534,7 +6857,8 @@ public TargetRoll getTargetFor(final IPartWork partWork, final Person tech) { } final boolean isOvertime; - if (isOvertimeAllowed() && (tech.isTaskOvertime(partWork) || partWork.hasWorkedOvertime())) { + if (isOvertimeAllowed() + && (tech.isTaskOvertime(partWork) || partWork.hasWorkedOvertime())) { target.addModifier(3, "overtime"); isOvertime = true; } else { @@ -6595,7 +6919,8 @@ public TargetRoll getTargetForMaintenance(IPartWork partWork, Person tech) { if (getLocation().isOnPlanet() && campaignOptions.isUsePlanetaryModifiers()) { Planet planet = getLocation().getPlanet(); Atmosphere atmosphere = planet.getAtmosphere(getLocalDate()); - megamek.common.planetaryconditions.Atmosphere planetaryConditions = megamek.common.planetaryconditions.Atmosphere.getAtmosphere(planet.getPressure(getLocalDate())); + megamek.common.planetaryconditions.Atmosphere planetaryConditions = megamek.common.planetaryconditions.Atmosphere + .getAtmosphere(planet.getPressure(getLocalDate())); int temperature = planet.getTemperature(getLocalDate()); if (planet.getGravity() < 0.8) { @@ -6667,38 +6992,58 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition) { return getTargetForAcquisition(acquisition, getLogisticsPerson()); } - public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, final @Nullable Person person) { + public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, + final @Nullable Person person) { return getTargetForAcquisition(acquisition, person, false); } - public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, final @Nullable Person person, final boolean checkDaysToWait) { + public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, + final @Nullable Person person, + final boolean checkDaysToWait) { if (getCampaignOptions().getAcquisitionSkill().equals(CampaignOptions.S_AUTO)) { return new TargetRoll(TargetRoll.AUTOMATIC_SUCCESS, "Automatic Success"); } if (null == person) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, "No one on your force is capable of acquiring parts"); + return new TargetRoll(TargetRoll.IMPOSSIBLE, + "No one on your force is capable of acquiring parts"); } final Skill skill = person.getSkillForWorkingOn(getCampaignOptions().getAcquisitionSkill()); - if (null != getShoppingList().getShoppingItem(acquisition.getNewEquipment()) && checkDaysToWait) { - return new TargetRoll(TargetRoll.AUTOMATIC_FAIL, "You must wait until the new cycle to check for this part. Further attempts will be added to the shopping list."); - } - if (acquisition.getTechBase() == Part.T_CLAN && !getCampaignOptions().isAllowClanPurchases()) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, "You cannot acquire clan parts"); - } - if (acquisition.getTechBase() == Part.T_IS && !getCampaignOptions().isAllowISPurchases()) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, "You cannot acquire inner sphere parts"); - } - if (getCampaignOptions().getTechLevel() < Utilities.getSimpleTechLevel(acquisition.getTechLevel())) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, "You cannot acquire parts of this tech level"); - } - if (getCampaignOptions().isLimitByYear() && !acquisition.isIntroducedBy(getGameYear(), useClanTechBase(), getTechFaction())) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, "It has not been invented yet!"); - } - if (getCampaignOptions().isDisallowExtinctStuff() && (acquisition.isExtinctIn(getGameYear(), useClanTechBase(), getTechFaction()) || acquisition.getAvailability() == EquipmentType.RATING_X)) { - return new TargetRoll(TargetRoll.IMPOSSIBLE, "It is extinct!"); - } - if (getCampaignOptions().isUseAtB() && getCampaignOptions().isRestrictPartsByMission() && acquisition instanceof Part) { + if (null != getShoppingList().getShoppingItem( + acquisition.getNewEquipment()) + && checkDaysToWait) { + return new TargetRoll( + TargetRoll.AUTOMATIC_FAIL, + "You must wait until the new cycle to check for this part. Further attempts will be added to the shopping list."); + } + if (acquisition.getTechBase() == Part.T_CLAN + && !getCampaignOptions().isAllowClanPurchases()) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, + "You cannot acquire clan parts"); + } + if (acquisition.getTechBase() == Part.T_IS + && !getCampaignOptions().isAllowISPurchases()) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, + "You cannot acquire inner sphere parts"); + } + if (getCampaignOptions().getTechLevel() < Utilities + .getSimpleTechLevel(acquisition.getTechLevel())) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, + "You cannot acquire parts of this tech level"); + } + if (getCampaignOptions().isLimitByYear() + && !acquisition.isIntroducedBy(getGameYear(), useClanTechBase(), getTechFaction())) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, + "It has not been invented yet!"); + } + if (getCampaignOptions().isDisallowExtinctStuff() && + (acquisition.isExtinctIn(getGameYear(), useClanTechBase(), getTechFaction()) + || acquisition.getAvailability() == EquipmentType.RATING_X)) { + return new TargetRoll(TargetRoll.IMPOSSIBLE, + "It is extinct!"); + } + if (getCampaignOptions().isUseAtB() && + getCampaignOptions().isRestrictPartsByMission() && acquisition instanceof Part) { int partAvailability = ((Part) acquisition).getAvailability(); EquipmentType et = null; if (acquisition instanceof EquipmentPart) { @@ -6708,7 +7053,8 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, fi } StringBuilder partAvailabilityLog = new StringBuilder(""); - partAvailabilityLog.append("Part Rating Level: ").append(partAvailability).append(" (").append(EquipmentType.ratingNames[partAvailability]).append(')'); + partAvailabilityLog.append("Part Rating Level: ").append(partAvailability) + .append(" (").append(EquipmentType.ratingNames[partAvailability]).append(')'); /* * Even if we can acquire Clan parts, they have a minimum availability of F for @@ -6726,7 +7072,9 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, fi * a minimum for non-flamer energy weapons, which was the reason this rule was * included in AtB to begin with. */ - if (et instanceof EnergyWeapon && !(et instanceof FlamerWeapon) && partAvailability < EquipmentType.RATING_C) { + if (et instanceof EnergyWeapon + && !(et instanceof FlamerWeapon) + && partAvailability < EquipmentType.RATING_C) { partAvailability = EquipmentType.RATING_C; partAvailabilityLog.append("
    [Non-Flamer Lasers]"); } @@ -6734,7 +7082,8 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, fi partAvailability -= 2; partAvailabilityLog.append("
    Autocannon: -2"); } - if (et instanceof GaussWeapon || et instanceof FlamerWeapon) { + if (et instanceof GaussWeapon + || et instanceof FlamerWeapon) { partAvailability--; partAvailabilityLog.append("
    Gauss Rifle or Flamer: -1"); } @@ -6749,20 +7098,28 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, fi partAvailabilityLog.append("
    Gauss Ammo: -1"); break; } - if (EnumSet.of(Munitions.M_STANDARD).containsAll(((AmmoType) et).getMunitionType())) { + if (EnumSet.of(Munitions.M_STANDARD).containsAll( + ((AmmoType) et).getMunitionType())) { partAvailability--; partAvailabilityLog.append("
    Standard Ammo: -1"); } } } - if (((getGameYear() < 2950) || (getGameYear() > 3040)) && (acquisition instanceof Armor || acquisition instanceof MissingMekActuator || acquisition instanceof MissingMekCockpit || acquisition instanceof MissingMekLifeSupport || acquisition instanceof MissingMekLocation || acquisition instanceof MissingMekSensor)) { + if (((getGameYear() < 2950) || (getGameYear() > 3040)) + && (acquisition instanceof Armor || acquisition instanceof MissingMekActuator + || acquisition instanceof MissingMekCockpit + || acquisition instanceof MissingMekLifeSupport + || acquisition instanceof MissingMekLocation + || acquisition instanceof MissingMekSensor)) { partAvailability--; partAvailabilityLog.append("
    Mek part prior to 2950 or after 3040: - 1"); } int AtBPartsAvailability = findAtBPartsAvailabilityLevel(acquisition, null); - partAvailabilityLog.append("
    Total part availability: ").append(partAvailability).append("
    Current campaign availability: ").append(AtBPartsAvailability).append(""); + partAvailabilityLog.append("
    Total part availability: ").append(partAvailability) + .append("
    Current campaign availability: ").append(AtBPartsAvailability) + .append(""); if (partAvailability > AtBPartsAvailability) { return new TargetRoll(TargetRoll.IMPOSSIBLE, partAvailabilityLog.toString()); } @@ -6789,7 +7146,8 @@ public int findAtBPartsAvailabilityLevel(IAcquisitionWork acquisition, StringBui */ if (hasActiveContract()) { for (final AtBContract c : getActiveAtBContracts()) { - if ((contract == null) || (c.getPartsAvailabilityLevel() > contract.getPartsAvailabilityLevel())) { + if ((contract == null) + || (c.getPartsAvailabilityLevel() > contract.getPartsAvailabilityLevel())) { contract = c; } } @@ -6798,7 +7156,8 @@ public int findAtBPartsAvailabilityLevel(IAcquisitionWork acquisition, StringBui // if we have a contract and it has started if ((null != contract) && contract.isActiveOn(getLocalDate(), true)) { if (reportBuilder != null) { - reportBuilder.append(contract.getPartsAvailabilityLevel()).append(" (").append(contract.getType()).append(')'); + reportBuilder.append(contract.getPartsAvailabilityLevel()).append(" (").append(contract.getType()) + .append(')'); } return contract.getPartsAvailabilityLevel(); } @@ -6829,8 +7188,10 @@ public int findAtBPartsAvailabilityLevel(IAcquisitionWork acquisition, StringBui } public void resetAstechMinutes() { - astechPoolMinutes = Person.PRIMARY_ROLE_SUPPORT_TIME * getNumberPrimaryAstechs() + Person.PRIMARY_ROLE_OVERTIME_SUPPORT_TIME * getNumberSecondaryAstechs(); - astechPoolOvertime = Person.SECONDARY_ROLE_SUPPORT_TIME * getNumberPrimaryAstechs() + Person.SECONDARY_ROLE_OVERTIME_SUPPORT_TIME * getNumberSecondaryAstechs(); + astechPoolMinutes = Person.PRIMARY_ROLE_SUPPORT_TIME * getNumberPrimaryAstechs() + + Person.PRIMARY_ROLE_OVERTIME_SUPPORT_TIME * getNumberSecondaryAstechs(); + astechPoolOvertime = Person.SECONDARY_ROLE_SUPPORT_TIME * getNumberPrimaryAstechs() + + Person.SECONDARY_ROLE_OVERTIME_SUPPORT_TIME * getNumberSecondaryAstechs(); } public void setAstechPoolMinutes(int minutes) { @@ -6878,7 +7239,8 @@ public boolean requiresAdditionalAstechs() { } public int getAstechNeed() { - return (Math.toIntExact(getActivePersonnel().stream().filter(Person::isTech).count()) * 6) - getNumberAstechs(); + return (Math.toIntExact(getActivePersonnel().stream().filter(Person::isTech).count()) * 6) + - getNumberAstechs(); } public void increaseAstechPool(int i) { @@ -6998,10 +7360,14 @@ public int getMedicsPerDoctor() { /** * @return the number of medics in the campaign including any in the temporary - * medic pool + * medic pool */ public int getNumberMedics() { - return getMedicPool() + Math.toIntExact(getActivePersonnel().stream().filter(p -> (p.getPrimaryRole().isMedic() || p.getSecondaryRole().isMedic()) && !p.isDeployed()).count()); + return getMedicPool() + + Math.toIntExact(getActivePersonnel().stream() + .filter(p -> (p.getPrimaryRole().isMedic() || p.getSecondaryRole().isMedic()) + && !p.isDeployed()) + .count()); } public boolean requiresAdditionalMedics() { @@ -7035,9 +7401,9 @@ public GameOptions getGameOptions() { public Vector getGameOptionsVector() { Vector options = new Vector<>(); - for (Enumeration i = gameOptions.getGroups(); i.hasMoreElements(); ) { + for (Enumeration i = gameOptions.getGroups(); i.hasMoreElements();) { IOptionGroup group = i.nextElement(); - for (Enumeration j = group.getOptions(); j.hasMoreElements(); ) { + for (Enumeration j = group.getOptions(); j.hasMoreElements();) { IOption option = j.nextElement(); options.add(option); } @@ -7057,16 +7423,24 @@ public void setGameOptions(final Vector options) { } public void updateCampaignOptionsFromGameOptions() { - getCampaignOptions().setUseTactics(getGameOptions().getOption(OptionsConstants.RPG_COMMAND_INIT).booleanValue()); - getCampaignOptions().setUseInitiativeBonus(getGameOptions().getOption(OptionsConstants.RPG_INDIVIDUAL_INITIATIVE).booleanValue()); + getCampaignOptions() + .setUseTactics(getGameOptions().getOption(OptionsConstants.RPG_COMMAND_INIT).booleanValue()); + getCampaignOptions().setUseInitiativeBonus( + getGameOptions().getOption(OptionsConstants.RPG_INDIVIDUAL_INITIATIVE).booleanValue()); getCampaignOptions().setUseToughness(getGameOptions().getOption(OptionsConstants.RPG_TOUGHNESS).booleanValue()); - getCampaignOptions().setUseArtillery(getGameOptions().getOption(OptionsConstants.RPG_ARTILLERY_SKILL).booleanValue()); - getCampaignOptions().setUseAbilities(getGameOptions().getOption(OptionsConstants.RPG_PILOT_ADVANTAGES).booleanValue()); + getCampaignOptions() + .setUseArtillery(getGameOptions().getOption(OptionsConstants.RPG_ARTILLERY_SKILL).booleanValue()); + getCampaignOptions() + .setUseAbilities(getGameOptions().getOption(OptionsConstants.RPG_PILOT_ADVANTAGES).booleanValue()); getCampaignOptions().setUseEdge(getGameOptions().getOption(OptionsConstants.EDGE).booleanValue()); - getCampaignOptions().setUseImplants(getGameOptions().getOption(OptionsConstants.RPG_MANEI_DOMINI).booleanValue()); - getCampaignOptions().setQuirks(getGameOptions().getOption(OptionsConstants.ADVANCED_STRATOPS_QUIRKS).booleanValue()); - getCampaignOptions().setAllowCanonOnly(getGameOptions().getOption(OptionsConstants.ALLOWED_CANON_ONLY).booleanValue()); - getCampaignOptions().setTechLevel(TechConstants.getSimpleLevel(getGameOptions().getOption(OptionsConstants.ALLOWED_TECHLEVEL).stringValue())); + getCampaignOptions() + .setUseImplants(getGameOptions().getOption(OptionsConstants.RPG_MANEI_DOMINI).booleanValue()); + getCampaignOptions() + .setQuirks(getGameOptions().getOption(OptionsConstants.ADVANCED_STRATOPS_QUIRKS).booleanValue()); + getCampaignOptions() + .setAllowCanonOnly(getGameOptions().getOption(OptionsConstants.ALLOWED_CANON_ONLY).booleanValue()); + getCampaignOptions().setTechLevel(TechConstants + .getSimpleLevel(getGameOptions().getOption(OptionsConstants.ALLOWED_TECHLEVEL).stringValue())); MekHQ.triggerEvent(new OptionsChangedEvent(this)); } @@ -7186,18 +7560,20 @@ public int getAtBUnitRatingMod() { return IUnitRating.DRAGOON_C; } - return getCampaignOptions().getUnitRatingMethod().isFMMR() ? getUnitRating().getUnitRatingAsInteger() : reputation.getAtbModifier(); + return getCampaignOptions().getUnitRatingMethod().isFMMR() ? getUnitRating().getUnitRatingAsInteger() + : reputation.getAtbModifier(); } /** * Returns the Strategy skill of the designated commander in the campaign. * * @return The value of the commander's strategy skill if a commander exists, - * otherwise 0. + * otherwise 0. */ public int getCommanderStrategy() { int cmdrStrategy = 0; - if (getFlaggedCommander() != null && getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) { + if (getFlaggedCommander() != null && + getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) { cmdrStrategy = getFlaggedCommander().getSkill(SkillType.S_STRATEGY).getLevel(); } return cmdrStrategy; @@ -7226,9 +7602,11 @@ public void setStartingSystem(final @Nullable Planet planet) { startingSystem = systemList.get(getFaction().getStartingPlanet(getLocalDate())); if (startingSystem == null) { - startingSystem = systemList.get(JOptionPane.showInputDialog("This faction does not have a starting planet for this era. Please choose a planet.")); + startingSystem = systemList.get(JOptionPane.showInputDialog( + "This faction does not have a starting planet for this era. Please choose a planet.")); while (startingSystem == null) { - startingSystem = systemList.get(JOptionPane.showInputDialog("This planet you entered does not exist. Please choose a valid planet.")); + startingSystem = systemList.get(JOptionPane.showInputDialog( + "This planet you entered does not exist. Please choose a valid planet.")); } } } else { @@ -7382,7 +7760,8 @@ public void refreshNetworks() { for (Entity e : game.getEntitiesVector()) { // C3 Checks if (entity.hasC3()) { - if ((entity.getC3MasterIsUUIDAsString() != null) && entity.getC3MasterIsUUIDAsString().equals(e.getC3UUIDAsString())) { + if ((entity.getC3MasterIsUUIDAsString() != null) + && entity.getC3MasterIsUUIDAsString().equals(e.getC3UUIDAsString())) { entity.setC3Master(e, false); break; } @@ -7394,7 +7773,9 @@ public void refreshNetworks() { // Well, they're the same value of 6... while (pos < Entity.MAX_C3i_NODES) { // We've found a network, join it. - if ((entity.getNC3NextUUIDAsString(pos) != null) && (e.getC3UUIDAsString() != null) && entity.getNC3NextUUIDAsString(pos).equals(e.getC3UUIDAsString())) { + if ((entity.getNC3NextUUIDAsString(pos) != null) + && (e.getC3UUIDAsString() != null) + && entity.getNC3NextUUIDAsString(pos).equals(e.getC3UUIDAsString())) { entity.setC3NetId(e); NC3Set = true; break; @@ -7409,7 +7790,9 @@ public void refreshNetworks() { int pos = 0; while (pos < Entity.MAX_C3i_NODES) { // We've found a network, join it. - if ((entity.getC3iNextUUIDAsString(pos) != null) && (e.getC3UUIDAsString() != null) && entity.getC3iNextUUIDAsString(pos).equals(e.getC3UUIDAsString())) { + if ((entity.getC3iNextUUIDAsString(pos) != null) + && (e.getC3UUIDAsString() != null) + && entity.getC3iNextUUIDAsString(pos).equals(e.getC3UUIDAsString())) { entity.setC3NetId(e); C3iSet = true; break; @@ -7427,7 +7810,8 @@ public void disbandNetworkOf(Unit u) { // collect all of the other units on this network to rebuild the uuids Vector networkedUnits = new Vector<>(); for (Unit unit : getUnits()) { - if (null != unit.getEntity().getC3NetId() && unit.getEntity().getC3NetId().equals(u.getEntity().getC3NetId())) { + if (null != unit.getEntity().getC3NetId() + && unit.getEntity().getC3NetId().equals(u.getEntity().getC3NetId())) { networkedUnits.add(unit); } } @@ -7453,7 +7837,8 @@ public void removeUnitsFromNetwork(Vector removedUnits) { if (removedUnits.contains(unit)) { continue; } - if (null != unit.getEntity().getC3NetId() && unit.getEntity().getC3NetId().equals(network)) { + if (null != unit.getEntity().getC3NetId() + && unit.getEntity().getC3NetId().equals(network)) { networkedUnits.add(unit); uuids.add(unit.getEntity().getC3UUIDAsString()); } @@ -7469,9 +7854,11 @@ public void removeUnitsFromNetwork(Vector removedUnits) { for (Unit nUnit : networkedUnits) { if (pos < uuids.size()) { if (nUnit.getEntity().hasNavalC3()) { - nUnit.getEntity().setNC3NextUUIDAsString(pos, uuids.get(pos)); + nUnit.getEntity().setNC3NextUUIDAsString(pos, + uuids.get(pos)); } else { - nUnit.getEntity().setC3iNextUUIDAsString(pos, uuids.get(pos)); + nUnit.getEntity().setC3iNextUUIDAsString(pos, + uuids.get(pos)); } } else { if (nUnit.getEntity().hasNavalC3()) { @@ -7497,7 +7884,8 @@ public void addUnitsToNetwork(Vector addedUnits, String netid) { if (addedUnits.contains(unit)) { continue; } - if (null != unit.getEntity().getC3NetId() && unit.getEntity().getC3NetId().equals(netid)) { + if (null != unit.getEntity().getC3NetId() + && unit.getEntity().getC3NetId().equals(netid)) { networkedUnits.add(unit); uuids.add(unit.getEntity().getC3UUIDAsString()); } @@ -7506,9 +7894,11 @@ public void addUnitsToNetwork(Vector addedUnits, String netid) { for (Unit nUnit : networkedUnits) { if (pos < uuids.size()) { if (nUnit.getEntity().hasNavalC3()) { - nUnit.getEntity().setNC3NextUUIDAsString(pos, uuids.get(pos)); + nUnit.getEntity().setNC3NextUUIDAsString(pos, + uuids.get(pos)); } else { - nUnit.getEntity().setC3iNextUUIDAsString(pos, uuids.get(pos)); + nUnit.getEntity().setC3iNextUUIDAsString(pos, + uuids.get(pos)); } } else { if (nUnit.getEntity().hasNavalC3()) { @@ -7537,7 +7927,8 @@ public Vector getAvailableC3iNetworks() { if (null == en) { continue; } - if (en.hasC3i() && en.calculateFreeC3Nodes() < 5 && en.calculateFreeC3Nodes() > 0) { + if (en.hasC3i() && en.calculateFreeC3Nodes() < 5 + && en.calculateFreeC3Nodes() > 0) { String[] network = new String[2]; network[0] = en.getC3NetId(); network[1] = "" + en.calculateFreeC3Nodes(); @@ -7572,7 +7963,8 @@ public Vector getAvailableNC3Networks() { if (null == en) { continue; } - if (en.hasNavalC3() && en.calculateFreeC3Nodes() < 5 && en.calculateFreeC3Nodes() > 0) { + if (en.hasNavalC3() && en.calculateFreeC3Nodes() < 5 + && en.calculateFreeC3Nodes() > 0) { String[] network = new String[2]; network[0] = en.getC3NetId(); network[1] = "" + en.calculateFreeC3Nodes(); @@ -7651,7 +8043,8 @@ public Vector getAvailableC3MastersForMasters() { public void removeUnitsFromC3Master(Unit master) { List removed = new ArrayList<>(); for (Unit unit : getUnits()) { - if (null != unit.getEntity().getC3MasterIsUUIDAsString() && unit.getEntity().getC3MasterIsUUIDAsString().equals(master.getEntity().getC3UUIDAsString())) { + if (null != unit.getEntity().getC3MasterIsUUIDAsString() + && unit.getEntity().getC3MasterIsUUIDAsString().equals(master.getEntity().getC3UUIDAsString())) { unit.getEntity().setC3MasterIsUUIDAsString(null); unit.getEntity().setC3Master(null, true); removed.add(unit); @@ -7687,7 +8080,8 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { // According to FMM(r) pg 179, both failure and breach lead to no // further payment even though this seems foolish if (contract.getStatus().isSuccess()) { - remainingMoney = contract.getMonthlyPayOut().multipliedBy(contract.getMonthsLeft(getLocalDate())); + remainingMoney = contract.getMonthlyPayOut() + .multipliedBy(contract.getMonthsLeft(getLocalDate())); if (contract instanceof AtBContract) { Money routedPayout = ((AtBContract) contract).getRoutedPayout(); @@ -7702,9 +8096,11 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { // Then, we check if the salvage percent is less than the percent salvaged by // the // unit in question. If it is, then they owe the assigner some cash - if (getCampaignOptions().isOverageRepaymentInFinalPayment() && (contract.getSalvagePct() < 100.0)) { + if (getCampaignOptions().isOverageRepaymentInFinalPayment() + && (contract.getSalvagePct() < 100.0)) { final double salvagePercent = contract.getSalvagePct() / 100.0; - final Money maxSalvage = contract.getSalvagedByEmployer().multipliedBy(salvagePercent / (1 - salvagePercent)); + final Money maxSalvage = contract.getSalvagedByEmployer() + .multipliedBy(salvagePercent / (1 - salvagePercent)); if (contract.getSalvagedByUnit().isGreaterThan(maxSalvage)) { final Money amountToRepay = contract.getSalvagedByUnit().minus(maxSalvage); remainingMoney = remainingMoney.minus(amountToRepay); @@ -7713,13 +8109,15 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { } if (getCampaignOptions().isUseShareSystem()) { - ResourceBundle financeResources = ResourceBundle.getBundle("mekhq.resources.Finances", MekHQ.getMHQOptions().getLocale()); + ResourceBundle financeResources = ResourceBundle.getBundle("mekhq.resources.Finances", + MekHQ.getMHQOptions().getLocale()); if (remainingMoney.isGreaterThan(Money.zero())) { Money shares = remainingMoney.multipliedBy(contract.getSharesPercent()).dividedBy(100); remainingMoney = remainingMoney.minus(shares); - if (getFinances().debit(TransactionType.SALARIES, getLocalDate(), shares, String.format(financeResources.getString("ContractSharePayment.text"), contract.getName()))) { + if (getFinances().debit(TransactionType.SALARIES, getLocalDate(), shares, + String.format(financeResources.getString("ContractSharePayment.text"), contract.getName()))) { addReport(financeResources.getString("DistributedShares.text"), shares.toAmountAndSymbolString()); getFinances().payOutSharesToPersonnel(this, shares); @@ -7728,11 +8126,15 @@ public void completeMission(@Nullable Mission mission, MissionStatus status) { } if (remainingMoney.isPositive()) { - getFinances().credit(TransactionType.CONTRACT_PAYMENT, getLocalDate(), remainingMoney, "Remaining payment for " + contract.getName()); - addReport("Your account has been credited for " + remainingMoney.toAmountAndSymbolString() + " for the remaining payout from contract " + contract.getName()); + getFinances().credit(TransactionType.CONTRACT_PAYMENT, getLocalDate(), remainingMoney, + "Remaining payment for " + contract.getName()); + addReport("Your account has been credited for " + remainingMoney.toAmountAndSymbolString() + + " for the remaining payout from contract " + contract.getName()); } else if (remainingMoney.isNegative()) { - getFinances().credit(TransactionType.CONTRACT_PAYMENT, getLocalDate(), remainingMoney, "Repaying payment overages for " + contract.getName()); - addReport("Your account has been debited for " + remainingMoney.absolute().toAmountAndSymbolString() + " to repay payment overages occurred during the contract " + contract.getName()); + getFinances().credit(TransactionType.CONTRACT_PAYMENT, getLocalDate(), remainingMoney, + "Repaying payment overages for " + contract.getName()); + addReport("Your account has been debited for " + remainingMoney.absolute().toAmountAndSymbolString() + + " to repay payment overages occurred during the contract " + contract.getName()); } // This relies on the mission being a Contract, and AtB to be on @@ -7811,8 +8213,10 @@ public int calculatePartTransitTime(int mos) { // now establish minimum date and if this is before LocalDate minimumDate = getLocalDate(); minimumDate = switch (getCampaignOptions().getAcquireMinimumTimeUnit()) { - case CampaignOptions.TRANSIT_UNIT_MONTH -> minimumDate.plusMonths(getCampaignOptions().getAcquireMinimumTime()); - case CampaignOptions.TRANSIT_UNIT_WEEK -> minimumDate.plusWeeks(getCampaignOptions().getAcquireMinimumTime()); + case CampaignOptions.TRANSIT_UNIT_MONTH -> + minimumDate.plusMonths(getCampaignOptions().getAcquireMinimumTime()); + case CampaignOptions.TRANSIT_UNIT_WEEK -> + minimumDate.plusWeeks(getCampaignOptions().getAcquireMinimumTime()); default -> minimumDate.plusDays(getCampaignOptions().getAcquireMinimumTime()); }; @@ -7829,7 +8233,7 @@ public int calculatePartTransitTime(int mos) { * * @param part A part to lookup its current inventory. * @return A PartInventory object detailing the current counts of - * the part on hand, in transit, and ordered. + * the part on hand, in transit, and ordered. * @see PartInventory */ public PartInventory getPartInventory(Part part) { @@ -7892,19 +8296,27 @@ public PartInventory getPartInventory(Part part) { } public void addLoan(Loan loan) { - addReport("You have taken out loan " + loan + ". Your account has been credited " + loan.getPrincipal().toAmountAndSymbolString() + " for the principal amount."); + addReport("You have taken out loan " + loan + + ". Your account has been credited " + + loan.getPrincipal().toAmountAndSymbolString() + + " for the principal amount."); finances.addLoan(loan); MekHQ.triggerEvent(new LoanNewEvent(loan)); - finances.credit(TransactionType.LOAN_PRINCIPAL, getLocalDate(), loan.getPrincipal(), "Loan principal for " + loan); + finances.credit(TransactionType.LOAN_PRINCIPAL, getLocalDate(), loan.getPrincipal(), + "Loan principal for " + loan); } public void payOffLoan(Loan loan) { - if (finances.debit(TransactionType.LOAN_PAYMENT, getLocalDate(), loan.determineRemainingValue(), "Loan payoff for " + loan)) { - addReport("You have paid off the remaining loan balance of " + loan.determineRemainingValue().toAmountAndSymbolString() + " on " + loan); + if (finances.debit(TransactionType.LOAN_PAYMENT, getLocalDate(), loan.determineRemainingValue(), + "Loan payoff for " + loan)) { + addReport("You have paid off the remaining loan balance of " + + loan.determineRemainingValue().toAmountAndSymbolString() + + " on " + loan); finances.removeLoan(loan); MekHQ.triggerEvent(new LoanPaidEvent(loan)); } else { - addReport("You do not have enough funds to pay off " + loan + ""); + addReport("You do not have enough funds to pay off " + loan + ""); } } @@ -7913,7 +8325,8 @@ public void setHealingTimeOptions(int newHeal, int newNaturalHeal) { // times for all // personnel, giving them credit for their current waiting time int currentHeal = getCampaignOptions().getHealingWaitingPeriod(); - int currentNaturalHeal = getCampaignOptions().getNaturalHealingWaitingPeriod(); + int currentNaturalHeal = getCampaignOptions() + .getNaturalHealingWaitingPeriod(); getCampaignOptions().setHealingWaitingPeriod(newHeal); getCampaignOptions().setNaturalHealingWaitingPeriod(newNaturalHeal); @@ -7924,9 +8337,11 @@ public void setHealingTimeOptions(int newHeal, int newNaturalHeal) { if (healDiff != 0 || naturalDiff != 0) { for (Person p : getPersonnel()) { if (p.getDoctorId() != null) { - p.setDaysToWaitForHealing(max(p.getDaysToWaitForHealing() + healDiff, 1)); + p.setDaysToWaitForHealing(max( + p.getDaysToWaitForHealing() + healDiff, 1)); } else { - p.setDaysToWaitForHealing(max(p.getDaysToWaitForHealing() + naturalDiff, 1)); + p.setDaysToWaitForHealing(max( + p.getDaysToWaitForHealing() + naturalDiff, 1)); } } } @@ -7935,7 +8350,8 @@ public void setHealingTimeOptions(int newHeal, int newNaturalHeal) { private CampaignTransporterMap getCampaignTransporterMap(CampaignTransportType campaignTransportType) { if (campaignTransportType.isTacticalTransport()) { return tacticalTransporters; - } else if (campaignTransportType.isShipTransport()) { + } + else if (campaignTransportType.isShipTransport()) { return shipTransporters; } return null; @@ -7959,7 +8375,7 @@ public Map>> getTransports(CampaignTransp * TransporterType and CampaignTransportType * * @param campaignTransportType type of campaign transport - * @param transporterType type of Transporter + * @param transporterType type of Transporter * @return units that have that transport type */ public Set getTransportsByType(CampaignTransportType campaignTransportType, TransporterType transporterType) { @@ -7977,8 +8393,8 @@ public Set getTransportsByType(CampaignTransportType campaignTransportType * open for the SHIP_TRANSPORT type of assignment. * * @param campaignTransportType type (Enum) of TransportedUnitSummary - * @param transporterType type (Enum) of Transporter - * @param unitSize capacity that the transport must be capable of + * @param transporterType type (Enum) of Transporter + * @param unitSize capacity that the transport must be capable of * @return units that have that transport type */ public Set getTransportsByType(CampaignTransportType campaignTransportType, TransporterType transporterType, double unitSize) { @@ -7995,14 +8411,14 @@ private boolean hasShipTransports() { /** * Do we have transports for the kind of transport? - * * @param campaignTransportType class of the TransportDetail * @return true if it has transporters, false otherwise */ public boolean hasTransports(CampaignTransportType campaignTransportType) { if (campaignTransportType.isTacticalTransport()) { return hasTacticalTransports(); - } else if (campaignTransportType.isShipTransport()) { + } + else if (campaignTransportType.isShipTransport()) { return hasShipTransports(); } return false; @@ -8016,7 +8432,8 @@ public void doMaintenance(Unit u) { Person tech = u.getTech(); int minutesUsed = u.getMaintenanceTime(); int astechsUsed = getAvailableAstechs(minutesUsed, false); - boolean maintained = ((tech != null) && (tech.getMinutesLeft() >= minutesUsed) && !tech.isMothballing()); + boolean maintained = ((tech != null) && (tech.getMinutesLeft() >= minutesUsed) + && !tech.isMothballing()); boolean paidMaintenance = true; if (maintained) { // use the time @@ -8037,8 +8454,11 @@ public void doMaintenance(Unit u) { if (u.getDaysSinceMaintenance() >= (getCampaignOptions().getMaintenanceCycleDays() * ruggedMultiplier)) { // maybe use the money if (campaignOptions.isPayForMaintain()) { - if (!(finances.debit(TransactionType.MAINTENANCE, getLocalDate(), u.getMaintenanceCost(), "Maintenance for " + u.getName()))) { - addReport("You cannot afford to pay maintenance costs for " + u.getHyperlinkedName() + "!"); + if (!(finances.debit(TransactionType.MAINTENANCE, getLocalDate(), u.getMaintenanceCost(), + "Maintenance for " + u.getName()))) { + addReport("You cannot afford to pay maintenance costs for " + + u.getHyperlinkedName() + "!"); paidMaintenance = false; } } @@ -8054,7 +8474,8 @@ public void doMaintenance(Unit u) { // concurrent mod problems // put it into a hash - 4 points of damage will mean destruction Map partsToDamage = new HashMap<>(); - StringBuilder maintenanceReport = new StringBuilder("" + techName + " performing maintenance

    "); + StringBuilder maintenanceReport = new StringBuilder( + "" + techName + " performing maintenance

    "); for (Part p : u.getParts()) { try { String partReport = doMaintenanceOnUnitPart(u, p, partsToDamage, paidMaintenance); @@ -8062,8 +8483,12 @@ public void doMaintenance(Unit u) { maintenanceReport.append(partReport).append("
    "); } } catch (Exception e) { - logger.error(String.format("Could not perform maintenance on part %s (%d) for %s (%s) due to an error", p.getName(), p.getId(), u.getName(), u.getId().toString()), e); - addReport(String.format("ERROR: An error occurred performing maintenance on %s for unit %s, check the log", p.getName(), u.getName())); + logger.error(String.format( + "Could not perform maintenance on part %s (%d) for %s (%s) due to an error", + p.getName(), p.getId(), u.getName(), u.getId().toString()), e); + addReport(String.format( + "ERROR: An error occurred performing maintenance on %s for unit %s, check the log", + p.getName(), u.getName())); } } @@ -8090,9 +8515,15 @@ public void doMaintenance(Unit u) { String qualityString; boolean reverse = getCampaignOptions().isReverseQualityNames(); if (quality.toNumeric() > qualityOrig.toNumeric()) { - qualityString = ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor(), "Overall quality improves from " + qualityOrig.toName(reverse) + " to " + quality.toName(reverse)); + qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), + "Overall quality improves from " + qualityOrig.toName(reverse) + + " to " + quality.toName(reverse)); } else if (quality.toNumeric() < qualityOrig.toNumeric()) { - qualityString = ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "Overall quality declines from " + qualityOrig.toName(reverse) + " to " + quality.toName(reverse)); + qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + "Overall quality declines from " + qualityOrig.toName(reverse) + + " to " + quality.toName(reverse)); } else { qualityString = "Overall quality remains " + quality.toName(reverse); } @@ -8104,13 +8535,18 @@ public void doMaintenance(Unit u) { damageString += nDestroy + " parts were destroyed."; } if (!damageString.isEmpty()) { - damageString = "" + damageString + " [Repair bay]"; + damageString = "" + + damageString + " [Repair bay]"; } String paidString = ""; if (!paidMaintenance) { - paidString = "Could not afford maintenance costs, so check is at a penalty."; + paidString = "Could not afford maintenance costs, so check is at a penalty."; } - addReport(techNameLinked + " performs maintenance on " + u.getHyperlinkedName() + ". " + paidString + qualityString + ". " + damageString + " [Get details]"); + addReport(techNameLinked + " performs maintenance on " + u.getHyperlinkedName() + ". " + paidString + + qualityString + ". " + damageString + " [Get details]"); u.resetDaysSinceMaintenance(); } @@ -8219,17 +8655,25 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT break; } if (p.getQuality().toNumeric() > oldQuality.toNumeric()) { - partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor(), "new quality is " + p.getQualityName()); + partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), + "new quality is " + p.getQualityName()); } else if (p.getQuality().toNumeric() < oldQuality.toNumeric()) { - partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "new quality is " + p.getQualityName()); + partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + "new quality is " + p.getQualityName()); } else { partReport += ": quality remains " + p.getQualityName(); } if (null != partsToDamage.get(p)) { if (partsToDamage.get(p) > 3) { - partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "part destroyed"); + partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + "part destroyed"); } else { - partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "part damaged"); + partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + "part damaged"); } } @@ -8267,7 +8711,8 @@ public void initTimeInRank() { join = e.getDate(); } - if (e.getDesc().startsWith("Joined ") || e.getDesc().startsWith("Freed ") || e.getDesc().startsWith("Promoted ") || e.getDesc().startsWith("Demoted ")) { + if (e.getDesc().startsWith("Joined ") || e.getDesc().startsWith("Freed ") + || e.getDesc().startsWith("Promoted ") || e.getDesc().startsWith("Demoted ")) { join = e.getDate(); } } @@ -8314,16 +8759,23 @@ public void initAtB(boolean newCampaign) { * that 'Mek (which is a less certain assumption) */ for (Person p : getPersonnel()) { - LocalDate join = p.getPersonnelLog().stream().filter(e -> e.getDesc().startsWith("Joined ")).findFirst().map(LogEntry::getDate).orElse(null); + LocalDate join = p.getPersonnelLog().stream() + .filter(e -> e.getDesc().startsWith("Joined ")) + .findFirst() + .map(LogEntry::getDate) + .orElse(null); if ((join != null) && join.equals(founding)) { p.setFounder(true); } - if (p.getPrimaryRole().isMekWarrior() || (p.getPrimaryRole().isAerospacePilot() && getCampaignOptions().isAeroRecruitsHaveUnits()) || p.getPrimaryRole().isProtoMekPilot()) { + if (p.getPrimaryRole().isMekWarrior() + || (p.getPrimaryRole().isAerospacePilot() && getCampaignOptions().isAeroRecruitsHaveUnits()) + || p.getPrimaryRole().isProtoMekPilot()) { for (LogEntry e : p.getPersonnelLog()) { if (e.getDate().equals(join) && e.getDesc().startsWith("Assigned to ")) { String mek = e.getDesc().substring(12); MekSummary ms = MekSummaryCache.getInstance().getMek(mek); - if (null != ms && (p.isFounder() || ms.getWeightClass() < EntityWeightClass.WEIGHT_ASSAULT)) { + if (null != ms && (p.isFounder() + || ms.getWeightClass() < EntityWeightClass.WEIGHT_ASSAULT)) { p.setOriginalUnitWeight(ms.getWeightClass()); if (ms.isClan()) { p.setOriginalUnitTech(Person.TECH_CLAN); @@ -8331,7 +8783,8 @@ public void initAtB(boolean newCampaign) { // TODO : Fix this so we aren't using a hack that just assumes IS2 p.setOriginalUnitTech(Person.TECH_IS2); } - if ((null != p.getUnit()) && ms.getName().equals(p.getUnit().getEntity().getShortNameRaw())) { + if ((null != p.getUnit()) + && ms.getName().equals(p.getUnit().getEntity().getShortNameRaw())) { p.setOriginalUnitId(p.getUnit().getId()); } } @@ -8364,7 +8817,13 @@ public boolean checkOverDueLoans() { Money overdueAmount = getFinances().checkOverdueLoanPayments(this); if (overdueAmount.isPositive()) { // FIXME : Localize - JOptionPane.showMessageDialog(null, "You have overdue loan payments totaling " + overdueAmount.toAmountAndSymbolString() + "\nYou must deal with these payments before advancing the day.\nHere are some options:\n - Sell off equipment to generate funds.\n - Pay off the collateral on the loan.\n - Default on the loan.\n - Just cheat and remove the loan via GM mode.", "Overdue Loan Payments", JOptionPane.WARNING_MESSAGE); + JOptionPane.showMessageDialog( + null, + "You have overdue loan payments totaling " + + overdueAmount.toAmountAndSymbolString() + + "\nYou must deal with these payments before advancing the day.\nHere are some options:\n - Sell off equipment to generate funds.\n - Pay off the collateral on the loan.\n - Default on the loan.\n - Just cheat and remove the loan via GM mode.", + "Overdue Loan Payments", + JOptionPane.WARNING_MESSAGE); return true; } return false; @@ -8375,10 +8834,10 @@ public boolean checkOverDueLoans() { * current date. * * @return An integer representing the user's choice: - * -1 if turnover prompt should not be displayed. - * 0 to indicate the user selected "Employee Turnover". - * 1 to indicate the user selected "Advance Day Regardless". - * 2 to indicate the user selected "Cancel Advance Day". + * -1 if turnover prompt should not be displayed. + * 0 to indicate the user selected "Employee Turnover". + * 1 to indicate the user selected "Advance Day Regardless". + * 2 to indicate the user selected "Cancel Advance Day". */ public int checkTurnoverPrompt() { if (getLocalDate().isBefore(getCampaignStartDate().plusDays(6))) { @@ -8394,7 +8853,9 @@ public int checkTurnoverPrompt() { triggerTurnoverPrompt = getLocalDate().getDayOfMonth() == getLocalDate().lengthOfMonth(); break; case QUARTERLY: - triggerTurnoverPrompt = (getLocalDate().getDayOfMonth() == getLocalDate().lengthOfMonth()) && (List.of(Month.MARCH, Month.JUNE, Month.SEPTEMBER, Month.DECEMBER).contains(getLocalDate().getMonth())); + triggerTurnoverPrompt = (getLocalDate().getDayOfMonth() == getLocalDate().lengthOfMonth()) + && (List.of(Month.MARCH, Month.JUNE, Month.SEPTEMBER, Month.DECEMBER) + .contains(getLocalDate().getMonth())); break; case ANNUALLY: triggerTurnoverPrompt = getLocalDate().getDayOfYear() == getLocalDate().lengthOfYear(); @@ -8418,9 +8879,21 @@ public int checkTurnoverPrompt() { dialogBody = resources.getString("turnoverPersonnelKilled.text"); } - Object[] options = { resources.getString("turnoverEmployeeTurnoverDialog.text"), resources.getString("turnoverAdvanceRegardless"), resources.getString("turnoverCancel.text") }; + Object[] options = { + resources.getString("turnoverEmployeeTurnoverDialog.text"), + resources.getString("turnoverAdvanceRegardless"), + resources.getString("turnoverCancel.text") + }; - return JOptionPane.showOptionDialog(null, dialogBody, dialogTitle, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); + return JOptionPane.showOptionDialog( + null, + dialogBody, + dialogTitle, + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.INFORMATION_MESSAGE, + null, + options, + options[0]); } /** @@ -8429,7 +8902,10 @@ public int checkTurnoverPrompt() { * @return {@code true} if there are scenarios due, {@code false} otherwise */ public boolean checkScenariosDue() { - return getActiveMissions(true).stream().flatMap(m -> m.getCurrentScenarios().stream()).anyMatch(s -> (s.getDate() != null) && !(s instanceof AtBScenario) && !getLocalDate().isBefore(s.getDate())); + return getActiveMissions(true).stream() + .flatMap(m -> m.getCurrentScenarios().stream()) + .anyMatch(s -> (s.getDate() != null) && !(s instanceof AtBScenario) + && !getLocalDate().isBefore(s.getDate())); } /** @@ -8563,7 +9039,7 @@ public void setAutoResolveBehaviorSettings(BehaviorSettings settings) { * @param isInformal A boolean flag indicating whether the address should be informal * (true for informal, false for formal). * @return A {@link String} representing the appropriate address for the commander, - * either formal or informal. + * either formal or informal. */ public String getCommanderAddress(boolean isInformal) { Person commander = getFlaggedCommander(); @@ -8582,22 +9058,25 @@ public String getCommanderAddress(boolean isInformal) { return switch (commanderGender) { case MALE -> resources.getString("informalAddressMale.text"); case FEMALE -> resources.getString("informalAddressFemale.text"); - case OTHER_MALE, OTHER_FEMALE, RANDOMIZE -> resources.getString("generalFallbackAddressInformal.text"); + case OTHER_MALE, OTHER_FEMALE, RANDOMIZE -> + resources.getString("generalFallbackAddressInformal.text"); }; } String commanderRank = commander.getRankName(); - if (commanderRank.equalsIgnoreCase("None") || commanderRank.equalsIgnoreCase("-") || commanderRank.isBlank()) { + if (commanderRank.equalsIgnoreCase("None") + || commanderRank.equalsIgnoreCase("-") + || commanderRank.isBlank()) { return resources.getString("generalFallbackAddress.text"); } return commanderRank; } - public int stockUpPartsInUse(Set partsInUse) { + public int stockUpPartsInUse(Set partsInUse) { int bought = 0; - for (PartInUse partInUse : partsInUse) { + for(PartInUse partInUse : partsInUse) { int toBuy = findStockUpAmount(partInUse); if (toBuy > 0) { IAcquisitionWork partToBuy = partInUse.getPartToBuy(); @@ -8608,13 +9087,13 @@ public int stockUpPartsInUse(Set partsInUse) { return bought; } - public void stockUpPartsInUseGM(Set partsInUse) { - for (PartInUse partInUse : partsInUse) { + public void stockUpPartsInUseGM(Set partsInUse) { + for(PartInUse partInUse : partsInUse) { int toBuy = findStockUpAmount(partInUse); while (toBuy > 0) { IAcquisitionWork partToBuy = partInUse.getPartToBuy(); getQuartermaster().addPart((Part) partToBuy.getNewEquipment(), 0); - --toBuy; + -- toBuy; } } } @@ -8622,11 +9101,11 @@ public void stockUpPartsInUseGM(Set partsInUse) { private int findStockUpAmount(PartInUse PartInUse) { IAcquisitionWork partToBuy = PartInUse.getPartToBuy(); int inventory = PartInUse.getStoreCount() + PartInUse.getTransferCount() + PartInUse.getPlannedCount(); - int needed = (int) Math.ceil(PartInUse.getRequestedStock() / 100.0 * PartInUse.getUseCount()); - int toBuy = needed - inventory; + int needed = (int)Math.ceil(PartInUse.getRequestedStock()/100.0 * PartInUse.getUseCount()); + int toBuy = needed-inventory; if (PartInUse.getIsBundle() == true) { - toBuy = (int) Math.ceil((float) toBuy * PartInUse.getTonnagePerItem() / 5); + toBuy = (int)Math.ceil((float)toBuy * PartInUse.getTonnagePerItem() / 5); //special case for armor only, as it's bought in 5 ton blocks. Armor is the only kind of item that's assigned isBundle() } @@ -8634,7 +9113,7 @@ private int findStockUpAmount(PartInUse PartInUse) { } //Simple getters and setters for our stock map - public Map getPartsInUseRequestedStockMap() { + public Map getPartsInUseRequestedStockMap() { return partsInUseRequestedStockMap; } @@ -8645,7 +9124,6 @@ public void setPartsInUseRequestedStockMap(Map partsInUseRequest public boolean getIgnoreMothballed() { return ignoreMothballed; } - public void setIgnoreMothballed(boolean ignoreMothballed) { this.ignoreMothballed = ignoreMothballed; } @@ -8653,7 +9131,6 @@ public void setIgnoreMothballed(boolean ignoreMothballed) { public boolean getTopUpWeekly() { return topUpWeekly; } - public void setTopUpWeekly(boolean topUpWeekly) { this.topUpWeekly = topUpWeekly; } @@ -8661,7 +9138,6 @@ public void setTopUpWeekly(boolean topUpWeekly) { public PartQuality getIgnoreSparesUnderQuality() { return ignoreSparesUnderQuality; } - public void setIgnoreSparesUnderQuality(PartQuality ignoreSparesUnderQuality) { this.ignoreSparesUnderQuality = ignoreSparesUnderQuality; } @@ -8670,21 +9146,22 @@ public void setIgnoreSparesUnderQuality(PartQuality ignoreSparesUnderQuality) { public void writePartInUseToXML(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ignoreMothBalled", ignoreMothballed); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "topUpWeekly", topUpWeekly); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ignoreSparesUnderQuality", ignoreSparesUnderQuality.name()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ignoreSparesUnderQuality", + ignoreSparesUnderQuality.name()); MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "partInUseMap"); writePartInUseMapToXML(pw, indent); MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "partInUseMap"); } public void writePartInUseMapToXML(final PrintWriter pw, int indent) { - for (String key : partsInUseRequestedStockMap.keySet()) { + for(String key : partsInUseRequestedStockMap.keySet()) { MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "partInUseMapEntry"); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partInUseMapKey", key); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partInUseMapVal", partsInUseRequestedStockMap.get(key)); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partInUseMapVal", + partsInUseRequestedStockMap.get(key)); MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "partInUseMapEntry"); } } - /** * Retrieves the campaign faction icon for the specified {@link Campaign}. * If a custom icon is defined in the campaign's unit icon configuration, that icon is used. @@ -8697,7 +9174,8 @@ public ImageIcon getCampaignFactionIcon() { StandardForceIcon campaignIcon = getUnitIcon(); if (campaignIcon.getFilename() == null) { - icon = getFactionLogo(this, getFaction().getShortName(), true); + icon = getFactionLogo(this, getFaction().getShortName(), + true); } else { icon = new ImageIcon(campaignIcon.getFilename()); } From 814b29009063fb68db52e7fa452b418f05ce07b2 Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 13:28:25 -0700 Subject: [PATCH 05/11] fix LinkedScenarioID -> linkedScenarioId and remove old function from manager --- MekHQ/src/mekhq/campaign/mission/Scenario.java | 16 ++++++++-------- .../campaign/stratcon/StratconRulesManager.java | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/Scenario.java b/MekHQ/src/mekhq/campaign/mission/Scenario.java index ef1e4f1f977..c51691261fe 100644 --- a/MekHQ/src/mekhq/campaign/mission/Scenario.java +++ b/MekHQ/src/mekhq/campaign/mission/Scenario.java @@ -92,7 +92,7 @@ public class Scenario implements IPlayerSettings { protected List botForcesStubs; // linked Interception Scenario - private int LinkedScenarioID; + private int linkedScenarioID; // stores external id of bot forces private Map externalIDLookup; @@ -153,7 +153,7 @@ public Scenario(String name) { botForces = new ArrayList<>(); botForcesStubs = new ArrayList<>(); externalIDLookup = new HashMap<>(); - LinkedScenarioID = 0; + linkedScenarioID = 0; light = Light.DAY; weather = Weather.CLEAR; @@ -229,11 +229,11 @@ public void setDate(final @Nullable LocalDate date) { } public int getLinkedScenario() { - return LinkedScenarioID; + return linkedScenarioID; } - public void setLinkedScenarioID(int ScenarioID) { - LinkedScenarioID = ScenarioID; + public void setlinkedScenarioID(int ScenarioID) { + linkedScenarioID = ScenarioID; } public boolean hasObjectives() { @@ -903,7 +903,7 @@ protected int writeToXMLBegin(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "startingAnySEy", startingAnySEy); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "status", getStatus().name()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "id", id); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "LinkedScenarioID", LinkedScenarioID); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "linkedScenarioID", linkedScenarioID); if (null != stub) { stub.writeToXML(pw, indent); } else { @@ -1039,8 +1039,8 @@ public static Scenario generateInstanceFromXML(Node wn, Campaign c, Version vers retVal.setReport(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("forceStub")) { retVal.stub = ForceStub.generateInstanceFromXML(wn2, version); - } else if (wn2.getNodeName().equalsIgnoreCase("LinkedScenarioID")) { - retVal.LinkedScenarioID = Integer.parseInt(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("linkedScenarioID")) { + retVal.linkedScenarioID = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("date")) { retVal.date = MHQXMLUtility.parseDate(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("cloaked")) { diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index e41367180ac..0cd25e900c2 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -498,8 +498,7 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam finalizeBackingScenario(campaign, contract, track, true, scenario); scenario.setActionDate(campaign.getLocalDate()); scenario.getBackingScenario().setStatus(ScenarioStatus.CURRENT); - scenario.getBackingScenario().setLinkedScenarioID(linkedScenario.getBackingScenario().getId()); - linkedScenario.getBackingScenario().setLinkedScenarioID(scenario.getBackingScenario().getId()); + scenario.getBackingScenario().setlinkedScenarioID(linkedScenario.getBackingScenario().getId()); } /** From 1fef6d5638bbb560d4abefa60532f09b43e29207 Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:08:38 -0700 Subject: [PATCH 06/11] add back in mission link check, add check for other linked missions before reinforcement --- MekHQ/src/mekhq/campaign/Campaign.java | 9 +++++++++ MekHQ/src/mekhq/gui/BriefingTab.java | 4 +++- .../mekhq/gui/dialog/ResolveScenarioWizardDialog.java | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index a744a0583ff..6e49c0adf79 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -9181,4 +9181,13 @@ public ImageIcon getCampaignFactionIcon() { } return icon; } + + public boolean checkLinkedScenario(int sID){ + for(Scenario s : getScenarios()){ + if(s.getId() == sID){ + return true; + } + } + return false; + } } diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index 13255533cc9..f41b3f43dab 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -1297,7 +1297,9 @@ public void refreshScenarioView() { // later SwingUtilities.invokeLater(() -> scrollScenarioView.getVerticalScrollBar().setValue(0)); - final boolean canStartGame = scenario.canStartScenario(getCampaign()); + final boolean canStartGame = ( + (getCampaign().checkLinkedScenario(scenario.getId())) && (scenario.canStartScenario(getCampaign())) + ); btnStartGame.setEnabled(canStartGame); btnJoinGame.setEnabled(canStartGame); diff --git a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java index 727301a8a0e..3fb49f8f8c1 100644 --- a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java @@ -1618,7 +1618,8 @@ private void finish() { StratconRulesManager.processScenarioCompletion(tracker); - if (reinforcementsSent || tracker.getScenario().getStatus().isVictory()) { + if (reinforcementsSent && tracker.getScenario().getStatus().isVictory() + && tracker.getCampaign().checkLinkedScenario(tracker.getScenario().getId())) { StratconRulesManager.linkedScenerioProcessing(tracker, forces); } From 32241f6202d30df7b7ee2277b2a1be960a6ab585 Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 16:03:54 -0700 Subject: [PATCH 07/11] fix Campaign formatting and variable names, added back victory -> OverAllVictory --- MekHQ/src/mekhq/campaign/Campaign.java | 252 ++++++++---------- .../dialog/ResolveScenarioWizardDialog.java | 2 +- 2 files changed, 111 insertions(+), 143 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 6e49c0adf79..ffa6ed20707 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -243,8 +243,7 @@ public class Campaign implements ITechManager { private Boolean fieldKitchenWithinCapacity; - // this is updated and used per gaming session, it is enabled/disabled via the - // Campaign options + // this is updated and used per gaming session, it is enabled/disabled via the Campaign options // we're re-using the LogEntry class that is used to store Personnel entries public LinkedList inMemoryLogHistory = new LinkedList<>(); @@ -491,8 +490,7 @@ public PlanetarySystem getCurrentSystem() { } /** - * Returns the Hiring Hall level from the force's current system on the current - * date. If there + * Returns the Hiring Hall level from the force's current system on the current date. If there * is no hiring hall present, the level is HiringHallLevel.NONE. * * @return The Hiring Hall level of the current system at the present date. @@ -1657,8 +1655,7 @@ public Unit getUnit(UUID id) { // region Personnel // region Person Creation /** - * Creates a new dependent with given gender. The origin faction and planet are - * set to null. + * Creates a new dependent with given gender. The origin faction and planet are set to null. * * @param gender The {@link Gender} of the new dependent. * @return Return a {@link Person} object representing the new dependent. @@ -1670,24 +1667,20 @@ public Person newDependent(Gender gender) { /** * Creates a new dependent with given gender, origin faction and origin planet. * - * @param gender The {@link Gender} of the new dependent. - * @param originFaction The {@link Faction} that represents the origin faction - * for the new dependent. - * This can be null, suggesting the faction will be chosen - * based on campaign options. - * @param originPlanet The {@link Planet} that represents the origin planet for - * the new dependent. - * This can be null, suggesting the planet will be chosen - * based on campaign options. + * @param gender The {@link Gender} of the new dependent. + * @param originFaction The {@link Faction} that represents the origin faction for the new dependent. + * This can be null, suggesting the faction will be chosen based on campaign options. + * @param originPlanet The {@link Planet} that represents the origin planet for the new dependent. + * This can be null, suggesting the planet will be chosen based on campaign options. * @return Return a {@link Person} object representing the new dependent. */ public Person newDependent(Gender gender, @Nullable Faction originFaction, - @Nullable Planet originPlanet) { + @Nullable Planet originPlanet) { return newPerson(PersonnelRole.DEPENDENT, - PersonnelRole.NONE, - new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), originFaction), - new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions(), originPlanet), - gender); + PersonnelRole.NONE, + new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions(), originFaction), + new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions(), originPlanet), + gender); } /** @@ -1806,8 +1799,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); @@ -1918,8 +1911,7 @@ private void simulateRelationshipHistory(Person person) { if (!person.getGenealogy().hasSpouse()) { List toRemove = new ArrayList<>(); - // there is a chance a departing spouse might take some of their children with - // them + // 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) { @@ -1942,8 +1934,7 @@ private void simulateRelationshipHistory(Person person) { // then we check for children if ((person.getGender().isFemale()) && (!person.isPregnant())) { - getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, - true); + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, true); if (person.isPregnant()) { @@ -1954,8 +1945,7 @@ private void simulateRelationshipHistory(Person person) { } if ((currentSpouse != null) && (currentSpouse.getGender().isFemale()) && (!currentSpouse.isPregnant())) { - getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), - currentSpouse, true); + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), currentSpouse, true); if (currentSpouse.isPregnant()) { @@ -1965,27 +1955,25 @@ private void simulateRelationshipHistory(Person person) { } } - if ((person.isPregnant()) && (currentDate.isAfter(person.getDueDate()))) { + if((person.isPregnant()) && (currentDate.isAfter(person.getDueDate()))) { children.addAll(getProcreation().birthHistoric(this, localDate, person, babysFather)); babysFather = null; } - if ((currentSpouse != null) && (currentSpouse.isPregnant()) - && (currentDate.isAfter(currentSpouse.getDueDate()))) { + if((currentSpouse != null) && (currentSpouse.isPregnant()) && (currentDate.isAfter(currentSpouse.getDueDate()))) { children.addAll(getProcreation().birthHistoric(this, localDate, currentSpouse, spousesBabysFather)); spousesBabysFather = null; } } - // with the simulation concluded, we add the current spouse (if any) and any - // remaining children to the unit + // 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"))); + currentSpouse.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), + resources.getString("relativeJoinsForceSpouse.text"))); MekHQ.triggerEvent(new PersonChangedEvent(currentSpouse)); } @@ -2030,16 +2018,16 @@ private void simulateRelationshipHistory(Person person) { recruitPerson(child, PrisonerStatus.FREE, true, false); addReport(String.format(resources.getString("relativeJoinsForce.text"), - child.getHyperlinkedFullTitle(), - person.getHyperlinkedFullTitle(), - resources.getString("relativeJoinsForceChild.text"))); + child.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), + resources.getString("relativeJoinsForceChild.text"))); MekHQ.triggerEvent(new PersonChangedEvent(child)); } MekHQ.triggerEvent(new PersonChangedEvent(person)); } - // endregion Personnel Recruitment + //endregion Personnel Recruitment // region Bloodnames /** @@ -2050,8 +2038,7 @@ private void simulateRelationshipHistory(Person person) { * appropriate to the person's phenotype and the player's faction. * * @param person The Bloodname candidate - * @param ignoreDice If true, skips the random roll and assigns a Bloodname - * automatically + * @param ignoreDice If true, skips the random roll and assigns a Bloodname automatically */ public void checkBloodnameAdd(Person person, boolean ignoreDice) { // if person is non-clan or does not have a phenotype @@ -2059,8 +2046,7 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) { return; } - // Person already has a bloodname, we open up the dialog to ask if they want to - // keep the + // Person already has a bloodname, we open up the dialog to ask if they want to keep the // current bloodname or assign a new one if (!person.getBloodname().isEmpty()) { int result = JOptionPane.showConfirmDialog(null, @@ -2271,7 +2257,6 @@ public List getActiveCombatPersonnel() { /** * Provides a filtered list of personnel including only active Dependents. - * * @return a {@link Person} List containing all active personnel */ public List getActiveDependents() { @@ -2288,8 +2273,8 @@ public List getActiveDependents() { */ public List getCurrentPrisoners() { return getActivePersonnel().stream() - .filter(person -> person.getPrisonerStatus().isCurrentPrisoner()) - .collect(Collectors.toList()); + .filter(person -> person.getPrisonerStatus().isCurrentPrisoner()) + .collect(Collectors.toList()); } /** @@ -2543,13 +2528,10 @@ private int getDefaultStockPercent(Part part) { /** * Add data from an actual part to a PartInUse data element - * - * @param partInUse part in use record to update - * @param incomingPart new part that needs to be added to this - * record - * @param ignoreMothballedUnits don't count parts in mothballed units - * @param ignoreSparesUnderQuality don't count spare parts lower than this - * quality + * @param partInUse part in use record to update + * @param incomingPart new part that needs to be added to this record + * @param ignoreMothballedUnits don't count parts in mothballed units + * @param ignoreSparesUnderQuality don't count spare parts lower than this quality */ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { @@ -2572,11 +2554,9 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, /** * Find all the parts that match this PartInUse and update their data - * - * @param partInUse part in use record to update - * @param ignoreMothballedUnits don't count parts in mothballed units - * @param ignoreSparesUnderQuality don't count spare parts lower than this - * quality + * @param partInUse part in use record to update + * @param ignoreMothballedUnits don't count parts in mothballed units + * @param ignoreSparesUnderQuality don't count spare parts lower than this quality */ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { @@ -2588,26 +2568,24 @@ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartInUse newPartInUse = getPartInUse(incomingPart); if (partInUse.equals(newPartInUse)) { updatePartInUseData(partInUse, incomingPart, - ignoreMothballedUnits, ignoreSparesUnderQuality); + ignoreMothballedUnits, ignoreSparesUnderQuality); } }); for (IAcquisitionWork maybePart : shoppingList.getPartList()) { PartInUse newPartInUse = getPartInUse((Part) maybePart); if (partInUse.equals(newPartInUse)) { partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() - : (Part) maybePart) * maybePart.getQuantity()); + + getQuantity((maybePart instanceof MissingPart) ? + ((MissingPart) maybePart).getNewPart() : + (Part) maybePart) * maybePart.getQuantity()); } } } /** - * Create a data set detailing all the parts being used (or not) and their - * warehouse spares - * - * @param ignoreMothballedUnits don't count parts in mothballed units - * @param ignoreSparesUnderQuality don't count spare parts lower than this - * quality + * Create a data set detailing all the parts being used (or not) and their warehouse spares + * @param ignoreMothballedUnits don't count parts in mothballed units + * @param ignoreSparesUnderQuality don't count spare parts lower than this quality * @return a Set of PartInUse data for display or inspection */ public Set getPartsInUse(boolean ignoreMothballedUnits, @@ -2651,14 +2629,15 @@ public Set getPartsInUse(boolean ignoreMothballedUnits, inUse.put(partInUse, partInUse); } partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() - : (Part) maybePart) * maybePart.getQuantity()); + + getQuantity((maybePart instanceof MissingPart) ? + ((MissingPart) maybePart).getNewPart() : + (Part) maybePart) * maybePart.getQuantity()); } return inUse.keySet().stream() - // Hacky but otherwise we end up with zero lines when filtering things out - .filter(p -> p.getUseCount() != 0 || p.getStoreCount() != 0 || p.getPlannedCount() != 0) - .collect(Collectors.toSet()); + // Hacky but otherwise we end up with zero lines when filtering things out + .filter(p -> p.getUseCount() != 0 || p.getStoreCount() != 0 || p.getPlannedCount() != 0) + .collect(Collectors.toSet()); } @Deprecated @@ -2776,16 +2755,14 @@ public List getTechs(final boolean noZeroMinute, final boolean eliteFirs * excluded from the list. * @param eliteFirst If TRUE and sorted also TRUE, then return the list sorted * from best to worst - * @param expanded If TRUE, then include techs with expanded roles (e.g. - * Tech/Vessel skill) + * @param expanded If TRUE, then include techs with expanded roles (e.g. Tech/Vessel skill) * @return The list of active {@link Person}s who qualify as technicians - * ({@link Person#isTech()}), or who qualify as expanded technicians - * ({@link Person#isTechExpanded()}). + * ({@link Person#isTech()}), or who qualify as expanded technicians ({@link Person#isTechExpanded()}). */ public List getTechsExpanded(final boolean noZeroMinute, final boolean eliteFirst, final boolean expanded) { final List techs = getActivePersonnel().stream() .filter(person -> (expanded ? person.isTechExpanded() : person.isTech()) - && (!noZeroMinute || (person.getMinutesLeft() > 0))) + && (!noZeroMinute || (person.getMinutesLeft() > 0))) .collect(Collectors.toList()); // also need to loop through and collect engineers on self-crewed vessels @@ -3646,11 +3623,9 @@ public void activate(Unit u) { } public void refit(Refit theRefit) { - Person tech = (theRefit.getUnit().getEngineer() == null) ? theRefit.getTech() - : theRefit.getUnit().getEngineer(); + Person tech = (theRefit.getUnit().getEngineer() == null) ? theRefit.getTech() : theRefit.getUnit().getEngineer(); if (tech == null) { - addReport("No tech is assigned to refit " + theRefit.getOriginalEntity().getShortName() - + ". Refit cancelled."); + addReport("No tech is assigned to refit " + theRefit.getOriginalEntity().getShortName() + ". Refit cancelled."); theRefit.cancel(); return; } @@ -3711,7 +3686,7 @@ public void refit(Refit theRefit) { if (daysLeft == 1) { report += " tomorrow."; } else { - report += " in " + daysLeft + " days."; + report += " in " + daysLeft + " days."; } } } @@ -4207,10 +4182,8 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem * It generates contract offers in the contract market, * updates ship search expiration and results, * processes ship search on Mondays, - * awards training experience to eligible training lances on active contracts on - * Mondays, - * adds or removes dependents at the start of the year if the options are - * enabled, + * awards training experience to eligible training lances on active contracts on Mondays, + * adds or removes dependents at the start of the year if the options are enabled, * rolls for morale at the start of the month, * and processes ATB scenarios. */ @@ -4250,7 +4223,7 @@ private void processNewDayATB() { report = resources.getString("garrisonDutyRouted.text"); } else if (oldMorale != newMorale) { report = String.format(resources.getString("contractMoraleReport.text"), - newMorale, contract.getName(), newMorale.getToolTipText()); + newMorale, contract.getName(), newMorale.getToolTipText()); } if (!report.isBlank()) { @@ -4279,7 +4252,7 @@ private void processNewDayATB() { if (campaignOptions.isUseGenericBattleValue()) { if (contract.getStartDate().equals(getLocalDate())) { if (getCampaignOptions().isUseGenericBattleValue() - && BatchallFactions.usesBatchalls(contract.getEnemyCode())) { + && BatchallFactions.usesBatchalls(contract.getEnemyCode())) { contract.setBatchallAccepted(contract.initiateBatchall(this)); } } @@ -4501,11 +4474,9 @@ private boolean processMonthlyVocationalXp(Person person, int vocationalXpRate) /** * Process weekly relationship events for a given {@link Person} on Monday. - * This method triggers specific events related to divorce, marriage, - * procreation, and maternity leave. + * This method triggers specific events related to divorce, marriage, procreation, and maternity leave. * - * @param person The {@link Person} for which to process weekly relationship - * events + * @param person The {@link Person} for which to process weekly relationship events */ private void processWeeklyRelationshipEvents(Person person) { if (currentDay.getDayOfWeek() == DayOfWeek.MONDAY) { @@ -4516,21 +4487,19 @@ private void processWeeklyRelationshipEvents(Person person) { } /** - * Process anniversaries for a given person, including birthdays and recruitment - * anniversaries. + * Process anniversaries for a given person, including birthdays and recruitment anniversaries. * * @param person The {@link Person} for whom the anniversaries will be processed */ private void processAnniversaries(Person person) { if ((person.getRank().isOfficer()) || (!getCampaignOptions().isAnnounceOfficersOnly())) { if ((person.getBirthday(getGameYear()).isEqual(getLocalDate())) - && (campaignOptions.isAnnounceBirthdays())) { + && (campaignOptions.isAnnounceBirthdays())) { addReport(String.format(resources.getString("anniversaryBirthday.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities - .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - person.getAge(getLocalDate()), - CLOSING_SPAN_TAG)); + person.getHyperlinkedFullTitle(), + ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + person.getAge(getLocalDate()), + CLOSING_SPAN_TAG)); } LocalDate recruitmentDate = person.getRecruitment(); @@ -4539,29 +4508,26 @@ private void processAnniversaries(Person person) { int yearsOfEmployment = (int) ChronoUnit.YEARS.between(recruitmentDate, currentDay); if ((recruitmentAnniversary.isEqual(getLocalDate())) - && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { + && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { addReport(String.format(resources.getString("anniversaryRecruitment.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities - .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - yearsOfEmployment, CLOSING_SPAN_TAG, name)); + person.getHyperlinkedFullTitle(), + ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + yearsOfEmployment, CLOSING_SPAN_TAG, name)); } } } else if ((person.getAge(getLocalDate()) == 18) && (campaignOptions.isAnnounceChildBirthdays())) { if (person.getBirthday(getGameYear()).isEqual(getLocalDate())) { addReport(String.format(resources.getString("anniversaryBirthday.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities - .spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - person.getAge(getLocalDate()), - CLOSING_SPAN_TAG)); + person.getHyperlinkedFullTitle(), + ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + person.getAge(getLocalDate()), + CLOSING_SPAN_TAG)); } } } /** - * Process monthly auto awards for a given person based on their roles and - * experience level. + * Process monthly auto awards for a given person based on their roles and experience level. * * @param person the person for whom the monthly auto awards are being processed */ @@ -4596,8 +4562,7 @@ private void processMonthlyAutoAwards(Person person) { } /** - * Retrieves the date of birth of the youngest child among the provided list of - * children. + * Retrieves the date of birth of the youngest child among the provided list of children. * * @param children the list children * @return the date of birth of the youngest child @@ -4996,8 +4961,7 @@ private void processRandomDependents() { } /** - * Randomly removes dependents from the given list if the campaign options allow - * random + * Randomly removes dependents from the given list if the campaign options allow random * dependent removal. * * @param dependents The list of dependents. @@ -5032,11 +4996,9 @@ int dependentsRollForRemoval(List dependents, int dependentCapacity) { } /** - * @return The lower integer value between the given input and the randomly - * generated integer. + * @return The lower integer value between the given input and the randomly generated integer. * - * @param firstRoll The input integer to compare with the randomly generated - * integer. + * @param firstRoll The input integer to compare with the randomly generated integer. */ private int getLowerRandomInt(int firstRoll) { int secondRoll = Compute.randomInt(100); @@ -5046,10 +5008,9 @@ private int getLowerRandomInt(int firstRoll) { /** * Checks if a dependent is eligible for removal. * - * @param dependent the person to check + * @param dependent the person to check * @param currentDate the current date - * @return {@code true} if the person is eligible for removal, {@code false} - * otherwise + * @return {@code true} if the person is eligible for removal, {@code false} otherwise */ boolean isRemovalEligible(Person dependent, LocalDate currentDate) { boolean hasNonAdultChildren = dependent.getGenealogy().hasNonAdultChildren(currentDate); @@ -5062,7 +5023,7 @@ boolean isRemovalEligible(Person dependent, LocalDate currentDate) { /** * Randomly adds new dependents to the campaign. * - * @param dependentCount the current number of dependents + * @param dependentCount the current number of dependents * @param dependentCapacity the maximum capacity for dependents */ void dependentsAddNew(int dependentCount, int dependentCapacity) { @@ -6092,8 +6053,7 @@ public void writeToXML(final PrintWriter pw) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchType", shipSearchType); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchResult", shipSearchResult); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchExpiration", getShipSearchExpiration()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveBehaviorSettings", - autoResolveBehaviorSettings.getDescription()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveBehaviorSettings", autoResolveBehaviorSettings.getDescription()); } retirementDefectionTracker.writeToXML(pw, indent); @@ -7567,13 +7527,12 @@ public int getAtBUnitRatingMod() { /** * Returns the Strategy skill of the designated commander in the campaign. * - * @return The value of the commander's strategy skill if a commander exists, - * otherwise 0. + * @return The value of the commander's strategy skill if a commander exists, otherwise 0. */ public int getCommanderStrategy() { int cmdrStrategy = 0; if (getFlaggedCommander() != null && - getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) { + getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) { cmdrStrategy = getFlaggedCommander().getSkill(SkillType.S_STRATEGY).getLevel(); } return cmdrStrategy; @@ -8518,7 +8477,7 @@ public void doMaintenance(Unit u) { qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( MekHQ.getMHQOptions().getFontColorPositiveHexColor(), "Overall quality improves from " + qualityOrig.toName(reverse) - + " to " + quality.toName(reverse)); + + " to " + quality.toName(reverse)); } else if (quality.toNumeric() < qualityOrig.toNumeric()) { qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( MekHQ.getMHQOptions().getFontColorNegativeHexColor(), @@ -8706,8 +8665,7 @@ public void initTimeInRank() { LocalDate join = null; for (LogEntry e : p.getPersonnelLog()) { if (join == null) { - // If by some nightmare there is no date from the below, just use the first - // entry. + // If by some nightmare there is no date from the below, just use the first entry. join = e.getDate(); } @@ -8717,8 +8675,7 @@ public void initTimeInRank() { } } - // For that one in a billion chance the log is empty. Clone today's date and - // subtract a year + // For that one in a billion chance the log is empty. Clone today's date and subtract a year p.setLastRankChangeDate((join != null) ? join : getLocalDate().minusYears(1)); } } @@ -8830,8 +8787,7 @@ public boolean checkOverDueLoans() { } /** - * Checks if a turnover prompt should be displayed based on campaign options and - * current date. + * Checks if a turnover prompt should be displayed based on campaign options and current date. * * @return An integer representing the user's choice: * -1 if turnover prompt should not be displayed. @@ -8893,7 +8849,8 @@ public int checkTurnoverPrompt() { JOptionPane.INFORMATION_MESSAGE, null, options, - options[0]); + options[0] + ); } /** @@ -9162,6 +9119,14 @@ public void writePartInUseMapToXML(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "partInUseMapEntry"); } } + + /** + * Wipes the Parts in use map for the purpose of resetting all values to their default + */ + public void wipePartsInUseMap() { + this.partsInUseRequestedStockMap.clear(); + } + /** * Retrieves the campaign faction icon for the specified {@link Campaign}. * If a custom icon is defined in the campaign's unit icon configuration, that icon is used. @@ -9181,10 +9146,13 @@ public ImageIcon getCampaignFactionIcon() { } return icon; } - - public boolean checkLinkedScenario(int sID){ - for(Scenario s : getScenarios()){ - if(s.getId() == sID){ + + /** + * Checks if another active scenario has this scenarioID as it's linkedScenarioID and returns true if it finds one. + */ + public boolean checkLinkedScenario(int scenarioID) { + for (Scenario scenario : getScenarios()) { + if (scenario.getId() == scenarioID && getScenario(scenario.getId())) { return true; } } diff --git a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java index 3fb49f8f8c1..8486dfd2e5d 100644 --- a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java @@ -1618,7 +1618,7 @@ private void finish() { StratconRulesManager.processScenarioCompletion(tracker); - if (reinforcementsSent && tracker.getScenario().getStatus().isVictory() + if (reinforcementsSent && tracker.getScenario().getStatus().isOverallVictory() && tracker.getCampaign().checkLinkedScenario(tracker.getScenario().getId())) { StratconRulesManager.linkedScenerioProcessing(tracker, forces); } From 3d1cc684b013ca8052449ab40b4365020a4c9213 Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 16:51:01 -0700 Subject: [PATCH 08/11] formatting and checkLinkedScenario fixes --- MekHQ/src/mekhq/campaign/Campaign.java | 3 +- .../stratcon/StratconRulesManager.java | 953 ++++++++++-------- MekHQ/src/mekhq/gui/BriefingTab.java | 2 +- .../dialog/ResolveScenarioWizardDialog.java | 6 +- 4 files changed, 555 insertions(+), 409 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index ffa6ed20707..e06e9ed2962 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -9152,7 +9152,8 @@ public ImageIcon getCampaignFactionIcon() { */ public boolean checkLinkedScenario(int scenarioID) { for (Scenario scenario : getScenarios()) { - if (scenario.getId() == scenarioID && getScenario(scenario.getId())) { + if ((scenario.getLinkedScenario() == scenarioID) + && (getScenario(scenario.getId()).getStatus().isCurrent())) { return true; } } diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 0cd25e900c2..479471e688c 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -154,24 +154,21 @@ public enum ReinforcementResultsType { * The method first determines the number of turning point scenario rolls based on the required * lance count from the track, then multiplies that count depending on the contract's morale level. *

    - * If auto-assign for lances is enabled, and either there are no available - * forces or the number of - * weekly scenarios equals or exceeds the number of available forces, it breaks - * from the scenario + * If auto-assign for lances is enabled, and either there are no available forces or the number of + * weekly scenarios equals or exceeds the number of available forces, it breaks from the scenario * generation loop. *

    - * For each scenario, a scenario odds target number is calculated, and a roll is - * made against - * this target. If the roll is less than the target number, a new weekly - * scenario is created + * For each scenario, a scenario odds target number is calculated, and a roll is made against + * this target. If the roll is less than the target number, a new weekly scenario is created * with a random date within the week. * - * @param campaign The campaign. + * @param campaign The campaign. * @param campaignState The state of the StratCon campaign. - * @param contract The AtBContract for the campaign. - * @param track The StratCon campaign track. + * @param contract The AtBContract for the campaign. + * @param track The StratCon campaign track. */ - public static void generateScenariosDatesForWeek(Campaign campaign, StratconCampaignState campaignState, AtBContract contract, StratconTrackState track) { + public static void generateScenariosDatesForWeek(Campaign campaign, StratconCampaignState campaignState, + AtBContract contract, StratconTrackState track) { // maps scenarios to force IDs final boolean autoAssignLances = contract.getCommandRights().isIntegrated(); List availableForceIDs = getAvailableForceIDs(campaign, contract); @@ -184,8 +181,7 @@ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCamp case ADVANCING -> scenarioRolls = (int) round(scenarioRolls * 1.33); case DOMINATING -> scenarioRolls = (int) round(scenarioRolls * 1.66); case OVERWHELMING -> scenarioRolls = scenarioRolls * 2; - default -> { - } + default -> {} } for (int scenarioIndex = 0; scenarioIndex < scenarioRolls; scenarioIndex++) { @@ -213,38 +209,32 @@ public static void generateScenariosDatesForWeek(Campaign campaign, StratconCamp /** * This method generates a weekly scenario for a specific track. *

    - * First, it initializes empty collections for generated scenarios and available - * forces, and + * First, it initializes empty collections for generated scenarios and available forces, and * determines whether lances are auto-assigned. *

    - * Then it generates a requested number of scenarios. If auto-assign is enabled - * and there + * Then it generates a requested number of scenarios. If auto-assign is enabled and there * are no available forces, it breaks from the scenario generation loop. *

    - * For each scenario, it first tries to create a scenario for existing forces on - * the track. - * If that is not possible, it selects random force, removes it from available - * forces, and - * creates a scenario for it. For any scenario, if it is under liaison command, - * it may set the + * For each scenario, it first tries to create a scenario for existing forces on the track. + * If that is not possible, it selects random force, removes it from available forces, and + * creates a scenario for it. For any scenario, if it is under liaison command, it may set the * scenario as required and attaches the liaison. *

    - * After scenarios are generated, OpFors, events, etc. are finalized for each - * scenario. + * After scenarios are generated, OpFors, events, etc. are finalized for each scenario. * * @param campaign The current campaign. * @param campaignState The relevant StratCon campaign state. * @param contract The relevant contract. * @param scenarioCount The number of scenarios to generate. */ - public static void generateDailyScenariosForTrack(Campaign campaign, StratconCampaignState campaignState, AtBContract contract, int scenarioCount) { + public static void generateDailyScenariosForTrack(Campaign campaign, StratconCampaignState campaignState, + AtBContract contract, int scenarioCount) { final boolean autoAssignLances = contract.getCommandRights().isIntegrated(); // get this list just so we have it available List availableForceIDs = getAvailableForceIDs(campaign, contract); - // Build the available force pool - this ensures operational forces have an - // increased + // Build the available force pool - this ensures operational forces have an increased // chance of being picked if (autoAssignLances && !availableForceIDs.isEmpty()) { List availableForcePool = new ArrayList<>(); @@ -309,19 +299,18 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam continue; } - // if forces are already assigned to these coordinates, use those instead of - // randomly + // if forces are already assigned to these coordinates, use those instead of randomly // selected ones StratconScenario scenario; if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { - scenario = generateScenarioForExistingForces(scenarioCoords, track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track); - // otherwise, pick a random force from the avail + scenario = generateScenarioForExistingForces(scenarioCoords, + track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track); + // otherwise, pick a random force from the avail } else { int randomForceIndex = randomInt(availableForceIDs.size()); int randomForceID = availableForceIDs.get(randomForceIndex); - // remove the force from the available lists, so we don't designate it as - // primary + // remove the force from the available lists, so we don't designate it as primary // twice if (autoAssignLances) { availableForceIDs.removeIf(id -> id.equals(randomForceIndex)); @@ -335,7 +324,8 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam // two scenarios on the same coordinates wind up increasing in size if (track.getScenarios().containsKey(scenarioCoords)) { track.getScenarios().get(scenarioCoords).incrementRequiredPlayerLances(); - assignAppropriateExtraForceToScenario(track.getScenarios().get(scenarioCoords), sortedAvailableForceIDs); + assignAppropriateExtraForceToScenario(track.getScenarios().get(scenarioCoords), + sortedAvailableForceIDs); continue; } @@ -350,182 +340,186 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam /** * Generates a StratCon scenario. - * This is a utility method that allows us to generate a scenario quickly - * without specifying + * This is a utility method that allows us to generate a scenario quickly without specifying * track state and scenario template. * * @param campaign The current campaign. * @param contract The contract associated with the scenario. - * @return A newly generated {@link StratconScenario}, or {@code null} if - * scenario creation fails. + * @return A newly generated {@link StratconScenario}, or {@code null} if scenario creation fails. */ public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract) { - return generateExternalScenario(campaign, contract, null, null, null, false, null); + return generateExternalScenario(campaign, contract, null, null, + null, false, null); } /** * Generates a new StratCon scenario using advanced configuration. * It provides a scenario based on a given campaign, contract, track, template. - * This is meant for scenario control on a higher level than the overloading - * methods. + * This is meant for scenario control on a higher level than the overloading methods. * - * @param campaign The current campaign. - * @param contract The contract associated with the scenario. - * @param track The {@link StratconTrackState} the scenario should be assigned to, or - * {@code null} to select a random track. - * @param scenarioCoords The {@link StratconCoords} where in the track to place the scenario, or - * {@code null} to select a random hex. If populated, {@code track} cannot be - * {@code null} - * @param template A specific {@link ScenarioTemplate} to use for scenario generation, - * or {@code null} to select scenario template randomly. + * @param campaign The current campaign. + * @param contract The contract associated with the scenario. + * @param track The {@link StratconTrackState} the scenario should be assigned to, or + * {@code null} to select a random track. + * @param scenarioCoords The {@link StratconCoords} where in the track to place the scenario, or + * {@code null} to select a random hex. If populated, {@code track} cannot be + * {@code null} + * @param template A specific {@link ScenarioTemplate} to use for scenario generation, + * or {@code null} to select scenario template randomly. * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top of - * player-allied facilities. - * @param daysTilDeployment How many days beyond current date until the scenario, or {@code null} - * to pick a random date within the next 7 days. + * player-allied facilities. + * @param daysTilDeployment How many days beyond current date until the scenario, or {@code null} + * to pick a random date within the next 7 days. * @return A newly generated {@link StratconScenario}, or {@code null} if scenario creation fails. */ - public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract, @Nullable StratconTrackState track, @Nullable StratconCoords scenarioCoords, @Nullable ScenarioTemplate template, boolean allowPlayerFacilities, @Nullable Integer daysTilDeployment) { - // If we're not generating for a specific track, randomly pick one. - if (track == null) { - track = getRandomTrack(contract); - - if (track == null) { - logger.error("Failed to generate a random track, aborting scenario generation."); - return null; - } - } - - // Are we automatically assigning lances? - boolean autoAssignLances = contract.getCommandRights().isIntegrated(); - - // Grab the available lances and sort them by map type - List availableForceIDs = getAvailableForceIDs(campaign, contract); - Map> sortedAvailableForceIDs = sortForcesByMapType(availableForceIDs, campaign); - - // Select the target coords. - if (scenarioCoords == null) { - scenarioCoords = getUnoccupiedCoords(track, allowPlayerFacilities); - } - - if (scenarioCoords == null) { - logger.warn("Target track is full, aborting scenario generation."); - return null; - } - - // If forces are already assigned to the target coordinates, use those instead of randomly - // selected a new force - StratconScenario scenario = null; - if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { - scenario = generateScenarioForExistingForces(scenarioCoords, track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track, template, daysTilDeployment); - } - - // Otherwise, pick a random force from those available - // If a template has been specified, remove forces that aren't appropriate for - // the - // template. - if (template != null) { - MapLocation location = template.mapParameters.getMapLocation(); - - switch (location) { - case AllGroundTerrain, SpecificGroundTerrain -> { - sortedAvailableForceIDs.get(LowAtmosphere).clear(); - sortedAvailableForceIDs.get(Space).clear(); - } - case LowAtmosphere -> { - sortedAvailableForceIDs.get(AllGroundTerrain).clear(); - sortedAvailableForceIDs.get(Space).clear(); - } - case Space -> { - sortedAvailableForceIDs.get(AllGroundTerrain).clear(); - sortedAvailableForceIDs.get(LowAtmosphere).clear(); - } - } - } - - // If we haven't generated a scenario yet, it's because we need to pick a random - // force. - if (scenario == null) { - int availableForces = availableForceIDs.size(); - int randomForceID = FORCE_NONE; - - if (availableForces > 0) { - int randomForceIndex = randomInt(availableForces); - randomForceID = availableForceIDs.get(randomForceIndex); - } - - scenario = setupScenario(scenarioCoords, randomForceID, campaign, contract, track, template, false, daysTilDeployment); - } - - if (scenario == null) { - return null; - } - - // We end by finalizing the scenario - finalizeBackingScenario(campaign, contract, track, autoAssignLances, scenario); - - // We return the scenario in case we want to make specific changes. - return scenario; - } + public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract, + @Nullable StratconTrackState track, + @Nullable StratconCoords scenarioCoords, + @Nullable ScenarioTemplate template, + boolean allowPlayerFacilities, + @Nullable Integer daysTilDeployment) { + // If we're not generating for a specific track, randomly pick one. + if (track == null) { + track = getRandomTrack(contract); + + if (track == null) { + logger.error("Failed to generate a random track, aborting scenario generation."); + return null; + } + } + + // Are we automatically assigning lances? + boolean autoAssignLances = contract.getCommandRights().isIntegrated(); + + // Grab the available lances and sort them by map type + List availableForceIDs = getAvailableForceIDs(campaign, contract); + Map> sortedAvailableForceIDs = sortForcesByMapType(availableForceIDs, campaign); + + // Select the target coords. + if (scenarioCoords == null) { + scenarioCoords = getUnoccupiedCoords(track, allowPlayerFacilities); + } + + if (scenarioCoords == null) { + logger.warn("Target track is full, aborting scenario generation."); + return null; + } + + // If forces are already assigned to the target coordinates, use those instead of randomly + // selected a new force + StratconScenario scenario = null; + if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { + scenario = generateScenarioForExistingForces(scenarioCoords, + track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track, + template, daysTilDeployment); + } + + // Otherwise, pick a random force from those available + // If a template has been specified, remove forces that aren't appropriate for the + // template. + if (template != null) { + MapLocation location = template.mapParameters.getMapLocation(); + + switch (location) { + case AllGroundTerrain, SpecificGroundTerrain -> { + sortedAvailableForceIDs.get(LowAtmosphere).clear(); + sortedAvailableForceIDs.get(Space).clear(); + } + case LowAtmosphere -> { + sortedAvailableForceIDs.get(AllGroundTerrain).clear(); + sortedAvailableForceIDs.get(Space).clear(); + } + case Space -> { + sortedAvailableForceIDs.get(AllGroundTerrain).clear(); + sortedAvailableForceIDs.get(LowAtmosphere).clear(); + } + } + } + + // If we haven't generated a scenario yet, it's because we need to pick a random force. + if (scenario == null) { + int availableForces = availableForceIDs.size(); + int randomForceID = FORCE_NONE; + + if (availableForces > 0) { + int randomForceIndex = randomInt(availableForces); + randomForceID = availableForceIDs.get(randomForceIndex); + } + + scenario = setupScenario(scenarioCoords, randomForceID, campaign, contract, track, + template, false, daysTilDeployment); + } + + if (scenario == null) { + return null; + } + + // We end by finalizing the scenario + finalizeBackingScenario(campaign, contract, track, autoAssignLances, scenario); + + // We return the scenario in case we want to make specific changes. + return scenario; + } /** * Generates a reinforcement interception scenario for a given StratCon track. * An interception scenario is set up at unoccupied coordinates on the track. - * If the scenario setup is successful, it is finalized and the deployment date - * for the + * If the scenario setup is successful, it is finalized and the deployment date for the * scenario is set as the current date. * - * @param campaign the current campaign - * @param contract the {@link AtBContract} for which the scenario is - * created - * @param track the {@link StratconTrackState} where the scenario is - * located, or {@code null} - * if not located on a track - * @param template the {@link ScenarioTemplate} used to create the - * scenario - * @param interceptedForce the {@link Force} that's being intercepted in the - * scenario + * @param campaign the current campaign + * @param contract the {@link AtBContract} for which the scenario is created + * @param track the {@link StratconTrackState} where the scenario is located, or {@code null} + * if not located on a track + * @param template the {@link ScenarioTemplate} used to create the scenario + * @param interceptedForce the {@link Force} that's being intercepted in the scenario */ - public static @Nullable void generateReinforcementInterceptionScenario(Campaign campaign, StratconScenario linkedScenario, AtBContract contract, StratconTrackState track, ScenarioTemplate template, Force interceptedForce) { - StratconCoords scenarioCoords = getUnoccupiedCoords(track, false); + public static @Nullable void generateReinforcementInterceptionScenario( + Campaign campaign, StratconScenario linkedScenario, AtBContract contract, + StratconTrackState track, ScenarioTemplate template, Force interceptedForce) { + StratconCoords scenarioCoords = getUnoccupiedCoords(track, false); - StratconScenario scenario = setupScenario(scenarioCoords, interceptedForce.getId(), campaign, contract, track, template, true, 0); + StratconScenario scenario = setupScenario(scenarioCoords, interceptedForce.getId(), campaign, + contract, track, template, true, 0); - if (scenario == null) { - logger.error("Failed to generate a random interception scenario, aborting scenario generation."); - return; - } + if (scenario == null) { + logger.error("Failed to generate a random interception scenario, aborting scenario generation."); + return; + } - finalizeBackingScenario(campaign, contract, track, true, scenario); - scenario.setActionDate(campaign.getLocalDate()); - scenario.getBackingScenario().setStatus(ScenarioStatus.CURRENT); - scenario.getBackingScenario().setlinkedScenarioID(linkedScenario.getBackingScenario().getId()); - } + finalizeBackingScenario(campaign, contract, track, true, scenario); + scenario.setActionDate(campaign.getLocalDate()); + scenario.getBackingScenario().setStatus(ScenarioStatus.CURRENT); + scenario.getBackingScenario().setlinkedScenarioID(linkedScenario.getBackingScenario().getId()); + } /** - * Adds a {@link StratconScenario} to the specified contract. This scenario is - * cloaked so will + * Adds a {@link StratconScenario} to the specified contract. This scenario is cloaked so will * not be visible until the player uncovers it. - * If no {@link StratconTrackState} or {@link ScenarioTemplate} is provided, - * random one will be + * If no {@link StratconTrackState} or {@link ScenarioTemplate} is provided, random one will be * picked. * - * @param campaign The current campaign. - * @param contract The {@link AtBContract} associated with the scenario. - * @param trackState The {@link StratconTrackState} in which the scenario occurs. - * If {@code null}, a random trackState is selected. - * @param template The {@link ScenarioTemplate} for the scenario. - * If {@code null}, the default template is used. + * @param campaign The current campaign. + * @param contract The {@link AtBContract} associated with the scenario. + * @param trackState The {@link StratconTrackState} in which the scenario occurs. + * If {@code null}, a random trackState is selected. + * @param template The {@link ScenarioTemplate} for the scenario. + * If {@code null}, the default template is used. * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top of - * player-allied facilities. - * @param daysTilDeployment How many days until the scenario takes place, or {@code null} to - * pick a random day within the next 7 days. + * player-allied facilities. + * @param daysTilDeployment How many days until the scenario takes place, or {@code null} to + * pick a random day within the next 7 days. + * * @return The created {@link StratconScenario} or @code null}, - * if no {@link ScenarioTemplate} is found or if all coordinates in the - * provided - * {@link StratconTrackState} are occupied (and therefore, scenario - * placement is not possible). + * if no {@link ScenarioTemplate} is found or if all coordinates in the provided + * {@link StratconTrackState} are occupied (and therefore, scenario placement is not possible). */ - public static @Nullable StratconScenario addHiddenExternalScenario(Campaign campaign, AtBContract contract, @Nullable StratconTrackState trackState, @Nullable ScenarioTemplate template, boolean allowPlayerFacilities, @Nullable Integer daysTilDeployment) { + public static @Nullable StratconScenario addHiddenExternalScenario(Campaign campaign, + AtBContract contract, + @Nullable StratconTrackState trackState, + @Nullable ScenarioTemplate template, + boolean allowPlayerFacilities, + @Nullable Integer daysTilDeployment) { // If we're not generating for a specific track, randomly pick one. if (trackState == null) { trackState = getRandomTrack(contract); @@ -539,12 +533,15 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam StratconCoords coords = getUnoccupiedCoords(trackState, allowPlayerFacilities); if (coords == null) { - logger.error(String.format("Unable to place objective scenario on track %s," + " as all coords were occupied. Aborting.", trackState.getDisplayableName())); + logger.error(String.format("Unable to place objective scenario on track %s," + + " as all coords were occupied. Aborting.", + trackState.getDisplayableName())); return null; } // create scenario - don't assign a force yet - StratconScenario scenario = StratconRulesManager.generateScenario(campaign, contract, trackState, FORCE_NONE, coords, template, daysTilDeployment); + StratconScenario scenario = StratconRulesManager.generateScenario(campaign, contract, + trackState, FORCE_NONE, coords, template, daysTilDeployment); if (scenario == null) { return null; @@ -564,41 +561,37 @@ public static void generateDailyScenariosForTrack(Campaign campaign, StratconCam } /** - * Fetches a random {@link StratconTrackState} from the - * {@link StratconCampaignState}. + * Fetches a random {@link StratconTrackState} from the {@link StratconCampaignState}. * If no tracks are present, it logs an error message and returns {@code null}. * - * @param contract The {@link AtBContract} from which the track state will be - * fetched. - * @return The randomly chosen {@link StratconTrackState}, or {@code null} if no - * tracks are available. + * @param contract The {@link AtBContract} from which the track state will be fetched. + * @return The randomly chosen {@link StratconTrackState}, or {@code null} if no tracks are available. */ - public static @Nullable StratconTrackState getRandomTrack(AtBContract contract) { - List tracks = contract.getStratconCampaignState().getTracks(); - Random rand = new Random(); - - if (!tracks.isEmpty()) { - return tracks.get(rand.nextInt(tracks.size())); - } else { - logger.error("No tracks available. Unable to fetch random track"); - return null; - } - } + public static @Nullable StratconTrackState getRandomTrack(AtBContract contract) { + List tracks = contract.getStratconCampaignState().getTracks(); + Random rand = new Random(); + + if (!tracks.isEmpty()) { + return tracks.get(rand.nextInt(tracks.size())); + } else { + logger.error("No tracks available. Unable to fetch random track"); + return null; + } + } /** - * Finalizes the backing scenario, setting up the OpFor, scenario parameters, - * and other + * Finalizes the backing scenario, setting up the OpFor, scenario parameters, and other * necessary steps. * - * @param campaign The current campaign. - * @param contract The contract associated with the scenario. - * @param track The relevant {@link StratconTrackState}. - * @param autoAssignLances Flag indicating whether lances are to be - * auto-assigned. - * @param scenario The {@link StratconScenario} scenario to be - * finalized. + * @param campaign The current campaign. + * @param contract The contract associated with the scenario. + * @param track The relevant {@link StratconTrackState}. + * @param autoAssignLances Flag indicating whether lances are to be auto-assigned. + * @param scenario The {@link StratconScenario} scenario to be finalized. */ - private static void finalizeBackingScenario(Campaign campaign, AtBContract contract, @Nullable StratconTrackState track, boolean autoAssignLances, StratconScenario scenario) { + private static void finalizeBackingScenario(Campaign campaign, AtBContract contract, + @Nullable StratconTrackState track, boolean autoAssignLances, + StratconScenario scenario) { final AtBDynamicScenario backingScenario = scenario.getBackingScenario(); // First determine if the scenario is a Turning Point (that win/lose will affect CVP) @@ -623,8 +616,7 @@ private static void finalizeBackingScenario(Campaign campaign, AtBContract contr track.addScenario(scenario); } else { commitPrimaryForces(campaign, scenario, track); - // if we're auto-assigning lances, deploy all assigned forces to the track as - // well + // if we're auto-assigning lances, deploy all assigned forces to the track as well for (int forceID : scenario.getPrimaryForceIDs()) { processForceDeployment(scenario.getCoords(), forceID, campaign, track, false); } @@ -657,9 +649,11 @@ private static void addCadreDutyTrainees(AtBDynamicScenario backingScenario) { boolean isAirBattle = (mapLocation == LowAtmosphere) || (mapLocation == Space); if (isAirBattle) { - backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_AIR)); + backingScenario.addScenarioModifier( + AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_AIR)); } else { - backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_GROUND)); + backingScenario.addScenarioModifier( + AtBScenarioModifier.getScenarioModifier(MHQConstants.SCENARIO_MODIFIER_TRAINEES_GROUND)); } } @@ -733,7 +727,8 @@ public static void setScenarioParametersFromBiome(StratconTrackState track, Stra StratconBiomeManifest biomeManifest = StratconBiomeManifest.getInstance(); // for non-surface scenarios, we will skip the temperature update - if (backingScenario.getBoardType() != Scenario.T_SPACE && backingScenario.getBoardType() != Scenario.T_ATMOSPHERE) { + if (backingScenario.getBoardType() != Scenario.T_SPACE && + backingScenario.getBoardType() != Scenario.T_ATMOSPHERE) { backingScenario.setTemperature(track.getTemperature()); } @@ -748,11 +743,13 @@ public static void setScenarioParametersFromBiome(StratconTrackState track, Stra // if facility doesn't have a biome temp map or no entry for the current // temperature, use the default one if (facility.getBiomes().isEmpty() || (facility.getBiomeTempMap().floorEntry(kelvinTemp) == null)) { - facilityBiome = biomeManifest.getTempMap(StratconBiomeManifest.TERRAN_FACILITY_BIOME).floorEntry(kelvinTemp).getValue(); + facilityBiome = biomeManifest.getTempMap(StratconBiomeManifest.TERRAN_FACILITY_BIOME) + .floorEntry(kelvinTemp).getValue(); } else { facilityBiome = facility.getBiomeTempMap().floorEntry(kelvinTemp).getValue(); } - terrainType = facilityBiome.allowedTerrainTypes.get(randomInt(facilityBiome.allowedTerrainTypes.size())); + terrainType = facilityBiome.allowedTerrainTypes + .get(randomInt(facilityBiome.allowedTerrainTypes.size())); } else { terrainType = track.getTerrainTile(coords); } @@ -788,12 +785,17 @@ public static void setScenarioParametersFromBiome(StratconTrackState track, Stra private static void swapInPlayerUnits(StratconScenario scenario, Campaign campaign, int explicitForceID) { for (ScenarioForceTemplate sft : scenario.getScenarioTemplate().getAllScenarioForces()) { if (sft.getGenerationMethod() == ForceGenerationMethod.PlayerOrFixedUnitCount.ordinal()) { - int unitCount = (int) scenario.getBackingScenario().getBotUnitTemplates().values().stream().filter(template -> template.getForceName().equals(sft.getForceName())).count(); + int unitCount = (int) scenario.getBackingScenario().getBotUnitTemplates().values().stream() + .filter(template -> template.getForceName().equals(sft.getForceName())) + .count(); // get all the units that have been generated for this template // or the units embedded in bot forces - unitCount += scenario.getBackingScenario().getBotForceTemplates().entrySet().stream().filter(tuple -> tuple.getValue().getForceName().equals(sft.getForceName())).mapToInt(tuple -> tuple.getKey().getFullEntityList(campaign).size()).sum(); + unitCount += scenario.getBackingScenario().getBotForceTemplates().entrySet().stream() + .filter(tuple -> tuple.getValue().getForceName().equals(sft.getForceName())) + .mapToInt(tuple -> tuple.getKey().getFullEntityList(campaign).size()) + .sum(); // now we have a unit count. Don't bother with the next step if we don't have // any substitutions to make @@ -803,17 +805,17 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai Collection potentialUnits = new HashSet<>(); - // find units in player's campaign by default, all units in the TO&E are - // eligible + // find units in player's campaign by default, all units in the TO&E are eligible if (explicitForceID == FORCE_NONE) { for (UUID unitId : campaign.getForces().getUnits()) { try { potentialUnits.add(campaign.getUnit(unitId)); } catch (Exception exception) { - logger.error(String.format("Error retrieving unit (%s): %s", unitId, exception.getMessage())); + logger.error(String.format("Error retrieving unit (%s): %s", + unitId, exception.getMessage())); } } - // if we're using a seed force, then units transporting this force are eligible + // if we're using a seed force, then units transporting this force are eligible } else { Force force = campaign.getForce(explicitForceID); for (UUID unitID : force.getUnits()) { @@ -829,12 +831,15 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai } // if it's the right type of unit and is around - if (forceCompositionMatchesDeclaredUnitType(unit.getEntity().getUnitType(), sft.getAllowedUnitType()) && unit.isAvailable() && unit.isFunctional()) { + if (forceCompositionMatchesDeclaredUnitType(unit.getEntity().getUnitType(), + sft.getAllowedUnitType()) && + unit.isAvailable() && unit.isFunctional()) { // add the unit to the scenario and bench the appropriate bot unit if one is // present scenario.addUnit(unit, sft.getForceName(), false); - AtBDynamicScenarioFactory.benchAllyUnit(unit.getId(), sft.getForceName(), scenario.getBackingScenario()); + AtBDynamicScenarioFactory.benchAllyUnit(unit.getId(), sft.getForceName(), + scenario.getBackingScenario()); unitCount--; // once we've supplied enough units, end the process @@ -848,52 +853,54 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai } /** - * Generates a StratCon scenario for forces already existing at the given - * coordinates on the + * Generates a StratCon scenario for forces already existing at the given coordinates on the * provided track. * - * @param scenarioCoords The coordinates where the scenario will be placed on - * the track. - * @param forceIDs The set of force IDs (ideally for the forces already at - * the - * specified location). - * @param contract The contract associated with the current scenario. - * @param campaign The current campaign. - * @param track The relevant StratCon track. + * @param scenarioCoords The coordinates where the scenario will be placed on the track. + * @param forceIDs The set of force IDs (ideally for the forces already at the + * specified location). + * @param contract The contract associated with the current scenario. + * @param campaign The current campaign. + * @param track The relevant StratCon track. * @return The newly generated {@link StratconScenario}. */ - public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, Set forceIDs, AtBContract contract, Campaign campaign, StratconTrackState track) { - return generateScenarioForExistingForces(scenarioCoords, forceIDs, contract, campaign, track, null, null); + public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, + Set forceIDs, AtBContract contract, Campaign campaign, + StratconTrackState track) { + return generateScenarioForExistingForces(scenarioCoords, forceIDs, contract, campaign, + track, null, null); } /** - * Generates a StratCon scenario for forces already existing at the given - * coordinates on the - * provided track. This method allows us to specify a specific scenario - * template. + * Generates a StratCon scenario for forces already existing at the given coordinates on the + * provided track. This method allows us to specify a specific scenario template. * - * @param scenarioCoords The coordinates where the scenario will be placed on - * the track. - * @param forceIDs The set of force IDs (ideally for the forces already - * at the + * @param scenarioCoords The coordinates where the scenario will be placed on the track. + * @param forceIDs The set of force IDs (ideally for the forces already at the * specified location). * @param contract The contract associated with the current scenario. * @param campaign The current campaign. * @param track The relevant StratCon track. - * @param template A specific {@link ScenarioTemplate} to use, or - * {@code null} to + * @param template A specific {@link ScenarioTemplate} to use, or {@code null} to * select a random template. * @param daysTilDeployment How many days until the scenario takes place, or {@code null} to - * pick a random day within the next 7 days. + * pick a random day within the next 7 days. * @return The newly generated {@link StratconScenario}. */ - public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, Set forceIDs, AtBContract contract, Campaign campaign, StratconTrackState track, @Nullable ScenarioTemplate template, @Nullable Integer daysTilDeployment) { + public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, + Set forceIDs, + AtBContract contract, + Campaign campaign, + StratconTrackState track, + @Nullable ScenarioTemplate template, + @Nullable Integer daysTilDeployment) { boolean firstForce = true; StratconScenario scenario = null; for (int forceID : forceIDs) { if (firstForce) { - scenario = setupScenario(scenarioCoords, forceID, campaign, contract, track, template, false, daysTilDeployment); + scenario = setupScenario(scenarioCoords, forceID, campaign, contract, track, + template, false, daysTilDeployment); firstForce = false; if (scenario == null) { @@ -954,7 +961,8 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai * the forces remain at the deployment location without automatically updating * their position. */ - public static void deployForceToCoords(StratconCoords coords, int forceID, Campaign campaign, AtBContract contract, StratconTrackState track, boolean sticky) { + public static void deployForceToCoords(StratconCoords coords, int forceID, Campaign campaign, + AtBContract contract, StratconTrackState track, boolean sticky) { CombatTeam combatTeam = campaign.getCombatTeamsTable().get(forceID); // This shouldn't be possible, but never hurts to have a little insurance @@ -1011,8 +1019,9 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa Set preDeployedForce = track.getAssignedCoordForces().get(coords); if (preDeployedForce != null && !preDeployedForce.isEmpty()) { - scenario = generateScenarioForExistingForces(coords, track.getAssignedCoordForces().get(coords), contract, campaign, track); - // Otherwise, pick a random force from those available + scenario = generateScenarioForExistingForces(coords, + track.getAssignedCoordForces().get(coords), contract, campaign, track); + // Otherwise, pick a random force from those available } else { List availableForceIDs = getAvailableForceIDs(campaign, contract); Collections.shuffle(availableForceIDs); @@ -1056,7 +1065,8 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa * @param trackState the state of the track containing information about scenarios, facilities, and forces * @return a randomly selected unoccupied adjacent coordinate, or {@code null} if none are available */ - private static @Nullable StratconCoords getUnoccupiedAdjacentCoords(StratconCoords originCoords, StratconTrackState trackState) { + private static @Nullable StratconCoords getUnoccupiedAdjacentCoords(StratconCoords originCoords, + StratconTrackState trackState) { // We need to reduce width/height by one because coordinates index from 0, not 1 final int trackWidth = trackState.getWidth() - 1; final int trackHeight = trackState.getHeight() - 1; @@ -1078,7 +1088,10 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa } // This is to ensure we're not trying to place a scenario off the map - if ((newCoords.getX() < 0) || (newCoords.getX() > trackWidth) || (newCoords.getY() < 0) || (newCoords.getY() > trackHeight)) { + if ((newCoords.getX() < 0) + || (newCoords.getX() > trackWidth) + || (newCoords.getY() < 0) + || (newCoords.getY() > trackHeight)) { continue; } @@ -1095,44 +1108,45 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa /** * Sets up a StratCon scenario with the given parameters. * - * @param coords The coordinates where the scenario is to be placed on the - * track. - * @param forceID The ID of the forces involved in the scenario. - * @param campaign The current campaign. - * @param contract The contract associated with the current scenario. - * @param track The relevant StratCon track. + * @param coords The coordinates where the scenario is to be placed on the track. + * @param forceID The ID of the forces involved in the scenario. + * @param campaign The current campaign. + * @param contract The contract associated with the current scenario. + * @param track The relevant StratCon track. * @return The newly set up {@link StratconScenario}. */ - private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, AtBContract contract, StratconTrackState track) { + private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, + AtBContract contract, StratconTrackState track) { return setupScenario(coords, forceID, campaign, contract, track, null, false, null); } /** - * Sets up a Stratcon scenario with the given parameters optionally allowing use - * a specific scenario template. + * Sets up a Stratcon scenario with the given parameters optionally allowing use a specific scenario template. *

    - * If a facility is already present at the provided coordinates, the scenario - * will be setup for that facility. - * If there is no facility, a new scenario will be generated; if the - * ScenarioTemplate argument provided was non-null, + * If a facility is already present at the provided coordinates, the scenario will be setup for that facility. + * If there is no facility, a new scenario will be generated; if the ScenarioTemplate argument provided was non-null, * it will be used, else a randomly selected scenario will be generated. - * In case the generated scenario turns out to be a facility scenario, a new - * facility will be added to the track at + * In case the generated scenario turns out to be a facility scenario, a new facility will be added to the track at * the provided coordinates and setup for that facility. * - * @param coords The coordinates where the scenario is to be placed on the track. - * @param forceID The ID of the forces involved in the scenario. - * @param campaign The current campaign. - * @param contract The contract associated with the current scenario. - * @param track The relevant StratCon track. - * @param template A specific {@link ScenarioTemplate} to use for scenario setup, or - * {@code null} to select the scenario template randomly. + * @param coords The coordinates where the scenario is to be placed on the track. + * @param forceID The ID of the forces involved in the scenario. + * @param campaign The current campaign. + * @param contract The contract associated with the current scenario. + * @param track The relevant StratCon track. + * @param template A specific {@link ScenarioTemplate} to use for scenario setup, or + * {@code null} to select the scenario template randomly. * @param ignoreFacilities Whether we should ignore any facilities at the selected location * @param daysTilDeployment How many days until the scenario takes place, or {@code null} to - * pick a random day within the next 7 days. + * pick a random day within the next 7 days. * @return The newly set up {@link StratconScenario}. */ - private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, AtBContract contract, StratconTrackState track, @Nullable ScenarioTemplate template, boolean ignoreFacilities, @Nullable Integer daysTilDeployment) { + private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, + Campaign campaign, AtBContract contract, + StratconTrackState track, + @Nullable ScenarioTemplate template, + boolean ignoreFacilities, + @Nullable Integer daysTilDeployment) { StratconScenario scenario; if (track.getFacilities().containsKey(coords) && !ignoreFacilities) { @@ -1155,7 +1169,9 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa // we may generate a facility scenario randomly - if so, do the facility-related // stuff and add a new facility to the track if (scenario.getBackingScenario().getTemplate().isFacilityScenario()) { - StratconFacility facility = scenario.getBackingScenario().getTemplate().isHostileFacility() ? StratconFacilityFactory.getRandomHostileFacility() : StratconFacilityFactory.getRandomAlliedFacility(); + StratconFacility facility = scenario.getBackingScenario().getTemplate().isHostileFacility() + ? StratconFacilityFactory.getRandomHostileFacility() + : StratconFacilityFactory.getRandomAlliedFacility(); facility.setVisible(true); track.addFacility(coords, facility); setupFacilityScenario(scenario, facility); @@ -1182,11 +1198,13 @@ private static void setupFacilityScenario(StratconScenario scenario, StratconFac AtBScenarioModifier objectiveModifier = null; boolean alliedFacility = facility.getOwner() == Allied; - objectiveModifier = alliedFacility ? AtBScenarioModifier.getRandomAlliedFacilityModifier() : AtBScenarioModifier.getRandomHostileFacilityModifier(); + objectiveModifier = alliedFacility ? AtBScenarioModifier.getRandomAlliedFacilityModifier() + : AtBScenarioModifier.getRandomHostileFacilityModifier(); if (objectiveModifier != null) { scenario.getBackingScenario().addScenarioModifier(objectiveModifier); - scenario.getBackingScenario().setName(String.format("%s - %s - %s", facility.getFacilityType(), alliedFacility ? "Allied" : "Hostile", objectiveModifier.getModifierName())); + scenario.getBackingScenario().setName(String.format("%s - %s - %s", facility.getFacilityType(), + alliedFacility ? "Allied" : "Hostile", objectiveModifier.getModifierName())); } // add the "fixed" hostile facility modifiers after the primary ones @@ -1202,7 +1220,8 @@ private static void setupFacilityScenario(StratconScenario scenario, StratconFac /** * Applies time-sensitive facility effects. */ - private static void processFacilityEffects(StratconTrackState track, StratconCampaignState campaignState, boolean isStartOfMonth) { + private static void processFacilityEffects(StratconTrackState track, + StratconCampaignState campaignState, boolean isStartOfMonth) { for (StratconFacility facility : track.getFacilities().values()) { if (isStartOfMonth) { campaignState.addSupportPoints(facility.getMonthlySPModifier()); @@ -1234,14 +1253,16 @@ private static void processFacilityEffects(StratconTrackState track, StratconCam * within the scan range efficiently, avoiding redundant processing using a visited set. * * - * @param coords The coordinates where the force is being deployed. - * @param forceID The ID of the force being deployed. - * @param campaign The current campaign context, used to retrieve combat teams and update game events. - * @param track The current track state where the deployment is happening. - * @param sticky Whether the force should be persistently assigned to the track. + * @param coords The coordinates where the force is being deployed. + * @param forceID The ID of the force being deployed. + * @param campaign The current campaign context, used to retrieve combat teams and update game events. + * @param track The current track state where the deployment is happening. + * @param sticky Whether the force should be persistently assigned to the track. + * * @throws IllegalStateException if the force or the associated combat team is missing or invalid. */ - public static void processForceDeployment(StratconCoords coords, int forceID, Campaign campaign, StratconTrackState track, boolean sticky) { + public static void processForceDeployment(StratconCoords coords, int forceID, Campaign campaign, + StratconTrackState track, boolean sticky) { // we want to ensure we only increase Fatigue once boolean hasFatigueIncreased = false; @@ -1379,7 +1400,7 @@ private static void increaseFatigue(int forceID, Campaign campaign) { * @param campaign the overarching campaign instance managing the scenario * @param reinforcementTargetNumber the target number that the reinforcement roll must meet or exceed * @param isGMReinforcement {@code true} if the player is using GM powers to bypass the - * reinforcement check, {@code false} otherwise. + * reinforcement check, {@code false} otherwise. * @return a {@link ReinforcementResultsType} indicating the result of the reinforcement deployment: *

      *
    • {@link ReinforcementResultsType#SUCCESS} - The reinforcement is deployed successfully.
    • @@ -1389,8 +1410,15 @@ private static void increaseFatigue(int forceID, Campaign campaign) { * possibly resulting in a new scenario. *
    */ - public static ReinforcementResultsType processReinforcementDeployment(Force force, ReinforcementEligibilityType reinforcementType, StratconCampaignState campaignState, StratconScenario scenario, Campaign campaign, int reinforcementTargetNumber, boolean isGMReinforcement) { - final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", MekHQ.getMHQOptions().getLocale()); + public static ReinforcementResultsType processReinforcementDeployment(Force force, + ReinforcementEligibilityType reinforcementType, + StratconCampaignState campaignState, + StratconScenario scenario, + Campaign campaign, + int reinforcementTargetNumber, + boolean isGMReinforcement) { + final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", + MekHQ.getMHQOptions().getLocale()); if (reinforcementType.equals(ReinforcementEligibilityType.CHAINED_SCENARIO)) { return SUCCESS; @@ -1421,19 +1449,25 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc StringBuilder reportStatus = new StringBuilder(); if (isGMReinforcement) { - reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text.gm"), scenario.getName())); + reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text.gm"), + scenario.getName())); reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsAutomaticSuccess.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsAutomaticSuccess.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return SUCCESS; } else { - reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text"), scenario.getName(), roll, maneuverRoleReport, reinforcementTargetNumber)); + reportStatus.append(String.format(resources.getString("reinforcementsAttempt.text"), + scenario.getName(), roll, maneuverRoleReport, reinforcementTargetNumber)); } // Critical Failure if (roll == 2) { reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsCriticalFailure.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsCriticalFailure.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return FAILED; } @@ -1441,7 +1475,9 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc // Reinforcement successful if (roll >= reinforcementTargetNumber) { reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsSuccess.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsSuccess.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return SUCCESS; } @@ -1453,7 +1489,9 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc // Check passed if (interceptionRoll >= interceptionOdds) { reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsCommandFailure.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsCommandFailure.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return DELAYED; } @@ -1461,14 +1499,18 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc // Check failed, but enemy is routed if (contract.getMoraleLevel().isRouted()) { reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsSuccessRouted.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsSuccessRouted.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return SUCCESS; } // Check failed, enemy attempt interception reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsInterceptionAttempt.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsInterceptionAttempt.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorWarningHexColor()), + CLOSING_SPAN_TAG)); UUID commanderId = force.getForceCommanderID(); @@ -1476,7 +1518,9 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc logger.error("Force Commander ID is null."); reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsErrorNoCommander.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsErrorNoCommander.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return FAILED; } @@ -1487,7 +1531,9 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc logger.error("Failed to fetch commander from ID."); reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementsErrorUnableToFetchCommander.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG)); + reportStatus.append(String.format(resources.getString("reinforcementsErrorUnableToFetchCommander.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); campaign.addReport(reportStatus.toString()); return FAILED; } @@ -1506,8 +1552,12 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc if (roll >= targetNumber) { reportStatus.append(' '); - String reportString = tactics != null ? resources.getString("reinforcementEvasionSuccessful.text") : resources.getString("reinforcementEvasionSuccessful.noSkill"); - reportStatus.append(String.format(reportString, spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), CLOSING_SPAN_TAG, roll, targetNumber)); + String reportString = tactics != null + ? resources.getString("reinforcementEvasionSuccessful.text") + : resources.getString("reinforcementEvasionSuccessful.noSkill"); + reportStatus.append(String.format(reportString, + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG, roll, targetNumber)); campaign.addReport(reportStatus.toString()); @@ -1520,7 +1570,9 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc } reportStatus.append(' '); - reportStatus.append(String.format(resources.getString("reinforcementEvasionUnsuccessful.text"), spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), CLOSING_SPAN_TAG, roll, targetNumber)); + reportStatus.append(String.format(resources.getString("reinforcementEvasionUnsuccessful.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG, roll, targetNumber)); campaign.addReport(reportStatus.toString()); ScenarioTemplate scenarioTemplate = getInterceptionScenarioTemplate(force, campaign); @@ -1552,7 +1604,7 @@ public static ReinforcementResultsType processReinforcementDeployment(Force forc * @param campaign The {@link Campaign} in which the interception is taking place. * Provides context for evaluating the {@link Force}. * @return A {@link ScenarioTemplate} instance based on the template file matching the logic above, - * or a default template if no specific case is matched. + * or a default template if no specific case is matched. * @see ScenarioTemplate#Deserialize(String) */ private static ScenarioTemplate getInterceptionScenarioTemplate(Force force, Campaign campaign) { @@ -1562,7 +1614,8 @@ private static ScenarioTemplate getInterceptionScenarioTemplate(Force force, Cam int primaryUnitType = force.getPrimaryUnitType(campaign); - if ((primaryUnitType == CONV_FIGHTER) || (primaryUnitType == AEROSPACEFIGHTER) && (randomInt(3) == 0)) { + if ((primaryUnitType == CONV_FIGHTER) + || (primaryUnitType == AEROSPACEFIGHTER) && (randomInt(3) == 0)) { scenarioTemplate = ScenarioTemplate.Deserialize(String.format(templateString, "Low-Atmosphere ")); } else if (primaryUnitType >= AEROSPACEFIGHTER) { scenarioTemplate = ScenarioTemplate.Deserialize(String.format(templateString, "Space ")); @@ -1600,15 +1653,19 @@ private static ScenarioTemplate getInterceptionScenarioTemplate(Force force, Cam *
  • -- If command rights indicate that a liaison is required, the modifier is adjusted.
  • * * - * @param campaign the {@link Campaign} instance representing the current operational campaign. - * @param scenario the {@link StratconScenario} for which reinforcement details are being determined. - * @param commandLiaison the {@link Person} acting as the command liaison, or {@code null} if no liaison exists. - * @param campaignState the {@link StratconCampaignState} representing the state of the overarching campaign. - * @param contract the {@link AtBContract} defining the terms of the contract for this scenario. - * @return a {@link TargetRoll} object representing the calculated reinforcement target number, - * with appropriate modifiers applied. + * @param campaign the {@link Campaign} instance representing the current operational campaign. + * @param scenario the {@link StratconScenario} for which reinforcement details are being determined. + * @param commandLiaison the {@link Person} acting as the command liaison, or {@code null} if no liaison exists. + * @param campaignState the {@link StratconCampaignState} representing the state of the overarching campaign. + * @param contract the {@link AtBContract} defining the terms of the contract for this scenario. + * @return a {@link TargetRoll} object representing the calculated reinforcement target number, + * with appropriate modifiers applied. */ - public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, StratconScenario scenario, @Nullable Person commandLiaison, StratconCampaignState campaignState, AtBContract contract) { + public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, + StratconScenario scenario, + @Nullable Person commandLiaison, + StratconCampaignState campaignState, + AtBContract contract) { // Create Target Roll TargetRoll reinforcementTargetNumber = new TargetRoll(); @@ -1626,9 +1683,11 @@ public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, S skillTargetNumber = skill.getFinalSkillValue(); } - reinforcementTargetNumber.addModifier(skillTargetNumber, "Administration (" + commandLiaison.getFullTitle() + ')'); + reinforcementTargetNumber.addModifier(skillTargetNumber, + "Administration (" + commandLiaison.getFullTitle() +')'); } else { - reinforcementTargetNumber.addModifier(skillTargetNumber, "Administration (Unskilled)"); + reinforcementTargetNumber.addModifier(skillTargetNumber, + "Administration (Unskilled)"); } // Facilities Modifier @@ -1662,7 +1721,7 @@ public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, S reinforcementTargetNumber.addModifier(skillModifier, "Skill Modifier"); // Liaison Modifier - if (commandRights.isLiaison()) { + if (commandRights.isLiaison()) { int liaisonModifier = -1; reinforcementTargetNumber.addModifier(liaisonModifier, "Liaison Command Rights"); } @@ -1675,7 +1734,8 @@ public static TargetRoll calculateReinforcementTargetNumber(Campaign campaign, S * Assigns a force to the scenario such that the majority of the force can be * deployed */ - private static void assignAppropriateExtraForceToScenario(StratconScenario scenario, Map> sortedAvailableForceIDs) { + private static void assignAppropriateExtraForceToScenario(StratconScenario scenario, + Map> sortedAvailableForceIDs) { // the goal of this function is to avoid assigning ground units to air battles // and ground units/conventional fighters to space battle @@ -1688,7 +1748,8 @@ private static void assignAppropriateExtraForceToScenario(StratconScenario scena mapLocations.add(LowAtmosphere); // can add conventional fighters to ground or low atmo battles } - if ((scenarioMapLocation == AllGroundTerrain) || (scenarioMapLocation == SpecificGroundTerrain)) { + if ((scenarioMapLocation == AllGroundTerrain) + || (scenarioMapLocation == SpecificGroundTerrain)) { mapLocations.add(AllGroundTerrain); // can only add ground units to ground battles } @@ -1706,7 +1767,8 @@ private static void assignAppropriateExtraForceToScenario(StratconScenario scena * visible in the * briefing room, adds it to the track */ - public static void commitPrimaryForces(Campaign campaign, StratconScenario scenario, StratconTrackState trackState) { + public static void commitPrimaryForces(Campaign campaign, StratconScenario scenario, + StratconTrackState trackState) { trackState.addScenario(scenario); // set up dates for the scenario if doesn't have them already @@ -1747,7 +1809,7 @@ public static void commitPrimaryForces(Campaign campaign, StratconScenario scena */ private static boolean commanderLanceHasDefensiveAssignment(AtBDynamicScenario scenario, Campaign campaign) { Person lanceCommander = scenario.getLanceCommander(campaign); - if (lanceCommander != null) { + if (lanceCommander != null){ Unit commanderUnit = lanceCommander.getUnit(); if (commanderUnit != null) { CombatTeam lance = campaign.getCombatTeamsTable().get(commanderUnit.getForceId()); @@ -1806,16 +1868,19 @@ private static Map> sortForcesByMapType(List * then delegates the scenario creation and configuration to another overloaded {@code generateScenario} method * which handles specific template-based scenario generation.

    * - * @param campaign the {@link Campaign} managing the overall gameplay state - * @param contract the {@link AtBContract} governing the StratCon campaign - * @param track the {@link StratconTrackState} where the scenario is placed - * @param forceID the ID of the force for which the scenario is generated - * @param coords the {@link StratconCoords} specifying where the scenario will be generated - * @param daysTilDeployment the number of days until the scenario is deployed; if {@code null}, + * @param campaign the {@link Campaign} managing the overall gameplay state + * @param contract the {@link AtBContract} governing the StratCon campaign + * @param track the {@link StratconTrackState} where the scenario is placed + * @param forceID the ID of the force for which the scenario is generated + * @param coords the {@link StratconCoords} specifying where the scenario will be generated + * @param daysTilDeployment the number of days until the scenario is deployed; if {@code null}, * deployment dates are determined dynamically * @return the generated {@link StratconScenario}, or {@code null} if scenario generation fails */ - private static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, StratconTrackState track, int forceID, StratconCoords coords, @Nullable Integer daysTilDeployment) { + private static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, + StratconTrackState track, int forceID, + StratconCoords coords, + @Nullable Integer daysTilDeployment) { int unitType = campaign.getForce(forceID).getPrimaryUnitType(campaign); ScenarioTemplate template = StratconScenarioFactory.getRandomScenario(unitType); // useful for debugging specific scenario types @@ -1843,19 +1908,22 @@ private static Map> sortForcesByMapType(List *
  • If no force is provided, the scenario is treated as part of contract initialization (e.g., allied forces).
  • * * - * @param campaign the {@link Campaign} managing the gameplay state - * @param contract the {@link AtBContract} governing the StratCon campaign - * @param track the {@link StratconTrackState} to which the scenario belongs - * @param forceID the ID of the force for which the scenario is generated, or - * {@link Force#FORCE_NONE} if none - * @param coords the {@link StratconCoords} specifying where the scenario will be placed - * @param template the {@link ScenarioTemplate} to use for scenario generation; if - * {@code null}, a random one is selected - * @param daysTilDeployment the number of days until the scenario is deployed; if {@code null}, + * @param campaign the {@link Campaign} managing the gameplay state + * @param contract the {@link AtBContract} governing the StratCon campaign + * @param track the {@link StratconTrackState} to which the scenario belongs + * @param forceID the ID of the force for which the scenario is generated, or + * {@link Force#FORCE_NONE} if none + * @param coords the {@link StratconCoords} specifying where the scenario will be placed + * @param template the {@link ScenarioTemplate} to use for scenario generation; if + * {@code null}, a random one is selected + * @param daysTilDeployment the number of days until the scenario is deployed; if {@code null}, * dates will be dynamically set * @return the generated {@link StratconScenario}, or {@code null} if scenario generation failed */ - static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, StratconTrackState track, int forceID, StratconCoords coords, ScenarioTemplate template, @Nullable Integer daysTilDeployment) { + static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, + StratconTrackState track, int forceID, + StratconCoords coords, ScenarioTemplate template, + @Nullable Integer daysTilDeployment) { StratconScenario scenario = new StratconScenario(); if (template == null) { @@ -1875,7 +1943,8 @@ private static Map> sortForcesByMapType(List return null; } - AtBDynamicScenario backingScenario = AtBDynamicScenarioFactory.initializeScenarioFromTemplate(template, contract, campaign); + AtBDynamicScenario backingScenario = AtBDynamicScenarioFactory.initializeScenarioFromTemplate(template, + contract, campaign); scenario.setBackingScenario(backingScenario); scenario.setCoords(coords); @@ -1886,7 +1955,8 @@ private static Map> sortForcesByMapType(List applyFacilityModifiers(scenario, track, coords); applyGlobalModifiers(scenario, contract.getStratconCampaignState()); - AtBDynamicScenarioFactory.setScenarioModifiers(campaign.getCampaignOptions(), scenario.getBackingScenario()); + AtBDynamicScenarioFactory.setScenarioModifiers(campaign.getCampaignOptions(), + scenario.getBackingScenario()); scenario.setCurrentState(ScenarioState.UNRESOLVED); if (daysTilDeployment == null) { @@ -1932,7 +2002,8 @@ private static void applyGlobalModifiers(StratconScenario scenario, StratconCamp /** * Applies scenario modifiers from the current track to the given scenario. */ - private static void applyFacilityModifiers(StratconScenario scenario, StratconTrackState track, StratconCoords coords) { + private static void applyFacilityModifiers(StratconScenario scenario, StratconTrackState track, + StratconCoords coords) { // loop through all the facilities on the track // if a facility has been revealed, then it has a 100% chance to apply its // effect @@ -1955,11 +2026,13 @@ private static void applyFacilityModifiers(StratconScenario scenario, StratconTr for (String modifierID : modifierIDs) { AtBScenarioModifier modifier = AtBScenarioModifier.getScenarioModifier(modifierID); if (modifier == null) { - logger.error(String.format("Modifier %s not found for facility %s", modifierID, facility.getFormattedDisplayableName())); + logger.error(String.format("Modifier %s not found for facility %s", modifierID, + facility.getFormattedDisplayableName())); continue; } - modifier.setAdditionalBriefingText('(' + facility.getDisplayableName() + ") " + modifier.getAdditionalBriefingText()); + modifier.setAdditionalBriefingText('(' + facility.getDisplayableName() + ") " + + modifier.getAdditionalBriefingText()); scenario.getBackingScenario().addScenarioModifier(modifier); } } @@ -1974,18 +2047,26 @@ private static void applyFacilityModifiers(StratconScenario scenario, StratconTr */ public static void setAttachedUnitsModifier(StratconScenario scenario, AtBContract contract) { AtBDynamicScenario backingScenario = scenario.getBackingScenario(); - boolean airBattle = (backingScenario.getTemplate().mapParameters.getMapLocation() == LowAtmosphere) || (backingScenario.getTemplate().mapParameters.getMapLocation() == Space); + boolean airBattle = (backingScenario.getTemplate().mapParameters.getMapLocation() == LowAtmosphere) + || (backingScenario.getTemplate().mapParameters.getMapLocation() == Space); // if we're under non-independent command rights, a supervisor may come along switch (contract.getCommandRights()) { case INTEGRATED: - backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_INTEGRATED_UNITS_AIR : MHQConstants.SCENARIO_MODIFIER_INTEGRATED_UNITS_GROUND)); + backingScenario.addScenarioModifier(AtBScenarioModifier + .getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_INTEGRATED_UNITS_AIR + : MHQConstants.SCENARIO_MODIFIER_INTEGRATED_UNITS_GROUND)); break; case HOUSE: - backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_HOUSE_CO_AIR : MHQConstants.SCENARIO_MODIFIER_HOUSE_CO_GROUND)); + backingScenario.addScenarioModifier( + AtBScenarioModifier.getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_HOUSE_CO_AIR + : MHQConstants.SCENARIO_MODIFIER_HOUSE_CO_GROUND)); break; case LIAISON: if (scenario.isTurningPoint()) { - backingScenario.addScenarioModifier(AtBScenarioModifier.getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_LIAISON_AIR : MHQConstants.SCENARIO_MODIFIER_LIAISON_GROUND)); + backingScenario.addScenarioModifier( + AtBScenarioModifier + .getScenarioModifier(airBattle ? MHQConstants.SCENARIO_MODIFIER_LIAISON_AIR + : MHQConstants.SCENARIO_MODIFIER_LIAISON_GROUND)); } break; default: @@ -2004,15 +2085,16 @@ private static void setScenarioDates(StratconTrackState track, Campaign campaign /** * Worker function that sets scenario deploy/battle/return dates based on the - * track's properties and current campaign date. Takes a fixed deployment day of - * X days from + * track's properties and current campaign date. Takes a fixed deployment day of X days from * campaign's today date. */ - private static void setScenarioDates(int deploymentDay, StratconTrackState track, Campaign campaign, StratconScenario scenario) { + private static void setScenarioDates(int deploymentDay, StratconTrackState track, Campaign campaign, + StratconScenario scenario) { // set up deployment day, battle day, return day here // safety code to prevent attempts to generate random int with upper bound of 0 // which is apparently illegal - int battleDay = deploymentDay + (track.getDeploymentTime() > 0 ? randomInt(track.getDeploymentTime()) : 0); + int battleDay = deploymentDay + + (track.getDeploymentTime() > 0 ? randomInt(track.getDeploymentTime()) : 0); int returnDay = deploymentDay + track.getDeploymentTime(); LocalDate deploymentDate = campaign.getLocalDate().plusDays(deploymentDay); @@ -2032,7 +2114,11 @@ private static void setScenarioDates(int deploymentDay, StratconTrackState track private static boolean unitTypeIsAirborne(ScenarioForceTemplate template) { int unitType = template.getAllowedUnitType(); - return ((unitType == AEROSPACEFIGHTER) || (unitType == CONV_FIGHTER) || (unitType == DROPSHIP) || (unitType == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX)) && (template.getStartingAltitude() > 0); + return ((unitType == AEROSPACEFIGHTER) || + (unitType == CONV_FIGHTER) || + (unitType == DROPSHIP) || + (unitType == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX)) && + (template.getStartingAltitude() > 0); } /** @@ -2115,24 +2201,28 @@ public static List getAvailableForceIDs(Campaign campaign, AtBContract * * Forces that meet all conditions are returned as a list of unique force IDs. * - * @param unitType the desired type of unit to evaluate for deployment eligibility. - * @param campaign the {@link Campaign} containing the forces to evaluate. - * @param currentTrack the {@link StratconTrackState} representing the current track, used to - * filter eligible forces. - * @param reinforcements {@code true} if the forces are being deployed as reinforcements; - * otherwise {@code false}. + * @param unitType the desired type of unit to evaluate for deployment eligibility. + * @param campaign the {@link Campaign} containing the forces to evaluate. + * @param currentTrack the {@link StratconTrackState} representing the current track, used to + * filter eligible forces. + * @param reinforcements {@code true} if the forces are being deployed as reinforcements; + * otherwise {@code false}. * @param currentScenario the current {@link StratconScenario}, if any, used to exclude failed - * reinforcements. Can be {@code null}. - * @param campaignState the current {@link StratconCampaignState} representing the campaign - * state for further filtering of eligible forces. + * reinforcements. Can be {@code null}. + * @param campaignState the current {@link StratconCampaignState} representing the campaign + * state for further filtering of eligible forces. * @return a {@link List} of unique force IDs that meet all deployment criteria. */ - public static List getAvailableForceIDs(int unitType, Campaign campaign, StratconTrackState currentTrack, boolean reinforcements, @Nullable StratconScenario currentScenario, StratconCampaignState campaignState) { + public static List getAvailableForceIDs(int unitType, Campaign campaign, StratconTrackState currentTrack, + boolean reinforcements, @Nullable StratconScenario currentScenario, StratconCampaignState campaignState) { List retVal = new ArrayList<>(); - // assemble a set of all force IDs that are currently assigned to tracks that - // are not this one - Set forcesInTracks = campaign.getActiveAtBContracts().stream().flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()).filter(track -> (!Objects.equals(track, currentTrack)) || !reinforcements).flatMap(track -> track.getAssignedForceCoords().keySet().stream()).collect(Collectors.toSet()); + // assemble a set of all force IDs that are currently assigned to tracks that are not this one + Set forcesInTracks = campaign.getActiveAtBContracts().stream() + .flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()) + .filter(track -> (!Objects.equals(track, currentTrack)) || !reinforcements) + .flatMap(track -> track.getAssignedForceCoords().keySet().stream()) + .collect(Collectors.toSet()); // if there's an existing scenario, and we're doing reinforcements, // prevent forces that failed to deploy from trying to deploy again @@ -2160,9 +2250,15 @@ public static List getAvailableForceIDs(int unitType, Campaign campaign } int primaryUnitType = force.getPrimaryUnitType(campaign); - boolean noReinforcementRestriction = !reinforcements || (getReinforcementType(force.getId(), currentTrack, campaign, campaignState) != ReinforcementEligibilityType.NONE); + boolean noReinforcementRestriction = !reinforcements || + (getReinforcementType(force.getId(), currentTrack, campaign, campaignState) != ReinforcementEligibilityType.NONE); - if ((force.getScenarioId() <= 0) && !force.getAllUnits(true).isEmpty() && !forcesInTracks.contains(force.getId()) && forceCompositionMatchesDeclaredUnitType(primaryUnitType, unitType) && noReinforcementRestriction && !subElementsOrSelfDeployed(force, campaign)) { + if ((force.getScenarioId() <= 0) + && !force.getAllUnits(true).isEmpty() + && !forcesInTracks.contains(force.getId()) + && forceCompositionMatchesDeclaredUnitType(primaryUnitType, unitType) + && noReinforcementRestriction + && !subElementsOrSelfDeployed(force, campaign)) { retVal.add(force.getId()); } @@ -2180,11 +2276,14 @@ private static boolean subElementsOrSelfDeployed(Force force, Campaign campaign) return true; } - if (force.getUnits().stream().map(campaign::getUnit).anyMatch(Unit::isDeployed)) { + if (force.getUnits().stream() + .map(campaign::getUnit) + .anyMatch(Unit::isDeployed)) { return true; } - return force.getSubForces().stream().anyMatch(child -> subElementsOrSelfDeployed(child, campaign)); + return force.getSubForces().stream() + .anyMatch(child -> subElementsOrSelfDeployed(child, campaign)); } /** @@ -2200,11 +2299,16 @@ public static List getEligibleDefensiveUnits(Campaign campaign) { // "defensive" units are infantry, battle armor and (Weisman help you) gun // emplacements // and also said unit should be intact/alive/etc - boolean isEligibleInfantry = ((u.getEntity().getUnitType() == INFANTRY) || (u.getEntity().getUnitType() == BATTLE_ARMOR)) && !u.isUnmanned(); + boolean isEligibleInfantry = ((u.getEntity().getUnitType() == INFANTRY) + || (u.getEntity().getUnitType() == BATTLE_ARMOR)) && !u.isUnmanned(); boolean isEligibleGunEmplacement = u.getEntity().getUnitType() == GUN_EMPLACEMENT; - if ((isEligibleInfantry || isEligibleGunEmplacement) && !u.isDeployed() && !u.isMothballed() && (u.checkDeployment() == null) && !isUnitDeployedToStratCon(u)) { + if ((isEligibleInfantry || isEligibleGunEmplacement) + && !u.isDeployed() + && !u.isMothballed() + && (u.checkDeployment() == null) + && !isUnitDeployedToStratCon(u)) { // this is a little inefficient, but probably there aren't too many active AtB // contracts at a time @@ -2228,7 +2332,8 @@ public static List getEligibleDefensiveUnits(Campaign campaign) { * * @return List of unit IDs. */ - public static List getEligibleLeadershipUnits(Campaign campaign, ArrayList forceIDs, int leadershipSkill) { + public static List getEligibleLeadershipUnits(Campaign campaign, ArrayList forceIDs, + int leadershipSkill) { List eligibleUnits = new ArrayList<>(); // If there is no leadership skill, we shouldn't continue @@ -2257,12 +2362,17 @@ public static List getEligibleLeadershipUnits(Campaign campaign, ArrayList continue; } - // the general idea is that we want something that can be deployed to the - // scenario - + // the general idea is that we want something that can be deployed to the scenario - // e.g., no infantry on air scenarios etc. - boolean validUnitType = (forceCompositionMatchesDeclaredUnitType(unit.getEntity().getUnitType(), generalUnitType)); - - if (validUnitType && !unit.isDeployed() && !unit.isMothballed() && (unit.getEntity().calculateBattleValue(true, true) <= totalBudget) && (unit.checkDeployment() == null) && !isUnitDeployedToStratCon(unit)) { + boolean validUnitType = (forceCompositionMatchesDeclaredUnitType(unit.getEntity().getUnitType(), + generalUnitType)); + + if (validUnitType + && !unit.isDeployed() + && !unit.isMothballed() + && (unit.getEntity().calculateBattleValue(true, true) <= totalBudget) + && (unit.checkDeployment() == null) + && !isUnitDeployedToStratCon(unit)) { eligibleUnits.add(unit); } } @@ -2281,7 +2391,9 @@ public static boolean isUnitDeployedToStratCon(Unit u) { // this is a little inefficient, but probably there aren't too many active AtB // contracts at a time - return u.getCampaign().getActiveAtBContracts().stream().anyMatch(contract -> (contract.getStratconCampaignState() != null) && contract.getStratconCampaignState().isForceDeployedHere(u.getForceId())); + return u.getCampaign().getActiveAtBContracts().stream() + .anyMatch(contract -> (contract.getStratconCampaignState() != null) && + contract.getStratconCampaignState().isForceDeployedHere(u.getForceId())); } /** @@ -2322,9 +2434,13 @@ private static int getPrimaryUnitType(Campaign campaign, ArrayList forc * Determines what rules to use when deploying a force for reinforcements to the * given track. */ - public static ReinforcementEligibilityType getReinforcementType(int forceID, StratconTrackState trackState, Campaign campaign, StratconCampaignState campaignState) { + public static ReinforcementEligibilityType getReinforcementType(int forceID, StratconTrackState trackState, + Campaign campaign, StratconCampaignState campaignState) { // if the force is deployed elsewhere, it cannot be deployed as reinforcements - if (campaign.getActiveAtBContracts().stream().flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()).anyMatch(track -> !Objects.equals(track, trackState) && track.getAssignedForceCoords().containsKey(forceID))) { + if (campaign.getActiveAtBContracts().stream() + .flatMap(contract -> contract.getStratconCampaignState().getTracks().stream()) + .anyMatch(track -> !Objects.equals(track, trackState) + && track.getAssignedForceCoords().containsKey(forceID))) { return ReinforcementEligibilityType.NONE; } @@ -2357,7 +2473,8 @@ public static ReinforcementEligibilityType getReinforcementType(int forceID, Str * track * for the given contract? */ - public static boolean canManuallyDeployAnyForce(StratconCoords coords, StratconTrackState track, AtBContract contract) { + public static boolean canManuallyDeployAnyForce(StratconCoords coords, + StratconTrackState track, AtBContract contract) { // Rules: can't manually deploy under integrated command // can't manually deploy if there's already a force deployed there // exception: on allied facilities @@ -2381,7 +2498,8 @@ public static boolean canManuallyDeployAnyForce(StratconCoords coords, StratconT * a force or not, * figure out the odds of a scenario occurring. */ - public static int calculateScenarioOdds(StratconTrackState track, AtBContract contract, boolean isReinforcements) { + public static int calculateScenarioOdds(StratconTrackState track, AtBContract contract, + boolean isReinforcements) { if (contract.getMoraleLevel().isRouted()) { return -1; } @@ -2422,7 +2540,8 @@ public static int calculateScenarioOdds(StratconTrackState track, AtBContract co * Removes the facility associated with the given scenario from the relevant * track */ - public static void updateFacilityForScenario(AtBScenario scenario, AtBContract contract, boolean destroy, boolean capture) { + public static void updateFacilityForScenario(AtBScenario scenario, AtBContract contract, boolean destroy, + boolean capture) { if (contract.getStratconCampaignState() == null) { return; } @@ -2511,16 +2630,17 @@ public static void processScenarioCompletion(ResolveScenarioTracker tracker) { if (backingScenario.getStratConScenarioType().isLosTech()) { if (victory) { int roll = randomInt(10); - StarLeagueCache cache = new StarLeagueCache(campaign, ((AtBContract) mission), CacheType.TRASH_CACHE.ordinal()); + StarLeagueCache cache = new StarLeagueCache(campaign, ((AtBContract) mission), + CacheType.TRASH_CACHE.ordinal()); // The rumor is a dud - // if (false) { // TODO replace placeholder value - // cache.createDudDialog(track, scenario); - // } else { - // if (Objects.equals(cache.getFaction().getShortName(), "SL")) { - // cache.createProposalDialog(); - // } - // } +// if (false) { // TODO replace placeholder value +// cache.createDudDialog(track, scenario); +// } else { +// if (Objects.equals(cache.getFaction().getShortName(), "SL")) { +// cache.createProposalDialog(); +// } +// } } } break; @@ -2529,11 +2649,18 @@ public static void processScenarioCompletion(ResolveScenarioTracker tracker) { } } + /** + * Processes completion of a Stratcon scenario that is linked to another scenario + * pulls forces off completed scenario and moves them to linked one. + * + * Should only be used after a scenario is resolved + */ public static void linkedScenerioProcessing(ResolveScenarioTracker tracker, List forces) { Scenario nextScenario = tracker.getCampaign().getScenario(tracker.getScenario().getLinkedScenario()); if (nextScenario instanceof AtBScenario) { - StratconCampaignState campaignState = ((AtBScenario) nextScenario).getContract(tracker.getCampaign()).getStratconCampaignState(); + StratconCampaignState campaignState = ((AtBScenario) nextScenario).getContract(tracker.getCampaign()) + .getStratconCampaignState(); if (campaignState == null) { return; } @@ -2557,12 +2684,14 @@ public static void linkedScenerioProcessing(ResolveScenarioTracker tracker, List * scenario, track and campaign state. For example, "win scenario A" or "win X * scenarios". */ - private static void updateStrategicObjectives(boolean victory, StratconScenario scenario, StratconTrackState track) { + private static void updateStrategicObjectives(boolean victory, StratconScenario scenario, + StratconTrackState track) { // first, we check if this scenario is associated with any specific scenario // objectives StratconStrategicObjective specificObjective = track.getObjectivesByCoords().get(scenario.getCoords()); - if ((specificObjective != null) && (specificObjective.getObjectiveType() == StrategicObjectiveType.SpecificScenarioVictory)) { + if ((specificObjective != null) && + (specificObjective.getObjectiveType() == StrategicObjectiveType.SpecificScenarioVictory)) { if (victory) { specificObjective.incrementCurrentObjectiveCount(); @@ -2610,7 +2739,8 @@ public static void switchFacilityOwner(StratconFacility facility) { * return date is on or before the given date. */ public static void processTrackForceReturnDates(StratconTrackState track, Campaign campaign) { - final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", MekHQ.getMHQOptions().getLocale()); + final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.AtBStratCon", + MekHQ.getMHQOptions().getLocale()); List forcesToUndeploy = new ArrayList<>(); LocalDate date = campaign.getLocalDate(); @@ -2621,10 +2751,14 @@ public static void processTrackForceReturnDates(StratconTrackState track, Campai for (int forceID : track.getAssignedForceReturnDates().keySet()) { Force force = campaign.getForce(forceID); - if ((track.getAssignedForceReturnDates().get(forceID).equals(date) || track.getAssignedForceReturnDates().get(forceID).isBefore(date)) && (force != null) && !track.getBackingScenariosMap().containsKey(force.getScenarioId()) && !track.getStickyForces().contains(forceID)) { + if ((track.getAssignedForceReturnDates().get(forceID).equals(date) + || track.getAssignedForceReturnDates().get(forceID).isBefore(date)) + && (force != null) && !track.getBackingScenariosMap().containsKey(force.getScenarioId()) + && !track.getStickyForces().contains(forceID)) { forcesToUndeploy.add(forceID); - campaign.addReport(String.format(resources.getString("force.undeployed"), force.getName())); + campaign.addReport(String.format(resources.getString("force.undeployed"), + force.getName())); } } @@ -2639,10 +2773,15 @@ public static void processTrackForceReturnDates(StratconTrackState track, Campai * 'ignored scenario' routine. * * @return Whether or not we also need to get rid of the backing scenario from - * the campaign + * the campaign */ public static boolean processIgnoredScenario(AtBDynamicScenario scenario, StratconCampaignState campaignState) { - return campaignState.getTracks().stream().filter(track -> track.getBackingScenariosMap().containsKey(scenario.getId())).findFirst().map(track -> processIgnoredScenario(track.getBackingScenariosMap().get(scenario.getId()), campaignState)).orElse(true); + return campaignState.getTracks().stream() + .filter(track -> track.getBackingScenariosMap().containsKey(scenario.getId())) + .findFirst() + .map(track -> processIgnoredScenario(track.getBackingScenariosMap().get(scenario.getId()), + campaignState)) + .orElse(true); } @@ -2650,7 +2789,7 @@ public static boolean processIgnoredScenario(AtBDynamicScenario scenario, Stratc * Processes an ignored Stratcon scenario * * @return Whether or not we also need to get rid of the backing scenario from - * the campaign + * the campaign */ public static boolean processIgnoredScenario(StratconScenario scenario, StratconCampaignState campaignState) { for (StratconTrackState track : campaignState.getTracks()) { @@ -2678,10 +2817,12 @@ public static boolean processIgnoredScenario(StratconScenario scenario, Stratcon } else { // if it's an open-field // move scenario towards nearest allied facility - StratconCoords closestAlliedFacilityCoords = track.findClosestAlliedFacilityCoords(scenario.getCoords()); + StratconCoords closestAlliedFacilityCoords = track + .findClosestAlliedFacilityCoords(scenario.getCoords()); if (closestAlliedFacilityCoords != null) { - StratconCoords newCoords = scenario.getCoords().translate(scenario.getCoords().direction(closestAlliedFacilityCoords)); + StratconCoords newCoords = scenario.getCoords() + .translate(scenario.getCoords().direction(closestAlliedFacilityCoords)); boolean objectiveMoved = track.moveObjective(scenario.getCoords(), newCoords); if (!objectiveMoved) { @@ -2729,7 +2870,6 @@ public void startup() { MekHQ.registerHandler(this); } - /** * Event handler for the new day event. */ @@ -2766,7 +2906,9 @@ public void handleNewDay(NewDayEvent ev) { // loop through scenarios - if we haven't deployed in time, // fail it and apply consequences for (StratconScenario scenario : track.getScenarios().values()) { - if ((scenario.getDeploymentDate() != null) && scenario.getDeploymentDate().isBefore(campaign.getLocalDate()) && scenario.getPrimaryForceIDs().isEmpty()) { + if ((scenario.getDeploymentDate() != null) && + scenario.getDeploymentDate().isBefore(campaign.getLocalDate()) && + scenario.getPrimaryForceIDs().isEmpty()) { processIgnoredScenario(scenario, campaignState); } } @@ -2799,7 +2941,10 @@ public void handleNewDay(NewDayEvent ev) { * required data */ private void cleanupPhantomScenarios(StratconTrackState track) { - List cleanupList = track.getScenarios().values().stream().filter(scenario -> (scenario.getDeploymentDate() == null) && !scenario.isStrategicObjective()).collect(Collectors.toList()); + List cleanupList = track.getScenarios().values().stream() + .filter(scenario -> (scenario.getDeploymentDate() == null) + && !scenario.isStrategicObjective()) + .collect(Collectors.toList()); for (StratconScenario scenario : cleanupList) { track.removeScenario(scenario); diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index f41b3f43dab..af89d761ed1 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -1298,7 +1298,7 @@ public void refreshScenarioView() { SwingUtilities.invokeLater(() -> scrollScenarioView.getVerticalScrollBar().setValue(0)); final boolean canStartGame = ( - (getCampaign().checkLinkedScenario(scenario.getId())) && (scenario.canStartScenario(getCampaign())) + (!getCampaign().checkLinkedScenario(scenario.getId())) && (scenario.canStartScenario(getCampaign())) ); btnStartGame.setEnabled(canStartGame); diff --git a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java index 8486dfd2e5d..c1ff2b7712b 100644 --- a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java @@ -1617,9 +1617,9 @@ private void finish() { } StratconRulesManager.processScenarioCompletion(tracker); - - if (reinforcementsSent && tracker.getScenario().getStatus().isOverallVictory() - && tracker.getCampaign().checkLinkedScenario(tracker.getScenario().getId())) { + + if (reinforcementsSent && tracker.getScenario().getStatus().isOverallVictory() + && tracker.getScenario().getLinkedScenario() != 0) { StratconRulesManager.linkedScenerioProcessing(tracker, forces); } From 4a31a08eb295c0e4f24885027862b043dd54cd82 Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:06:06 -0700 Subject: [PATCH 09/11] typo and JavaDocs --- .../mekhq/campaign/stratcon/StratconRulesManager.java | 2 +- .../mekhq/gui/dialog/ResolveScenarioWizardDialog.java | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 479471e688c..b964f79da39 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -2655,7 +2655,7 @@ public static void processScenarioCompletion(ResolveScenarioTracker tracker) { * * Should only be used after a scenario is resolved */ - public static void linkedScenerioProcessing(ResolveScenarioTracker tracker, List forces) { + public static void linkedScenarioProcessing(ResolveScenarioTracker tracker, List forces) { Scenario nextScenario = tracker.getCampaign().getScenario(tracker.getScenario().getLinkedScenario()); if (nextScenario instanceof AtBScenario) { diff --git a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java index c1ff2b7712b..1f7c8b99ee2 100644 --- a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java @@ -1620,7 +1620,7 @@ private void finish() { if (reinforcementsSent && tracker.getScenario().getStatus().isOverallVictory() && tracker.getScenario().getLinkedScenario() != 0) { - StratconRulesManager.linkedScenerioProcessing(tracker, forces); + StratconRulesManager.linkedScenarioProcessing(tracker, forces); } @@ -2018,6 +2018,14 @@ public void actionPerformed(ActionEvent evt) { } } + + + /** + * Event handler for "Continue as Reinforcment" Button that + * adjusts boolean used after scenario resolution to signal + * player want to send units to linked scenario + * + */ private class ReinforcementListener implements ActionListener { public ReinforcementListener() { From 143397e2686af01110ddedc001c9536212629898 Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:29:26 -0700 Subject: [PATCH 10/11] typos and cleanup --- .../src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java index 1f7c8b99ee2..09e2fe61280 100644 --- a/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ResolveScenarioWizardDialog.java @@ -378,7 +378,6 @@ private JPanel makeUnitStatusPanel() { btnSendReinforcements = new JButton("Continue as Reinforcements"); btnSendReinforcements.setEnabled(tracker.getScenario().getLinkedScenario() != 0); - btnSendReinforcements.setActionCommand("1"); btnSendReinforcements.setName("Confirm Reinforcement"); btnSendReinforcements.addActionListener(new ReinforcementListener()); @@ -2021,10 +2020,9 @@ public void actionPerformed(ActionEvent evt) { /** - * Event handler for "Continue as Reinforcment" Button that - * adjusts boolean used after scenario resolution to signal - * player want to send units to linked scenario - * + * Event handler for "Continue as Reinforcements" Button that + * adjusts boolean used after scenario resolution to signal + * player wants to send units to linked scenario */ private class ReinforcementListener implements ActionListener { From ad20773d2c6fd78c484e75377d9a4cb0d5a0a282 Mon Sep 17 00:00:00 2001 From: DM0000 <98051919+DM0000@users.noreply.github.com> Date: Fri, 31 Jan 2025 20:44:33 -0700 Subject: [PATCH 11/11] remove cast and whitespace --- MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java | 4 ++-- MekHQ/src/mekhq/gui/BriefingTab.java | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index b964f79da39..3c426faa479 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -2658,8 +2658,8 @@ public static void processScenarioCompletion(ResolveScenarioTracker tracker) { public static void linkedScenarioProcessing(ResolveScenarioTracker tracker, List forces) { Scenario nextScenario = tracker.getCampaign().getScenario(tracker.getScenario().getLinkedScenario()); - if (nextScenario instanceof AtBScenario) { - StratconCampaignState campaignState = ((AtBScenario) nextScenario).getContract(tracker.getCampaign()) + if (nextScenario instanceof AtBScenario nextAtBScenario) { + StratconCampaignState campaignState = nextAtBScenario.getContract(tracker.getCampaign()) .getStratconCampaignState(); if (campaignState == null) { return; diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index af89d761ed1..160893347fd 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -1317,9 +1317,7 @@ public void refreshScenarioView() { if (scenario instanceof AtBScenario) { btnAutoResolveScenario.setEnabled(canStartGame); } - btnPrintRS.setEnabled(canStartGame); - - + btnPrintRS.setEnabled(canStartGame); } public void refreshLanceAssignments() {