News:

Simutrans Wiki Manual
The official on-line manual for Simutrans. Read and contribute.

Low staffing in low production factories math

Started by kylofon, May 16, 2026, 08:23:34 PM

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

kylofon

As an early start player (I don't think I've played beyond 1850) I ran into an issue with the 1750 start, where the industries are not producing anything due to low staffing. After digging into it, I found this to be a math issue.

In simfab.cc, the following block applies production limits based on staffing:

sint32 p_menge = prod;
if (staff_shortage)
{
    p_menge *= building->get_staffing_level_percentage();
    p_menge /= 100;
}

p_menge is sint32. When prod is small and staffing is low you get 2 * 25 = 50, then 50 / 100 = 0 instead of the expected 0.5. This is only an issue on the early start values, as the boosts applied later in the game offset the problem.

Additionally, this calculation is run per output slot, so for multioutput factories, there is a redundant calculation per every additional output. However, the calculations are always the same - the actual production output modifiers are applied by the pakset further down the line.

The solution here would be to keep track of the fractions and add them to output when they reach 1.

I'd instead take the staff scaling out of the loop and carry the remainder on the factory itself.

sint32 prod_staffed = prod;
if (staff_shortage)
{
    const sint64 staffing  = (sint64)building->get_staffing_level_percentage();
    const sint64 numerator = (sint64)prod * staffing + (sint64)staffing_scaled_remainder;
    prod_staffed = (sint32)(numerator / 100);
    staffing_scaled_remainder = (uint32)(numerator % 100);
}

...and in the loop do sint32 p_menge = prod_staffed;

staffing_scaled_remainder is a new member on fabrik_t, initialized to 0. Each tick it adds last tick's leftover into the numerator before dividing by 100, so fractional production accumulates and rolls over into whole units instead of vanishing. Done once outside the loop since we have a single shared budget and a single shared remainder per factory. It's also save/load friendly, since it's not serialized and a potential loss here is max 1 product.

The same issue exists in end-chain factories and power consumers, but that can be easily handled with the same approach.

This is a huge and complex codebase, and reading different PR discussions, there are (most) areas here I have zero knowledge about, like multiplayer server-client syncing and so on. One potential issue I found, for example, is that factory outputs used to have per output production amounts:

// sint32 p_menge = (sint32)scale_output_production(product, prod);
In any case, you can check it here for testing, especially mid to late game would be welcome:

https://github.com/kylofon/simutrans-extended/tree/staffing-remainder

https://github.com/jamespetts/simutrans-extended/compare/master...kylofon:simutrans-extended:staffing-remainder

I'm attaching a save game that shows the effect, best seen on the fishing port - try the nightly, then try in my build.