From da8ce4e6cd27e2504bc5076366cc98557f6a3584 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Mon, 2 Dec 2024 20:18:49 -0600 Subject: [PATCH 1/3] Refactored formation size determination logic Unified the method for calculating standard force sizes based on factions, enabling consistent and flexible force size determination. Replaced magic numbers with descriptive constants to improve code readability and maintainability. Simplified the process of calculating required combat forces and adjusted labels to reflect this change. --- .../resources/ContractMarketDialog.properties | 2 +- .../resources/NewContractDialog.properties | 2 +- .../campaign/force/StrategicFormation.java | 52 +++++++++++--- .../AbstractContractMarket.java | 70 +++++++++++++------ .../AtbMonthlyContractMarket.java | 26 +++---- .../contractMarket/CamOpsContractMarket.java | 6 +- .../mekhq/campaign/mission/AtBContract.java | 39 +++++++---- .../mission/AtBDynamicScenarioFactory.java | 33 +-------- .../mission/CommonObjectiveFactory.java | 4 +- .../atb/AtBScenarioModifierApplicator.java | 5 +- .../mekhq/gui/view/ContractSummaryPanel.java | 13 ++-- .../mekhq/gui/view/LanceAssignmentView.java | 2 +- 12 files changed, 150 insertions(+), 104 deletions(-) diff --git a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties index 5a4d46bfec..d4fa8f98bd 100644 --- a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties +++ b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties @@ -34,7 +34,7 @@ lblTransport.text=Transport Terms: lblSalvageRights.text=Salvage Rights: lblStraightSupport.text=Straight Support: lblBattleLossComp.text=Battle Loss Compensation: -lblRequiredLances.text=Required Lances: +lblRequiredLances.text=Required Combat Forces: lblRenegotiate.text=Renegotiate diff --git a/MekHQ/resources/mekhq/resources/NewContractDialog.properties b/MekHQ/resources/mekhq/resources/NewContractDialog.properties index e53aec9e9f..f44e6ca6fe 100644 --- a/MekHQ/resources/mekhq/resources/NewContractDialog.properties +++ b/MekHQ/resources/mekhq/resources/NewContractDialog.properties @@ -44,7 +44,7 @@ lblAllyBotName.text=Ally Bot Name: lblEnemyBotName.text=Enemy Bot Name: lblAllyCamo.text=Ally Camo lblEnemyCamo.text=Enemy Camo -lblRequiredLances.text=Required Lances: +lblRequiredLances.text=Required Combat Forces: lblEnemyMorale.text=Enemy Morale: lblContractScoreArbitraryModifier.text=Contract Score Modifier: lblBasePay.text=Base Pay: diff --git a/MekHQ/src/mekhq/campaign/force/StrategicFormation.java b/MekHQ/src/mekhq/campaign/force/StrategicFormation.java index cf20b4268a..b0212b0e0a 100644 --- a/MekHQ/src/mekhq/campaign/force/StrategicFormation.java +++ b/MekHQ/src/mekhq/campaign/force/StrategicFormation.java @@ -52,6 +52,7 @@ import static megamek.common.EntityWeightClass.WEIGHT_ULTRA_LIGHT; import static mekhq.campaign.force.Force.STRATEGIC_FORMATION_OVERRIDE_NONE; import static mekhq.campaign.force.Force.STRATEGIC_FORMATION_OVERRIDE_TRUE; +import static mekhq.campaign.force.FormationLevel.LANCE; /** * Used by Against the Bot & StratCon to track additional information about each force @@ -64,9 +65,9 @@ public class StrategicFormation { private static final MMLogger logger = MMLogger.create(StrategicFormation.class); - public static final int STR_IS = 4; - public static final int STR_CLAN = 5; - public static final int STR_CS = 6; + public static final int LANCE_SIZE = 4; + public static final int STAR_SIZE = 5; + public static final int LEVEL_II_SIZE = 6; public static final long ETYPE_GROUND = ETYPE_MEK | ETYPE_TANK | Entity.ETYPE_INFANTRY | ETYPE_PROTOMEK; @@ -81,20 +82,51 @@ public class StrategicFormation { /** * Determines the standard size for a given faction. The size varies depending on whether the - * faction is a Clan, ComStar/WoB, or others (Inner Sphere). + * faction is a Clan, ComStar/WoB, or others (Inner Sphere). This overloaded method defaults to + * Lance/Star/Level II * * @param faction The {@link Faction} object for which the standard force size is to be calculated. - * @return The standard force size for the given faction. It returns {@code STR_CLAN} if the - * faction is a Clan, {@code STR_CS} if the faction is ComStar or WoB, and {@code STR_IS} otherwise. + * @return The standard force size, at the provided formation level, for the provided faction */ public static int getStandardForceSize(Faction faction) { - if (faction.isClan()) { - return STR_CLAN; + return getStandardForceSize(faction, LANCE.getDepth()); + } + + /** + * Determines the standard size for a given faction. The size varies depending on whether the + * faction is a Clan, ComStar/WoB, or others (Inner Sphere). + * + * @param faction The {@link Faction} object for which the standard force size is to be calculated. + * @param formationLevelDepth The {@link FormationLevel} {@code Depth} from which the standard + * force size is to be calculated. + * @return The standard force size, at the provided formation level, for the provided faction + */ + public static int getStandardForceSize(Faction faction, int formationLevelDepth) { + int formationSize; + if (faction.isClan() || faction.isMarianHegemony()) { + formationSize = STAR_SIZE; } else if (faction.isComStarOrWoB()) { - return STR_CS; + formationSize = LEVEL_II_SIZE; } else { - return STR_IS; + formationSize = LANCE_SIZE; } + + if (formationLevelDepth == LANCE.getDepth()) { + return formationSize; + } + + formationLevelDepth++; // Lance is depth 0, so we need to add +1 to get the number of iterations + + for (int i = 0; i < formationLevelDepth; i++) { + + if (faction.isComStarOrWoB()) { + formationSize *= 6; + } else { + formationSize *= 3; + } + } + + return formationSize; } /** diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java index 80f929e24b..34cecd4763 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java @@ -2,7 +2,6 @@ import megamek.Version; import megamek.codeUtilities.MathUtility; -import megamek.common.Compute; import megamek.common.enums.SkillLevel; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; @@ -22,6 +21,13 @@ import java.io.PrintWriter; import java.util.*; +import static java.lang.Math.floor; +import static java.lang.Math.max; +import static java.lang.Math.round; +import static megamek.common.Compute.d6; +import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; +import static mekhq.campaign.mission.AtBContract.getEffectiveNumUnits; + /** * Abstract base class for various Contract Market types in AtB/Stratcon. Responsible for generation * and initialization of AtBContracts. @@ -174,18 +180,43 @@ protected void updateReport(Campaign campaign) { /** * Determines the number of required lances to be deployed for a contract. For Mercenary subcontracts - * this defaults to 1; otherwise the number is based on the number of combat units in the campaign. - * @param campaign - * @param contract + * this defaults to 1; otherwise, the number is based on the number of combat units in the + * campaign. Modified by a 2d6 roll if {@code bypassVariance} is {@code false}. + * @param campaign the current campaign + * @param contract the relevant contract + * @param bypassVariance if {@code true} requirements will not be semi-randomized. * @return The number of lances required to be deployed. */ - public int calculateRequiredLances(Campaign campaign, AtBContract contract) { + public int calculateRequiredLances(Campaign campaign, AtBContract contract, boolean bypassVariance) { int maxDeployedLances = calculateMaxDeployedLances(campaign); if (contract.isSubcontract()) { return 1; } else { - int requiredLances = Math.max(AtBContract.getEffectiveNumUnits(campaign) / 6, 1); - return Math.min(requiredLances, maxDeployedLances); + int formationSize = getStandardForceSize(campaign.getFaction()); + int availableForces = max(getEffectiveNumUnits(campaign) / formationSize, 1); + + // We allow for one reserve force per 3 depth 0 forces (lances, etc) + availableForces -= max((int) floor((double) availableForces / 3), 1); + + if (!bypassVariance) { + int roll = d6(2); + + if (roll == 2) { + availableForces = (int) round((double) availableForces * 0.25); + } else if (roll <= 3) { + availableForces = (int) round((double) availableForces * 0.5); + } else if (roll < 5) { + availableForces = (int) round((double) availableForces * 0.75); + } else if (roll == 12) { + availableForces = (int) round((double) availableForces * 1.75); + } else if (roll == 11) { + availableForces = (int) round((double) availableForces * 1.5); + } else if (roll > 9) { + availableForces = (int) round((double) availableForces * 1.25); + } + } + + return MathUtility.clamp(availableForces, 1, maxDeployedLances); } } @@ -228,7 +259,7 @@ protected int getQualityRating(int roll) { } protected void rollCommandClause(final Contract contract, final int modifier) { - final int roll = Compute.d6(2) + modifier; + final int roll = d6(2) + modifier; if (roll < 3) { contract.setCommandRights(ContractCommandRights.INTEGRATED); } else if (roll < 8) { @@ -242,14 +273,14 @@ protected void rollCommandClause(final Contract contract, final int modifier) { protected void rollSalvageClause(AtBContract contract, int mod, int contractMaxSalvagePercentage) { contract.setSalvageExchange(false); - int roll = Math.min(Compute.d6(2) + mod, 13); + int roll = Math.min(d6(2) + mod, 13); if (roll < 2) { contract.setSalvagePct(0); } else if (roll < 4) { contract.setSalvageExchange(true); int r; do { - r = Compute.d6(2); + r = d6(2); } while (r < 4); contract.setSalvagePct(Math.min((r - 3) * 10, contractMaxSalvagePercentage)); } else { @@ -258,7 +289,7 @@ protected void rollSalvageClause(AtBContract contract, int mod, int contractMaxS } protected void rollSupportClause(AtBContract contract, int mod) { - int roll = Compute.d6(2) + mod; + int roll = d6(2) + mod; contract.setStraightSupport(0); contract.setBattleLossComp(0); if (roll < 3) { @@ -273,7 +304,7 @@ protected void rollSupportClause(AtBContract contract, int mod) { } protected void rollTransportClause(AtBContract contract, int mod) { - int roll = Compute.d6(2) + mod; + int roll = d6(2) + mod; if (roll < 2) { contract.setTransportComp(0); } else if (roll < 6) { @@ -299,7 +330,7 @@ protected AtBContractType findMissionType(int unitRatingMod, boolean majorPower) AtBContractType.SECURITY_DUTY, AtBContractType.OBJECTIVE_RAID, AtBContractType.GARRISON_DUTY, AtBContractType.CADRE_DUTY, AtBContractType.DIVERSIONARY_RAID } }; - int roll = MathUtility.clamp(Compute.d6(2) + unitRatingMod - IUnitRating.DRAGOON_C, 2, 12); + int roll = MathUtility.clamp(d6(2) + unitRatingMod - IUnitRating.DRAGOON_C, 2, 12); return table[majorPower ? 0 : 1][roll - 2]; } @@ -316,7 +347,7 @@ protected void setEnemyCode(AtBContract contract) { protected void setAttacker(AtBContract contract) { boolean isAttacker = !contract.getContractType().isGarrisonType() - || (contract.getContractType().isReliefDuty() && (Compute.d6() < 4)) + || (contract.getContractType().isReliefDuty() && (d6() < 4)) || contract.getEnemy().isRebel(); contract.setAttacker(isAttacker); } @@ -372,12 +403,12 @@ protected void setAllyRating(AtBContract contract, int year) { // facing front-line units mod += 1; } - contract.setAllySkill(getSkillRating(Compute.d6(2) + mod)); + contract.setAllySkill(getSkillRating(d6(2) + mod)); if (year > 2950 && year < 3039 && !Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) { mod -= 1; } - contract.setAllyQuality(getQualityRating(Compute.d6(2) + mod)); + contract.setAllyQuality(getQualityRating(d6(2) + mod)); } protected void setEnemyRating(AtBContract contract, int year) { @@ -397,12 +428,12 @@ protected void setEnemyRating(AtBContract contract, int year) { if (Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) { mod += contract.isAttacker() ? 2 : 4; } - contract.setEnemySkill(getSkillRating(Compute.d6(2) + mod)); + contract.setEnemySkill(getSkillRating(d6(2) + mod)); if (year > 2950 && year < 3039 && !Factions.getInstance().getFaction(contract.getEnemyCode()).isClan()) { mod -= 1; } - contract.setEnemyQuality(getQualityRating(Compute.d6(2) + mod)); + contract.setEnemyQuality(getQualityRating(d6(2) + mod)); } public void writeToXML(final PrintWriter pw, int indent) { @@ -475,8 +506,7 @@ public static AbstractContractMarket generateInstanceFromXML(Node wn, Campaign c // Restore any parent contract references for (Contract contract : retVal.contracts) { - if (contract instanceof AtBContract) { - final AtBContract atbContract = (AtBContract) contract; + if (contract instanceof AtBContract atbContract) { atbContract.restore(c); } } diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java index b07f628147..6bb62f6819 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java @@ -20,10 +20,6 @@ */ package mekhq.campaign.market.contractMarket; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Set; - import megamek.common.Compute; import megamek.common.annotations.Nullable; import megamek.common.enums.SkillLevel; @@ -39,11 +35,11 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.rating.IUnitRating; -import mekhq.campaign.universe.Faction; -import mekhq.campaign.universe.Factions; -import mekhq.campaign.universe.PlanetarySystem; -import mekhq.campaign.universe.RandomFactionGenerator; -import mekhq.campaign.universe.Systems; +import mekhq.campaign.universe.*; + +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Set; /** * Contract offers that are generated monthly under AtB rules. @@ -302,7 +298,7 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u contract.calculateLength(campaign.getCampaignOptions().isVariableContractLength()); setContractClauses(contract, unitRatingMod, campaign); - contract.setRequiredLances(calculateRequiredLances(campaign, contract)); + contract.setRequiredLances(calculateRequiredLances(campaign, contract, false)); contract.setMultiplier(calculatePaymentMultiplier(campaign, contract)); contract.setPartsAvailabilityLevel(contract.getContractType().calculatePartsAvailabilityLevel()); @@ -396,7 +392,7 @@ protected AtBContract generateAtBSubcontract(Campaign campaign, } contract.setTransportComp(100); - contract.setRequiredLances(calculateRequiredLances(campaign, contract)); + contract.setRequiredLances(calculateRequiredLances(campaign, contract, true)); contract.setMultiplier(calculatePaymentMultiplier(campaign, contract)); contract.setPartsAvailabilityLevel(contract.getContractType().calculatePartsAvailabilityLevel()); contract.calculateContract(campaign); @@ -438,7 +434,7 @@ private void addFollowup(Campaign campaign, followup.calculateLength(campaign.getCampaignOptions().isVariableContractLength()); setContractClauses(followup, campaign.getAtBUnitRatingMod(), campaign); - contract.setRequiredLances(calculateRequiredLances(campaign, contract)); + contract.setRequiredLances(calculateRequiredLances(campaign, contract, false)); contract.setMultiplier(calculatePaymentMultiplier(campaign, contract)); followup.setPartsAvailabilityLevel(followup.getContractType().calculatePartsAvailabilityLevel()); @@ -488,7 +484,11 @@ public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract multiplier *= 1.1; } - int requiredLances = calculateRequiredLances(campaign, contract); + int baseRequiredLances = calculateRequiredLances(campaign, contract, true); + int requiredLances = contract.getRequiredLances(); + + multiplier *= (double) requiredLances / baseRequiredLances; + int maxDeployedLances = calculateMaxDeployedLances(campaign); if (requiredLances > maxDeployedLances && campaign.getCampaignOptions().isAdjustPaymentForStrategy()) { multiplier *= (double) maxDeployedLances / (double) requiredLances; diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java index 9758f72992..062369afa8 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java @@ -43,8 +43,8 @@ */ public class CamOpsContractMarket extends AbstractContractMarket { private static final MMLogger logger = MMLogger.create(CamOpsContractMarket.class); - private static int BASE_NEGOTIATION_TARGET = 8; - private static int EMPLOYER_NEGOTIATION_SKILL_LEVEL = 5; + private static final int BASE_NEGOTIATION_TARGET = 8; + private static final int EMPLOYER_NEGOTIATION_SKILL_LEVEL = 5; public CamOpsContractMarket() { super(ContractMarketMethod.CAM_OPS); @@ -204,7 +204,7 @@ private Optional generateContract(Campaign campaign, ReputationCont // Step 6: Determine the initial contract clauses setContractClauses(contract, contractTerms); // Step 7: Determine the number of required lances (Not CamOps RAW) - contract.setRequiredLances(calculateRequiredLances(campaign, contract)); + contract.setRequiredLances(calculateRequiredLances(campaign, contract, false)); // Step 8: Calculate the payment contract.setMultiplier(calculatePaymentMultiplier(campaign, contract)); // Step 9: Determine parts availability diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 070c4b7f1d..b504f2751c 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -75,14 +75,19 @@ import java.util.*; import static java.lang.Math.ceil; +import static java.lang.Math.max; import static java.lang.Math.round; import static megamek.client.ratgenerator.ModelRecord.NETWORK_NONE; import static megamek.client.ratgenerator.UnitTable.findTable; +import static megamek.common.Compute.d6; import static megamek.common.UnitType.MEK; import static megamek.common.enums.SkillLevel.ELITE; import static megamek.common.enums.SkillLevel.REGULAR; import static megamek.common.enums.SkillLevel.parseFromInteger; import static megamek.common.enums.SkillLevel.parseFromString; +import static mekhq.campaign.force.FormationLevel.BATTALION; +import static mekhq.campaign.force.FormationLevel.COMPANY; +import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; import static mekhq.campaign.mission.AtBDynamicScenarioFactory.getEntity; import static mekhq.campaign.mission.BotForceRandomizer.UNIT_WEIGHT_UNSPECIFIED; import static mekhq.campaign.rating.IUnitRating.*; @@ -229,9 +234,12 @@ public AtBContract(String name) { } public void initContractDetails(Campaign campaign) { - if (getEffectiveNumUnits(campaign) <= 12) { + int companySize = getStandardForceSize(campaign.getFaction(), COMPANY.getDepth()); + int battalionSize = getStandardForceSize(campaign.getFaction(), BATTALION.getDepth()); + + if (getEffectiveNumUnits(campaign) <= companySize) { setOverheadComp(OH_FULL); - } else if (getEffectiveNumUnits(campaign) <= 48) { + } else if (getEffectiveNumUnits(campaign) <= battalionSize) { setOverheadComp(OH_HALF); } else { setOverheadComp(OH_NONE); @@ -394,7 +402,8 @@ public void calculateLength(final boolean variable) { * @return The number of lances required. */ public static int calculateRequiredLances(Campaign campaign) { - return Math.max(getEffectiveNumUnits(campaign) / 6, 1); + int formationSize = getStandardForceSize(campaign.getFaction()); + return max(getEffectiveNumUnits(campaign) / formationSize, 1); } public static int getEffectiveNumUnits(Campaign campaign) { @@ -495,7 +504,7 @@ public void checkMorale(Campaign campaign, LocalDate today) { Faction enemy = getEnemy(); if (enemy.isClan()) { - reliability = Math.max(5, reliability + 1); + reliability = max(5, reliability + 1); } reliability = switch (reliability) { @@ -590,7 +599,7 @@ public void checkMorale(Campaign campaign, LocalDate today) { "+" + performanceModifier : performanceModifier)); // Total morale modifier calculation - int roll = Compute.d6(2) + targetNumber.getValue(); + int roll = d6(2) + targetNumber.getValue(); logger.info(String.format("Total Modifier: %s", targetNumber.getValue())); logger.info(String.format("Roll: %s", roll)); @@ -598,10 +607,10 @@ public void checkMorale(Campaign campaign, LocalDate today) { final AtBMoraleLevel[] moraleLevels = AtBMoraleLevel.values(); if (roll < 2) { - setMoraleLevel(moraleLevels[Math.max(getMoraleLevel().ordinal() - 2, 0)]); + setMoraleLevel(moraleLevels[max(getMoraleLevel().ordinal() - 2, 0)]); logger.info("Result: Morale Level -2"); } else if (roll < 5) { - setMoraleLevel(moraleLevels[Math.max(getMoraleLevel().ordinal() - 1, 0)]); + setMoraleLevel(moraleLevels[max(getMoraleLevel().ordinal() - 1, 0)]); logger.info("Result: Morale Level -1"); } else if ((roll > 12)) { setMoraleLevel(moraleLevels[Math.min(getMoraleLevel().ordinal() + 2, moraleLevels.length - 1)]); @@ -616,7 +625,7 @@ public void checkMorale(Campaign campaign, LocalDate today) { // 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); + routEnd = today.plusMonths(max(1, d6() - 3)).minusDays(1); } else { campaign.addReport("With the enemy routed, any remaining objectives have been successfully completed." + " The contract will conclude tomorrow."); @@ -751,12 +760,12 @@ public int getContractScoreArbitraryModifier() { public void doBonusRoll(Campaign campaign) { int number; - int roll = Compute.d6(); + int roll = d6(); switch (roll) { case 1: /* 1d6 dependents */ if (campaign.getCampaignOptions().isUseRandomDependentAddition()) { - number = Compute.d6(); + number = d6(); campaign.addReport("Bonus: " + number + " dependent" + ((number > 1) ? "s" : "")); for (int i = 0; i < number; i++) { @@ -893,7 +902,7 @@ public void checkEvents(Campaign c) { break; case EVT_BETRAYAL: String text = "Special Event: Betrayal (employer minor breach)
"; - switch (Compute.d6()) { + switch (d6()) { case 1: text += "Major logistics problem: parts availability level for the rest of the contract becomes one level lower."; partsAvailabilityLevel--; @@ -937,7 +946,7 @@ public void checkEvents(Campaign c) { break; case EVT_SPECIALEVENTS: text = "Special Event: "; - switch (Compute.d6()) { + switch (d6()) { case 1: text += "Change of Alliance: Next Enemy Morale roll gets a +1 modifier."; moraleMod++; @@ -1023,9 +1032,9 @@ public boolean contractExtended(final Campaign campaign) { } final int extension; - final int roll = Compute.d6(); + final int roll = d6(); if (roll == 1) { - extension = Math.max(1, getLength() / 2); + extension = max(1, getLength() / 2); } else if (roll == 2) { extension = 1; } else { @@ -1917,7 +1926,7 @@ public int calculateContractDifficulty(Campaign campaign) { mappedValue = 5 + mappedValue; } - return Math.min(Math.max(mappedValue, 1), 10); + return Math.min(max(mappedValue, 1), 10); } /** diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java index 6971a79c47..5fc247e841 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java @@ -71,6 +71,7 @@ import static megamek.common.Compute.randomInt; import static megamek.common.UnitType.*; import static megamek.common.planetaryconditions.Wind.TORNADO_F4; +import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; import static mekhq.campaign.mission.Scenario.T_GROUND; import static mekhq.campaign.mission.ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_AERO_MIX; import static mekhq.campaign.mission.ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_CIVILIANS; @@ -94,10 +95,6 @@ public class AtBDynamicScenarioFactory { // indexed by dragoons rating private static final int[] infantryToBAUpgradeTNs = { 12, 10, 8, 6, 4, 2 }; - private static final int IS_LANCE_SIZE = 4; - private static final int CLAN_MH_LANCE_SIZE = 5; - private static final int COMSTAR_LANCE_SIZE = 6; - private static final double STRICT = 0.75; private static final double OPPORTUNISTIC = 1.0; private static final double LIBERAL = 1.25; @@ -439,7 +436,7 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac // Get the number of units in the typical ground tactical formation. // This will differ depending on whether the owner uses Inner Sphere lances, // Clan stars, or CS/WOB Level II formations. - int lanceSize = getLanceSize(factionCode); + int lanceSize = getStandardForceSize(faction); // determine generation parameters int forceBV = 0; @@ -3758,32 +3755,6 @@ private static void setScenarioRerolls(AtBDynamicScenario scenario, Campaign cam scenario.setRerolls(tacticsSkill); } - /** - * Convenience function to get the standard ground tactical formation size, - * based on faction. In - * the case of Clan factions, this returns the number of points rather than a - * number of units, - * as points may be 2 ground vehicles or 5 ProtoMeks. - * TODO: conventional infantry typically uses 3 units per formation (company) - - * make a separate method - * - * @param factionCode string with faction short name/lookup key - * @return Number of units (points for Clan) in the formation - */ - public static int getLanceSize(String factionCode) { - Faction faction = Factions.getInstance().getFaction(factionCode); - if (faction != null) { - if (faction.isClan() || faction.isMarianHegemony()) { - // Clans and the Marian Hegemony use a fundamental unit size of 5. - return CLAN_MH_LANCE_SIZE; - } else if (faction.isComStarOrWoB()) { - // ComStar and WoB use a fundamental unit size of 6. - return COMSTAR_LANCE_SIZE; - } - } - - return IS_LANCE_SIZE; - } /** * Worker function to determine the formation size of fixed wing aircraft. diff --git a/MekHQ/src/mekhq/campaign/mission/CommonObjectiveFactory.java b/MekHQ/src/mekhq/campaign/mission/CommonObjectiveFactory.java index bdb8938f83..dea23e1026 100644 --- a/MekHQ/src/mekhq/campaign/mission/CommonObjectiveFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/CommonObjectiveFactory.java @@ -31,6 +31,8 @@ import java.util.ResourceBundle; import java.util.UUID; +import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; + /** * This class contains code for the creation of some common objectives for an AtB scenario * @author NickAragua @@ -219,7 +221,7 @@ public static ScenarioObjective getBreakthrough(AtBContract contract, AtBScenari * Worker function - adds designated lance or currently assigned player units to objective */ private static void addAssignedPlayerUnitsToObjective(AtBScenario scenario, Campaign campaign, ScenarioObjective objective) { - int expectedNumUnits = AtBDynamicScenarioFactory.getLanceSize(campaign.getFactionCode()); + int expectedNumUnits = getStandardForceSize(campaign.getFaction()); if (scenario.isBigBattle()) { expectedNumUnits *= 2; } else if (scenario.isSpecialScenario()) { diff --git a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java index a558b6d06c..186304ce72 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java @@ -35,10 +35,12 @@ import mekhq.campaign.personnel.Skills; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.unit.Unit; +import mekhq.campaign.universe.Faction; import mekhq.campaign.universe.Factions; import java.util.UUID; +import static mekhq.campaign.force.StrategicFormation.getStandardForceSize; import static mekhq.campaign.mission.AtBDynamicScenarioFactory.generateForce; import static mekhq.campaign.mission.AtBDynamicScenarioFactory.randomForceWeight; @@ -125,7 +127,8 @@ public static void removeUnits(AtBDynamicScenario scenario, Campaign campaign, F factionCode = scenario.getContract(campaign).getEnemyCode(); } - actualUnitsToRemove = AtBDynamicScenarioFactory.getLanceSize(factionCode); + Faction faction = Factions.getInstance().getFaction(factionCode); + actualUnitsToRemove = getStandardForceSize(faction); } for (int x = 0; x < actualUnitsToRemove; x++) { diff --git a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java index cae55caeba..de3b6673bf 100644 --- a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java +++ b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java @@ -49,13 +49,13 @@ */ public class ContractSummaryPanel extends JPanel { //region Variable Declarations - private Campaign campaign; - private Contract contract; - private boolean allowRerolls; + private final Campaign campaign; + private final Contract contract; + private final boolean allowRerolls; private int cmdRerolls; private int logRerolls; private int tranRerolls; - private ContractMarketMethod method; + private final ContractMarketMethod method; private JPanel mainPanel; @@ -65,7 +65,7 @@ public class ContractSummaryPanel extends JPanel { private JLabel txtStraightSupport; private JLabel txtBattleLossComp; - private ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.ContractMarketDialog", + private final ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.ContractMarketDialog", MekHQ.getMHQOptions().getLocale()); private ContractPaymentBreakdown contractPaymentBreakdown; @@ -516,8 +516,7 @@ public void mouseClicked(MouseEvent e) { gridBagConstraintsLabels.gridy = ++y; mainPanel.add(lblRequiredLances, gridBagConstraintsLabels); - JLabel txtRequiredLances = new JLabel(((AtBContract) contract).getRequiredLances() - + " Lance(s)"); + JLabel txtRequiredLances = new JLabel(String.valueOf(((AtBContract) contract).getRequiredLances())); txtRequiredLances.setName("txtRequiredLances"); gridBagConstraintsText.gridy = y; mainPanel.add(txtRequiredLances, gridBagConstraintsText); diff --git a/MekHQ/src/mekhq/gui/view/LanceAssignmentView.java b/MekHQ/src/mekhq/gui/view/LanceAssignmentView.java index c1b10728e2..ce47f4f65a 100644 --- a/MekHQ/src/mekhq/gui/view/LanceAssignmentView.java +++ b/MekHQ/src/mekhq/gui/view/LanceAssignmentView.java @@ -203,7 +203,7 @@ public boolean include(Entry Date: Mon, 2 Dec 2024 20:53:32 -0600 Subject: [PATCH 2/3] Refactor and adjust lance calculation logic Simplified import statements by rearranging and removing unused ones, which improved code readability. Modified lance calculation logic to prevent potential zero-lance deployment by ensuring at least one lance is required. This change ensures more reliable and consistent contract setups. --- .../campaign/market/contractMarket/AbstractContractMarket.java | 2 +- .../market/contractMarket/AtbMonthlyContractMarket.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java index 34cecd4763..41e21f81df 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java @@ -188,7 +188,7 @@ protected void updateReport(Campaign campaign) { * @return The number of lances required to be deployed. */ public int calculateRequiredLances(Campaign campaign, AtBContract contract, boolean bypassVariance) { - int maxDeployedLances = calculateMaxDeployedLances(campaign); + int maxDeployedLances = max(calculateMaxDeployedLances(campaign), 1); if (contract.isSubcontract()) { return 1; } else { diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java index 6bb62f6819..9dcac0d8bf 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java @@ -392,7 +392,7 @@ protected AtBContract generateAtBSubcontract(Campaign campaign, } contract.setTransportComp(100); - contract.setRequiredLances(calculateRequiredLances(campaign, contract, true)); + contract.setRequiredLances(calculateRequiredLances(campaign, contract, false)); contract.setMultiplier(calculatePaymentMultiplier(campaign, contract)); contract.setPartsAvailabilityLevel(contract.getContractType().calculatePartsAvailabilityLevel()); contract.calculateContract(campaign); From 0b5ea2eebc0244eb5f9aaf13d15dc93a355c9a09 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Mon, 2 Dec 2024 21:16:37 -0600 Subject: [PATCH 3/3] Fix conditional logic for available forces calculation Replaced conditional check from 'roll <= 3' to 'roll == 3' to ensure the correct scaling factor is applied for available forces. This change prevents incorrect force allocation for other values of roll greater than 3. --- .../campaign/market/contractMarket/AbstractContractMarket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java index 41e21f81df..b106407860 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java @@ -203,7 +203,7 @@ public int calculateRequiredLances(Campaign campaign, AtBContract contract, bool if (roll == 2) { availableForces = (int) round((double) availableForces * 0.25); - } else if (roll <= 3) { + } else if (roll == 3) { availableForces = (int) round((double) availableForces * 0.5); } else if (roll < 5) { availableForces = (int) round((double) availableForces * 0.75);