Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed Negative Contract Base Pay #5900

Merged
merged 6 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
import java.util.ArrayList;
import java.util.Set;

import static java.lang.Math.floor;
import static megamek.codeUtilities.MathUtility.clamp;
import static mekhq.campaign.mission.AtBContract.getEffectiveNumUnits;

/**
* Contract offers that are generated monthly under AtB rules.
*
Expand Down Expand Up @@ -451,12 +455,16 @@ private void addFollowup(Campaign campaign,

@Override
public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract) {
int unitRatingMod = campaign.getAtBUnitRatingMod();
double multiplier = 1.0;
// IntOps reputation factor then Dragoons rating

// Operations tempo
multiplier *= contract.getContractType().getOperationsTempoMultiplier();

// Reputation multiplier
if (campaign.getCampaignOptions().getUnitRatingMethod().isCampaignOperations()) {
multiplier *= (unitRatingMod * 0.2) + 0.5;
multiplier *= (campaign.getReputation().getReputationRating() * 0.2) + 0.5;
} else {
int unitRatingMod = campaign.getAtBUnitRatingMod();
if (unitRatingMod >= IUnitRating.DRAGOON_A) {
multiplier *= 2.0;
} else if (unitRatingMod == IUnitRating.DRAGOON_B) {
Expand All @@ -468,8 +476,7 @@ public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract
}
}

multiplier *= contract.getContractType().getOperationsTempoMultiplier();

// Employer multiplier
final Faction employer = Factions.getInstance().getFaction(contract.getEmployerCode());
final Faction enemy = contract.getEnemy();
if (employer.isISMajorOrSuperPower() || employer.isClan()) {
Expand All @@ -484,28 +491,43 @@ public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract
multiplier *= 1.1;
}

// Unofficial modifiers
double unofficialMultiplier = getUnofficialMultiplier(campaign, contract);

if (unofficialMultiplier > 0) {
multiplier *= (1.0 + unofficialMultiplier);
} else if (unofficialMultiplier < 0) {
multiplier *= (1.0 - unofficialMultiplier);
}

return multiplier;
}

private static double getUnofficialMultiplier(Campaign campaign, AtBContract contract) {
double modifier = 0; // we apply these modifiers all together to avoid spiking the final pay

// Adjust pay based on the percentage of the players' forces required by the contract
int maximumLanceCount = campaign.getAllCombatTeams().size();
int reserveLanceCount = (int) floor(maximumLanceCount / COMBAT_FORCE_DIVIDER);
int requiredCombatTeams = contract.getRequiredCombatTeams();
double totalCombatTeams = campaign.getAllCombatTeams().size();
totalCombatTeams /= COMBAT_FORCE_DIVIDER;

if (totalCombatTeams > 0) {
multiplier *= (double) requiredCombatTeams / totalCombatTeams;
if (reserveLanceCount > 0) { // Ensure we don't divide by zero
double reservesDifference = ((requiredCombatTeams - reserveLanceCount) / (double) reserveLanceCount);

modifier += reservesDifference;
}

// Adjust pay based on difficulty if FG3 is enabled
if (campaign.getCampaignOptions().isUseGenericBattleValue()) {
double skulls = contract.calculateContractDifficulty(campaign);
skulls -= 5; // 5 skulls (or 2.5) is equivalent to the player force, so no modifier.
int difficulty = clamp(contract.calculateContractDifficulty(campaign), 0, 10);
int baseDifficulty = 5; // 2.5 skulls

if (skulls != 0) {
skulls *= 0.05; // each half-skull is a 5% pay change
multiplier *= (1 + skulls);
}
}
double difficultyDifference = ((difficulty - baseDifficulty) / (double) baseDifficulty);

modifier += difficultyDifference;
}

return multiplier;
return modifier;
}

@Override
Expand Down Expand Up @@ -552,7 +574,7 @@ private void setContractClauses(AtBContract contract, int unitRatingMod, Campaig
if (campaign.getCampaignOptions().isMercSizeLimited() &&
campaign.getFaction().isMercenary()) {
int max = (unitRatingMod + 1) * 12;
int numMods = (AtBContract.getEffectiveNumUnits(campaign) - max) / 2;
int numMods = (getEffectiveNumUnits(campaign) - max) / 2;
while (numMods > 0) {
mods.mods[Compute.randomInt(4)]--;
numMods--;
Expand Down
32 changes: 29 additions & 3 deletions MekHQ/src/mekhq/campaign/mission/AtBContract.java
Original file line number Diff line number Diff line change
Expand Up @@ -1936,10 +1936,36 @@ public JPanel getContractDifficultySkulls(Campaign campaign) {
}

/**
* Calculates the contract difficulty based on the given campaign and parameters.
* Calculates the difficulty of a contract based on the relative power of enemy forces,
* player forces, and any allied forces involved in the campaign.
*
* <p>The method evaluates the enemy's estimated power against the player's strengths
* and considers allied contributions depending on the assigned command rights.
* The result is a difficulty level mapped between 1 and 10, where higher values
* represent more challenging contracts.</p>
*
* @param campaign The {@link Campaign} object representing the current game state.
* Used to extract information about the player's forces, enemy forces,
* and allied forces.
*
* @return An integer representing the difficulty of the contract:
* <ul>
* <li>1 = very easy</li>
* <li>10 = extremely difficult</li>
* </ul>
* <p>
* <b>WARNING: </b>Returns `-99` (defined as `ERROR`) if the enemy's power cannot be calculated.
* </p>
* <p><b>Mapped Result Explanation:</b></p>
* The method divides the absolute percentage difference between enemy and player forces by 20
* (rounding up), then adjusts the difficulty accordingly:
* <ul>
* <li>If the player's forces are stronger, the difficulty is adjusted downward from a baseline of 5.</li>
* <li>If the enemy's forces are stronger, the difficulty is adjusted upward from a baseline of 5.</li>
* <li>If an error is encountered, the difficulty is returned as -99</li>
* </ul>
* The result is clamped to fit between the valid range of 1 and 10. Or -99 if an error is encounterd.
*
* @param campaign The campaign object containing the necessary data.
* @return The contract difficulty as an integer value.
*/
public int calculateContractDifficulty(Campaign campaign) {
final int ERROR = -99;
Expand Down
Loading