diff --git a/MekHQ/data/scenariotemplates/Chasing a Rumor.xml b/MekHQ/data/scenariotemplates/Chasing a Rumor.xml new file mode 100644 index 0000000000..2be638cc82 --- /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/Emergency Convoy Defense - Player - Low-Atmosphere.xml b/MekHQ/data/scenariotemplates/Emergency Convoy Defense - Player - Low-Atmosphere.xml new file mode 100644 index 0000000000..61966491d6 --- /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 0000000000..d3c7657cd2 --- /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 + 2.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 + 2.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 0000000000..285f872b9c --- /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 0000000000..c85a4e155a --- /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 + true + true + true + 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/mmconf/campaignPresets/Campaign Operations (StratCon).xml b/MekHQ/mmconf/campaignPresets/Campaign Operations (StratCon).xml index df5731efdb..2edb8ce0b7 100644 --- a/MekHQ/mmconf/campaignPresets/Campaign Operations (StratCon).xml +++ b/MekHQ/mmconf/campaignPresets/Campaign Operations (StratCon).xml @@ -528,8 +528,6 @@ 1 true true - 500000 - 10 false false false diff --git a/MekHQ/mmconf/campaignPresets/Campaign Operations.xml b/MekHQ/mmconf/campaignPresets/Campaign Operations.xml index 052ff00b9c..64dd372da7 100644 --- a/MekHQ/mmconf/campaignPresets/Campaign Operations.xml +++ b/MekHQ/mmconf/campaignPresets/Campaign Operations.xml @@ -528,8 +528,6 @@ 1 false true - 500000 - 10 false false false diff --git a/MekHQ/mmconf/campaignPresets/Complete Experience.xml b/MekHQ/mmconf/campaignPresets/Complete Experience.xml index a59809d15b..1c151164d3 100644 --- a/MekHQ/mmconf/campaignPresets/Complete Experience.xml +++ b/MekHQ/mmconf/campaignPresets/Complete Experience.xml @@ -528,8 +528,6 @@ 1 true true - 500000 - 10 false false false diff --git a/MekHQ/mmconf/campaignPresets/New Pilot Program.xml b/MekHQ/mmconf/campaignPresets/New Pilot Program.xml index bd0e31edf3..4e93075999 100644 --- a/MekHQ/mmconf/campaignPresets/New Pilot Program.xml +++ b/MekHQ/mmconf/campaignPresets/New Pilot Program.xml @@ -528,8 +528,6 @@ 1 true true - 500000 - 10 false false false diff --git a/MekHQ/mmconf/campaignPresets/Tabula Rasa.xml b/MekHQ/mmconf/campaignPresets/Tabula Rasa.xml index 63341e1fca..f1c859f475 100644 --- a/MekHQ/mmconf/campaignPresets/Tabula Rasa.xml +++ b/MekHQ/mmconf/campaignPresets/Tabula Rasa.xml @@ -530,8 +530,6 @@ 1 false true - 500000 - 10 false false false diff --git a/MekHQ/resources/mekhq/resources/Campaign.properties b/MekHQ/resources/mekhq/resources/Campaign.properties index cf41999826..6d61b7934d 100644 --- a/MekHQ/resources/mekhq/resources/Campaign.properties +++ b/MekHQ/resources/mekhq/resources/Campaign.properties @@ -79,7 +79,6 @@ 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! atbScenarioTodayWithForce.format=Scenario "{0}" is today, {1} has been deployed! diff --git a/MekHQ/resources/mekhq/resources/CampaignGUI.properties b/MekHQ/resources/mekhq/resources/CampaignGUI.properties index 9cebb0b90a..7e8aea2780 100644 --- a/MekHQ/resources/mekhq/resources/CampaignGUI.properties +++ b/MekHQ/resources/mekhq/resources/CampaignGUI.properties @@ -232,5 +232,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/CampaignOptionsDialog.properties b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties index c0561a6fc8..3e755fee48 100644 --- a/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties +++ b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties @@ -958,10 +958,6 @@ 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 diff --git a/MekHQ/resources/mekhq/resources/ContractViewPanel.properties b/MekHQ/resources/mekhq/resources/ContractViewPanel.properties index fb1cfdff30..3e606bb248 100644 --- a/MekHQ/resources/mekhq/resources/ContractViewPanel.properties +++ b/MekHQ/resources/mekhq/resources/ContractViewPanel.properties @@ -18,8 +18,8 @@ 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: diff --git a/MekHQ/resources/mekhq/resources/PartsStoreDialog.properties b/MekHQ/resources/mekhq/resources/PartsStoreDialog.properties index 709f08ae72..1c3efcf083 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 0000000000..2472944816 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/Resupply.properties @@ -0,0 +1,3500 @@ +# General +dialog.title=++ INCOMING TRANSMISSION ++ +confirmReceipt.text=Confirm Itinerary +confirmAccept.text=Accept +confirmRefuse.text=Refuse +convoyConfirm.text=Understood +commander.text=Commander +dialogBorderTitle.text=%s +dialogBorderCampaignSpeaker.text=%s Actual +dialogBorderConvoySpeakerDefault.text=%s Convoy +hiss.text=[Hiss] +static.text=[Static] +smugglerFee.text=Local Resupply + +convoySuccessful.text=The 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 +convoyInterceptedAtB.text=The convoy was %sintercepted%s, and all supplies have been lost. +convoyInterceptedStratCon.text=The convoy was %sintercepted%s, and must be defended or the\ + \ supplies will be lost. +convoyRescuedStratCon.text=Thanks to your timely intervention the supplies were\ + \ %srecovered%s and are now being distributed by your logistics team. +convoyDefeatedStratCon.text=Even with your intervention the supplies were %slost%s. +convoyDestroyed.text=All communications with the convoy have been %slost%s. There wasn't\ + \ enough time to send reinforcements. + +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. 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 a total of %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. + +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 a total of %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, where possible. + +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. + +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 a total of %s tons of cargo space across all convoys. We currently\ + \ have a total of %s available space across %s convoy%s. Any excess space will be used to help\ + \ resupply allied forces operating in this AO. Damaged or uncrewed 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? + +usePlayerConvoyForced.text=%s, our employer has a delivery ready for us, but we will need to supply\ + \ our own convoys.\ +
\ +
This delivery requires a total of %s tons of cargo space across all convoys. We currently\ + \ have a total of %s available space across %s convoy%s. Any excess space will be used to help\ + \ resupply allied forces operating in this AO. Damaged or uncrewed 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 +optionAmmo.text=Ammunition +optionArmor.text=Armor +focusDescription.text=%s, we might be able to lean a little on our contact and ask them to focus on\ + \ specific types of supplies.\ +
\ +
Would you like to pick a focus? + +# 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. +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. +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. +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. +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. +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. +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. +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. +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. +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. + +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. + +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. + + +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. + + +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +weakenedSupplies11.text=We have processed your recent supply request, and the shipment is now en\ + \ route. The enemy's incapacity ensures a smooth process. Confirm delivery upon\ + \ receipt and report any discrepancies as needed. +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. +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. +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. +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. +weakenedSupplies16.text=The supplies requested by your unit have been shipped and are in transit.\ + \ The enemy's incapacity means an easy delivery. Follow all standard protocols\ + \ upon receipt. +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. +weakenedSupplies18.text=Your supply request has been fulfilled, and the shipment is currently en\ + \ route. The enemy's state ensures a smooth receiving process, so logistics\ + \ personnel can follow routine intake and verification. +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. + + +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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 clashes. +stalemateSupplies13.text=The supplies you requisitioned have been approved and dispatched. With\ + \ the conflict unresolved, ensure thorough inventory checks upon delivery,\ + \ and notify us of any concerns immediately. +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. +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. +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. +stalemateSupplies17.text=The shipment you requested is on its way. Given the uncertain outcome,\ + \ confirm the quantity and condition of the supplies upon arrival and let us\ + \ know if any adjustments are needed for future shipments. +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. +stalemateSupplies19.text=The supplies you requested have been dispatched and are on schedule for\ + \ delivery. With both sides still fighting, ensure your team is prepared for\ + \ receipt and confirm delivery as per usual protocols. + + +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. +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. +advancingSupplies2.text=The supplies you requested have been dispatched and should arrive soon.\ + \ With the enemy making coordinated strikes, ensure personnel are ready to\ + \ receive the shipment and verify its contents promptly, following standard\ + \ protocols. +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. +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. +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. +advancingSupplies6.text=Supplies requested by your unit have been shipped and are currently in\ + \ transit. Given the enemy's strong advances, follow standard protocols for\ + \ receiving and inventory verification upon arrival, but be ready for potential\ + \ disruptions. +advancingSupplies7.text=We are delivering the requested supplies as per your requisition form.\ + \ With the enemy asserting dominance, complete standard verification upon\ + \ receipt swiftly, and let us know of any issues requiring immediate attention. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. + + +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. +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. +dominatingSupplies2.text=The supplies you requested have been dispatched and should arrive soon.\ + \ As the enemy exerts pressure, ensure personnel are ready to receive the\ + \ shipment and verify its contents swiftly, following standard protocols. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. + + +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. + +guerrillaAddressGeneric.text=Boss +guerrillaAddressMale.text=Bossman +guerrillaAddressFemale.text=Bossma'am + +guerrillaSupplies0.text=%s, word's out that you're tangling with some rough %s forces.\ + \ 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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. +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. + + +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 +logisticsMessage.text=%s, we're getting an update from the supply convoy. +logisticsPatch.text=Patch Them Through +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 the\ + \ convoy pushes forward more out of habit than hope. +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 old 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, eyes constantly scanning the horizon. ETA to delivery remains 18\ + \ hours, with no delays expected. +statusUpdate27.text=An allied logistics convoy needed basic rations, %s. The exchange was just\ + \ another handoff interaction in a war that's filled with them - brief moments of connection\ + \ between those worn down by the same relentless grind. As we move forward, the crew can't help\ + \ but feel the weight of each stop; each encounter a reminder of the endless cycle we're part of.\ + \ 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\ + \ focus was sharp 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 couldn'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 - one that stirs little emotion now. Morale is low, but it doesn't hinder our progress.\ + \ 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 impact has been primarily concentrated on rear positions, with limited\ + \ effectiveness against. 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 convoy resumes its\ + \ advance, maintaining current 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 strikes. 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 for immediate coordination.\ + \ 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 precision fire. Convoy continues forward, maintaining formation\ + \ integrity. 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 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, clashes continue to threaten our route, as enemy\ + \ forces maintain sporadic but intense autocannon fire. Convoy is taking evasive\ + \ maneuvers, keeping movement steady under pressure. Crews are focused, executing\ + \ defensive tactics while maintaining operational speed. Enemy persistence 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 advances. 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, they failed\ + \ to secure any ground and eventually fell back under sustained pressure. The convoy kept\ + \ moving, but the tension in the air is thick. +statusUpdateEnemyStalemate21.text=%s, the 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 exchanges of fire keep both sides pinned 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 holds its ground,\ + \ maintaining formation under relentless pressure. Crews are running on pure resolve,\ + \ pushing through the exhaustion as we wait for a moment to break the deadlock. +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. For several tense minutes, there was no clear path forward. 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. The convoy pressed on, but the intensity of the assault has left\ + \ us wary of 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. The crew is adapting to the shifting battlefield, 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. The battlefield is littered with 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 trigger another clash.\ + \ Despite the uncertainty, we're holding the line. +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. CWhile the progress is steady, caution remains\ + \ the priority. +statusUpdateEnemyStalemate31.text=%s, sustained heavy fire left us locked in place for\ + \ several hours. The engagement was intense, with incoming rounds forcing a defensive\ + \ posture. Despite the pressure, the convoy remains intact, with armor holding against\ + \ repeated impacts. It was a close situation, but effective countermeasures ensured our\ + \ safety. Crews maintained composure throughout, and no critical systems were\ + \ compromised. We have now resumed forward movement. +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, another engagement concluded inconclusively, with\ + \ neither side able to secure control. The exchange of fire was sustained, but the\ + \ convoy's defensive stance held firm. Enemy forces eventually withdrew to their\ + \ positions, unable to break through. Convoy integrity remains intact, and forward\ + \ momentum continues. Crews are focused, displaying calm determination despite the\ + \ ongoing challenges. +statusUpdateEnemyStalemate34.text=%s, 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\ + \ formations under pressure. While no major gains have been made, the convoy remains\ + \ operational, and we're pressing forward with unwavering resolve. +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, the convoy\ + \ maintained defensive formations, repelling 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,\ + \ resulting in negligible gains for either side. 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 systems reporting operational. +statusUpdateEnemyStalemate37.text=%s, encountered heavy resistance in this sector,\ + \ characterized by sustained LRM and PPC fire. Crews remain composed, with focus\ + \ centered on 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 every effort is made to keep moving forward. +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. Crews are adapting quickly,\ + \ executing 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 tightening lines. Defensive actions are\ + \ active, keeping advancing forces at bay. The convoy pushes 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. Morale remains low, but the sight of clearer\ + \ ground ahead provides a glimmer of hope. +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\ + \ aligned 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 resupply points. +statusUpdateEnemyOverwhelming9.text=%s, enemy launched a strong push at the\ + \ river crossing, using entrenched positions to disrupt movement. Defensive\ + \ structures provided cover for concentrated fire, limiting convoy progression.\ + \ 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 under 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. Crews are focused solely on survival, 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\ + \ sustained catastrophic damage. 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. Enemy positions remain active, 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.\ + \ All personnel remain focused, aware that any hesitation could prove fatal. +statusUpdateEnemyOverwhelming16.text=%s, convoy's position 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. The convoy pushes forward. +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, though every second feels like a race against time. +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 the convoy remains composed. +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. Convoy continues forward, adapting to the evolving threat. +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 the mission remains the priority. +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. Urgent efforts are underway to rally crews, emphasizing unity.\ + \ Progress is slower than expected, but the convoy remains intact. +statusUpdateEnemyOverwhelming29.text=%s, maintaining formation under heavy fire is\ + \ proving difficult. Enemy units are targeting weak spots, 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 desperate actions\ + \ are needed to right this sinking ship. +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 its 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\ + \ fill the air. Crews are pushing 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 continuing to pursue from multiple directions,\ + \ and we're struggling to keep ahead. +statusUpdateEnemyOverwhelming40.text=%s, pulling back swiftly, leaving 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 convoy integrity is preserved. +statusUpdateEnemyOverwhelming41.text=%s, enemy's relentless assault at the river\ + \ crossing nearly broke the convoy's 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\ + \ rapid convoy adjustments. 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 dogging our steps,\ + \ 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 to avoid entanglement. +statusUpdateEnemyOverwhelming47.text=%s, enemy's assault at the depot\ + \ 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.The convoy presses on,\ + \ 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 must keep pushing forward. +statusUpdateEnemyOverwhelming49.text=%s, faced a series of well-timed strikes at\ + \ the last checkpoint. Enemy assault 'Meks gained control of key paths.\ + \ 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. + + +statusUpdateIntercepted0.text=%s, rear assault underway. Hostile 'Meks penetrating our lines.\ + \ Transports are fully exposed; formation in disarray. Incoming contacts unrelenting.\ + \ Reinforcements needed urgently. Attempting to fall back, but chaos reigns. They're closing in\ + \ fast - holding this position much longer isn't viable, we're going to try regrouping in %s-%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. Our current position is %s-%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 to\ + \ %s-%s. +statusUpdateIntercepted3.text=%s, we're surrounded. Central defenses collapsed. Crew making a final\ + \ stand, barely holding. Supplies are exposed, no fallback route. Immediate reinforcements needed\ + \ at %s-%s to prevent total loss. +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. Current position: %s-%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 to %s-%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 at %s-%s. +statusUpdateIntercepted7.text=%s, we're at breaking point. Hostiles control key supply routes,\ + \ regroup attempts have failed. Supplies dwindling, morale fading. Reinforcements must arrive now,\ + \ or total defeat is inevitable.

[Convoy's last known location is %s-%s] +statusUpdateIntercepted8.text=%s, surrounded on all fronts. Hostile forces closing in rapidly.\ + \ Immediate reinforcements essential - without them, complete collapse is certain. Situation\ + \ critical at %s-%s. +statusUpdateIntercepted9.text=%s, convoy devastated. Sustained bombardment by enemy 'Meks. Supplies\ + \ fully exposed - crew attempting to destroy remaining stock. Reinforcements required urgently at\ + \ %s-%s; convoy is coming apart. +statusUpdateIntercepted10.text=%s, convoy in disarray. Defensive lines breached in all sectors. Crew\ + \ falling back, supplies being seized. Reinforcements requested urgently at %s-%s.\ + \ Situation dire. +statusUpdateIntercepted11.text=%s, critical state at %s-%s. Hostiles advancing quickly. Supplies\ + \ compromised. Reinforcements essential - total defeat imminent without immediate support. +statusUpdateIntercepted12.text=%s, surrounded with no escape at %s-%s. Crew attempting fallback\ + \ but with no route available. Immediate support required to avoid total annihilation. +statusUpdateIntercepted13.text=%s, overwhelmed on all sides at %s-%s. Final line breached.\ + \ Immediate support required, but time is running out. +statusUpdateIntercepted14.text=%s, we're at the edge. Convoy in ruins, crew desperately holding\ + \ ground at %s-%s. Reinforcements needed urgently, or this is the end. +statusUpdateIntercepted15.text=%s, defenses completely breached. Convoy collapsing under enemy\ + \ pressure at %s-%s. Reinforcements urgently needed to have any chance of survival. +statusUpdateIntercepted16.text=%s, enemy closing in, convoy overrun. Regroup efforts have failed.\ + \ Reinforcements required urgently at %s-%s to prevent complete collapse. +statusUpdateIntercepted17.text=%s, enemy advancing uncontested in %s-%s. Supplies exposed and\ + \ unable to secure or destroy. Reinforcements required urgently - time is critical. +statusUpdateIntercepted18.text=%s, under relentless fire in %s-%s. Supplies\ + \ compromised, crew taking heavy losses. Reinforcements needed immediately - situation has\ + \ reached critical level. +statusUpdateIntercepted19.text=%s, situation hopeless. Last line of defense lost. Convoy collapsing,\ + \ all units breaking down. Reinforcements needed urgently at %s-%s. + + +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 holding out with desperation, 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 comms are\ + \ lost! Crew is fighting to survive, but without reinforcements, it's over!\ + \ Trying to regroup, but enemy is too strong! They've broken through -\ + \ [explosions and gunfire] - morale is gone! 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! Enemy has full control, and convoy is falling\ + \ apart! Supplies exposed, and crew is fighting with desperation! 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/src/mekhq/MekHQ.java b/MekHQ/src/mekhq/MekHQ.java index 7689c4b838..113674b331 100644 --- a/MekHQ/src/mekhq/MekHQ.java +++ b/MekHQ/src/mekhq/MekHQ.java @@ -532,8 +532,9 @@ public void gameVictory(PostGameResolution gve) { 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()) { diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index a610f94fe1..d7ff94e5ef 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -72,6 +72,7 @@ import mekhq.campaign.mission.enums.AtBMoraleLevel; import mekhq.campaign.mission.enums.MissionStatus; import mekhq.campaign.mission.enums.ScenarioStatus; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; import mekhq.campaign.mod.am.InjuryUtil; import mekhq.campaign.parts.*; import mekhq.campaign.parts.enums.PartQuality; @@ -144,13 +145,15 @@ import static mekhq.campaign.force.StrategicFormation.recalculateStrategicFormations; import static mekhq.campaign.market.contractMarket.ContractAutomation.performAutomatedActivation; +import static mekhq.campaign.mission.AtBContract.pickRandomCamouflage; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.convoyFinalMessageDialog; import static mekhq.campaign.personnel.SkillType.S_ADMIN; import static mekhq.campaign.personnel.backgrounds.BackgroundsController.randomMercenaryCompanyNameGenerator; import static mekhq.campaign.personnel.education.EducationController.getAcademy; +import static mekhq.campaign.personnel.enums.PersonnelStatus.KIA; import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.Payout.isBreakingContract; import static mekhq.campaign.unit.Unit.SITE_FACILITY_BASIC; import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; -import static mekhq.campaign.mission.AtBContract.pickRandomCamouflage; /** * The main campaign class, keeps track of teams and units * @@ -3787,6 +3790,10 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem if (stub) { scenario.convertToStub(this, ScenarioStatus.DEFEAT); addReport("Failure to deploy for " + scenario.getName() + " resulted in defeat."); + + if (scenario.getStratConScenarioType().isResupply()) { + processAbandonedConvoy(contract, (AtBDynamicScenario) scenario); + } } else { scenario.clearAllForcesAndPersonnel(this); } @@ -3847,6 +3854,57 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem } } + /** + * Processes an abandoned convoy. The player is presented with a defeat dialog, + * and checks each player template force in the scenario for being a convoy force. If it is a + * convoy force, its units are treated as abandoned units. + * Each crew member of these units is set as either KIA or POW based on a die roll. + * It finally removes each unit from the campaign. + * + * @param contract The current {@link AtBContract}. + * @param scenario The relevant {@link AtBDynamicScenario}. + */ + private void processAbandonedConvoy(AtBContract contract, AtBDynamicScenario scenario) { + convoyFinalMessageDialog(this, contract.getEmployerFaction()); + + for (Integer forceId : scenario.getPlayerTemplateForceIDs()) { + try { + Force force = getForce(forceId); + + if (force.isConvoyForce()) { + for (UUID unitID : force.getAllUnits(false)) { + Unit unit = getUnit(unitID); + + for (Person crewMember : unit.getCrew()) { + decideCrewMemberFate(crewMember); + } + + removeUnit(unitID); + } + } + } catch (Exception ex) { + logger.warn(ex.getMessage(), ex); + } + } + } + + /** + * Determines the fate of a given crew member based on a random roll and updates their status + * accordingly. + *

+ * We're using the CamOps rules for infantry survival here and assuming anyone who isn't dead + * has been captured. + * + * @param person The crew member whose fate will be decided + */ + private void decideCrewMemberFate(Person person) { + PersonnelStatus status = KIA; + if (Compute.d6(2) > 7) { + status = PersonnelStatus.POW; + } + person.changeStatus(this, currentDay, status); + } + /** * Processes the new day actions for various AtB systems *

@@ -3906,6 +3964,11 @@ private void processNewDayATB() { if (!report.isBlank()) { addReport(report); } + + // Resupply + if (getLocation().isOnPlanet() && getLocation().getCurrentSystem().equals(contract.getSystem())) { + processResupply(contract); + } } } @@ -4008,6 +4071,24 @@ public void negotiateAdditionalSupportPoints() { } } + /** + * 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} + */ + private void processResupply(AtBContract contract) { + if (!contract.getContractType().isGuerrillaWarfare() || Compute.d6(1) > 4) { + int dropCount = (int) Math.max(1, Math.floor((double) contract.getRequiredLances() / 3)); + Resupply resupplies = new Resupply(this, contract); + resupplies.getResupply(dropCount, false); + } + } + /** * Processes the new day for all personnel present in the campaign. *

@@ -6769,57 +6850,6 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, 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; diff --git a/MekHQ/src/mekhq/campaign/CampaignOptions.java b/MekHQ/src/mekhq/campaign/CampaignOptions.java index a8f2769c36..ca483428c9 100644 --- a/MekHQ/src/mekhq/campaign/CampaignOptions.java +++ b/MekHQ/src/mekhq/campaign/CampaignOptions.java @@ -563,8 +563,6 @@ 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; @@ -1199,8 +1197,6 @@ public CampaignOptions() { // Contract Operations mercSizeLimited = false; restrictPartsByMission = true; - bonusPartExchangeValue = 500000; - bonusPartMaxExchangeCount = 10; limitLanceWeight = true; limitLanceNumUnits = true; useStrategy = true; @@ -4556,22 +4552,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; } @@ -5192,8 +5172,6 @@ public void writeToXml(final PrintWriter pw, int indent) { 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); @@ -6218,10 +6196,6 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve 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")) { diff --git a/MekHQ/src/mekhq/campaign/CampaignSummary.java b/MekHQ/src/mekhq/campaign/CampaignSummary.java index 93face382c..6093fb8ee5 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,44 @@ 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) { + if (comparison > 0) { report.append(""); - } else if (cargoTonsRounded == cargoCapacityRounded) { + } else 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(""); diff --git a/MekHQ/src/mekhq/campaign/force/Force.java b/MekHQ/src/mekhq/campaign/force/Force.java index 85ea4d1656..0a33fa7236 100644 --- a/MekHQ/src/mekhq/campaign/force/Force.java +++ b/MekHQ/src/mekhq/campaign/force/Force.java @@ -77,6 +77,7 @@ public class Force { private Camouflage camouflage; private String desc; private boolean combatForce; + private boolean convoyForce; private boolean isStrategicFormation; private int overrideStrategicFormation; private FormationLevel formationLevel; @@ -100,6 +101,7 @@ public Force(String name) { setCamouflage(new Camouflage()); setDescription(""); this.combatForce = true; + this.convoyForce = false; this.isStrategicFormation = false; this.overrideStrategicFormation = STRATEGIC_FORMATION_OVERRIDE_NONE; this.formationLevel = FormationLevel.NONE; @@ -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,6 +173,24 @@ public void setCombatForce(boolean combatForce, boolean setForSubForces) { } } + /** + * @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 boolean isStrategicFormation() { return isStrategicFormation; } @@ -390,7 +410,7 @@ public Vector getUnits() { public Vector getAllUnits(boolean combatForcesOnly) { Vector allUnits; - if (combatForcesOnly && !isCombatForce()) { + if (combatForcesOnly && !isCombatForce() && !isConvoyForce()) { allUnits = new Vector<>(); } else { allUnits = new Vector<>(units); @@ -685,6 +705,7 @@ public void writeToXML(PrintWriter pw1, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "desc", desc); } MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "combatForce", combatForce); + MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "convoyForce", convoyForce); MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "overrideStrategicFormation", overrideStrategicFormation); MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "formationLevel", formationLevel.toString()); MHQXMLUtility.writeSimpleXMLTag(pw1, indent, "populateOriginNode", overrideFormationLevel.toString()); @@ -733,6 +754,8 @@ public void writeToXML(PrintWriter pw1, int indent) { retVal.setDescription(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("combatForce")) { retVal.setCombatForce(Boolean.parseBoolean(wn2.getTextContent().trim()), false); + } else if (wn2.getNodeName().equalsIgnoreCase("convoyForce")) { + retVal.setConvoyForce(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("overrideStrategicFormation")) { retVal.setOverrideStrategicFormation(Integer.parseInt(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("formationLevel")) { 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 0000000000..630b13074b --- /dev/null +++ b/MekHQ/src/mekhq/campaign/market/procurement/Procurement.java @@ -0,0 +1,366 @@ +/* + * 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.warn("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); + } + } + + logger.info(successfulParts.toString()); + + 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/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 6bf29b5b2b..e936ee41c8 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -44,6 +44,7 @@ import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.enums.AtBContractType; import mekhq.campaign.mission.enums.AtBMoraleLevel; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; import mekhq.campaign.personnel.Bloodname; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.backgrounds.BackgroundsController; @@ -156,7 +157,6 @@ public class AtBContract extends Contract { protected int contractScoreArbitraryModifier; protected int moraleMod = 0; - protected int numBonusParts; /* lasts for a month, then removed at next events roll */ protected boolean priorLogisticsFailure; @@ -226,7 +226,6 @@ public AtBContract(String name) { batchallAccepted = true; setMoraleLevel(AtBMoraleLevel.STALEMATE); routEnd = null; - numBonusParts = 0; priorLogisticsFailure = false; specialEventScenarioDate = null; battleTypeMod = 0; @@ -760,46 +759,66 @@ public int getContractScoreArbitraryModifier() { return contractScoreArbitraryModifier; } - public void doBonusRoll(Campaign campaign) { + /** + * Performs a bonus roll with varying outcomes based on the roll results. + * + *

The outcomes are as follows: + *

+ * + * @param campaign the campaign to modify based on the bonus roll. + * @return {@code true} if the bonus roll result is a Resupply, otherwise {@code false}. + * @throws IllegalStateException if the bonus roll result is not between 1 and 6 (inclusive). + */ + public boolean doBonusRoll(Campaign campaign) { int number; int roll = d6(); switch (roll) { - case 1: /* 1d6 dependents */ + case 1 -> { /* 1d6 dependents */ if (campaign.getCampaignOptions().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 { + return true; } - break; - case 2: + } + case 2 -> { campaign.addReport("Bonus: Ronin"); recruitRonin(campaign); - break; - case 3: - numBonusParts++; - campaign.addReport("Bonus: Part"); - break; - case 4: + } + case 3 -> { // Resupply + return true; + } + case 4 -> { campaign.addReport("Bonus: Unit"); addBonusUnit(campaign, UnitType.TANK); - break; - case 5: + } + case 5 -> { campaign.addReport("Bonus: Unit"); addBonusUnit(campaign, UnitType.AEROSPACEFIGHTER); - break; - case 6: + } + case 6 -> { campaign.addReport("Bonus: Unit"); addBonusUnit(campaign, MEK); - break; - default: - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/mission/AtBContract.java/doBonusRoll: " + roll); + } + default -> throw new IllegalStateException( + "Unexpected value in mekhq/campaign/mission/AtBContract.java/doBonusRoll: " + roll); } + + return false; } /** @@ -868,12 +887,12 @@ 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; @@ -881,25 +900,30 @@ public void checkEvents(Campaign c) { switch (getContractType().generateEventType()) { case EVT_BONUSROLL: - c.addReport("Special Event: "); - doBonusRoll(c); + campaign.addReport("Special Event: "); + if (doBonusRoll(campaign)) { + campaign.addReport("Bonus: Captured Supplies"); + Resupply supplyDrops = new Resupply(campaign, this); + supplyDrops.getResupply(1, true, true); + } + 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 +952,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,7 +979,7 @@ public void checkEvents(Campaign c) { break; case 2: text += "Internal Dissension"; - specialEventScenarioDate = getRandomDayOfMonth(c.getLocalDate()); + specialEventScenarioDate = getRandomDayOfMonth(campaign.getLocalDate()); specialEventScenarioType = AtBScenario.AMBUSH; break; case 3: @@ -971,7 +995,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 +1005,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 +1023,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; } } @@ -1108,7 +1132,6 @@ protected int writeToXMLBegin(final PrintWriter pw, int indent) { if (routEnd != null) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "routEnd", routEnd); } - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "numBonusParts", getNumBonusParts()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "partsAvailabilityLevel", getPartsAvailabilityLevel()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "extensionLength", extensionLength); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "sharesPct", sharesPct); @@ -1193,8 +1216,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")) { @@ -1480,18 +1501,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; } diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java index 20f56264b9..698744f9de 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java @@ -119,6 +119,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()); diff --git a/MekHQ/src/mekhq/campaign/mission/Scenario.java b/MekHQ/src/mekhq/campaign/mission/Scenario.java index 4bf26f8e86..4b82f70bec 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; @@ -145,8 +135,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); @@ -193,6 +184,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; } @@ -248,6 +247,7 @@ public void setCloaked(boolean cloaked) { this.cloaked = cloaked; } + @Override public int getStartingPos() { return startingPos; } @@ -472,7 +472,7 @@ public void setMinWindStrength(Wind strength) { /** * Create a PlanetaryConditions object from variables - * + * * @return PlanetaryConditions object */ public PlanetaryConditions createPlanetaryConditions() { @@ -498,7 +498,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 +531,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 +701,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 +718,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 +736,12 @@ 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 +768,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 +805,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 +832,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; } @@ -875,10 +865,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 +876,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.ordinal()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "desc", desc); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "report", report); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "startingPos", startingPos); @@ -1023,6 +1011,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("scenarioType")) { + retVal.setStratConScenarioType(ScenarioType.fromOrdinal(Integer.parseInt(wn2.getTextContent()))); } else if (wn2.getNodeName().equalsIgnoreCase("status")) { retVal.setStatus(ScenarioStatus.parseFromString(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("id")) { diff --git a/MekHQ/src/mekhq/campaign/mission/ScenarioObjectiveProcessor.java b/MekHQ/src/mekhq/campaign/mission/ScenarioObjectiveProcessor.java index 78d975624b..78bdac7630 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,12 +21,15 @@ 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.stratcon.StratconRulesManager; +import org.apache.logging.log4j.LogManager; import java.util.*; @@ -201,22 +204,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 +309,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 +327,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 +337,21 @@ 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 + * This method processes individual objective effects based on their type. + * + * @param campaign The current campaign. + * @param effect The objective effect that needs to be processed. + * @param scaleFactor If the effect is not fixed, the scale factor for how much the effect will + * influence the outcome. + * @param tracker The {@link ResolveScenarioTracker} instance that tracks the resolution of the + * scenario in the current campaign. + * @param dryRun If {@code true}, the function will not perform the actual updates on the + * campaign and contract, but will return a {@link String} containing a description + * of what will happen instead. + * @return A text description of what actions would be performed if it is a dry run. Empty string + * otherwise. */ - 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 +366,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 +378,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 +411,24 @@ 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()); + if (contract.doBonusRoll(tracker.getCampaign())) { + dropSize++; + } + } + + if (dropSize > 0) { + LogManager.getLogger().info("ScenarioObjectiveProcessor.java"); + campaign.addReport("Bonus: Captured Supplies"); + Resupply supplyDrops = new Resupply(campaign, contract); + supplyDrops.getResupply(1, false, true); } } } diff --git a/MekHQ/src/mekhq/campaign/mission/ScenarioTemplate.java b/MekHQ/src/mekhq/campaign/mission/ScenarioTemplate.java index b48c2fdf67..415541d196 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; @@ -44,7 +30,20 @@ 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.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 @@ -60,6 +59,7 @@ public class ScenarioTemplate implements Cloneable { public static final String PRIMARY_PLAYER_FORCE_ID = "Player"; public String name; + private ScenarioType stratConScenarioType; public String shortBriefing; public String detailedBriefing; @@ -77,27 +77,32 @@ 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 = getStratConScenarioType(); + 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; } /** 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 0000000000..6c38dde592 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/enums/ScenarioType.java @@ -0,0 +1,54 @@ +/* + * 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; + +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; + } + + public static ScenarioType fromOrdinal(int ordinal) { + for (ScenarioType scenarioType : values()) { + if (scenarioType.ordinal() == ordinal) { + return scenarioType; + } + } + + final MMLogger logger = MMLogger.create(ScenarioType.class); + logger.error(String.format("Unknown Scenario Type ordinal: %s - returning NONE.", ordinal)); + + return NONE; + } +} 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 0000000000..6f843cab87 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/Resupply.java @@ -0,0 +1,2393 @@ +/* + * 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.Mek; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; +import mekhq.MekHQ; +import mekhq.campaign.Campaign; +import mekhq.campaign.finances.Money; +import mekhq.campaign.force.Force; +import mekhq.campaign.force.StrategicFormation; +import mekhq.campaign.icons.StandardForceIcon; +import mekhq.campaign.market.procurement.Procurement; +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.parts.*; +import mekhq.campaign.parts.enums.PartQuality; +import mekhq.campaign.parts.equipment.*; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.Skill; +import mekhq.campaign.personnel.SkillType; +import mekhq.campaign.personnel.enums.PersonnelRole; +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 javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.math.BigInteger; +import java.util.List; +import java.util.*; +import java.util.stream.Collectors; + +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.finances.enums.TransactionType.EQUIPMENT_PURCHASE; +import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; +import static mekhq.campaign.market.procurement.Procurement.getFactionTechCode; +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.stratcon.StratconContractInitializer.getUnoccupiedCoords; +import static mekhq.campaign.stratcon.StratconRulesManager.generateExternalScenario; +import static mekhq.campaign.stratcon.StratconRulesManager.getRandomTrack; +import static mekhq.campaign.unit.Unit.getRandomUnitQuality; +import static mekhq.campaign.universe.Factions.getFactionLogo; +import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; +import static mekhq.utilities.ReportingUtilities.spanOpeningWithCustomColor; + +public class Resupply { + private final Campaign campaign; + private final AtBContract contract; + private final Faction employerFaction; + private final Faction enemyFaction; + 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 final Random random; + private boolean usePlayerConvoy; + private Map playerConvoys; + private double totalPlayerCargoCapacity; + private int targetCargoTonnage; + private int negotiatorSkill; + + private static final Money HIGH_VALUE_ITEM = Money.of(250000); + private static final int CARGO_MULTIPLIER = 2; + private static final double INTERCEPTION_LOAD_INFLUENCE = 50; + + private static final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + private static final MMLogger logger = MMLogger.create(Resupply.class); + + /** + * Resupply constructor, initializing the supply drop with a provided campaign and contract. + * It also sets up this instance with default values, and collects potential parts for the supply + * drop. + * + * @param campaign The campaign this supply drop is part of. + * @param contract The relevant contract. + */ + public Resupply(Campaign campaign, AtBContract contract) { + this.campaign = campaign; + this.contract = contract; + random = new Random(); + + employerFaction = contract.getEmployerFaction(); + enemyFaction = contract.getEnemy(); + usePlayerConvoy = contract.getCommandRights().isIndependent(); + targetCargoTonnage = calculateTargetCargoTonnage(campaign, contract); + + currentYear = campaign.getGameYear(); + employerIsClan = enemyFaction.isClan(); + employerTechCode = getFactionTechCode(employerFaction); + + focusAmmo = 0.25; + focusArmor = 0.25; + focusParts = 0.5; + + buildPartsPools(collectParts()); + calculateNegotiationSkill(); + calculatePlayerConvoyValues(); + } + + /** + * @return {@code true} if the player's convoy is being used, {@code false} otherwise. + */ + public boolean isUsePlayerConvoy() { + return usePlayerConvoy; + } + + /** + * Enumeration of the different item types that can be dropped by a Resupply + */ + public enum DropType { + PARTS, + ARMOR, + AMMO + } + + /** + * Calculates the target cargo tonnage for a resupply. + * The calculation considers the total tonnage of combat units in the campaign, + * compared with the tonnage cap defined by the employer. + *

+ * A 'combat unit' is defined as any unit not flagged as non-combat, that is in a Combat + * Team and not in a Force flagged as non-combat. The tonnage cap is the maximum tonnage + * the employer is willing to support, calculated based on the required number of lances + * for the contract and a set tonnage allowance per unit. + *

+ * The final target cargo tonnage is calculated as the minimum of the total unit tonnage + * and the tonnage cap, divided by 100, and then multiplied by required lances divided by 3. + * This total is then rounded to the nearest integer. + * + * @param campaign The current campaign. + * @param contract The relevant {@link AtBContract} for which the cargo tonnage is being calculated. + * @return The calculated target cargo tonnage. + */ + private 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 (StrategicFormation formation : campaign.getStrategicFormationsTable().values()) { + Force force = campaign.getForce(formation.getForceId()); + + if (force == null) { + continue; + } + + if (!force.isCombatForce()) { + continue; + } + + for (UUID unitId : force.getAllUnits(true)) { + try { + Unit unit = campaign.getUnit(unitId); + + Entity entity = unit.getEntity(); + + if (isProhibitedUnitType(entity, false)) { + continue; + } + + unitTonnage += entity.getWeight(); + } catch (Exception ignored) { + // If we get an exception, it's because one of the getters returned null, + // at which point we'd just continue anyway, so we might as well just ignore the exception. + } + } + } + + // 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.getRequiredLances() * 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 = 100; + final double dropSize = baseTonnage / TONNAGE_DIVIDER; + + // Finally, we calculate the total convoy size. This is dropSize multiplied by dropCount + final double dropCount = (double) contract.getRequiredLances() / 3; + + return (int) round(dropSize * dropCount); + } + + /** + * Calculate the estimated cargo requirements for a given campaign and contract. + * + * @param campaign The campaign for which the cargo requirements are being estimated. + * @param contract The contract associated with the campaign. + * @return The estimated cargo requirements in tons as a formatted string, e.g., "50t". + */ + public static String getEstimatedCargoRequirements(Campaign campaign, AtBContract contract) { + int baseCapacity = calculateTargetCargoTonnage(campaign, contract); + + return (baseCapacity * CARGO_MULTIPLIER) + "t"; + } + + /** + * Calculate the negotiationModifier for the Resupply. + *

+ * If the contract type is Guerrilla Warfare, then the flagged commander of the campaign + * will be set as the negotiator. For other contract types, this method searches for the best + * Logistics Administrator among campaign admins to represent as the negotiator. If one is found + * and their negotiation skill is determined, the negotiationModifier is calculated based on + * their skill level, with a max value capped at 3. + *

+ * Resulting negotiationModifier is stored in {@code negotiationModifier} instance variable. + */ + 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); + } + } + } + + /** + * Displays a dialog depicting a resupply of parts and possibly cash. + * + * @param droppedItems The items being dropped. + * @param isLoot A boolean indicating if the supply drop is categorized as loot. + * @param isContractEnd A boolean indicating if the supply drop is related to the contract's conclusion. + */ + private void supplyDropDialog(List droppedItems, boolean isLoot, boolean isContractEnd) { + ImageIcon icon = getFactionLogo(campaign, employerFaction.getShortName(), true); + + if (isLoot || isContractEnd) { + icon = getCampaignFactionIcon(campaign); + } + icon = scaleImageIconToWidth(icon, 100); + + StringBuilder message = new StringBuilder(getInitialDescription(isLoot, isContractEnd)); + + List partsReport = createPartsReport(droppedItems); + if (!partsReport.isEmpty()) { + if (!isLoot && !isContractEnd) { + 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); + + 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" + Compute.randomInt(50) + + ".text") + " x" + (int) Math.ceil((double) rationPacks / 5)); + } + } + + String[] columns = formatColumnData(partsReport); + + message.append("") + .append("") + .append("") + .append("") + .append("
").append(columns[0]).append("").append(columns[1]).append("").append(columns[2]).append("
"); + + createResupplyDialog(icon, message.toString(), droppedItems, isLoot || isContractEnd, + false); + } + + /** + * This method checks if the current campaign has a defined unit icon and returns it. + * If the campaign doesn't have a defined icon, it retrieves the campaign faction logo. + * + * @param campaign The current campaign. + * + * @return an {@link ImageIcon} representing the faction of the current campaign. + */ + private static ImageIcon getCampaignFactionIcon(Campaign campaign) { + ImageIcon icon; + StandardForceIcon campaignIcon = campaign.getUnitIcon(); + + if (campaignIcon.getFilename() == null) { + icon = getFactionLogo(campaign, campaign.getFaction().getShortName(), + true); + } else { + icon = new ImageIcon(campaignIcon.getFilename()); + } + return icon; + } + + /** + * Creates a resupply dialog window with the specified parameters. + * + * @param icon The icon to be displayed in the dialog. + * @param message The message to be displayed in the dialog. + * @param droppedItems List of items to be dropped. Can be null. + * @param isLootOrContractEnd {@link Boolean} value representing if the dialog is being created + * as the result of either loot or contract end. + * @param isSmuggler {@link Boolean} value representing if the dialog is an offer from a + * smuggler. + */ + public void createResupplyDialog(ImageIcon icon, String message, List droppedItems, + boolean isLootOrContractEnd, boolean isSmuggler) { + final int DIALOG_WIDTH = UIUtil.scaleForGUI(700); + final String title = resources.getString("dialog.title"); + + JDialog dialog = new JDialog(); + dialog.setLayout(new BorderLayout()); + dialog.setTitle(title); + + dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + ActionListener dialogActionListener = e -> { + dialog.dispose(); + + if (isLootOrContractEnd) { + deliverDrop(droppedItems); + // Smuggler deliveries are handled elsewhere + } else if (!isSmuggler) { + if (isUsePlayerConvoy()) { + while (!droppedItems.isEmpty() && !playerConvoys.isEmpty()) { + Force chosenConvoy = getRandomConvoy(); + double cargoCapacity = getTotalCargoCapacity(chosenConvoy); + + List delivery = new ArrayList<>(); + + boolean loadingUp = true; + while (loadingUp) { + Part randomItem = droppedItems.get(random.nextInt(droppedItems.size())); + + double itemWeight = randomItem.getTonnage(); + // The multiplier is there as the convoy is assumed to also be running + // supplies to allies in the AO. + cargoCapacity -= itemWeight * 2; + + if (cargoCapacity <= 0) { + loadingUp = false; + } + + delivery.add(randomItem); + } + + processSingleConvoy(delivery, chosenConvoy); + playerConvoys.remove(chosenConvoy); + } + // If we're not using a player convoy, only make a single delivery. + // While not necessarily realistic, we don't want the player getting spammed with + // NPC interception missions. + } else { + processSingleConvoy(droppedItems, null); + } + } + }; + + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent windowEvent) { + dialogActionListener.actionPerformed(null); + } + }); + + JLabel labelIcon = new JLabel("", SwingConstants.CENTER); + labelIcon.setIcon(icon); + labelIcon.setAlignmentX(Component.CENTER_ALIGNMENT); + + JPanel panel = new JPanel(); + BoxLayout boxlayout = new BoxLayout(panel, BoxLayout.Y_AXIS); + panel.setLayout(boxlayout); + panel.add(labelIcon); + + JLabel description = new JLabel( + String.format("

%s
", + UIUtil.scaleForGUI(DIALOG_WIDTH), message)); + description.setAlignmentX(Component.CENTER_ALIGNMENT); + panel.add(description); + + JScrollPane scrollPane = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + dialog.add(scrollPane, BorderLayout.CENTER); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout()); + + JButton confirmButton = new JButton(resources.getString("confirmAccept.text")); + confirmButton.addActionListener(e -> { + dialog.dispose(); + campaign.getFinances().debit(EQUIPMENT_PURCHASE, campaign.getLocalDate(), + getSmugglerFee(droppedItems), resources.getString("smugglerFee.text")); + processSmuggler(droppedItems); + }); + + JButton refuseButton = new JButton(resources.getString("confirmRefuse.text")); + refuseButton.addActionListener(dialogActionListener); + + JButton okButton = new JButton(resources.getString("confirmReceipt.text")); + okButton.addActionListener(dialogActionListener); + + if (isSmuggler) { + buttonPanel.add(confirmButton); + buttonPanel.add(refuseButton); + } else { + buttonPanel.add(okButton); + } + + dialog.add(buttonPanel, BorderLayout.SOUTH); + + dialog.setResizable(false); + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + /** + * Calculates the total cargo capacity of a given convoy. + *

+ * This method iterates over each unit in the convoy. If the unit is fully crewed, not damaged, + * and not of a prohibited type, its cargo capacity is added to the total cargo capacity. + *

+ * If an exception occurs while retrieving the unit or its entity, + * that particular unit is skipped and not factored into the total cargo capacity. + * + * @param convoy the convoy whose total cargo capacity is to be calculated. + * @return the total cargo capacity of the convoy. + */ + private double getTotalCargoCapacity(Force convoy) { + double cargoCapacity = 0; + + for (UUID unitId : convoy.getAllUnits(false)) { + try { + Unit unit = campaign.getUnit(unitId); + Entity entity = unit.getEntity(); + + if (!unit.isFullyCrewed() || unit.isDamaged() + || isProhibitedUnitType(entity, true)) { + cargoCapacity += unit.getCargoCapacity(); + } + } catch (Exception ignored) { + // If we fail to get entity or unit, we just skip that unit. + } + } + + return cargoCapacity; + } + + /** + * Generates and displays a dialog detailing the smuggler's offer. + * The dialog features a formatted description of the offer, along with a parts report. + * + * @param droppedItems The list of items offered by the smuggler. + */ + private void smugglerOfferDialog(List droppedItems) { + ImageIcon icon = Factions.getFactionLogo(campaign, "PIR", true); + icon = scaleImageIconToWidth(icon, 100); + + StringBuilder message = new StringBuilder(getSmugglerDescription(droppedItems, false)); + + List partsReport = new ArrayList<>(); + if (droppedItems != null) { + partsReport = createPartsReport(droppedItems); + } + + List entries = new ArrayList<>(partsReport); + String[] columns = formatColumnData(entries); + + message.append("") + .append("") + .append("") + .append("") + .append("
").append(columns[0]).append("").append(columns[1]).append("").append(columns[2]).append("
"); + + createResupplyDialog(icon, message.toString(), droppedItems, false, true); + } + + /** + * Processes a convoy by determining interception chances based on morale level and performs + * actions accordingly. + * + * @param droppedItems List of items dropped by the convoy. + */ + private void processSingleConvoy(List droppedItems, @Nullable Force playerConvoy) { + final String STATUS_FORWARD = "statusUpdate"; + final String STATUS_AFTERWARD = ".text"; + + AtBMoraleLevel morale = contract.getMoraleLevel(); + int interceptionChance = morale.ordinal(); + + // This allows a convoy to weigh up to 200t before suffering a detection Malus. + // It also ensures that very small convoys will only get a maximum detection bonus of -3. + // We deliberately do not want a cap on detection Malus, as eventually convoys will reach + // such a size that even a blind OpFor will be able to find them. + double convoyWeight = -200; + if (playerConvoy != null) { + for (UUID unitId : playerConvoy.getAllUnits(false)) { + try { + Unit unit = campaign.getUnit(unitId); + Entity entity = unit.getEntity(); + convoyWeight += entity.getWeight(); + } catch (Exception ignored) { + // If we fail to fetch any necessary information, we just skip the unit. + } + } + + interceptionChance += (int) Math.ceil(convoyWeight / INTERCEPTION_LOAD_INFLUENCE); + } + + if (morale.isRouted()) { + // If the enemy has been Routed, there is zero chance of Interception. + // Even if there are still enemy forces in the AO, they are more concerned with immediate + // survival than preventing the player from getting new toys. + interceptionChance = 0; + } else { + // If the enemy hasn't been Routed, there is always a 1in10 chance of Interception, + // no matter how stealthy the convoy. + interceptionChance = Math.max(0, interceptionChance); + } + + boolean isIntercepted = false; + String message = ""; + + if (Compute.randomInt(10) < interceptionChance) { + message = resources.getString(STATUS_FORWARD + "Intercepted" + + Compute.randomInt(20) + STATUS_AFTERWARD); + isIntercepted = true; + // We don't include roleplay events for NPC convoys + } else if ((playerConvoy != null) && (Compute.randomInt(10) < interceptionChance)) { + if (Compute.d6() == 1) { + message = 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; + } + + message = resources.getString(STATUS_FORWARD + "Enemy" + morale + + Compute.randomInt(50) + STATUS_AFTERWARD); + } + } + + if (playerConvoy != null) { + if (message.isEmpty()) { + interceptionChance = (int) round((double) interceptionChance / 2); + } + + if (campaign.getCampaignOptions().isUseFatigue()) { + increaseFatigue(playerConvoy, interceptionChance); + } + } + + if (!message.isEmpty()) { + if (isIntercepted + || (!forceContainsOnlyVTOLForces(playerConvoy) && !forceContainsOnlyAerialForces(playerConvoy))) { + createConvoyMessage(playerConvoy, droppedItems, message, isIntercepted); + } + } else { + campaign.addReport(String.format(resources.getString("convoySuccessful.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); + deliverDrop(droppedItems); + } + } + + /** + * Processes a smuggler's offer, including whether the player has been swindled. + * Swindle chance is based on contract morale. + * If the player is swindled a follow-up dialog is triggered. + * + * @param droppedItems The list of items dropped by smuggler. + */ + private void processSmuggler(List droppedItems) { + AtBMoraleLevel morale = contract.getMoraleLevel(); + int swindleChance = morale.ordinal(); + + if (Compute.randomInt(10) < swindleChance) { + String message = getSmugglerDescription(null, true); + createSwindledMessage(message); + } else { + campaign.addReport(String.format(resources.getString("convoySuccessfulSmuggler.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); + deliverDrop(droppedItems); + } + } + + public void createConvoyMessage(@Nullable Force targetConvoy, List droppedItems, + String convoyStatusMessage, boolean isIntercepted) { + createConvoyMessage(targetConvoy, droppedItems, convoyStatusMessage, isIntercepted, true); + } + + public void createConvoyMessage(@Nullable Force targetConvoy, List droppedItems, + String convoyStatusMessage, boolean isIntercepted, + boolean isIntroduction) { + StratconTrackState track = getRandomTrack(contract); + + StratconCoords convoyGridReference; + if (track != null) { + convoyGridReference = getUnoccupiedCoords(track, false); + } else { + convoyGridReference = null; + } + + // Dialog dimensions and representative + final int DIALOG_WIDTH = UIUtil.scaleForGUI(400); + + // Creates and sets up the dialog + JDialog dialog = new JDialog(); + dialog.setTitle(resources.getString("dialog.title")); + dialog.setLayout(new BorderLayout()); + + // Defines the action when the dialog is being dismissed + ActionListener dialogDismissActionListener = e -> { + dialog.dispose(); + if (isIntroduction) { + createConvoyMessage(targetConvoy, droppedItems, convoyStatusMessage, isIntercepted, + false); + } else { + if (isIntercepted) { + if (campaign.getCampaignOptions().isUseStratCon()) { + processConvoyInterception(droppedItems, targetConvoy, track, convoyGridReference); + } else { + campaign.addReport(String.format(resources.getString("convoyInterceptedAtB.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + } + } else { + campaign.addReport(String.format(resources.getString("convoySuccessful.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); + deliverDrop(droppedItems); + } + } + }; + + // 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); + + Person logisticsOfficer = pickLogisticsRepresentative(); + ImageIcon speakerIcon = getSpeakerIcon(targetConvoy, isIntroduction, logisticsOfficer); + speakerIcon = scaleImageIconToWidth(speakerIcon, UIUtil.scaleForGUI(100)); + iconLabel.setIcon(speakerIcon); + dialog.add(iconLabel, BorderLayout.NORTH); + + // Prepares and adds the description + String message = convoyStatusMessage; + String speaker = null; + if (isIntroduction) { + message = String.format(resources.getString("logisticsMessage.text"), + getCommanderTitle(campaign, false)) + "
"; + + if (logisticsOfficer != null) { + speaker = logisticsOfficer.getFullTitle(); + } else { + speaker = String.format(resources.getString("dialogBorderCampaignSpeaker.text"), + campaign.getName()); + } + } else { + if (isIntercepted) { + String coords = resources.getString("static.text"); + if (convoyGridReference != null) { + coords = convoyGridReference.toBTString(); + } + + String sector = resources.getString("hiss.text"); + if (track != null) { + sector = track.getDisplayableName(); + } + + message = String.format(message, getCommanderTitle(campaign, false), + sector, coords); + } else { + message = String.format(message, getCommanderTitle(campaign, false)); + } + + if (usePlayerConvoy) { + if (targetConvoy != null) { + speaker = getConvoySpeaker(targetConvoy); + } + + if (speaker == null) { + speaker = String.format(resources.getString("dialogBorderConvoySpeakerDefault.text"), + campaign.getName()); + } + } else { + speaker = String.format(resources.getString("dialogBorderConvoySpeakerDefault.text"), + contract.getEmployer()); + } + } + + 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"), speaker))); + descriptionPanel.add(description); + dialog.add(descriptionPanel, BorderLayout.CENTER); + + // Prepares and adds the confirm button + JButton confirmButton = new JButton(resources.getString("logisticsPatch.text")); + if (!isIntroduction) { + if (isIntercepted) { + confirmButton.setText(resources.getString("logisticsDestroyed.text")); + } else { + confirmButton.setText(resources.getString("logisticsReceived.text")); + } + } + confirmButton.addActionListener(dialogDismissActionListener); + dialog.add(confirmButton, BorderLayout.SOUTH); + + // Pack, position and display the dialog + dialog.setResizable(false); + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + /** + * Retrieves the speaker's icon for use in message dialogs. + * + * @param targetConvoy The target convoy in the current context, used to determine the convoy + * commander's icon. May be {@code null} if a player convoy is not present. + * @param isIntroduction A boolean flag indicating whether the communication is an introduction. + * @param logisticsOfficer The logistics officer involved in the communication, may be + * {@code null} if not present. + * + * @return The {@link ImageIcon} representing the speaker. If no specific speaker icon is + * available, the method will return {@code null}. + */ + private @Nullable ImageIcon getSpeakerIcon(@Nullable Force targetConvoy, boolean isIntroduction, + @Nullable Person logisticsOfficer) { + ImageIcon speakerIcon = null; + if (isIntroduction) { + if (logisticsOfficer == null) { + speakerIcon = getCampaignFactionIcon(campaign); + } else { + speakerIcon = logisticsOfficer.getPortrait().getImageIcon(); + } + } else { + if (usePlayerConvoy) { + speakerIcon = getConvoyIcon(targetConvoy); + } + + if (speakerIcon == null) { + if (usePlayerConvoy) { + speakerIcon = getCampaignFactionIcon(campaign); + } else { + speakerIcon = getFactionLogo(campaign, employerFaction.getShortName(), + true); + } + } + } + return speakerIcon; + } + + /** + * Generates and displays the 'swindled' follow-up dialog. + * + * @param message The text message to be displayed in the dialog detailing the swindle event. + */ + public void createSwindledMessage(String message) { + // Dialog dimensions and representative + final int DIALOG_WIDTH = UIUtil.scaleForGUI(400); + + // Creates and sets up the dialog + JDialog dialog = new JDialog(); + dialog.setTitle(resources.getString("dialog.title")); + dialog.setLayout(new BorderLayout()); + + // Prepares and adds the icon of the representative as a label + JLabel iconLabel = new JLabel(); + iconLabel.setHorizontalAlignment(JLabel.CENTER); + + ImageIcon factionLogo = getFactionLogo(campaign, "PIR", true); + factionLogo = scaleImageIconToWidth(factionLogo, UIUtil.scaleForGUI(100)); + iconLabel.setIcon(factionLogo); + dialog.add(iconLabel, BorderLayout.NORTH); + + // Prepares and adds the description + 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"), ""))); + descriptionPanel.add(description); + dialog.add(descriptionPanel, BorderLayout.CENTER); + + // Prepares and adds the confirm button + JButton confirmButton = new JButton(resources.getString("logisticsDestroyed.text")); + confirmButton.addActionListener(e -> dialog.dispose()); + dialog.add(confirmButton, BorderLayout.SOUTH); + + // Pack, position and display the dialog + dialog.setResizable(false); + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + /** + * Increases the fatigue convoy crews. + * + * @param playerConvoy The id of the convoy whose crew's fatigue should be increased. + * @param fatigueIncrease How much fatigue should be adjusted. + */ + private void increaseFatigue(Force playerConvoy, int fatigueIncrease) { + for (UUID unitId : playerConvoy.getAllUnits(false)) { + Unit unit = campaign.getUnit(unitId); + + if (unit != null) { + for (Person crewMember : unit.getCrew()) { + crewMember.increaseFatigue(fatigueIncrease); + } + } + } + } + + /** + * Scales an {@link ImageIcon} to the specified width while maintaining its aspect ratio. + * + * @param icon The {@link ImageIcon} to be scaled. + * @param width The desired width. + * @return The scaled {@link ImageIcon}. + */ + static ImageIcon scaleImageIconToWidth(ImageIcon icon, int 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); + } + + /** + * Retrieves the speaker of the convoy. + * + * @param targetConvoy The relevant convoy. + * @return The speaker of the convoy. + */ + private String getConvoySpeaker(@Nullable Force targetConvoy) { + String speaker = null; + + if (targetConvoy != null) { + UUID convoyCommanderId = targetConvoy.getForceCommanderID(); + + if (convoyCommanderId != null) { + Person convoyCommander = campaign.getPerson(convoyCommanderId); + + if (convoyCommander != null) { + speaker = convoyCommander.getFullTitle(); + } + } + } + + if (targetConvoy != null) { + if (speaker == null) { + speaker = targetConvoy.getName(); + } else { + speaker = speaker + ", " + targetConvoy.getName(); + } + } else { + speaker = String.format(resources.getString("dialogBorderConvoySpeakerDefault.text"), + campaign.getName()); + } + + return speaker; + } + + /** + * Retrieves the icon of the convoy. + * + * @param targetConvoy The convoy target. + * @return The {@link ImageIcon} of the convoy. + */ + private @Nullable ImageIcon getConvoyIcon(@Nullable Force targetConvoy) { + if (targetConvoy == null) { + return null; + } + + UUID convoyCommanderId = targetConvoy.getForceCommanderID(); + + if (convoyCommanderId == null) { + return null; + } + + Person convoyCommander = campaign.getPerson(convoyCommanderId); + + if (convoyCommander == null) { + return null; + } + + return convoyCommander.getPortrait().getImageIcon(); + } + + /** + * Processes the interception of a convoy. + * + * @param droppedItems List of items dropped by the convoy. + * @param targetConvoy The target convoy. Can be {@code null}. + * @param track The track reference for the convoy's location. + * @param convoyGridReference The grid reference for the convoy's location. + */ + private void processConvoyInterception(List droppedItems, @Nullable Force targetConvoy, + StratconTrackState track, StratconCoords convoyGridReference) { + 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"; + + String templateAddress = GENERIC; + + if (targetConvoy != null) { + if (forceContainsOnlyAerialForces(targetConvoy)) { + templateAddress = PLAYER_AEROSPACE_CONVOY; + } else if (forceContainsMajorityVTOLForces(targetConvoy)) { + templateAddress = PLAYER_VTOL_CONVOY; + } else { + templateAddress = PLAYER_CONVOY; + } + } + ScenarioTemplate template = ScenarioTemplate.Deserialize(templateAddress); + + if (template == null) { + campaign.addReport(String.format(resources.getString("convoyErrorTemplate.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + templateAddress, CLOSING_SPAN_TAG)); + deliverDrop(droppedItems); + return; + } + + if (track == null) { + campaign.addReport(String.format(resources.getString("convoyErrorTracks.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + templateAddress, CLOSING_SPAN_TAG)); + deliverDrop(droppedItems); + return; + } + StratconScenario scenario = generateExternalScenario(campaign, contract, track, + convoyGridReference, template, false); + + // 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(); + backingScenario.setDate(campaign.getLocalDate()); + + if (targetConvoy != null) { + backingScenario.addForce(targetConvoy.getId(), "Player"); + targetConvoy.setScenarioId(backingScenario.getId(), campaign); + scenario.commitPrimaryForces(); + } + + Loot loot = new Loot(); + + if (droppedItems != null) { + for (Part part : droppedItems) { + 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("convoyDestroyed.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + } + } + + /** + * This method iterates over all units in the given force and checks if each unit + * is an aerospace unit or a conventional fighter. If it finds a unit that isn't + * one of these types, it immediately returns {@code false}, indicating that the force + * contains non-aerial units. If it successfully goes through all units without + * finding a non-aerial unit, it returns {@code true}. + *

+ * If a unit cannot be accounted for (for example, due to a {@code null} reference), + * it will be ignored and the method will proceed to the next unit. + * + * @param targetConvoy The force that is being checked for unit types. + * @return A boolean indicating whether all units in the force are Aerospace or Conventional + * Fighter units. + */ + private boolean forceContainsOnlyAerialForces(@Nullable Force targetConvoy) { + if (targetConvoy == null) { + return false; + } + + for (UUID unitId : targetConvoy.getAllUnits(false)) { + try { + Unit unit = campaign.getUnit(unitId); + Entity entity = unit.getEntity(); + + if (!entity.isAerospace() && !entity.isConventionalFighter()) { + return false; + } + } catch (Exception ignored) { + // If we run into issues, we can just skip that unit and check the others. + } + } + + return true; + } + + /** + * This method iterates over all units in the given force and counts the total number of units + * and the number of VTOL (or WIGE) units. If an error occurs while processing a unit (for + * example, due to a {@code null} reference), it gets ignored and the method proceeds with the + * next unit. + *

+ * Once all units are processed, it checks if the number of VTOL (or WIGE) units is more than + * or equal to a third the total number of units in the force. + * + * @param targetConvoy The force that is being checked for unit types. + * @return A boolean indicating whether the majority of units in the force are of VTOL (or + * WIGE) types. + */ + private boolean forceContainsMajorityVTOLForces(@Nullable Force targetConvoy) { + if (targetConvoy == null) { + return false; + } + + int convoySize = 0; + int vtolCount = 0; + + for (UUID unitId : targetConvoy.getAllUnits(false)) { + try { + Unit unit = campaign.getUnit(unitId); + Entity entity = unit.getEntity(); + + if (entity.isAirborneVTOLorWIGE()) { + vtolCount++; + } + + convoySize++; + } catch (Exception ignored) { + // If we run into issues, we can just skip that unit and check the others. + } + } + + return vtolCount >= Math.floor((double) convoySize / 3); + } + + /** + * This method iterates over every unit in the given force and checks if it's a VTOL (or WIGE) + * unit. If it encounters a unit that is not a VTOL (or WIGE), it immediately returns {@code false}, + * indicating that there exist non-VTOL units in the force. Upon checking all units without + * finding a non-VTOL (or WIGE) unit, it will return {@code true}. + *

+ * If an error occurs while processing a unit (for example, due to a {@code null} reference), + * that unit will be ignored and the method proceeds with the next unit. + * + * @param targetConvoy The force to be verified if all units are of VTOL (or WIGE) types. + * @return A boolean indicating whether all units in the force are of VTOL (or WIGE) types or not. + */ + private boolean forceContainsOnlyVTOLForces(@Nullable Force targetConvoy) { + if (targetConvoy == null) { + return false; + } + + for (UUID unitId : targetConvoy.getAllUnits(false)) { + try { + Unit unit = campaign.getUnit(unitId); + Entity entity = unit.getEntity(); + + if (!entity.isAirborneVTOLorWIGE()) { + return false; + } + } catch (Exception ignored) { + // If we run into issues, we can just skip that unit and check the others. + } + } + + return true; + } + + /** + * Picks a random convoy from available ones. + * + * @return The ID of the randomly selected convoy. + */ + @Nullable + private Force getRandomConvoy() { + Force[] keys = playerConvoys.keySet().toArray(new Force[0]); + return keys[random.nextInt(keys.length)]; + } + + /** + * Creates the final message dialog for the convoy. + * + * @param campaign The current campaign. + * @param employerFaction The employing faction. + */ + public static void convoyFinalMessageDialog(Campaign campaign, Faction employerFaction) { + // Dialog dimensions and representative + final int DIALOG_WIDTH = UIUtil.scaleForGUI(400); + + // Creates and sets up the dialog + JDialog dialog = new JDialog(); + dialog.setTitle(resources.getString("dialog.title")); + dialog.setLayout(new BorderLayout()); + + // Prepares and adds the icon of the representative as a label + JLabel iconLabel = new JLabel(); + iconLabel.setHorizontalAlignment(JLabel.CENTER); + iconLabel.setIcon(Factions.getFactionLogo(campaign, employerFaction.getShortName(), + true)); + dialog.add(iconLabel, BorderLayout.NORTH); + + // Prepares and adds the description + String convoyStatusMessage = resources.getString("statusUpdateAbandoned" + + Compute.randomInt(20) + ".text"); + String message = String.format(convoyStatusMessage, getCommanderTitle(campaign, false)); + + JLabel description = new JLabel( + String.format("

%s
", + UIUtil.scaleForGUI(DIALOG_WIDTH), message)); + description.setHorizontalAlignment(JLabel.CENTER); + dialog.add(description, BorderLayout.CENTER); + + // Prepares and adds the confirm button + JButton confirmButton = new JButton(resources.getString("logisticsPatch.text")); + confirmButton.setText(resources.getString("logisticsDestroyed.text")); + confirmButton.addActionListener(e -> dialog.dispose()); + dialog.add(confirmButton, BorderLayout.SOUTH); + + // Pack, position and display the dialog + dialog.setResizable(false); + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + /** + * Picks a logistics representative from available personnel. + * + * @return The chosen logistics representative. + */ + @Nullable + private Person pickLogisticsRepresentative() { + Person highestRankedCharacter = null; + + for (Person person : campaign.getLogisticsPersonnel()) { + if (highestRankedCharacter == null) { + highestRankedCharacter = person; + continue; + } + + if (person.outRanksUsingSkillTiebreaker(campaign, highestRankedCharacter)) { + highestRankedCharacter = person; + } + } + + return highestRankedCharacter; + } + + /** + * Retrieves the commander's title. + * + * @param campaign The current campaign. + * @param includeSurname {@link Boolean} indicating if the surname is to be included. + * @return The title of the commander. + */ + static String getCommanderTitle(Campaign campaign, boolean includeSurname) { + String placeholder = resources.getString("commander.text"); + Person commander = campaign.getFlaggedCommander(); + + if (commander == null) { + return placeholder; + } + + String rank = commander.getRankName(); + String title = (rank == null || rank.contains("None")) ? "" : rank; + + return (includeSurname) ? title + ' ' + commander.getSurname() : title; + } + + /** + * This method is responsible for delivering the actual supply drop and updating the campaign's + * inventory and finances. + * + * @param droppedItems A list of items to be dropped. + */ + private void deliverDrop(List droppedItems) { + for (Part part : droppedItems) { + if (part instanceof AmmoBin) { + campaign.getQuartermaster().addAmmo(((AmmoBin) part).getType(), + ((AmmoBin) part).getFullShots() * 5); + } else if (part instanceof Armor) { + int quantity = (int) Math.ceil(((Armor) part).getArmorPointsPerTon() * 5); + ((Armor) part).setAmount(quantity); + campaign.getWarehouse().addPart(part, true); + } else { + campaign.getWarehouse().addPart(part, true); + } + } + } + + /** + * This function formats parts into column data for display purposes. + * + * @param partsReport A list of parts. + * @return An array of strings representing columns for display. + */ + public 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; + } + + /** + * Method that returns the drop weight of a given part. The weight is determined based on the type + * of part, with "MissingPart" instances given a higher weight. + * + * @param part Part object whose weight is to be determined. + * @return int representing the weight of the part. + */ + static int getDropWeight(Part part) { + int weight = 1; + + if (part instanceof MissingPart) { + return weight * 10; + } else { + return weight; + } + } + + /** + * Extracts parts from the campaign's units and applies warehouse and industry weight modifiers. + *

+ * The method checks each part of each unit in the campaign's units. Only parts from units that + * are available and not marked as salvage are considered. Parts are ignored if they meet the + * following criteria: + * - the part is from a large vessel, super heavy, or conventional infantry unit + * - the part relates to a certain locations or is extinct + * - the acquisition is taking place before Battle of Tukayyid and is Clan or Mixed tech, while + * the employer faction isn't Clan + *

+ * For each valid part, we create a "PartDetails" object which includes the part and its adjusted + * weight, then add it to a map. If the part is already in the map we increase its weight by the + * weight of the new part. + * + * @return A map of parts and their respective details. If an error occurs during parts collection, + * an empty map is returned and the error is logged. + */ + private Map collectParts() { + final Collection units = campaign.getUnits(); + Map processedParts = new HashMap<>(); + + try { + for (Unit unit : units) { + Entity entity = unit.getEntity(); + + if (isProhibitedUnitType(entity, false)) { + continue; + } + + if (!unit.isSalvage() && unit.isAvailable()) { + List parts = unit.getParts(); + for (Part part : parts) { + if (isIneligiblePart(part, unit)) { + continue; + } + + PartDetails partDetails = new PartDetails(part, 1); + + processedParts.merge(part.toString(), partDetails, (oldValue, newValue) -> { + oldValue.setWeight(oldValue.getWeight() + newValue.getWeight()); + return oldValue; + }); + } + } + } + + applyWarehouseWeightModifiers(processedParts); + } catch (Exception exception) { + logger.error("Aborted parts collection.", exception); + } + + return processedParts; + } + + /** + * Adjusts the weights of the parts based on how many are stored in the warehouse. + * It takes into consideration both the quantity of part in storage and the multiplier specific + * for each part based on its type. + * + * @param partsList The map of parts and their details to be adjusted. + */ + private void applyWarehouseWeightModifiers(Map partsList) { + // Because of how AmmoBins work, we're always considering the campaign to have 0 rounds + // of ammo in storage, we could avoid this, but I don't think it's necessary. + for (Part part : campaign.getWarehouse().getSpareParts()) { + PartDetails targetPart = partsList.get(part.toString()); + if (targetPart != null) { + int spareCount = part.getQuantity(); + double multiplier = getPartMultiplier(part); + + double targetPartCount = targetPart.getWeight() * multiplier; + if ((targetPartCount - spareCount) < 1) { + partsList.remove(part.toString()); + } else { + targetPart.setWeight(targetPartCount); + } + } + } + } + + /** + * Checks if the given entity is a large craft, super heavy, or conventional infantry. + * + * @param entity The entity to check. + * @param excludeDropShips Whether to exclude DropShips from the check + * @return Boolean value {@code true} if the entity is large craft, super heavy or conventional + * infantry, otherwise {@code false}. + */ + private static boolean isProhibitedUnitType(Entity entity, boolean excludeDropShips) { + if (entity.isDropShip() && excludeDropShips) { + return false; + } + + return entity.isLargeCraft() || entity.isSuperHeavy() || entity.isConventionalInfantry(); + } + + /** + * Checks if a given part is ineligible for inclusion in a resupply. + * + * @param part The part to check for eligibility. + * @param unit The unit to associate the part with. + * @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) + || checkTransporter(part); + } + + /** + * Checks if a given part is of a type that is specifically excluded from resupplies. + * This method should be expanded as ineligible parts are found. + * + * @param part The part to check for eligibility. + * @return {@code true} if the part is ineligible; {@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 whether a part is a center torso location of a Mek or if the Mek is extinct. + * + * @param part The part to check. + * @param mek The Mek to which the part belongs. + * @return {@code True} if the part is a central torso location or the Mek is extinct. + * {@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)); + } + + /** + * Checks if the given part is a location on a Tank but not a Rotor or Turret. + * + * @param part The part to check. + * @return {@code True} if the part is a location on a Tank but not a Rotor or Turret. + * {@code False} otherwise. + */ + private boolean checkTankLocation(Part part) { + return part instanceof TankLocation && !(part instanceof Rotor || part instanceof Turret); + } + + /** + * Checks if the given part is a Transport Bay or door. + * + * @param part The part to check. + * @return {@code True} if the part is a transport bay or door. {@code False} otherwise. + */ + private boolean checkTransporter(Part part) { + return part instanceof TransportBayPart; + } + + /** + * Calculates a multiplier for a part's weight based on the part's type. + * + * @param part The part for which to get the multiplier. + * @return The multiplier for the part's weight. + */ + 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; + } + + /** + * Checks if a given part is a specific class of equipment subtype. + * + *

The function returns false if the part is an instance of any one of the following: + *

    + *
  • AmmoBin
  • + *
  • AmmoStorage
  • + *
  • BattleArmorEquipmentPart
  • + *
  • HeatSink
  • + *
  • JumpJet
  • + *
+ * + * @param part The part to check for equipment subtype. + * @return If the part is not an instance of {@link EquipmentPart}, or it's an instance of + * {@link EquipmentPart} but not an instance of any one of the above classes, {@code true} is returned. + */ + 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; + } + + /** + * Populates the 'partsPool' instance variable with parts from 'potentialParts'. + */ + 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); + } + + /** + * Creates a copy of a given part with specific properties adjusted to fit a resupply. + * The properties adjusted include fixing any damage on the part, setting its quality to a randomly + * determined value (except for AmmoBins), and marking the part as brand new and not OmniPodded. + *

+ * If the part fails to clone, an error is logged and the method returns {@code null}. + * + * @param originPart The {@link Part} object to clone and modify. + * @return A cloned {@link Part} object , or {@code null} if cloning fails. + */ + 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; + } + + try { + clonedPart.fix(); + } catch (Exception e) { + clonedPart.setHits(0); + } + + clonedPart.setBrandNew(true); + clonedPart.setOmniPodded(false); + + return clonedPart; + } + + /** + * @return A randomly selected {@link Part} from the parts pool. + */ + private @Nullable Part getRandomPart() { + if (partsPool.isEmpty()) { + return null; + } + + Part randomPart = partsPool.get(random.nextInt(partsPool.size())); + randomPart.setQuality(getRandomPartQuality(negotiatorSkill)); + return randomPart; + } + + /** + * @return A randomly selected {@link Armor} from the armor pool. + */ + private @Nullable Part getRandomArmor() { + if (armorPool.isEmpty()) { + return null; + } + + Part randomArmor = armorPool.get(random.nextInt(armorPool.size())); + randomArmor.setQuality(getRandomPartQuality(negotiatorSkill)); + return randomArmor; + } + + /** + * @return A randomly selected {@link AmmoBin} from the ammo bin pool. + */ + private @Nullable Part getRandomAmmoBin() { + if (ammoBinPool.isEmpty()) { + return null; + } + + return ammoBinPool.get(random.nextInt(ammoBinPool.size())); + } + + /** + * This method initiates to generate the supply drop parts and display them on the dialog. + * The ‘dropCount’ parameter determines the number of times the part generation loop runs. + * + * @param dropCount Number of times the part-generation loop must run. + * @param bypassConvoyNeeds If {@code true} an NPC convoy will be provided, even for contracts + * where this wouldn't normally be possible. + */ + public void getResupply(int dropCount, boolean bypassConvoyNeeds) { + getResupply(dropCount, bypassConvoyNeeds, false, false); + } + + /** + * Overloaded method that also allows supply drops to consider whether the drops are classified + * as 'loot'. + * + * @param dropCount Number of times the part-generation loop must run. + * @param bypassConvoyNeeds If {@code true} an NPC convoy will be provided, even for contracts + * where this wouldn't normally be possible. + * @param isLoot Boolean that flags whether drops are classified as loot. + */ + public void getResupply(int dropCount, boolean bypassConvoyNeeds, boolean isLoot) { + getResupply(dropCount, isLoot, bypassConvoyNeeds, false); + } + + /** + * Overloaded method that also allows supply drops to consider whether the drops are at the end + * of the contract. + * + * @param dropCount Number of times the part-generation loop must run. + * @param bypassConvoyNeeds If {@code true} an NPC convoy will be provided, even for contracts + * where this wouldn't normally be possible. + * @param isLoot Boolean that flags whether drops are classified as loot. Should be set to + * {@code false} if this is being generated at the end of a contract. + * @param isContractEnd Boolean that flags whether drops are at the end of a contract. + */ + public void getResupply(int dropCount, boolean bypassConvoyNeeds, boolean isLoot, + boolean isContractEnd) { + // If the player hasn't negotiated for straight support, + // and they are not on a Guerrilla contract, + // then there is no need to handle resupplies beyond this point. + if (targetCargoTonnage == 0) { + return; + } + + boolean isIndependent = contract.getCommandRights().isIndependent(); + + if (!contract.getContractType().isGuerrillaWarfare() && !bypassConvoyNeeds) { + createPlayerConvoyOptionalDialog(); + } + + if (isIndependent && !usePlayerConvoy && !bypassConvoyNeeds) { + return; + } + + if (usePlayerConvoy) { + targetCargoTonnage = targetCargoTonnage * CARGO_MULTIPLIER; + } + + if (!isLoot) { + createResupplyFocusDialog(); + } + + List droppedItems = new ArrayList<>(); + for (int i = 0; i < dropCount; i++) { + droppedItems.addAll(getDrops(DropType.ARMOR)); + droppedItems.addAll(getDrops(DropType.AMMO)); + droppedItems.addAll(getDrops(DropType.PARTS)); + } + + if (droppedItems.isEmpty()) { + campaign.addReport(String.format(resources.getString("convoyUnsuccessful.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + return; + } + + if (contract.getContractType().isGuerrillaWarfare() && !(isLoot || isContractEnd)) { + smugglerOfferDialog(droppedItems); + } else { + supplyDropDialog(droppedItems, isLoot, isContractEnd); + } + } + + /** + * Gets a list of dropped items based on the specified drop type. + *

+ * This method generates a list of dropped parts, armor, or ammo items until a target weight + * is reached, which is calculated as a certain percentage of the total cargo tonnage, depending + * on the drop type. The target percentages are 50% for PARTS, 25% for ARMOR, and 25% for AMMO. + *

+ * The parts are chosen randomly, and for high value items, a die roll determines if the item is + * selected or substituted with another item. High value items are only given one chance per + * distinct part. The loop continues until the total tonnage of the dropped items reaches + * the target value. + *

+ * This method modifies the partsPool by removing the dropped items. + * If the partsPool becomes empty during the generation, the method ends early and returns + * the list of dropped items so far. + * + * @param dropType the type of items to drop, represented as an ItemType. + * @return a list of dropped parts. + */ + private List getDrops(DropType dropType) { + List droppedItems = new ArrayList<>(); + double runningTotal = 0; + + double targetValue = switch (dropType) { + case PARTS -> targetCargoTonnage * focusParts; + case ARMOR -> targetCargoTonnage * focusArmor; + case AMMO -> targetCargoTonnage * focusAmmo; + }; + + if (targetValue == 0) { + return droppedItems; + } + + while (runningTotal < targetValue) { + Part potentialPart = switch(dropType) { + case PARTS -> getRandomPart(); + case ARMOR -> getRandomArmor(); + case AMMO -> getRandomAmmoBin(); + }; + + // 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) { + return droppedItems; + } + + 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 PARTS -> partsPool.removeAll(Collections.singleton(potentialPart)); + case ARMOR -> armorPool.removeAll(Collections.singleton(potentialPart)); + case AMMO -> ammoBinPool.removeAll(Collections.singleton(potentialPart)); + } + } else { + partFetched = true; + } + + if (partFetched) { + switch (dropType) { + case PARTS -> partsPool.remove(potentialPart); + case ARMOR -> armorPool.remove(potentialPart); + case AMMO -> ammoBinPool.remove(potentialPart); + } + + runningTotal += potentialPart.getTonnage(); + droppedItems.add(potentialPart); + } + } + return droppedItems; + } + + /** + * This method creates a mapping report based on the provided dropped items. The report indicates + * the types of parts that have been dropped, the quantity of each type of part, and makes + * special note of those parts that are considered 'extinct' according to the year and the origin + * faction. + * + * @param droppedItems List of {@link Part} objects that were included in the supply drop. + * @return A map containing the dropped parts and their quantities. + */ + public List createPartsReport(List droppedItems) { + int year = campaign.getGameYear(); + Faction originFaction = campaign.getFaction(); + + Map entries = droppedItems.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()); + } + + /** + * This method generates a description for the supply drop, with text selection based on whether + * the drop is considered loot, whether it's tied to a contract ending or it's tied to a specific + * morale and contract type situation. It fetches a specific string from the resource bundle via + * a key that's built dynamically based on these factors. + * + * @param isLoot Boolean flag indicating whether the supply drop is considered as 'loot'. + * This should be set to {@code false} if the supply drop is being made at + * the end of the contract. + * @param isContractEnd Boolean flag indicating whether the supply drop occurs at the end of a contract. + * @return A {@link String} message picked from the resources, based on the given + * input parameters. + */ + private String getInitialDescription(boolean isLoot, boolean isContractEnd) { + if (isLoot) { + return resources.getString("salvaged" + Compute.randomInt(10) + ".text"); + } + + if (isContractEnd) { + return resources.getString("looted" + Compute.randomInt(10) + ".text"); + } + + AtBMoraleLevel morale = contract.getMoraleLevel(); + return resources.getString(morale.toString().toLowerCase() + "Supplies" + + Compute.randomInt(20) + ".text"); + } + /** + * Retrieves a formatted string indended for Smuggler dialogs. + * + * @param droppedItems List of items offered by the smuggler. Can be {@code null} if + * {@code wasSwindled} is {@code true}. + * @param wasSwindled Boolean indicating if the player has been swindled. + * @return A formatted string representing the smuggler's address to the player. + */ + private String getSmugglerDescription(@Nullable List droppedItems, boolean wasSwindled) { + String address = resources.getString("guerrillaAddressGeneric.text"); + + Person commander = campaign.getFlaggedCommander(); + if (commander != null) { + if (commander.getGender().isFemale()) { + address = resources.getString("guerrillaAddressFemale.text"); + } + + if (commander.getGender().isMale()) { + address = resources.getString("guerrillaAddressMale.text"); + } + } + + String enemyFactionReference = enemyFaction.getFullName(campaign.getGameYear()); + if (!enemyFactionReference.contains("Clan")) { + enemyFactionReference = "the " + enemyFactionReference; + } + + if (wasSwindled) { + return String.format( + resources.getString("guerrillaSwindled" + Compute.randomInt(25) + ".text"), + address, enemyFactionReference); + } else { + Money value = getSmugglerFee(droppedItems); + + return String.format( + resources.getString("guerrillaSupplies" + Compute.randomInt(25) + ".text"), + address, enemyFactionReference, value.toAmountAndSymbolString()); + } + } + + /** + * Calculates the smuggler's fee for a list of dropped items. + * The fee is 2 times the total actual value of all items. + * + * @param droppedItems The list of items dropped by smuggler. + * @return The calculated smuggler's fee as a {@link Money} object. + */ + private static Money getSmugglerFee(List droppedItems) { + Money value = Money.zero(); + for (Part part : droppedItems) { + value = value.plus(part.getActualValue()); + } + + value = value.multipliedBy(2); + return value; + } + + /** + * This method generates a randomized {@link PartQuality} entry. The randomness is adjusted by + * the modifier. The result describes the quality of a part that's delivered in the supply drop + * - it could range from poor to excellent, modelled in the {@link PartQuality} enum. Note that + * it uses the imported static {@code getRandomUnitQuality()} method. + * + * @param modifier An integer that will be used to adjust the range or distribution of the random + * part quality that's returned. + * @return A {@link PartQuality} object representing the quality of a part. + */ + static PartQuality getRandomPartQuality(int modifier) { + return getRandomUnitQuality(modifier); + } + + /** + * Triggers a dialog window providing convoy related information meant to be triggered at the + * beginning of a contract. + * + * @param campaign The current campaign. + * @param contract The relevant contract. + */ + public static void triggerContractStartDialog(Campaign campaign, AtBContract contract) { + // Retrieves the title from the resources + String title = resources.getString("dialog.title"); + + // An ImageIcon to hold the faction icon + ImageIcon icon = getCampaignFactionIcon(campaign); + + // Create a text pane to display the message + String message = getContractStartMessage(campaign, contract); + JTextPane textPane = new JTextPane(); + textPane.setContentType("text/html"); + textPane.setText(message); + textPane.setEditable(false); + + // Create a panel to display the icon and the message + JPanel panel = new JPanel(new BorderLayout()); + JLabel imageLabel = new JLabel(icon); + panel.add(imageLabel, BorderLayout.CENTER); + panel.add(textPane, BorderLayout.SOUTH); + + // Create a custom dialog + JDialog dialog = new JDialog(); + dialog.setTitle(title); + dialog.setLayout(new BorderLayout()); + + // Create an accept button and add its action listener. + JButton acceptButton = new JButton(resources.getString("convoyConfirm.text")); + acceptButton.addActionListener(e -> dialog.dispose()); + + // Create a panel for buttons and add buttons to it + JPanel buttonPanel = new JPanel(); + buttonPanel.add(acceptButton); + + // Add the original panel and button panel to the dialog + dialog.add(panel, BorderLayout.CENTER); + dialog.add(buttonPanel, BorderLayout.SOUTH); + + dialog.setResizable(false); + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + /** + * Calculates the convoy count and total cargo capacity of the player's convoys. + */ + 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); + } + } + } + } + } + + public void createPlayerConvoyOptionalDialog() { + final int DIALOG_WIDTH = UIUtil.scaleForGUI(400); + + // 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()); + + // Establish the speaker + Person logisticsOfficer = pickLogisticsRepresentative(); + + String speaker; + if (logisticsOfficer != null) { + speaker = logisticsOfficer.getFullTitle(); + } else { + speaker = String.format(resources.getString("dialogBorderCampaignSpeaker.text"), + campaign.getName()); + } + + // An ImageIcon to hold the faction icon + ImageIcon speakerIcon = getSpeakerIcon(null, true, logisticsOfficer); + speakerIcon = scaleImageIconToWidth(speakerIcon, UIUtil.scaleForGUI(100)); + + // Create and display the message + String pluralizer = playerConvoys.size() != 1 ? "s" : ""; + String messageResource; + + if (isUsePlayerConvoy()) { + messageResource = resources.getString("usePlayerConvoyForced.text"); + } else { + messageResource = resources.getString("usePlayerConvoyOptional.text"); + } + + String message = String.format(messageResource, getCommanderTitle(campaign, false), + calculateTargetCargoTonnage(campaign, contract) * CARGO_MULTIPLIER, + totalPlayerCargoCapacity, playerConvoys.size(), pluralizer, pluralizer); + + // 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"), speaker))); + descriptionPanel.add(description); + dialog.add(descriptionPanel, BorderLayout.CENTER); + + // Create a panel to display the icon and the message + JPanel panel = new JPanel(new BorderLayout()); + JLabel imageLabel = new JLabel(speakerIcon); + panel.add(imageLabel, BorderLayout.CENTER); + panel.add(descriptionPanel, BorderLayout.SOUTH); + + // Create the buttons and add their action listener. + JButton acceptButton = new JButton(resources.getString("confirmAccept.text")); + acceptButton.addActionListener(e -> { + dialog.dispose(); + usePlayerConvoy = true; + }); + acceptButton.setEnabled(!playerConvoys.isEmpty()); + + JButton refuseButton = new JButton(resources.getString("confirmRefuse.text")); + refuseButton.addActionListener(e -> { + dialog.dispose(); + usePlayerConvoy = false; + }); + + // Create a panel for buttons and add buttons to it + JPanel buttonPanel = new JPanel(); + buttonPanel.add(acceptButton); + buttonPanel.add(refuseButton); + + // Add a WindowListener to handle the close operation + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + usePlayerConvoy = false; + } + }); + + // Add the original panel and button panel to the dialog + dialog.add(panel, BorderLayout.CENTER); + dialog.add(buttonPanel, BorderLayout.SOUTH); + + dialog.setResizable(false); + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + public void createResupplyFocusDialog() { + final int DIALOG_WIDTH = UIUtil.scaleForGUI(400); + + // 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()); + + // Establish the speaker + Person logisticsOfficer = pickLogisticsRepresentative(); + + String speaker; + if (logisticsOfficer != null) { + speaker = logisticsOfficer.getFullTitle(); + } else { + speaker = String.format(resources.getString("dialogBorderCampaignSpeaker.text"), + campaign.getName()); + } + + // An ImageIcon to hold the faction icon + ImageIcon speakerIcon = getSpeakerIcon(null, true, logisticsOfficer); + speakerIcon = scaleImageIconToWidth(speakerIcon, UIUtil.scaleForGUI(100)); + + // Create and display the message + String messageResource = resources.getString("focusDescription.text"); + String message = String.format(messageResource, getCommanderTitle(campaign, false)); + + // 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"), speaker))); + descriptionPanel.add(description); + dialog.add(descriptionPanel, BorderLayout.CENTER); + + // Create a panel to display the icon and the message + JPanel panel = new JPanel(new BorderLayout()); + JLabel imageLabel = new JLabel(speakerIcon); + panel.add(imageLabel, BorderLayout.CENTER); + panel.add(descriptionPanel, BorderLayout.SOUTH); + + // Create the buttons and add their action listener. + JButton optionBalanced = new JButton(resources.getString("optionBalanced.text")); + optionBalanced.addActionListener(e -> { + dialog.dispose(); + // The class initialization assumes a balanced approach + }); + + // 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.addActionListener(e -> { + dialog.dispose(); + focusAmmo = 0; + focusArmor = 0.75; + focusParts = 0; + }); + + JButton optionAmmo = new JButton(resources.getString("optionAmmo.text")); + optionAmmo.addActionListener(e -> { + dialog.dispose(); + focusAmmo = 0.75; + focusArmor = 0; + focusParts = 0; + }); + + // Create a panel for buttons and add buttons to it + JPanel buttonPanel = new JPanel(); + buttonPanel.add(optionBalanced); + buttonPanel.add(optionArmor); + buttonPanel.add(optionAmmo); + + // Add the original panel and button panel to the dialog + dialog.add(panel, BorderLayout.CENTER); + dialog.add(buttonPanel, BorderLayout.SOUTH); + + dialog.setResizable(false); + dialog.pack(); + dialog.setModal(true); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + /** + * Creates a start message for a contract based on its type and resupply details. + * The message is formatted using a specific template according to the type of + * the contract and whether it involves guerrilla warfare or independent command rights. + * + * @param campaign The current campaign. + * @param contract The contract for which the start message is created. + * @return A formatted start message for the contract, enclosed within an HTML div with a defined width. + */ + private static String getContractStartMessage(Campaign campaign, AtBContract contract) { + int convoyCount = 0; + double totalCargoCapacity = 0; + + for (Force force : campaign.getAllForces()) { + if (force.isConvoyForce() && force.isStrategicFormation()) { + boolean hasCargo = false; + for (UUID unitId : force.getAllUnits(false)) { + Unit unit = campaign.getUnit(unitId); + + if (unit != null) { + if (unit.isDamaged()) { + continue; + } + + if (!unit.isFullyCrewed()) { + continue; + } + + double individualCargo = unit.getCargoCapacity(); + + if (!hasCargo && individualCargo > 0) { + hasCargo = true; + } + + totalCargoCapacity += individualCargo; + } + } + + if (hasCargo) { + convoyCount++; + } + } + } + + String convoyMessage; + String commanderTitle = getCommanderTitle(campaign, 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, + getEstimatedCargoRequirements(campaign, contract), totalCargoCapacity, convoyCount, + convoyCount != 1 ? "s" : ""); + } + + int width = UIUtil.scaleForGUI(500); + return String.format("
%s
", + width, convoyMessage); + } +} + +/** + * Class representing details about a part, including the part itself and its drop weight. + */ +class PartDetails { + private final Part part; + private double weight; + + /** + * Constructs a {@link PartDetails} object with associated part and weight. + * + * @param part The part to be stored in this object. + * @param weight The weight to be associated with the part. + */ + public PartDetails(Part part, double weight) { + this.part = part; + this.weight = weight; + } + + /** + * Returns the part associated with this object. + * + * @return the stored {@link Part} object. + */ + public Part getPart() { + return part; + } + + /** + * Gets the part's drop weight. + * + * @return The weight associated with the stored part. + */ + public double getWeight() { + return weight; + } + + /** + * Sets the drop weight for the part. + * + * @param weight The weight to be set. + */ + public void setWeight(double weight) { + this.weight = weight; + } +} 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 0000000000..942f46165c --- /dev/null +++ b/MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/StarLeagueCache.java @@ -0,0 +1,643 @@ +/* + * 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.mission.resupplyAndCaches.Resupply.getCommanderTitle; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.getDropWeight; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.scaleImageIconToWidth; +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.getRequiredLances(); 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/stratcon/StratconCampaignState.java b/MekHQ/src/mekhq/campaign/stratcon/StratconCampaignState.java index 17eaa6c5b9..39a8984ff3 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. * @@ -176,6 +176,20 @@ public void useSupportPoint() { supportPoints--; } + /** + * Decreases the number of support points by the specified increment. + * + * @param increment The number of support points to use/decrease. + */ + public void useSupportPoints(int increment) { + supportPoints -= increment; + } + + public void convertVictoryToSupportPoint() { + victoryPoints--; + supportPoints++; + } + /** * Convenience/speed method of determining whether or not a force with the given * ID has been deployed to a track in this campaign. diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 1918ce15e0..8c95060bf8 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -224,6 +224,26 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // Determine starting Support Points campaign.negotiateAdditionalSupportPoints(); + // 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 } diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index a87ad37985..3debf26b98 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -42,6 +42,8 @@ import mekhq.campaign.mission.atb.AtBScenarioModifier; import mekhq.campaign.mission.enums.AtBMoraleLevel; import mekhq.campaign.mission.enums.ContractCommandRights; +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.turnoverAndRetention.Fatigue; @@ -2191,6 +2193,35 @@ public static void processScenarioCompletion(ResolveScenarioTracker tracker) { processTrackForceReturnDates(track, campaign); track.removeScenario(scenario); + + if (backingScenario.getStratConScenarioType().isResupply()) { + ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Resupply"); + + if (victory) { + campaign.addReport(String.format(resources.getString("convoyRescuedStratCon.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + CLOSING_SPAN_TAG)); + } else { + campaign.addReport(String.format(resources.getString("convoyDefeatedStratCon.text"), + spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor()), + CLOSING_SPAN_TAG)); + } + } else 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; } } @@ -2313,6 +2344,10 @@ public static boolean processIgnoredScenario(StratconScenario scenario, Stratcon 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/unit/Unit.java b/MekHQ/src/mekhq/campaign/unit/Unit.java index 2eb4e33569..97cb430c3c 100644 --- a/MekHQ/src/mekhq/campaign/unit/Unit.java +++ b/MekHQ/src/mekhq/campaign/unit/Unit.java @@ -75,6 +75,8 @@ import java.util.*; import java.util.Map.Entry; import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static mekhq.campaign.parts.enums.PartQuality.*; @@ -1282,19 +1284,48 @@ public Money getSellValue() { return partsValue; } + /** + * Calculates and returns the total cargo capacity of a fully crewed entity. + * If the entity is not fully crewed, the cargo capacity will be returned as 0. + * The capacity is calculated based on the sum capacity of CargoBay and + * StandardSeatCargoBay type Bays in the entity, and from non-damaged + * EquipmentParts (with the 'part name' following the pattern "Cargo (X ton)" or + * "Cargo (X tons)"). Any erroneous cases are logged. + * + * @return The total cargo capacity of the fully crewed entity, + * or 0 if the entity is not fully crewed. + * @throws NumberFormatException If the equipment part named "Cargo (X ton)" or + * "Cargo (X tons)" does not contain a valid number for X. + */ public double getCargoCapacity() { + if (!isFullyCrewed()) { + return 0; + } + double capacity = 0; for (Bay bay : entity.getTransportBays()) { - if (bay instanceof CargoBay) { - capacity += bay.getCapacity(); - } - if (bay instanceof PillionSeatCargoBay) { + if (bay instanceof CargoBay || bay instanceof StandardSeatCargoBay) { capacity += bay.getCapacity(); } - if (bay instanceof StandardSeatCargoBay) { - capacity += bay.getCapacity(); + } + + Pattern cargoPattern = Pattern.compile("Cargo \\((.*) ton(s)?\\)"); + + for (Part part : getParts()) { + if (part instanceof EquipmentPart && !(part.needsFixing() || part.isMountedOnDestroyedLocation())) { + Matcher matcher = cargoPattern.matcher(part.getName()); + + if (matcher.find()) { + try { + double partCapacity = Double.parseDouble(matcher.group(1)); + capacity += partCapacity; + } catch (NumberFormatException e) { + logger.error(String.format("Failed to parse %s as double", matcher.group(1))); + } + } } } + return capacity; } diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index aed006bb84..f7e25f15b5 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -43,6 +43,7 @@ import mekhq.campaign.mission.*; import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.enums.MissionStatus; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.autoAwards.AutoAwardsController; @@ -477,10 +478,19 @@ && getCampaign().getFinances().getBalance().isGreaterOrEqualThan(rdd.totalPayout } } - // resolve bonus parts exchange + // exchange remaining support points to Resupplys + if (getCampaign().getCampaignOptions().isUseStratCon() && (mission instanceof AtBContract)) { + int remainingSupportPoints = ((AtBContract) mission).getStratconCampaignState().getSupportPoints(); + + if (remainingSupportPoints > 0) { + Resupply supplyDrops = new Resupply(getCampaign(), ((AtBContract) mission)); + supplyDrops.getResupply(remainingSupportPoints, true, false, + true); + } + } + if (getCampaign().getCampaignOptions().isUseAtB() && (mission instanceof AtBContract)) { getCampaign().getContractMarket().checkForFollowup(getCampaign(), (AtBContract) mission); - bonusPartExchange((AtBContract) mission); } // prompt autoAwards ceremony @@ -493,7 +503,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 +630,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) { @@ -727,7 +706,8 @@ private void resolveScenario() { return; } // tracker.postProcessEntities(control); - ResolveScenarioWizardDialog resolveDialog = new ResolveScenarioWizardDialog(getFrame(), true, tracker); + ResolveScenarioWizardDialog resolveDialog = + new ResolveScenarioWizardDialog(getCampaign(), getFrame(), true, tracker); resolveDialog.setVisible(true); if (!getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()) { diff --git a/MekHQ/src/mekhq/gui/ForceRenderer.java b/MekHQ/src/mekhq/gui/ForceRenderer.java index acecca9f71..2912635310 100644 --- a/MekHQ/src/mekhq/gui/ForceRenderer.java +++ b/MekHQ/src/mekhq/gui/ForceRenderer.java @@ -149,16 +149,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"; - } - - setText(String.format(format, force.getName())); + String formattedForceName = String.format("%s%s%s%s%s%s", + force.isStrategicFormation() ? "" : "", + force.getOverrideStrategicFormation() != STRATEGIC_FORMATION_OVERRIDE_NONE ? "" : "", + force.getName(), + force.isStrategicFormation() ? "" : "", + force.getOverrideStrategicFormation() != STRATEGIC_FORMATION_OVERRIDE_NONE ? "" : "", + force.isConvoyForce() ? " Ξ" : force.isCombatForce() ? "" : " ∅"); + + 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/StratconTab.java b/MekHQ/src/mekhq/gui/StratconTab.java index 896caa2fba..0224276117 100644 --- a/MekHQ/src/mekhq/gui/StratconTab.java +++ b/MekHQ/src/mekhq/gui/StratconTab.java @@ -431,7 +431,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/adapter/TOEMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java index f15a1ec29a..7fd539199a 100644 --- a/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java @@ -128,6 +128,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 +141,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|"; @@ -436,8 +438,27 @@ public void actionPerformed(ActionEvent action) { final boolean subforces = command.contains(TOEMouseAdapter.CHANGE_COMBAT_STATUSES); for (final Force force : forces) { force.setCombatForce(combatForce, subforces); + force.setConvoyForce(false); } - 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); + } + gui.getTOETab().refreshForceView(); for (Force formation : gui.getCampaign().getAllForces()) { @@ -1057,6 +1078,13 @@ protected Optional createPopupMenu() { menuItem.addActionListener(this); popup.add(menuItem); + 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.isStrategicFormation() ? "Never" : "Always") + " Consider Force a Strategic Formation"); optionStrategicForceOverride.setActionCommand(COMMAND_CHANGE_STRATEGIC_FORCE_OVERRIDE + forceIds); diff --git a/MekHQ/src/mekhq/gui/dialog/AcquisitionsDialog.java b/MekHQ/src/mekhq/gui/dialog/AcquisitionsDialog.java index 85f83b0de1..aebc99d059 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/ContractMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java index 2edd56216f..0db50e4091 100644 --- a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java @@ -50,6 +50,7 @@ import java.util.*; import static mekhq.campaign.market.contractMarket.ContractAutomation.contractStartPrompt; +import static mekhq.campaign.mission.resupplyAndCaches.Resupply.triggerContractStartDialog; import static mekhq.campaign.universe.Factions.getFactionLogo; /** @@ -480,6 +481,8 @@ private void acceptContract(ActionEvent evt) { if (!triggerConfirmationDialog()) { return; } + + triggerContractStartDialog(campaign, (AtBContract) selectedContract); } selectedContract.setName(contractView.getContractName()); diff --git a/MekHQ/src/mekhq/gui/dialog/PartsStoreDialog.java b/MekHQ/src/mekhq/gui/dialog/PartsStoreDialog.java index eeba1f1e6e..1c95391431 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 cb4e0d6892..d96774b736 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; @@ -97,6 +55,14 @@ 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.personnel.randomEvents.PersonalityController.writeDescription; + /** * @author Taharqa */ @@ -116,6 +82,7 @@ public class ResolveScenarioWizardDialog extends JDialog { OBJECTIVEPANEL, PREVIEWPANEL }; + private Campaign campaign; private final JFrame frame; private final ResolveScenarioTracker tracker; @@ -214,8 +181,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(); @@ -367,7 +335,7 @@ private void initComponents() { } tabChanged(); // Make sure the right buttons are active. - + setMinimumSize(UIUtil.scaleForGUI(850,600)); setPreferredSize(UIUtil.scaleForGUI(850,1000)); } @@ -379,7 +347,7 @@ private void initComponents() { */ private JPanel makeUnitStatusPanel() { GridBagConstraints gridBagConstraints; - + JPanel pnlUnitStatus = new JPanel(new GridBagLayout()); gridBagConstraints = new GridBagConstraints(); @@ -388,17 +356,17 @@ 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; for (Unit unit : tracker.getUnits()) { @@ -584,14 +552,8 @@ private JPanel makeSalvagePanel() { gridBagConstraints.gridy = gridy++; pnlSalvageValue.add(lblSalvagePct1, gridBagConstraints); - StringBuilder salvageUsed = new StringBuilder(); - salvageUsed.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 = new JLabel(salvageUsed.toString()); + String salvageUsed = "" + ((currentSalvagePct <= maxSalvagePct) ? "" : ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor())) + currentSalvagePct + '%' + ((currentSalvagePct <= maxSalvagePct) ? "" : ReportingUtilities.CLOSING_SPAN_TAG) + "(max " + maxSalvagePct + "%)"; + lblSalvagePct2 = new JLabel(salvageUsed); gridBagConstraints.gridx = gridx--; pnlSalvageValue.add(lblSalvagePct2, gridBagConstraints); @@ -842,7 +804,7 @@ private JPanel makePrisonerStatusPanel() { writeDescription(status.getPerson()); prisonerIndex++; } - + return pnlPrisonerStatus; } @@ -957,7 +919,7 @@ private JPanel makeKillsPanel() { return pnlKills; } - + // region Make Rewards /** * Sub-function of initComponents. Makes the Rewards Panel. @@ -1064,7 +1026,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(" "); @@ -1297,8 +1259,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 +1326,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 +1354,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 +1468,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(); } @@ -1627,7 +1585,7 @@ private void finish() { override = objectiveOverrideCheckboxes.get(objective).isSelected(); } - objectiveProcessor.processObjective(objective, qualifyingUnitCount, override, tracker, false); + objectiveProcessor.processObjective(campaign, objective, qualifyingUnitCount, override, tracker, false); } } @@ -1653,8 +1611,8 @@ private void setEnabledTabs() { 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 SALVAGEPANEL -> !tracker.getPotentialSalvage().isEmpty() + && (!(tracker.getMission() instanceof Contract) || ((Contract) tracker.getMission()).canSalvage()); case KILLSPANEL -> !tracker.getKillCredits().isEmpty(); case REWARDPANEL -> !loots.isEmpty(); @@ -1823,16 +1781,9 @@ private void checkSalvageRights() { lblSalvageValueUnit2.setText(salvageUnit.toAmountAndSymbolString()); lblSalvageValueEmployer2.setText(salvageEmployer.toAmountAndSymbolString()); - StringBuilder salvageUsed = new StringBuilder(); + String salvageUsed = "" + ((currentSalvagePct <= maxSalvagePct) ? "" : ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorNegativeHexColor())) + currentSalvagePct + '%' + ((currentSalvagePct <= maxSalvagePct) ? "" : ReportingUtilities.CLOSING_SPAN_TAG) + "(max " + maxSalvagePct + "%)"; - salvageUsed.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()); + lblSalvagePct2.setText(salvageUsed); } @@ -1854,7 +1805,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 +1831,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 diff --git a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java index 780b5d8874..c80f3bcad9 100644 --- a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java +++ b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java @@ -657,8 +657,6 @@ public class CampaignOptionsPane extends AbstractMHQTabbedPane { // contract operations private JCheckBox chkMercSizeLimited; private JCheckBox chkRestrictPartsByMission; - private JSpinner spnBonusPartExchangeValue; - private JSpinner spnBonusPartMaxExchangeCount; private JCheckBox chkLimitLanceWeight; private JCheckBox chkLimitLanceNumUnits; private JCheckBox chkUseStrategy; @@ -3052,31 +3050,6 @@ private JScrollPane createAgainstTheBotTab() { 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; @@ -8923,8 +8896,6 @@ public void setOptions(@Nullable CampaignOptions options, 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()); @@ -9559,8 +9530,6 @@ public void updateOptions() { 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()); diff --git a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java index da0b596dab..1f1ed75334 100644 --- a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java +++ b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java @@ -19,6 +19,10 @@ package mekhq.gui.stratcon; +import megamek.client.ui.swing.util.UIUtil; +import mekhq.campaign.Campaign; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.resupplyAndCaches.Resupply; import mekhq.campaign.stratcon.StratconCampaignState; import mekhq.campaign.stratcon.StratconRulesManager; import mekhq.campaign.stratcon.StratconTrackState; @@ -33,10 +37,11 @@ * @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 btnRequestResupply; private JButton btnGMAddVP; private JButton btnGMAddSP; private JLabel lblTrackScenarioOdds; @@ -50,19 +55,23 @@ 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); + btnRequestResupply.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))); + StratconRulesManager.calculateScenarioOdds(currentTrack, campaignState.getContract(), + false))); } + + this.campaign = campaign; } /** @@ -83,10 +92,10 @@ private void initializeUI() { btnRemoveCVP.addActionListener(this::removeCVP); getContentPane().add(btnRemoveCVP); - btnConvertSPtoBonusPart = new JButton(); - btnConvertSPtoBonusPart.setText("Convert SP to bonus part"); - btnConvertSPtoBonusPart.addActionListener(this::convertSPtoBonusPartHandler); - getContentPane().add(btnConvertSPtoBonusPart); + btnRequestResupply = new JButton(); + btnRequestResupply.setText("Request Resupply"); + btnRequestResupply.addActionListener(this::requestResupply); + getContentPane().add(btnRequestResupply); btnGMAddVP = new JButton(); btnGMAddVP.setText("Add CVP (GM)"); @@ -110,13 +119,76 @@ private void removeCVP(ActionEvent e) { parent.updateCampaignState(); } - private void convertSPtoBonusPartHandler(ActionEvent e) { - currentCampaignState.useSupportPoint(); - currentCampaignState.getContract().addBonusParts(1); - btnConvertSPtoBonusPart.setEnabled(currentCampaignState.getSupportPoints() > 0); + /** + * Requests resupply. If there are more than one available support points, it triggers a dialog + * to specify how many points to use for the resupply. + * If there is exactly one support point, it automatically uses this one point to resupply. + * It also updates the button state based on the remaining support points and updates the parent + * campaign state. + * + * @param event The triggering ActionEvent (not used in this method). + */ + private void requestResupply(ActionEvent event) { + if (currentCampaignState.getSupportPoints() > 1) { + supplyDropDialog(); + } else { + AtBContract contract = currentCampaignState.getContract(); + Resupply supplyDrops = new Resupply(campaign, contract); + supplyDrops.getResupply(1, false); + + currentCampaignState.useSupportPoint(); + } + + btnRequestResupply.setEnabled(currentCampaignState.getSupportPoints() > 0); parent.updateCampaignState(); } + public void supplyDropDialog() { + final JDialog dialog = new JDialog(); + dialog.setLayout(new GridBagLayout()); + dialog.setTitle("Requesting Resupply"); + dialog.setSize(400, 200); + dialog.setLocationRelativeTo(null); + + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(10, 10, 10, 10); + + constraints.gridx = 0; + constraints.gridy = 0; + JLabel description = new JLabel( + String.format("
%s
", + UIUtil.scaleForGUI(500), "How many Support Points would you like to spend?")); + description.setAlignmentX(Component.CENTER_ALIGNMENT); + dialog.add(description, constraints); + + constraints.gridx = 0; + constraints.gridy = 1; + SpinnerNumberModel numberModel = new SpinnerNumberModel(1, 1, + currentCampaignState.getSupportPoints(), 1); + JSpinner spinner = new JSpinner(numberModel); + dialog.add(spinner, constraints); + + constraints.gridx = 0; + constraints.gridy = 2; + constraints.gridwidth = 1; + constraints.anchor = GridBagConstraints.SOUTH; + JButton btnConfirm = new JButton("Confirm"); + btnConfirm.addActionListener( e-> { + dialog.dispose(); + + AtBContract contract = currentCampaignState.getContract(); + Resupply supplyDrops = new Resupply(campaign, contract); + supplyDrops.getResupply((int) numberModel.getValue(), false); + currentCampaignState.useSupportPoints((int) numberModel.getValue()); + }); + + dialog.add(btnConfirm, constraints); + + dialog.pack(); + dialog.setModal(true); + dialog.setVisible(true); + } + private void gmAddVPHandler(ActionEvent e) { currentCampaignState.updateVictoryPoints(1); btnRemoveCVP.setEnabled(currentCampaignState.getVictoryPoints() > 0); @@ -125,7 +197,7 @@ private void gmAddVPHandler(ActionEvent e) { private void gmAddSPHandler(ActionEvent e) { currentCampaignState.addSupportPoints(1); - btnConvertSPtoBonusPart.setEnabled(currentCampaignState.getSupportPoints() > 0); + btnRequestResupply.setEnabled(currentCampaignState.getSupportPoints() > 0); parent.updateCampaignState(); } } diff --git a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java index 0408600f33..d2b1bdb105 100644 --- a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java @@ -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 = "Non-Combat " + 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(" "); } diff --git a/MekHQ/src/mekhq/gui/view/MissionViewPanel.java b/MekHQ/src/mekhq/gui/view/MissionViewPanel.java index a457385ed6..fb651aa19d 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.Resupply.getEstimatedCargoRequirements; /** * 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()); @@ -961,6 +962,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 +972,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 +983,30 @@ public void mouseClicked(MouseEvent e) { pnlStats.add(txtSharePct, gridBagConstraints); } + if (campaign.getCampaignOptions().isUseStratCon()) { + lblCargoRequirement.setName("lblCargoRequirement"); + lblCargoRequirement.setText(resourceMap.getString("lblCargoRequirement.text")); + lblCargoRequirement.setToolTipText(wordWrap(contract.getMoraleLevel().getToolTipText())); + 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(getEstimatedCargoRequirements(campaign, contract)); + txtCargoRequirement.setToolTipText(wordWrap(contract.getMoraleLevel().getToolTipText())); + 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 +1035,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");