The update visits ships, planes and land units in increasing order of
country number. Within a country, it visits first ships, then planes,
then land units, each in increasing order of unit number.
The order is relevant when money, materials and work don't suffice to
build everything.
Money is charged to the owner, so only the relative order for the same
owner matters there. One order is as good as any.
Work and materials come from the sector, so only the relative order in
each sector matters. The current order unfairly prefers countries
with lower country numbers. Mitigating factor: the affected countries
need to be friendly (ships only) or allied.
The unfairness goes back to Chainsaw's option BUDGET. See the commit
before previous for more detailed historical notes.
The update test demonstrates the unfair behavior: sector 14,6 builds
ships 95/97 owned by country#1, but not 96 owned by country#7.
Likewise, planes 95/96/97 and land units 95/96/97.
Go back to the the pre-BUDGET order: first ships, then planes, then
land units, all in increasing order of unit number, regardless of
owner.
The update test now builds ship, plane and land unit 96 instead of 97.
Bonus: speeds up both the update and budget by a similar absolute
amount. For budget, this is roughly a factor of two in my testing.
For the update, which does much more, it's around 10%.
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
Split upd_slmilcosts() into prep_ships() and prep_lands(). Move the
sanity check for dead ships and land units from prod_ships() and
prod_lands() there. Move their call from prepare_sects() to its
caller, along with pay_reserve().
Create prep_planes() for symmetry. Pilots are now paid at the same
time as other military. Can matter only when the country goes broke
during the update.
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
The update tallies income and expenses in full dollars. Each debit or
credit is rounded before it is added to the tally. Different things
are rounded differently. Examples:
* Each sector's military pay is rounded down. In the stock game, 1m
is paid $4.999998, so n mil cost n*$5 -$1, for all practical n. 10m
in one sector cost $49, but spread over ten sectors they cost only
$40.
* Total pay for military in ships and land units is rounded down.
* Each plane's military pay is rounded down (used to be rounded up
except for broke countries until recent commit 2eb08f4). In the
stock game, flight pay is 5 * $4.999998 = $24.99999. For a plane
with n mil, that's n * $25 - $1. Filed under plane maintenance, not
military payroll.
* Each sector's civilian tax is rounded. In the stock game, 1c pays
$0.499998. 10c in one sector pay $5, but spread over ten sectors
they pay nothing.
* An occupied sector's civilian tax is first rounded, then divided by
four and rounded down *boggle*.
* Each sector's uw tax is rounded. In the stock game, 1u pays
$0.106662. 1-4u in one sector pay nothing. 5-14u pay $1.
This is madness. Has always been that way.
Drop the rounding and track money in type double throughout the
update. Round only the final amount, randomly. This is similar to
how commands accumulate a money delta in player->dolcost.
Likewise, tally the subtotals for budget in type double. Display them
rounded to full dollars.
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
The update simply updates each nation's nat_money as it goes. Works.
Except it doesn't update when it runs on behalf of budget. But it
still checks nat_money to determine whether the nation is solvent.
These checks are all broken. Leads to massive mispredictions when
you'd go broke or solvent during a real update.
Track money unconditionally in nat_budget[].money. Delay update of
nat_money until prod_nat(). Replace separate money[] by new
nat_budget[].start_money. Closes bug#235.
Remaining difference between budget and update in the update test:
* #1: budget mispredicts plane #100 gets built (to be fixed)
* #2: budget shows ship, plane and land unit maintenance when broke,
but update damages them instead (correct)
* #2: sector -14,0 converts, quadrupling its taxes (correct)
* #4 & #5: bank with dust and bars taken over by che (correct)
* #4: plague deaths (correct)
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
Extend struct budget member bm[] to cover ships, planes and land
units, too.
Plane maintenance changes because pilot pay is now consistently
rounded down. Before it was rounded down for broke countries, else
up. The stock game's pilots earn a little less than $25, and solvent
countries save $1 per plane. The rounding doesn't make much sense
either way. To be be addressed in a later commit.
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
update.h is a convenience header to include headers commonly needed in
update code. The price for the convenience is superfluous recompiles.
Include necessary headers directly, and drop update.h
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
shiprepair() limits the efficiency gain to how much the workers can
build, rounding randomly. It charges work, money and materials for
the efficiency actually gained, rounding work up, money down, and
materials randomly. Same for planerepair() and landrepair(). Has
always been that way.
If you get lucky with the random rounding, you may get a bit of extra
work done for free.
The budget command runs the update code, and can be off by one due to
different random rounding.
Sector production used to have the same issue, only more serious,
because a single unit of tech production matters much more for the
budget than a single point of unit efficiency gain. I fixed it in
commit 6f7c93c, v4.3.31.
Fix it for unit building the same way: limit efficiency gain to the
amount the workers can produce (no rounding). Work becomes a hard
limit, not subject to random fluctuations. Randomly round work and
money charged for actual gain, like we do for materials. On average,
this charges exactly the work and money that's used.
This lets budget predict how much gets built a bit more accurately.
It's still not exact, as the amount of work available for building
remains slightly random, and the build cost is randomly rounded.
The old rounding of work for ships carries the comment "I didn't use
roundavg here, because I want to penalize the player with a large
number of ships." Likewise for planes. Rounding work up rather than
randomly increases the work cost by 0.5 per ship, plane or land unit
on average. I could keep the penalty by adding 0.5 before random
rounding. Not worth it, since the effect is actually pretty trivial.
Let's examine a fairly extreme case: an airfield with 600 available
work repairing a huge number of lightly damaged planes, say f2 with
81% average efficiency. The old code lets the airfield repair roughly
600 / 6.5 = ~92 planes, the new code 600 / 6 = 100.
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
If player->simulation, shiprepair(), planerepair(), landrepair() must
use the bp map, and must not change game state.
Copy the sector to a scratch buffer, update it from the bp map, work
on the sector normally, then write back to the bp map. This is
simpler and safer.
Since get_materials() loses its connection to the bp map, move its
declaration out of budg.h.
While there, drop an ancient debugging logerror() from landrepair().
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
Use a single array member instead of multiple scalar members. Only
the array elements that replace scalar members are can be non-zero for
now.
This is a first step to permitting more build materials.
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
The work required for build and repairs is traditionally a function of
build materials: 20 + lcm + 2*hcm for ships, planes and land units,
and (lcm + 2*hcm + oil + rad)/5 for nukes. Make it independently
configurable instead, via new ship-chr, plane-chr, land-chr, nuke-chr
selector bwork, backed by new struct mchrstr member m_bwork, struct
plchrstr member pl_bwork, struct lchrstr member l_bwork, struct
nchrstr member n_bwork. Keep the required work exactly the same for
now.
Clients that compute work from materials need to be updated. Easy,
since build work is now exposed in xdump.
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
Damage due to lack of maintenance is now limited by the unit's minimum
efficiency.
Before, units could die. Unfortunately, the update left any embarked
units on their dead carrier. Should have seen this when I fixed a
related bug in commit c2c0d1ff, v4.3.22. Broken for ships and land
units when Empire 2 added their maintenance cost, and for planes when
commit 2e40a4bb (v4.3.4) replaced nuclear stockpiles by nuke units.
The common root cause of these bugs is the update bypassing pre-write
functions (bug#1010856).
If another unit with the same number got built, it picked up the stuck
cargo, triggering the oops from commit 6fb5caf6, which see.
In "stuck on dead carrier" state, units pretty much behave as if their
carrier was still alive, with additional protection from the fact that
a dead carrier can't be damaged or boarded.
The server detects this state on startup since commit 7da9aab5, and
refuses to start.
Only a deity can take units off a dead carrier.
Why upgrade? I'm not a lawyer, but here's my take on the differences
to version 2:
* Software patents: better protection against abuse of patents to
prevent users from exercising the rights under the GPL. I doubt
we'll get hit with a patent suit, but it's a good move just on
general principles.
* License compatibility: compatible with more free licenses, i.e. can
"steal" more free software for use in Empire. I don't expect to steal
much, but it's nice to have the option.
* Definition of "source code": modernization of some details for today's
networked world, to make it easier to distribute the software. Not
really relevant to us now, as we normally distribute full source code.
* Tivoization: this is about putting GPL-licensed software in hardware,
then make the hardware refuse to run modified software. "Neat" trick
to effectively deny its users their rights under the GPL. Abuse was
"pioneered" by TiVo (popular digital video recorders). GPLv3 forbids
it. Unlikely to become a problem for us.
* Internationalization: more careful wording, to harden the license
outside the US. The lawyers tell us it better be done that way.
* License violations: friendlier way to deal with license violations.
This has come out of past experience enforcing the GPL.
* Additional permissions: Probably not relevant to us.
Also include myself in the list of principal authors.
Replacing getrel(getnatp(US), THEM) by relations_with(US, THEM) makes
a difference only when US equals THEM.
Replace patterns like "us == them || getrel(getnatp(us), them)..." by
"relations_with(us, them)...".
upd_plane() upd_land() and left planes and land units lost to lack of
maintenance on their carriers. Cargo lists were fine anyway, because
unit_cargo_init() ignored dead units. But when the dead unit got
reused for building a new one, pln_prewrite() / lnd_prewrite() got
confused and attempted to take it off its carrier, which made
clink_rem() oops, because the unit wasn't on the cargo list. No real
harm done, as oops recovery was fine.
Fix upd_plane() and upd_land() to clear the carrier. Make
unit_cargo_init() oops when it finds dead units on carriers.
Use new pln_is_in_orbit() when we want to test for orbit specifically,
and test PLN_LAUNCHED when we want to test whether the plane not
sitting in the sector (because it is flying). This distinction is
pointless at this time, because the only way PLN_LAUNCHED gets set is
when a satellite goes into orbit. It will become useful in a later
commit, which will use PLN_LAUNCHED to mark flying planes.
type. Make it abstract because that's possible. Change data layout
so that the slots belonging to a sector are together in memory, it's
nicer to the cache.
(bp): The new type. Users changed.
(get_wp): Update accordingly.
(alloc_bp): New.
(update_main, calc_all): Use it. Before, calc_all() allocated 1/7
more than necessary.
(shiprepair, planerepair, landrepair): Use it. Behavioral change:
ship repairs outside harbors and plane repairs by a carrier can use
fewer materials. Before, such repairs consumed each required
commodity as far as available. Now, they consume the same fraction of
the real cost of each commodity, i.e. commodity use is limited by the
most scarce commodity. Neither old nor new behavior make much sense,
but the new code is simpler.
other. Ensure headers in include/ can be included in any order
(except for econfig-spec.h, which is special). New header types.h to
help avoid inclusion cycles. Sort include directives. Remove some
superflous includes.