Skip to content

Improvement: Added Contract Pay Diminishing Returns Campaign Option#8522

Merged
IllianiBird merged 12 commits intoMegaMek:mainfrom
IllianiBird:contractPayDiminishingReturns
Dec 15, 2025
Merged

Improvement: Added Contract Pay Diminishing Returns Campaign Option#8522
IllianiBird merged 12 commits intoMegaMek:mainfrom
IllianiBird:contractPayDiminishingReturns

Conversation

@IllianiBird
Copy link
Collaborator

Includes Improvement: Added New Alternate Contract Payment Model

A Note for Reviewers

This PR includes all of the changes found in the above. I recommend not trying to review this PR until the above has been merged.

The Problem

The issue of infinite growth is something I've been thinking about a lot, throughout this dev cycle and the end of 50.10's. To give a brief summary, under CamOps contract pay is based on the value of the available combat units in the campaign. In this way contracts scale based on the campaign. A campaign with only a company will be paid 5% of that company's value. While a campaign consisting of an Army Group will be paid 5% of the value of the entire Army Group.

On paper this seems reasonable and allows the system to scale smoothly. However this runs into a problem we see a lot, when translating CamOps rules: the disparity between the kinds of campaigns CamOps was designed for and MekHQ.

CamOps vs. MekHQ Disparity of Design

CamOps was written with in-person campaigns in mind. Where a group of friends get together once or twice a month to play a number of connected scenarios. Growth is inherently limited by the limited length of the campaign, or at least its slow progression.

In MekHQ players can complete multiple contracts in a single evening. Growth is limited only by a players' available RAM and patience. In the past there were effective caps on campaign size, mainly in the form of poor performance and tedium. Nowadays, thanks to the improvements we've made over the past two years, we're seeing a creeping trend towards larger and larger campaigns.

In the past a battalion was 'endgame,' now a battalion is just an established campaign. Now, 'endgame' can be multiple regiments, or larger.

Exponential Growth

Finally, we run into another issue. Once a campaign reaches a certain skill threshold (be that player skill, personnel skill, or both) we start seeing decreasing loses. Without a need to replace personnel and equipment that 5% of unit value quickly becomes effectively pure profit. When combined with salvage takings the player can growth their campaign exponentially.

A Solution

Our solution to the above issues is three part:

  1. Additional Money Sinks: over the past year there has been an increased focus on introducing reasonable money sinks. This helps slow down player economic growth and better fits with the overall emphasis on slower campaign growth (see also changes to the XP economics).
  2. Remove Financial Emphasis on Expensive Units: This is handled by the required PR cited above. In brief we normalize the evaluation of player units. This means players with poorer equipment are not penalized, while players with expensive customs cannot rely on contract pay to carry the economic weight of those units.
  3. Introduce A Soft Cap on Campaign Growth: The focus of this PR.

A Growth Soft Cap

Players often ask how big they should grow their campaign, the generic answer is "stop when you want to." This PR introduces an alternative answer: "stop when you can't afford to grow any larger."

When campaign pay is calculated there is a bunch of math involved, but the most relevant is unit value. As described above we total the value of the combat units in the players' TO&E and derive base contract pay from that value.

With this PR we introduce diminishing returns on that derived value. All units up to two battalions (or factional equivalent) are valued as normal. However, all units beyond that suffer diminishing returns on their value (capped at 10% around 4 battalions). To avoid overly penalizing the player their most expensive units are counted first.

Clarification

It is important to note that diminishing returns affects the value of the unit not the unit's final contributions. By default only 5% of the unit's value is contributed to base contract pay. Therefore we reduce the unit's value by the diminishing returns and that value is the one used by the 5% calculation.

@IllianiBird IllianiBird self-assigned this Dec 14, 2025
@IllianiBird IllianiBird requested a review from a team as a code owner December 14, 2025 21:39
@IllianiBird IllianiBird added Campaign Options Relates to, or includes, campaign option changes Improvement to Existing Feature Used with the RFE tag to indicate an improvement to an existing feature labels Dec 14, 2025
@codecov
Copy link

codecov bot commented Dec 14, 2025

Codecov Report

❌ Patch coverage is 20.83333% with 76 lines in your changes missing coverage. Please review.
✅ Project coverage is 12.43%. Comparing base (c8e71e5) to head (bbde9ef).
⚠️ Report is 42 commits behind head on main.

Files with missing lines Patch % Lines
MekHQ/src/mekhq/campaign/finances/Accountant.java 0.00% 23 Missing ⚠️
...mekhq/gui/campaignOptions/CampaignOptionsPane.java 0.00% 10 Missing ⚠️
...turnsCampaignOptionsChangedConfirmationDialog.java 0.00% 10 Missing ⚠️
...ctPayCampaignOptionsChangedConfirmationDialog.java 0.00% 10 Missing ⚠️
...mekhq/gui/campaignOptions/contents/MarketsTab.java 0.00% 7 Missing ⚠️
...campaignOptions/CampaignOptionsFreebieTracker.java 0.00% 4 Missing ⚠️
...et/contractMarket/AlternatePaymentModelValues.java 82.60% 1 Missing and 3 partials ⚠️
...ekhq/campaign/campaignOptions/CampaignOptions.java 25.00% 3 Missing ⚠️
...ign/campaignOptions/CampaignOptionsMarshaller.java 0.00% 2 Missing ⚠️
...HQ/src/mekhq/gui/dialog/CampaignUpgradeDialog.java 0.00% 2 Missing ⚠️
... and 1 more
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #8522      +/-   ##
============================================
+ Coverage     12.39%   12.43%   +0.03%     
- Complexity     7474     7493      +19     
============================================
  Files          1290     1290              
  Lines        165569   165620      +51     
  Branches      24924    24942      +18     
============================================
+ Hits          20526    20587      +61     
+ Misses       143084   143069      -15     
- Partials       1959     1964       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a diminishing returns system for contract pay calculations to address the issue of infinite campaign growth in MekHQ. The feature works in conjunction with the alternate payment model to create a soft cap on campaign size by progressively reducing the marginal value of units beyond two battalions (or factional equivalent).

Key Changes:

  • Adds a new diminishing returns calculation that reduces unit values for contract pay once combat forces exceed two battalions, capping at 10% value around four battalions
  • Integrates diminishing returns with both the new alternate payment model and existing equipment-based contract pay systems
  • Introduces user-facing confirmation dialogs to warn players about the economic impact when enabling these options mid-campaign

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
AlternatePaymentModelValues.java Core implementation of diminishing returns calculation with configurable slope, power, and floor values; also includes the alternate payment model unit valuation system
AlternatePaymentModelValuesTest.java Comprehensive test coverage for diminishing returns logic, including edge cases, formula verification, and sorting behavior
Accountant.java Integration of diminishing returns into contract base pay calculation for all relevant payment models
Force.java New getUnitsAsUnits(Hangar) convenience method to resolve unit IDs to Unit objects
CampaignOptions.java Adds useAlternatePaymentMode and useDiminishingContractPay boolean options
CampaignOptionsFreebieTracker.java Tracks changes to the new options to trigger appropriate dialogs when enabled
CampaignOptionsMarshaller.java XML serialization support for the new campaign options
CampaignOptionsUnmarshaller.java XML deserialization support for the new campaign options
CampaignOptionsPane.java Triggers confirmation dialogs when players enable the new options in existing campaigns
CampaignUpgradeDialog.java Reorders option tracking to occur after options are fully applied during campaign upgrades
MarketsTab.java Adds UI checkboxes for the two new campaign options in the Markets tab
NormalizedContractPayCampaignOptionsChangedConfirmationDialog.java User-facing dialog warning about potential contract pay reduction when enabling alternate payment mode
DiminishingReturnsCampaignOptionsChangedConfirmationDialog.java User-facing dialog explaining diminishing returns when enabling the option
*.properties files Localization strings for tooltips, dialog messages, and UI labels

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

IllianiBird and others added 7 commits December 14, 2025 19:07
…eTracker.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…tModelValues.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…onsChangedConfirmationDialog.properties

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
# Conflicts:
#	MekHQ/src/mekhq/campaign/market/contractMarket/AlternatePaymentModelValues.java
#	MekHQ/unittests/mekhq/campaign/market/contractMarket/AlternatePaymentModelValuesTest.java
Comment on lines +31 to +45
NormalizedContractPayCampaignOptionsChangedConfirmationDialog.message=<h1 style="text-align:center">Normalized \
Contract Pay</h1>\
You have just enabled normalized contract pay in a campaign that previously had it disabled. This option was \
introduced as a method to make things easier for new campaigns with poor equipment. It was also implemented to \
reduce the issue of growth spirals in late game campaigns.\
<p>This option significantly changes how contract base pay is calculated. It places the emphasis on the types of \
unit in your TO&E, rather than each unit's value. If you are familiar with Generic Battle Value, normalized \
contract pay works in a similar (though not identical) manner.</p>\
<p>As you are enabling this option in an ongoing campaign, you must be prepared for a potentially massive reduction \
in contract pay. Depending on the units in your roster, and your other campaign options, it is entirely possible \
that your campaign may no longer be financially viable.</p>\
<p>It is recommended that you generate a handful of contracts using GM mode to get a feel for the new pay scale. \
You should consider disabling this option in the event your campaign is no longer playable.</p>
NormalizedContractPayCampaignOptionsChangedConfirmationDialog.button.cancel=Turn Off Normalized Contract Pay
NormalizedContractPayCampaignOptionsChangedConfirmationDialog.button.confirm=Leave Normalized Contract Pay On
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed Diminishing Returns active before I could push the option change dialog confirmation for Normalized, so it made sense to merge the former into the latter

Copy link
Member

@psikomonkie psikomonkie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!!

@IllianiBird IllianiBird merged commit dbc85b3 into MegaMek:main Dec 15, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Campaign Options Relates to, or includes, campaign option changes Improvement to Existing Feature Used with the RFE tag to indicate an improvement to an existing feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants