From c8ebb63590f6f7f264a4bbdc6cd9cd3613e372f4 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 18 Sep 2024 19:05:17 -0500 Subject: [PATCH 1/2] Reworked AtB Morale, Rebranding it as MekHQ Morale Reworked AtB Morale removing some of the rough edges, adding full documentation, and better integrating it into StratCon. It is my hope this revised system will remove some instances of the 'one bad roll' death spirals that the old system suffered from. --- .../MekHQ Morale.pdf | Bin 0 -> 44814 bytes .../mekhq/resources/Mission.properties | 28 +-- MekHQ/src/mekhq/campaign/Campaign.java | 97 +++++----- MekHQ/src/mekhq/campaign/force/Lance.java | 23 +-- .../mekhq/campaign/mission/AtBContract.java | 173 ++++++++++-------- .../mission/atb/AtBScenarioFactory.java | 117 ++++++------ .../mission/enums/AtBMoraleLevel.java | 168 ++++++++++++----- .../stratcon/StratconRulesManager.java | 61 +++--- .../dialog/CustomizeAtBContractDialog.java | 24 +-- .../src/mekhq/gui/view/MissionViewPanel.java | 6 +- 10 files changed, 387 insertions(+), 310 deletions(-) create mode 100644 MekHQ/docs/Stratcon and Against the Bot/MekHQ Morale.pdf diff --git a/MekHQ/docs/Stratcon and Against the Bot/MekHQ Morale.pdf b/MekHQ/docs/Stratcon and Against the Bot/MekHQ Morale.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5a504c5560173677acce77d0fef4b6e1d84103c5 GIT binary patch literal 44814 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⁣T4moq(H-m2T?`gkH^%gt%(75?pLD?s*_Vxx#W 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 literal 0 HcmV?d00001 diff --git a/MekHQ/resources/mekhq/resources/Mission.properties b/MekHQ/resources/mekhq/resources/Mission.properties index 71e6e21395..6b82d8b25b 100644 --- a/MekHQ/resources/mekhq/resources/Mission.properties +++ b/MekHQ/resources/mekhq/resources/Mission.properties @@ -42,20 +42,20 @@ AtBLanceRole.UNASSIGNED.text=Unassigned AtBLanceRole.UNASSIGNED.toolTipText=The lance is not currently assigned to combat duties. # AtBMoraleLevel Enum -AtBMoraleLevel.BROKEN.text=Broken -AtBMoraleLevel.BROKEN.toolTipText=The unit's morale has broken, and its men are in full retreat. -AtBMoraleLevel.VERY_LOW.text=Very Low -AtBMoraleLevel.VERY_LOW.toolTipText=The unit is on precipice of breaking, their leadership barely holding the unit together. -AtBMoraleLevel.LOW.text=Low -AtBMoraleLevel.LOW.toolTipText=The unit is demoralized, but is still fighting cohesively. -AtBMoraleLevel.NORMAL.text=Normal -AtBMoraleLevel.NORMAL.toolTipText=The unit is ready to fight. -AtBMoraleLevel.HIGH.text=High -AtBMoraleLevel.HIGH.toolTipText=The unit is ready and glad to fight. -AtBMoraleLevel.VERY_HIGH.text=Very High -AtBMoraleLevel.VERY_HIGH.toolTipText=The unit is dedicated to their cause. -AtBMoraleLevel.UNBREAKABLE.text=Unbreakable -AtBMoraleLevel.UNBREAKABLE.toolTipText=The unit's trust in their leadership and dedication to their cause is nigh-on unbreakable, and they look forward to fighting their enemies. +AtBMoraleLevel.ROUTED.text=Routed +AtBMoraleLevel.ROUTED.toolTipText=The enemy is in full retreat, suffering devastating losses and scattered. They pose no significant threat and are incapable of organizing a counterattack. +AtBMoraleLevel.CRITICAL.text=Critical +AtBMoraleLevel.CRITICAL.toolTipText=The enemy is in a dire state, with most of their forces destroyed or incapacitated. Their ability to fight is severely compromised, and morale is near breaking. +AtBMoraleLevel.WEAKENED.text=Weakened +AtBMoraleLevel.WEAKENED.toolTipText=The enemy is losing ground, sustaining significant casualties, and is disorganized. However, they can still put up resistance in isolated areas. +AtBMoraleLevel.STALEMATE.text=Stalemate +AtBMoraleLevel.STALEMATE.toolTipText=Both sides are evenly matched, with neither gaining a clear advantage. Skirmishes continue, but the outcome remains uncertain. +AtBMoraleLevel.ADVANCING.text=Advancing +AtBMoraleLevel.ADVANCING.toolTipText=The enemy is gaining momentum, making coordinated strikes, and forcing your forces to fall back. They are beginning to dominate key areas of the battlefield. +AtBMoraleLevel.DOMINATING.text=Dominating +AtBMoraleLevel.DOMINATING.toolTipText=The enemy has the upper hand, controlling critical objectives and inflicting heavy casualties. Your forces are under significant pressure, and defeat is imminent. +AtBMoraleLevel.OVERWHELMING.text=Overwhelming +AtBMoraleLevel.OVERWHELMING.toolTipText=The enemy is completely overwhelming your forces, executing a final push for total victory. Your forces are on the verge of collapse, with no hope of recovery. # ContractCommandRights Enum ContractCommandRights.INTEGRATED.text=Integrated diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 12116d680e..e4026db788 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -21,23 +21,6 @@ */ package mekhq.campaign; -import static mekhq.campaign.personnel.backgrounds.BackgroundsController.randomMercenaryCompanyNameGenerator; -import static mekhq.campaign.personnel.education.EducationController.getAcademy; -import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.Payout.isBreakingContract; -import static mekhq.campaign.unit.Unit.SITE_FACILITY_MAINTENANCE; - -import java.io.PrintWriter; -import java.text.MessageFormat; -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.time.Month; -import java.time.temporal.ChronoUnit; -import java.util.*; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -import javax.swing.JOptionPane; - import megamek.client.generator.RandomGenderGenerator; import megamek.client.generator.RandomNameGenerator; import megamek.client.generator.RandomUnitGenerator; @@ -54,11 +37,7 @@ import megamek.common.loaders.BLKFile; import megamek.common.loaders.EntityLoadingException; import megamek.common.loaders.EntitySavingException; -import megamek.common.options.GameOptions; -import megamek.common.options.IBasicOption; -import megamek.common.options.IOption; -import megamek.common.options.IOptionGroup; -import megamek.common.options.OptionsConstants; +import megamek.common.options.*; import megamek.common.util.BuildingBlock; import megamek.common.weapons.autocannons.ACWeapon; import megamek.common.weapons.flamers.FlamerWeapon; @@ -71,11 +50,7 @@ import mekhq.campaign.Quartermaster.PartAcquisitionResult; import mekhq.campaign.againstTheBot.AtBConfiguration; import mekhq.campaign.event.*; -import mekhq.campaign.finances.Accountant; -import mekhq.campaign.finances.CurrencyManager; -import mekhq.campaign.finances.Finances; -import mekhq.campaign.finances.Loan; -import mekhq.campaign.finances.Money; +import mekhq.campaign.finances.*; import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.force.Force; import mekhq.campaign.force.Lance; @@ -90,14 +65,10 @@ import mekhq.campaign.market.ShoppingList; import mekhq.campaign.market.unitMarket.AbstractUnitMarket; import mekhq.campaign.market.unitMarket.DisabledUnitMarket; -import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.mission.AtBDynamicScenario; -import mekhq.campaign.mission.AtBScenario; -import mekhq.campaign.mission.Contract; -import mekhq.campaign.mission.Mission; -import mekhq.campaign.mission.Scenario; +import mekhq.campaign.mission.*; import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.enums.AtBLanceRole; +import mekhq.campaign.mission.enums.AtBMoraleLevel; import mekhq.campaign.mission.enums.MissionStatus; import mekhq.campaign.mission.enums.ScenarioStatus; import mekhq.campaign.mod.am.InjuryUtil; @@ -105,12 +76,7 @@ import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.parts.equipment.EquipmentPart; import mekhq.campaign.parts.equipment.MissingEquipmentPart; -import mekhq.campaign.personnel.Bloodname; -import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.PersonnelOptions; -import mekhq.campaign.personnel.Skill; -import mekhq.campaign.personnel.SkillType; -import mekhq.campaign.personnel.SpecialAbility; +import mekhq.campaign.personnel.*; import mekhq.campaign.personnel.autoAwards.AutoAwardsController; import mekhq.campaign.personnel.death.AbstractDeath; import mekhq.campaign.personnel.death.DisabledRandomDeath; @@ -118,12 +84,7 @@ import mekhq.campaign.personnel.divorce.DisabledRandomDivorce; import mekhq.campaign.personnel.education.Academy; import mekhq.campaign.personnel.education.EducationController; -import mekhq.campaign.personnel.enums.FamilialRelationshipType; -import mekhq.campaign.personnel.enums.PersonnelRole; -import mekhq.campaign.personnel.enums.PersonnelStatus; -import mekhq.campaign.personnel.enums.Phenotype; -import mekhq.campaign.personnel.enums.PrisonerStatus; -import mekhq.campaign.personnel.enums.SplittingSurnameStyle; +import mekhq.campaign.personnel.enums.*; import mekhq.campaign.personnel.generator.AbstractPersonnelGenerator; import mekhq.campaign.personnel.generator.DefaultPersonnelGenerator; import mekhq.campaign.personnel.generator.RandomPortraitGenerator; @@ -136,21 +97,16 @@ import mekhq.campaign.personnel.ranks.Ranks; import mekhq.campaign.personnel.turnoverAndRetention.Fatigue; import mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker; +import mekhq.campaign.rating.CamOpsReputation.ReputationController; import mekhq.campaign.rating.FieldManualMercRevDragoonsRating; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.rating.UnitRatingMethod; -import mekhq.campaign.rating.CamOpsReputation.ReputationController; import mekhq.campaign.storyarc.StoryArc; import mekhq.campaign.stratcon.StratconContractInitializer; import mekhq.campaign.stratcon.StratconRulesManager; import mekhq.campaign.stratcon.StratconTrackState; -import mekhq.campaign.unit.CargoStatistics; import mekhq.campaign.unit.CrewType; -import mekhq.campaign.unit.HangarStatistics; -import mekhq.campaign.unit.TestUnit; -import mekhq.campaign.unit.Unit; -import mekhq.campaign.unit.UnitOrder; -import mekhq.campaign.unit.UnitTechProgression; +import mekhq.campaign.unit.*; import mekhq.campaign.universe.*; import mekhq.campaign.universe.Planet.PlanetaryEvent; import mekhq.campaign.universe.PlanetarySystem.PlanetarySystemEvent; @@ -171,6 +127,22 @@ import mekhq.service.mrms.MRMSService; import mekhq.utilities.MHQXMLUtility; +import javax.swing.*; +import java.io.PrintWriter; +import java.text.MessageFormat; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.Month; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import static mekhq.campaign.personnel.backgrounds.BackgroundsController.randomMercenaryCompanyNameGenerator; +import static mekhq.campaign.personnel.education.EducationController.getAcademy; +import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.Payout.isBreakingContract; +import static mekhq.campaign.unit.Unit.SITE_FACILITY_MAINTENANCE; + /** * The main campaign class, keeps track of teams and units * @@ -3536,6 +3508,17 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem } } + /** + * Processes the new day actions for various AtB systems + *

+ * It generates contract offers in the contract market, + * updates ship search expiration and results, + * processes ship search on Mondays, + * awards training experience to eligible training lances on active contracts on Mondays, + * adds or removes dependents at the start of the year if the options are enabled, + * rolls for morale at the start of the month, + * and processes ATB scenarios. + */ private void processNewDayATB() { contractMarket.generateContractOffers(this); // TODO : AbstractContractMarket : Remove @@ -3609,9 +3592,13 @@ && getCampaignOptions().getRandomDependentMethod().isAgainstTheBot() } for (AtBContract contract : getActiveAtBContracts()) { - contract.checkMorale(getLocalDate(), getAtBUnitRatingMod()); - addReport("Enemy Morale is now " + contract.getMoraleLevel() - + " on contract " + contract.getName()); + contract.checkMorale(this, getLocalDate()); + + AtBMoraleLevel morale = contract.getMoraleLevel(); + + String report = "Current enemy condition is '" + morale + "' on contract " + contract.getName() + "

" + morale.getToolTipText() + ""; + + addReport(report); } } diff --git a/MekHQ/src/mekhq/campaign/force/Lance.java b/MekHQ/src/mekhq/campaign/force/Lance.java index 80ad56c3a4..c22758c257 100644 --- a/MekHQ/src/mekhq/campaign/force/Lance.java +++ b/MekHQ/src/mekhq/campaign/force/Lance.java @@ -21,19 +21,7 @@ */ package mekhq.campaign.force; -import java.io.PrintWriter; -import java.time.LocalDate; -import java.util.UUID; - -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import megamek.common.Compute; -import megamek.common.Entity; -import megamek.common.EntityWeightClass; -import megamek.common.Infantry; -import megamek.common.UnitType; +import megamek.common.*; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; import mekhq.campaign.mission.AtBContract; @@ -45,6 +33,13 @@ import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Faction; 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.time.LocalDate; +import java.util.UUID; /** * Used by Against the Bot to track additional information about each force @@ -288,7 +283,7 @@ public AtBScenario checkForBattle(Campaign c) { int roll; // thresholds are coded from charts with 1-100 range, so we add 1 to mod to // adjust 0-based random int - int battleTypeMod = 1 + (AtBMoraleLevel.NORMAL.ordinal() - getContract(c).getMoraleLevel().ordinal()) * 5; + int battleTypeMod = 1 + (AtBMoraleLevel.STALEMATE.ordinal() - getContract(c).getMoraleLevel().ordinal()) * 5; battleTypeMod += getContract(c).getBattleTypeMod(); // debugging code that will allow you to force the generation of a particular diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 897e2beedf..1095a6a5f9 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -21,24 +21,9 @@ */ package mekhq.campaign.mission; -import java.io.PrintWriter; -import java.text.ParseException; -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Objects; -import java.util.UUID; - -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.client.generator.RandomUnitGenerator; import megamek.client.ui.swing.util.PlayerColour; -import megamek.common.Compute; -import megamek.common.Entity; -import megamek.common.MekFileParser; -import megamek.common.MekSummary; -import megamek.common.UnitType; +import megamek.common.*; import megamek.common.annotations.Nullable; import megamek.common.enums.SkillLevel; import megamek.common.icons.Camouflage; @@ -64,6 +49,16 @@ import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.RandomFactionGenerator; import mekhq.utilities.MHQXMLUtility; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.PrintWriter; +import java.text.ParseException; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Objects; +import java.util.UUID; /** * Contract class for use with Against the Bot rules @@ -169,7 +164,7 @@ public AtBContract(String name) { extensionLength = 0; sharesPct = 0; - setMoraleLevel(AtBMoraleLevel.NORMAL); + setMoraleLevel(AtBMoraleLevel.STALEMATE); routEnd = null; numBonusParts = 0; priorLogisticsFailure = false; @@ -290,105 +285,121 @@ public void calculatePaymentMultiplier(Campaign campaign) { setMultiplier(multiplier); } - public void checkMorale(LocalDate today, int dragoonRating) { - if (null != routEnd) { + /** + * Checks and updates the morale which depends on various conditions such as the rout end date, + * skill levels, victories, defeats, etc. This method also updates the enemy status based on the + * morale level. + * + * @param today The current date in the context. + */ + public void checkMorale(Campaign campaign, LocalDate today) { + // Check whether enemy forces have been reinforced, and whether any current rout continues + // beyond its expected date + boolean routContinue = Compute.randomInt(4) < 3; + + // If there is a rout end date, and it's past today, update morale and enemy state accordingly + if (routEnd != null && !routContinue) { if (today.isAfter(routEnd)) { - setMoraleLevel(AtBMoraleLevel.NORMAL); + setMoraleLevel(AtBMoraleLevel.STALEMATE); routEnd = null; updateEnemy(today); // mix it up a little } else { - setMoraleLevel(AtBMoraleLevel.BROKEN); + setMoraleLevel(AtBMoraleLevel.ROUTED); } return; } + + // Initialize counters for victories and defeats int victories = 0; int defeats = 0; LocalDate lastMonth = today.minusMonths(1); - for (Scenario s : getScenarios()) { - if ((s.getDate() != null) && lastMonth.isAfter(s.getDate())) { + // Loop through scenarios, counting victories and defeats that fall within the target month + for (Scenario scenario : getScenarios()) { + if ((scenario.getDate() != null) && lastMonth.isAfter(scenario.getDate())) { continue; } - if (s.getStatus().isOverallVictory()) { + if (scenario.getStatus().isOverallVictory()) { victories++; - } else if (s.getStatus().isOverallDefeat()) { + } else if (scenario.getStatus().isOverallDefeat()) { defeats++; } - } - // - // From: Official AtB Rules 2.31 - // - - // Enemy skill rating: Green -1, Veteran +1, Elite +2 - int mod = Math.max(getEnemySkill().ordinal() - 3, -1); + if (scenario.getStatus().isDecisiveVictory()) { + victories++; + } else if (scenario.getStatus().isDecisiveDefeat()) { + defeats++; + } else if (scenario.getStatus().isPyrrhicVictory()) { + victories--; + } + } - // Player Dragoon/MRBC rating: F +2, D +1, B -1, A -2 - mod -= dragoonRating - IUnitRating.DRAGOON_C; + // Calculate various modifiers for morale + int enemySkillModifier = getEnemySkill().getAdjustedValue() - SkillLevel.REGULAR.getAdjustedValue(); + int allySkillModifier = getAllySkill().getAdjustedValue() - SkillLevel.REGULAR.getAdjustedValue(); - // For every 5 player victories in last month: -1 - mod -= victories / 5; + int performanceModifier = 0; - // For every 2 player defeats in last month: +1 - mod += defeats / 2; + if (victories > (defeats * 2)) { + performanceModifier -= 2; + } else if (victories > defeats) { + performanceModifier--; + } else if (defeats > (victories * 2)) { + performanceModifier += 2; + } else { + performanceModifier++; + } - // "Several weekly events affect the morale roll, so, beyond the - // modifiers presented here, notice that some events add - // bonuses/minuses to this roll." - mod += moraleMod; + int miscModifiers = moraleMod; - // Enemy type: Pirates: -2 - // Rebels/Mercs/Minor factions: -1 - // Clans: +2 + // Additional morale modifications depending on faction properties if (Factions.getInstance().getFaction(enemyCode).isPirate()) { - mod -= 2; - } else if (Factions.getInstance().getFaction(enemyCode).isRebel() || - isMinorPower(enemyCode) || - Factions.getInstance().getFaction(enemyCode).isMercenary()) { - mod -= 1; + miscModifiers -= 2; + } else if (Factions.getInstance().getFaction(enemyCode).isRebel() + || isMinorPower(enemyCode) + || Factions.getInstance().getFaction(enemyCode).isMercenary()) { + miscModifiers -= 1; } else if (Factions.getInstance().getFaction(enemyCode).isClan()) { - mod += 2; + miscModifiers += 2; } - // If no player victories in last month: +1 - if (victories == 0) { - mod++; - } - - // If no player defeats in last month: -1 - if (defeats == 0) { - mod--; - } - - // After finding the applicable modifiers, roll according to the - // following table to find the new morale level: - // 1 or less: Morale level decreases 2 levels - // 2 – 5: Morale level decreases 1 level - // 6 – 8: Morale level remains the same - // 9 - 12: Morale level increases 1 level - // 13 or more: Morale increases 2 levels - int roll = Compute.d6(2) + mod; + // Total morale modifier calculation + int totalModifier = enemySkillModifier - allySkillModifier + performanceModifier + miscModifiers; + int roll = Compute.d6(2) + totalModifier; + // Morale level determination based on roll value final AtBMoraleLevel[] moraleLevels = AtBMoraleLevel.values(); - if (roll <= 1) { + + if (roll < 2) { setMoraleLevel(moraleLevels[Math.max(getMoraleLevel().ordinal() - 2, 0)]); - } else if (roll <= 5) { + } else if (roll < 5) { setMoraleLevel(moraleLevels[Math.max(getMoraleLevel().ordinal() - 1, 0)]); - } else if ((roll >= 9) && (roll <= 12)) { - setMoraleLevel(moraleLevels[Math.min(getMoraleLevel().ordinal() + 1, moraleLevels.length - 1)]); - } else if (roll >= 13) { + } else if ((roll > 12)) { setMoraleLevel(moraleLevels[Math.min(getMoraleLevel().ordinal() + 2, moraleLevels.length - 1)]); + } else if ((roll > 9)) { + setMoraleLevel(moraleLevels[Math.min(getMoraleLevel().ordinal() + 1, moraleLevels.length - 1)]); } - // Enemy defeated, retreats or do not offer opposition to the player - // forces, equal to a early victory for contracts that are not - // Garrison-type, and a 1d6-3 (minimum 1) months without enemy - // activity for Garrison-type contracts. - if (getMoraleLevel().isRout() && getContractType().isGarrisonType()) { - routEnd = today.plusMonths(Math.max(1, Compute.d6() - 3)).minusDays(1); + // Additional morale updates if morale level is set to 'Routed' and contract type is a garrison type + if (getMoraleLevel().isRouted()) { + if (getContractType().isGarrisonType()) { + routEnd = today.plusMonths(Math.max(1, Compute.d6() - 3)).minusDays(1); + } else { + campaign.addReport("With the enemy routed, any remaining objectives have been successfully completed." + + " The contract will conclude tomorrow."); + setEndDate(today.plusDays(1)); + } + } + + // Process the results of the reinforcement roll + if (!getMoraleLevel().isRouted() && !routContinue) { + setMoraleLevel(moraleLevels[Math.min(getMoraleLevel().ordinal() + 1, moraleLevels.length - 1)]); + campaign.addReport("Long ranged scans have detected the arrival of additional enemy forces."); + return; } + // Reset external morale modifier moraleMod = 0; } @@ -474,7 +485,7 @@ public int getScore() { && (((AtBScenario) s).getScenarioType() == AtBScenario.BASEATTACK) && ((AtBScenario) s).isAttacker() && s.getStatus().isOverallVictory()) { earlySuccess = true; - } else if (getMoraleLevel().isRout() && !getContractType().isGarrisonType()) { + } else if (getMoraleLevel().isRouted() && !getContractType().isGarrisonType()) { earlySuccess = true; } } diff --git a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java index 5786e9444d..cb63e6ff0b 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java @@ -18,14 +18,6 @@ */ package mekhq.campaign.mission.atb; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; -import java.util.Random; - import megamek.codeUtilities.ObjectUtility; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; @@ -34,6 +26,9 @@ import mekhq.campaign.mission.AtBScenario; import mekhq.campaign.mission.atb.scenario.*; +import java.time.LocalDate; +import java.util.*; + public class AtBScenarioFactory { private static final MMLogger logger = MMLogger.create(AtBScenarioFactory.class); @@ -192,7 +187,7 @@ public static void createScenariosForNewWeek(Campaign c) { // Don't generate scenarios for contracts with morale below the morale limit of // Low - if (contract.getMoraleLevel().isVeryLow() || contract.getMoraleLevel().isRout()) { + if (contract.getMoraleLevel().isCritical() || contract.getMoraleLevel().isRouted()) { continue; } @@ -222,72 +217,74 @@ public static void createScenariosForNewWeek(Campaign c) { } // endregion Generate Scenarios - // region Unbreakable Morale Missions - // Make sure Unbreakable morale missions have a base attack scenario generated - if (!hasBaseAttack && contract.getMoraleLevel().isUnbreakable()) { - /* - * find a lance to act as defender, giving preference - * first to those assigned to the same contract, - * then to those assigned to defense roles - */ - List lList = new ArrayList<>(); - for (Lance l : lances.values()) { - if ((l.getMissionId() == contract.getId()) && l.getRole().isDefence() && l.isEligible(c)) { - lList.add(l); - } - } - - if (lList.isEmpty()) { + // region Overwhelming Morale Missions + // Make sure Overwhelming morale missions have a base attack scenario generated + if (!c.getCampaignOptions().isUseStratCon()) { + if (!hasBaseAttack && contract.getMoraleLevel().isOverwhelming()) { + /* + * find a lance to act as defender, giving preference + * first to those assigned to the same contract, + * then to those assigned to defense roles + */ + List lList = new ArrayList<>(); for (Lance l : lances.values()) { - if ((l.getMissionId() == contract.getId()) && l.isEligible(c)) { + if ((l.getMissionId() == contract.getId()) && l.getRole().isDefence() && l.isEligible(c)) { lList.add(l); } } - } - if (lList.isEmpty()) { - for (Lance l : lances.values()) { - if (l.isEligible(c)) { - lList.add(l); + if (lList.isEmpty()) { + for (Lance l : lances.values()) { + if ((l.getMissionId() == contract.getId()) && l.isEligible(c)) { + lList.add(l); + } + } + } + + if (lList.isEmpty()) { + for (Lance l : lances.values()) { + if (l.isEligible(c)) { + lList.add(l); + } } } - } - if (!lList.isEmpty()) { - Lance lance = ObjectUtility.getRandomItem(lList); - AtBScenario atbScenario = AtBScenarioFactory.createScenario(c, lance, - AtBScenario.BASEATTACK, false, Lance.getBattleDate(c.getLocalDate())); - if (atbScenario != null) { - if ((lance.getMissionId() == atbScenario.getMissionId()) - || (lance.getMissionId() == Lance.NO_MISSION)) { - for (int i = 0; i < sList.size(); i++) { - if (sList.get(i).getLanceForceId() == lance.getForceId()) { - if (dontGenerateForces.contains(atbScenario.getId())) { - dontGenerateForces.remove(atbScenario.getId()); + if (!lList.isEmpty()) { + Lance lance = ObjectUtility.getRandomItem(lList); + AtBScenario atbScenario = AtBScenarioFactory.createScenario(c, lance, + AtBScenario.BASEATTACK, false, Lance.getBattleDate(c.getLocalDate())); + if (atbScenario != null) { + if ((lance.getMissionId() == atbScenario.getMissionId()) + || (lance.getMissionId() == Lance.NO_MISSION)) { + for (int i = 0; i < sList.size(); i++) { + if (sList.get(i).getLanceForceId() == lance.getForceId()) { + if (dontGenerateForces.contains(atbScenario.getId())) { + dontGenerateForces.remove(atbScenario.getId()); + } + sList.set(i, atbScenario); + break; } - sList.set(i, atbScenario); - break; } + } else { + // edge case: lance assigned to another mission gets assigned the scenario, + // we need to remove any scenario they are assigned to already + c.getMission(lance.getMissionId()).getScenarios() + .removeIf(scenario -> (scenario instanceof AtBScenario) + && (((AtBScenario) scenario).getLanceForceId() == lance.getForceId())); + } + if (!sList.contains(atbScenario)) { + sList.add(atbScenario); + } + if (!assignedLances.contains(lance.getForceId())) { + assignedLances.add(lance.getForceId()); } } else { - // edge case: lance assigned to another mission gets assigned the scenario, - // we need to remove any scenario they are assigned to already - c.getMission(lance.getMissionId()).getScenarios() - .removeIf(scenario -> (scenario instanceof AtBScenario) - && (((AtBScenario) scenario).getLanceForceId() == lance.getForceId())); - } - if (!sList.contains(atbScenario)) { - sList.add(atbScenario); - } - if (!assignedLances.contains(lance.getForceId())) { - assignedLances.add(lance.getForceId()); + logger.error("Unable to generate Base Attack scenario."); } } else { - logger.error("Unable to generate Base Attack scenario."); + logger.warn("No lances assigned to mission " + contract.getName() + + ". Can't generate an Unbreakable Morale base defense mission for this force."); } - } else { - logger.warn("No lances assigned to mission " + contract.getName() - + ". Can't generate an Unbreakable Morale base defense mission for this force."); } } // endregion Unbreakable Morale Missions diff --git a/MekHQ/src/mekhq/campaign/mission/enums/AtBMoraleLevel.java b/MekHQ/src/mekhq/campaign/mission/enums/AtBMoraleLevel.java index 324591bca9..54cbbe5342 100644 --- a/MekHQ/src/mekhq/campaign/mission/enums/AtBMoraleLevel.java +++ b/MekHQ/src/mekhq/campaign/mission/enums/AtBMoraleLevel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,20 +18,23 @@ */ package mekhq.campaign.mission.enums; -import java.util.ResourceBundle; - import megamek.logging.MMLogger; import mekhq.MekHQ; +import java.util.ResourceBundle; + +/** + * The AtBMoraleLevel enum represents the different enemy morale conditions used by AtB systems. + */ public enum AtBMoraleLevel { // region Enum Declarations - BROKEN("AtBMoraleLevel.BROKEN.text", "AtBMoraleLevel.BROKEN.toolTipText"), - VERY_LOW("AtBMoraleLevel.VERY_LOW.text", "AtBMoraleLevel.VERY_LOW.toolTipText"), - LOW("AtBMoraleLevel.LOW.text", "AtBMoraleLevel.LOW.toolTipText"), - NORMAL("AtBMoraleLevel.NORMAL.text", "AtBMoraleLevel.NORMAL.toolTipText"), - HIGH("AtBMoraleLevel.HIGH.text", "AtBMoraleLevel.HIGH.toolTipText"), - VERY_HIGH("AtBMoraleLevel.VERY_HIGH.text", "AtBMoraleLevel.VERY_HIGH.toolTipText"), - UNBREAKABLE("AtBMoraleLevel.UNBREAKABLE.text", "AtBMoraleLevel.UNBREAKABLE.toolTipText"); + ROUTED("AtBMoraleLevel.ROUTED.text", "AtBMoraleLevel.ROUTED.toolTipText"), + CRITICAL("AtBMoraleLevel.CRITICAL.text", "AtBMoraleLevel.CRITICAL.toolTipText"), + WEAKENED("AtBMoraleLevel.WEAKENED.text", "AtBMoraleLevel.WEAKENED.toolTipText"), + STALEMATE("AtBMoraleLevel.STALEMATE.text", "AtBMoraleLevel.STALEMATE.toolTipText"), + ADVANCING("AtBMoraleLevel.ADVANCING.text", "AtBMoraleLevel.ADVANCING.toolTipText"), + DOMINATING("AtBMoraleLevel.DOMINATING.text", "AtBMoraleLevel.DOMINATING.toolTipText"), + OVERWHELMING("AtBMoraleLevel.OVERWHELMING.text", "AtBMoraleLevel.OVERWHELMING.toolTipText"); // endregion Enum Declarations // region Variable Declarations @@ -39,6 +42,12 @@ public enum AtBMoraleLevel { private final String toolTipText; // endregion Variable Declarations + /** + * Initializes a new {@link AtBMoraleLevel} object with the specified name and tooltip text. + * + * @param name the resource key for the name of the Morale Level + * @param toolTipText the resource key for the tooltip text of the Morale Level + */ // region Constructors AtBMoraleLevel(final String name, final String toolTipText) { final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Mission", @@ -48,6 +57,11 @@ public enum AtBMoraleLevel { } // endregion Constructors + /** + * Retrieves the tooltip text associated with this object. + * + * @return the tooltip text + */ // region Getters public String getToolTipText() { return toolTipText; @@ -55,73 +69,141 @@ public String getToolTipText() { // endregion Getters // region Boolean Comparison Methods - public boolean isRout() { - return this == BROKEN; + /** + * Checks if the current object is equal to the value of {@code ROUTED}. + * + * @return {@code true} if the current object is equal to {@code ROUTED}, {@code false} otherwise. + */ + public boolean isRouted() { + return this == ROUTED; } - public boolean isVeryLow() { - return this == VERY_LOW; + /** + * Checks if the current object is equal to the value of {@code CRITICAL}. + * + * @return {@code true} if the current object is equal to {@code CRITICAL}, {@code false} + * otherwise. + */ + public boolean isCritical() { + return this == CRITICAL; } - public boolean isLow() { - return this == LOW; + /** + * Checks if the current object is equal to the value of {@code WEAKENED}. + * + * @return {@code true} if the current object is equal to {@code WEAKENED}, {@code false} + * otherwise. + */ + public boolean isWeakened() { + return this == WEAKENED; } - public boolean isNormal() { - return this == NORMAL; + /** + * Checks if the current object is equal to the value of {@code STALEMATE}. + * + * @return {@code true} if the current object is equal to {@code STALEMATE}, {@code false} + * otherwise. + */ + public boolean isStalemate() { + return this == STALEMATE; } - public boolean isHigh() { - return this == HIGH; + /** + * Checks if the current object is equal to the value of {@code ADVANCING}. + * + * @return {@code true} if the current object is equal to {@code ADVANCING}, {@code false} + * otherwise. + */ + public boolean isAdvancing() { + return this == ADVANCING; } - public boolean isVeryHigh() { - return this == VERY_HIGH; + /** + * Checks if the current object is equal to the value of {@code DOMINATING}. + * + * @return {@code true} if the current object is equal to {@code DOMINATING}, {@code false} + * otherwise. + */ + public boolean isDominating() { + return this == DOMINATING; } - public boolean isUnbreakable() { - return this == UNBREAKABLE; + /** + * Checks if the current object is equal to the value of {@code OVERWHELMING}. + * + * @return {@code true} if the current object is equal to {@code OVERWHELMING}, {@code false} + * otherwise. + */ + public boolean isOverwhelming() { + return this == OVERWHELMING; } // endregion Boolean Comparison Methods - // region File I/O /** - * @param text containing the AtBMoraleLevel - * @return the saved AtBMoraleLevel + * Parses a string representation of a morale level and returns the corresponding + * {@link AtBMoraleLevel} enum value. + * + * @param moraleLevel the string representation of a morale level + * @return the {@link AtBMoraleLevel} enum value corresponding to the given morale level string, + * or {@code STALEMATE} if the string cannot be parsed */ - public static AtBMoraleLevel parseFromString(final String text) { + // region File I/O + public static AtBMoraleLevel parseFromString(final String moraleLevel) { try { - return valueOf(text); - } catch (Exception ignored) { - - } + return valueOf(moraleLevel); + } catch (Exception ignored) {} try { - switch (Integer.parseInt(text)) { + switch (Integer.parseInt(moraleLevel)) { case 0: - return BROKEN; + return ROUTED; case 1: - return VERY_LOW; + return CRITICAL; case 2: - return LOW; + return WEAKENED; case 3: - return NORMAL; + return STALEMATE; case 4: - return HIGH; + return ADVANCING; case 5: - return VERY_HIGH; + return DOMINATING; case 6: - return UNBREAKABLE; + return OVERWHELMING; default: break; } - } catch (Exception ignored) { + } catch (Exception ignored) {} + //start <50.01 compatibility handler, replace it after post-40.10.1 Milestone + switch (moraleLevel) { + case "BROKEN" -> { + return ROUTED; + } + case "VERY_LOW" -> { + return CRITICAL; + } + case "LOW" -> { + return WEAKENED; + } + case "NORMAL" -> { + return STALEMATE; + } + case "HIGH" -> { + return ADVANCING; + } + case "VERY_HIGH" -> { + return DOMINATING; + } + case "UNBREAKABLE" -> { + return OVERWHELMING; + } + default -> {} } + //end <50.01 compatibility handler MMLogger.create(AtBMoraleLevel.class) - .error("Unable to parse " + text + " into an AtBMoraleLevel. Returning NORMAL."); - return NORMAL; + .error("Unable to parse " + moraleLevel + " into an AtBMoraleLevel. Returning NORMAL."); + return STALEMATE; } // endregion File I/O diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index d63c171d36..edf4350ee8 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -18,11 +18,6 @@ */ package mekhq.campaign.stratcon; -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.util.*; -import java.util.stream.Collectors; - import megamek.codeUtilities.ObjectUtility; import megamek.common.Compute; import megamek.common.Minefield; @@ -39,16 +34,10 @@ import mekhq.campaign.event.StratconDeploymentEvent; import mekhq.campaign.force.Force; import mekhq.campaign.force.Lance; -import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.mission.AtBDynamicScenario; -import mekhq.campaign.mission.AtBDynamicScenarioFactory; -import mekhq.campaign.mission.AtBScenario; -import mekhq.campaign.mission.Scenario; -import mekhq.campaign.mission.ScenarioForceTemplate; +import mekhq.campaign.mission.*; import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; import mekhq.campaign.mission.ScenarioForceTemplate.ForceGenerationMethod; import mekhq.campaign.mission.ScenarioMapParameters.MapLocation; -import mekhq.campaign.mission.ScenarioTemplate; import mekhq.campaign.mission.atb.AtBScenarioModifier; import mekhq.campaign.mission.atb.AtBScenarioModifier.EventTiming; import mekhq.campaign.personnel.Person; @@ -58,6 +47,11 @@ import mekhq.campaign.stratcon.StratconScenario.ScenarioState; import mekhq.campaign.unit.Unit; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + /** * This class contains "rules" logic for the AtB-Stratcon state * @@ -112,7 +106,12 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont // generated // and use the random force to drive opfor generation (#required lances // multiplies the BV budget of all - for (int scenarioIndex = 0; scenarioIndex < track.getRequiredLanceCount(); scenarioIndex++) { + int scenarioRolls = track.getRequiredLanceCount(); + + if (contract.getMoraleLevel().isOverwhelming()) { + scenarioRolls++; + } + for (int scenarioIndex = 0; scenarioIndex < scenarioRolls; scenarioIndex++) { int targetNum = calculateScenarioOdds(track, contract, false); // if we haven't already used all the player forces and are required to randomly @@ -1414,35 +1413,43 @@ public static boolean canManuallyDeployAnyForce(StratconCoords coords, public static int calculateScenarioOdds(StratconTrackState track, AtBContract contract, boolean playerDeployingForce) { // rules: - // broken morale: 0% - // very low morale: -10% when deploying forces to track, 0% attack - // low morale: -5% - // high morale: +5% - // very high morale: +10% - // unbreakable: special case, let's do +15% for now + // ROUTED: 0% + // CRITICAL: -10% when deploying forces to track, 0% attack + // WEAKENED: -5% + // ADVANCING: +5% + // DOMINATING: +10% + // OVERWHELMING: +100% int moraleModifier = 0; switch (contract.getMoraleLevel()) { - case BROKEN: + case ROUTED: return 0; - case VERY_LOW: + case CRITICAL: if (playerDeployingForce) { moraleModifier = -10; } else { return 0; } break; - case LOW: + case WEAKENED: moraleModifier = -5; break; - case HIGH: + case ADVANCING: moraleModifier = 5; break; - case VERY_HIGH: - moraleModifier = 10; + case DOMINATING: + if (playerDeployingForce) { + moraleModifier = 20; + } else { + return 10; + } break; - case UNBREAKABLE: - moraleModifier = 15; + case OVERWHELMING: + if (playerDeployingForce) { + moraleModifier = 50; + } else { + return 25; + } break; default: break; diff --git a/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java b/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java index bfbc9f157f..a2c49d2baa 100644 --- a/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java @@ -20,19 +20,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.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ResourceBundle; -import java.util.Set; - -import javax.swing.*; - import megamek.client.ui.baseComponents.MMComboBox; import megamek.client.ui.dialogs.CamoChooserDialog; import megamek.client.ui.preferences.JWindowPreference; @@ -55,6 +42,15 @@ import mekhq.gui.utilities.JSuggestField; import mekhq.gui.utilities.MarkdownEditorPanel; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ResourceBundle; +import java.util.Set; + +import static megamek.client.ui.WrapLayout.wordWrap; + /** * @author Neoancient */ @@ -215,7 +211,7 @@ public Component getListCellRendererComponent(final JList list, final Object final boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof AtBMoraleLevel) { - list.setToolTipText(((AtBMoraleLevel) value).getToolTipText()); + list.setToolTipText(wordWrap(((AtBMoraleLevel) value).getToolTipText())); } return this; } diff --git a/MekHQ/src/mekhq/gui/view/MissionViewPanel.java b/MekHQ/src/mekhq/gui/view/MissionViewPanel.java index cccc07377a..147beba69a 100644 --- a/MekHQ/src/mekhq/gui/view/MissionViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/MissionViewPanel.java @@ -34,6 +34,8 @@ import java.awt.event.MouseEvent; import java.util.ResourceBundle; +import static megamek.client.ui.WrapLayout.wordWrap; + /** * A custom panel that gets filled in with goodies from a scenario object * @author Jay Lawson (jaylawson39 at yahoo.com) @@ -922,7 +924,7 @@ public void mouseClicked(MouseEvent e) { lblMorale.setName("lblMorale"); lblMorale.setText(resourceMap.getString("lblMorale.text")); - lblMorale.setToolTipText(contract.getMoraleLevel().getToolTipText()); + lblMorale.setToolTipText(wordWrap(contract.getMoraleLevel().getToolTipText())); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = y; @@ -932,7 +934,7 @@ public void mouseClicked(MouseEvent e) { txtMorale.setName("txtMorale"); txtMorale.setText(contract.getMoraleLevel().toString()); - txtMorale.setToolTipText(contract.getMoraleLevel().getToolTipText()); + txtMorale.setToolTipText(wordWrap(contract.getMoraleLevel().getToolTipText())); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = y++; From 28213938f851ceb850528e8de6d2854c1f7d8d8a Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 25 Sep 2024 19:25:14 -0500 Subject: [PATCH 2/2] merge conflicts --- .../mekhq/campaign/mission/AtBContract.java | 28 ++++++++----------- .../stratcon/StratconRulesManager.java | 4 ++- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index bed4e97b14..ddfe096aea 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -41,7 +41,6 @@ import mekhq.campaign.mission.enums.AtBMoraleLevel; import mekhq.campaign.personnel.Bloodname; import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.backgrounds.BackgroundsController; import mekhq.campaign.personnel.enums.Phenotype; import mekhq.campaign.rating.IUnitRating; @@ -57,16 +56,6 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.io.PrintWriter; -import java.text.ParseException; -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Objects; -import java.util.UUID; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import javax.swing.*; import java.awt.*; import java.io.PrintWriter; @@ -256,14 +245,19 @@ public static boolean isMinorPower(final String factionCode) { } /** - * Checks the morale level of the campaign based on various factors. + * Checks and updates the morale which depends on various conditions such as the rout end date, + * skill levels, victories, defeats, etc. This method also updates the enemy status based on the + * morale level. * - * @param campaign The ongoing campaign. - * @param today The current date. - * @param dragoonRating The player's dragoon rating + * @param today The current date in the context. */ - public void checkMorale(Campaign campaign, LocalDate today, int dragoonRating) { - if (null != routEnd) { + public void checkMorale(Campaign campaign, LocalDate today) { + // Check whether enemy forces have been reinforced, and whether any current rout continues + // beyond its expected date + boolean routContinue = Compute.randomInt(4) < 3; + + // If there is a rout end date, and it's past today, update morale and enemy state accordingly + if (routEnd != null && !routContinue) { if (today.isAfter(routEnd)) { setMoraleLevel(AtBMoraleLevel.STALEMATE); routEnd = null; diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index e13ceccaaa..dbb37eb785 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -108,8 +108,10 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont // multiplies the BV budget of all int scenarioRolls = track.getRequiredLanceCount(); - if (contract.getMoraleLevel().isOverwhelming()) { + if (contract.getMoraleLevel().isDominating()) { scenarioRolls++; + } else if (contract.getMoraleLevel().isOverwhelming()) { + scenarioRolls += 2; } for (int scenarioIndex = 0; scenarioIndex < scenarioRolls; scenarioIndex++) { int targetNum = calculateScenarioOdds(track, contract, false);