Skip to content

Clamp to avoid moving the loan out of bounds.#3645

Open
DarthSashna wants to merge 9 commits intoOpenLoco:masterfrom
DarthSashna:master
Open

Clamp to avoid moving the loan out of bounds.#3645
DarthSashna wants to merge 9 commits intoOpenLoco:masterfrom
DarthSashna:master

Conversation

@DarthSashna
Copy link

@DarthSashna DarthSashna commented Feb 15, 2026

This should have three observable effects:

  1. One cannot make their loan negative. This thus fixes Loan can be negative #3638.
  2. Existing companies with negative loans will have their loan clamped to 0 when adjusting it.
  3. Increasing the loan in a large increment will now increase the loan all the way to the max, rather than getting an error when the loan increment is more than the difference between the existing and maximum loan.

It was and remains possible to decrease the maximum loan in sandbox mode. If this causes the maximum loan to be less than the company's current loan, then attempting to adjust the company's loan will clamp it to the new maximum. Previously in this case, companies could gradually decrease their loan, but would get an error when increasing it.

{
const auto maxLoan = Economy::getInflationAdjustedCost(CompanyManager::getMaxLoanSize(), 0, 8);
if (newLoan > maxLoan)
if (company->currentLoan >= maxLoan)
Copy link
Contributor

Choose a reason for hiding this comment

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

why the change to >=?

Copy link
Author

@DarthSashna DarthSashna Feb 16, 2026

Choose a reason for hiding this comment

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

I could have left the original check, but that would have preserved an old quirk where one cannot maximise the loan simply by holding the increase loan button. A large increment from holding the button for a while could exceed the difference between the old and new loan and would thus be rejected even when the company could borrow a little bit more.

It would not make sense to simply drop clampedNewLoan in as it is clamped to never exceed the maximum loan, so the check would never trip and the user would never know why the loan suddenly stops increasing. Thus, I changed the check to be based on the company's currently loan: if the loan is already maxed out (the company's current loan should never exceed the maximum in non-sandbox gameplay, of course.), report an error. In theory, strict equality (i.e. company->currentLoan == maxLoan) would work just the same, but I didn't see any reason to not use >=, which may also help if the current loan were to somehow exceed the maximum loan.

Copy link
Author

@DarthSashna DarthSashna Feb 16, 2026

Choose a reason for hiding this comment

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

Using sandbox mode to reduce the maximum loan when the current loan is maxed out causes the loan to be clamped to the new maximum when one tries to reduce or increase the loan. This of course causes the company->currentLoan > clampedNewLoan branch to be taken, so one does not get an error when the company's loan exceeds the maximum loan. In some sense, it's not wrong - the loan does get back in bounds. But it doing so silently is a bit quirky. Then again, you can only see that behaviour using sandbox mode and it doesn't crash the game, so I'm not sure that it's worth 'fixing'. It may be more honest to use a strict equality in the check.

Copy link
Author

Choose a reason for hiding this comment

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

Then again, you can only see that behaviour using sandbox mode and it doesn't crash the game, so I'm not sure that it's worth 'fixing'. It may be more honest to use a strict equality in the check.

Actually, the game could clamp the loan to be between std::min<currency32_t>(company->currentLoan, 0) and std::max<currency32_t>(company->currentLoan, maxLoan). So if the user has a negative loan from an old save, it won't decrease, but they can increase it. Conversely if sandbox mode is used to have maxLoan < company->currentLoan, attempting to increase the loan won't cause the loan to clamp downwards and will thus error out, but they can repay it normally. That wouldn't immediately bring the loan back in bounds if it was already out of bounds, but it stops the user from making things worse.

It might be possible to have it clamp in bounds all at once when adjusting it towards the bounds, but I think that that would require additional checks, so simply letting users gradually bring things back in bounds is probably simpler.

Copy link
Author

@DarthSashna DarthSashna Feb 16, 2026

Choose a reason for hiding this comment

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

While my latest commit can allow clampedNewLoan > maxLoan when company->currentLoan > maxLoan, it can still only be increased to maxLoan if it was less than or equal to it, so we will still need to check company->currentLoan to get the error in normal gameplay without forbidding increases to maxLoan. And the comparison needs to be >= to retain the old behaviour. If it were ==, then attempting to increase an oversized loan would silently clamp. If it were >, then attempting to increase an exactly maxed-out loan would silently clamp.

I don't think that it makes sense for attempting to decrease a
negative loan (from an old save) to increases it zero. It also
does not make sense for trying to increase a previously-maxed out     
loan after decreasing the maximum loan using sandbox mode to   
decrease the loan.
@DarthSashna DarthSashna changed the title Clamp new loan to be between $0 and the maximum loan Clamp new loan to avoid decreasing loan below zero or attempting to increase them above the maximum. Feb 16, 2026
@DarthSashna DarthSashna changed the title Clamp new loan to avoid decreasing loan below zero or attempting to increase them above the maximum. Clamp to avoid moving the loan out of bounds. Feb 16, 2026
@DarthSashna
Copy link
Author

One outcome that I find interesting is that the maximum loan displayed in the scenario editor is evidently rounded down to the nearest £100, but the actual maximum loan is continuous.
Scenario in sandbox mode, with the actual loan slightly greater than the maximum displayed in the editor.

This shouldn't be hard to fix: either divide by 100 - thus rounding down - before multiply by 100 again or subtract maxLoan % 100. Or not round in the scenario editor, but I think that changing that is beyond a bugfix.

@DarthSashna
Copy link
Author

DarthSashna commented Feb 16, 2026

One consequence of that is that logic is duplicated between ScenarioOptions and changeLoan(). I'm not sure if that should be refactored, for example, by doing the rounding in CompanyManager::getMaxLoanSize(). But might that refactor be more in scope for another PR?

This is so that the maximum loan the user can reach is consistent with the maximum loan in the scenario editor/sandbox mode. Previously, the lack of rounding could not be observed as attempting to increase the loan when you were close to the maximum would fail.
if (company->currentLoan > newLoan)
// Old saves may have a negative loan as per #3836; allow gradual increases, but not decreases.
const currency32_t clampLow = std::min<currency32_t>(company->currentLoan, 0);
// Sandbox mode can be used to decrease `maxLoan` such that `company->maxLoan > maxLoan`. Don't clamp the loan downwards
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need to care about that. If you are in sandbox mode you already can and do want to be outside of bounds of normal play. Just use the normal clamp.

@duncanspumpkin
Copy link
Contributor

I'm starting to think we shouldn't be changing the parameters at all here and just reject a negative loan outright no clamping. The callers are the ones that should ensure they pass up to the max.

Don't worry about certain old saves or sandbox mode and just clamp between 0 and maxLoan.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Loan can be negative

2 participants