fix(game): charge a ship upgrade against production only once
Tests · Go / test (push) Successful in 2m7s
Tests · Go / test (pull_request) Successful in 2m10s
Tests · Integration / integration (pull_request) Successful in 1m41s

TurnPlanetProductions started its production budget from
PlanetProductionCapacity, which already subtracts the reserved upgrade
cost, and then subtracted each applied upgrade's cost again in the apply
loop — charging every applied upgrade twice. That both starved the
planet's build/research budget and could skip upgrades that were in fact
affordable.

The budget now starts from the planet's full production potential and the
apply loop deducts each upgrade once; PlanetProductionCapacity stays the
report's net-of-upgrades "free L".

Test: TestUpgradeDoesNotDoubleChargeProduction; the TestProduceShips MAT
expectation is updated to the once-charged value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-31 08:24:46 +02:00
parent b4abf90ec5
commit 53b3cafbc4
2 changed files with 41 additions and 7 deletions
+7 -2
View File
@@ -181,8 +181,13 @@ func (c *Cache) TurnPlanetProductions() {
ri := c.RaceIndex(*p.Owner)
r := &c.g.Race[ri]
// upgrade groups and return to in_orbit state
productionAvailable := c.PlanetProductionCapacity(pn)
// Upgrade groups (most expensive first) and return them to the
// in-orbit state, paying for each upgrade once out of the planet's
// full production potential; whatever remains feeds this turn's
// production below. Starting from PlanetProductionCapacity here would
// have charged every applied upgrade twice, since that helper already
// nets out the reserved upgrade cost for the report.
productionAvailable := p.ProductionCapacity()
for sg := range c.shipGroupsInUpgrade(p.Number) {
cost := c.upgradeCostNow(sg)
if productionAvailable >= cost {