3232 */
3333package mekhq .campaign .market .contractMarket ;
3434
35+ import static java .lang .Math .max ;
3536import static java .lang .Math .min ;
3637import static megamek .common .compute .Compute .d6 ;
3738import static megamek .common .enums .SkillLevel .REGULAR ;
5455import megamek .logging .MMLogger ;
5556import mekhq .campaign .Campaign ;
5657import mekhq .campaign .campaignOptions .CampaignOptions ;
58+ import mekhq .campaign .force .CombatTeam ;
59+ import mekhq .campaign .force .Force ;
5760import mekhq .campaign .market .enums .ContractMarketMethod ;
5861import mekhq .campaign .mission .AtBContract ;
5962import mekhq .campaign .mission .Contract ;
6063import mekhq .campaign .mission .Mission ;
6164import mekhq .campaign .mission .enums .AtBContractType ;
65+ import mekhq .campaign .mission .enums .CombatRole ;
6266import mekhq .campaign .mission .enums .ContractCommandRights ;
6367import mekhq .campaign .mission .utilities .ContractUtilities ;
6468import mekhq .campaign .rating .IUnitRating ;
@@ -86,12 +90,6 @@ public abstract class AbstractContractMarket {
8690 private static final int MERCENARY_THRESHOLD_LIAISON = 12 ;
8791 private static final int NON_MERCENARY_THRESHOLD = 12 ;
8892
89- /**
90- * The portion of combat teams we expect to be performing combat actions. This is one in 'x' where 'x' is the value
91- * set here.
92- */
93- static final double BASE_VARIANCE_FACTOR = 0.7 ;
94-
9593
9694 protected List <Contract > contracts = new ArrayList <>();
9795 protected int lastId = 0 ;
@@ -260,10 +258,12 @@ protected void updateReport(Campaign campaign) {
260258 * @param campaign the campaign containing relevant options and faction information
261259 * @param contract the contract that specifies details such as subcontract status
262260 * @param bypassVariance a flag indicating whether variance adjustments should be bypassed
261+ * @param varianceFactor the degree of variance to apply to required combat elements
263262 *
264263 * @return the calculated number of required units in combat teams, ensuring it meets game rules and constraints
265264 */
266- public int calculateRequiredCombatElements (Campaign campaign , AtBContract contract , boolean bypassVariance ) {
265+ public int calculateRequiredCombatElements (Campaign campaign , AtBContract contract , boolean bypassVariance ,
266+ double varianceFactor ) {
267267 // Return 1 combat team if the contract is a subcontract
268268 if (contract .isSubcontract ()) {
269269 return 1 ;
@@ -274,13 +274,9 @@ public int calculateRequiredCombatElements(Campaign campaign, AtBContract contra
274274
275275 // If bypassing variance, apply flat reduction (reduce force by 1/3)
276276 if (bypassVariance ) {
277- return Math . max (effectiveForces - calculateBypassVarianceReduction (effectiveForces ), 1 );
277+ return max (effectiveForces - calculateBypassVarianceReduction (effectiveForces ), 1 );
278278 }
279279
280- // Apply variance based on a die roll
281- int varianceRoll = d6 (2 );
282- double varianceFactor = calculateVarianceFactor (varianceRoll );
283-
284280 // Adjust available forces based on variance, ensuring minimum clamping
285281 int adjustedForces = (int ) Math .floor ((double ) effectiveForces * varianceFactor );
286282
@@ -293,43 +289,60 @@ public int calculateRequiredCombatElements(Campaign campaign, AtBContract contra
293289 }
294290
295291 /**
296- * Calculates the variance factor based on the given roll value and a fixed formation size divisor.
292+ * Calculates the required number of combat teams (intensity) for a contract based on campaign options, contract
293+ * details, and variance factors.
297294 *
298- * <p>
299- * The variance factor is determined by applying a multiplier to the fixed formation size divisor. The multiplier
300- * varies based on the roll value:
295+ * <p>This method determines the number of combat elements needed to deploy, taking into account factors such
296+ * as:</p>
301297 * <ul>
302- * <li><b>Roll 2:</b> Multiplier is 0.575.</li>
303- * <li><b>Roll 3:</b> Multiplier is 0.6.</li>
304- * <li><b>Roll 4:</b> Multiplier is 0.625</li>
305- * <li><b>Roll 5:</b> Multiplier is 0.65.</li>
306- * <li><b>Roll 6:</b> Multiplier is 0.675.</li>
307- * <li><b>Roll 7:</b> Multiplier is 0.7.</li>
308- * <li><b>Roll 7:</b> Multiplier is 0.725.</li>
309- * <li><b>Roll 7:</b> Multiplier is 0.75.</li>
310- * <li><b>Roll 7:</b> Multiplier is 0.775.</li>
311- * <li><b>Roll 7:</b> Multiplier is 0.8.</li>
312- * <li><b>Roll 7:</b> Multiplier is 0.825.</li>
298+ * <li>Whether the contract is a subcontract (returns 1 as a base case).</li>
299+ * <li>The effective unit forces.</li>
300+ * <li>Whether variance bypass is enabled, applying a flat reduction to available forces.</li>
301+ * <li>Variance adjustments applied through a die roll, affecting the availability of forces.</li>
313302 * </ul>
314303 *
315- * @param roll the roll value used to determine the multiplier
304+ * <p>The method ensures values are clamped to maintain a minimum deployment of at least 1 combat element while
305+ * not exceeding the maximum deployable combat elements.</p>
306+ *
307+ * @param campaign the campaign containing relevant options and faction information
308+ * @param contract the contract that specifies details such as subcontract status
309+ * @param bypassVariance a flag indicating whether variance adjustments should be bypassed
310+ * @param varianceFactor the degree of variance to apply to required combat elements
311+ *
312+ * @return the calculated number of required units in combat teams, ensuring it meets game rules and constraints
316313 *
317- * @return the calculated variance factor as a double
314+ * @since 0.50.10
318315 */
319- private double calculateVarianceFactor (int roll ) {
320- return switch (roll ) {
321- case 2 -> BASE_VARIANCE_FACTOR - 0.125 ;
322- case 3 -> BASE_VARIANCE_FACTOR - 0.1 ;
323- case 4 -> BASE_VARIANCE_FACTOR - 0.075 ;
324- case 5 -> BASE_VARIANCE_FACTOR - 0.05 ;
325- case 6 -> BASE_VARIANCE_FACTOR - 0.025 ;
326- case 8 -> BASE_VARIANCE_FACTOR + 0.025 ;
327- case 9 -> BASE_VARIANCE_FACTOR + 0.05 ;
328- case 10 -> BASE_VARIANCE_FACTOR + 0.075 ;
329- case 11 -> BASE_VARIANCE_FACTOR + 0.1 ;
330- case 12 -> BASE_VARIANCE_FACTOR + 0.125 ;
331- default -> BASE_VARIANCE_FACTOR ; // 7
332- };
316+ public int calculateRequiredCombatTeams (Campaign campaign , AtBContract contract , boolean bypassVariance ,
317+ double varianceFactor ) {
318+ // Return 1 combat team if the contract is a subcontract
319+ if (contract .isSubcontract ()) {
320+ return 1 ;
321+ }
322+
323+ // Calculate base formation size and effective unit force
324+ int effectCombatTeams = 0 ;
325+ for (Map .Entry <Integer , CombatTeam > combatTeam : campaign .getCombatTeamsAsMap ().entrySet ()) {
326+ Force force = campaign .getForce (combatTeam .getKey ());
327+ if (force != null ) {
328+ CombatRole combatRoleInMemory = force .getCombatRoleInMemory ();
329+ if (combatRoleInMemory != CombatRole .TRAINING ) {
330+ effectCombatTeams ++;
331+ }
332+ }
333+ }
334+
335+ // If bypassing variance, apply flat reduction (reduce force by 1/3)
336+ if (bypassVariance ) {
337+ return max (effectCombatTeams - calculateBypassVarianceReduction (effectCombatTeams ), 1 );
338+ }
339+
340+ // Adjust available forces based on variance, ensuring minimum clamping
341+ int adjustedCombatTeams = (int ) Math .floor ((double ) effectCombatTeams * varianceFactor );
342+ adjustedCombatTeams = max (adjustedCombatTeams , 1 );
343+
344+ // Return the clamped value, ensuring it does not exceed max-deployable forces
345+ return Math .min (adjustedCombatTeams , effectCombatTeams );
333346 }
334347
335348 /**
0 commit comments