{ "entries": [ { "name": "ServerSystem_CivilianSettleLimit", "addr": "0x0074a9a0", "convention": "thiscall", "prototype": "int64 (ServerSystem* sys, int species) // `ret 4`, ends 0x0074aa2d. Returns 0 without an owner. Otherwise min( MaxPopGeneric(1, sp, PID, &StrategyServer::IdealSuit(server, sp)) , Population::Count(sys->dcs /*+0x104*/, 1, sp) ). The first term is the capacity the colony would have if the planet were perfectly suited; the second is the system's own `dcs` Population, which is on the wire. On Gamma Cephei `dcs` = 1e9 and it is the binding limit, not either capacity", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "SpeciesDef_GroupCapacityFactor", "addr": "0x0053bb00", "convention": "thiscall", "prototype": "float (SpeciesDef* d, int groupType) // `ret 4`, 15 bytes. Returns d->float[0x168 + groupType*4]. The per-species, per-population-group capacity factor -- a DATA FILE value. Its imperial entry for Human is pinned at exactly 1.0 by the corpus (Gamma Cephei's pbon never drains and its Pop never shrinks, which bracket the imperial capacity at Size x 1e8); the civilian entry is bounded below at 0.27 by the observed growth and is otherwise unmeasured", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "SpeciesDef_GroupCrossSpeciesMod", "addr": "0x0053bb20", "convention": "thiscall", "prototype": "float (SpeciesDef* d, int groupType) // `ret 4`, 15 bytes. Returns d->float[0x174 + groupType*4]; applied by MaxPopGeneric only when the system owner's species differs from the population species and is not 4", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "SpeciesDef_HasPopulationGroup", "addr": "0x0053bb40", "convention": "thiscall", "prototype": "bool (SpeciesDef* d, int groupType) // `ret 4`. Returns `0.0 < d->float[0x168 + groupType*4]` -- the SAME array SpeciesDef_GroupCapacityFactor reads, so 'this species has this population group' and 'its capacity factor is positive' are one fact. MaxPopGeneric asks it about the system OWNER's species, not the population's", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "PopGrowthSuitabilityDistance", "addr": "0x00536eb0", "convention": "cdecl", "prototype": "float (ServerPlayer* p, int species, float suit) // ends 0x00536f8b. Returns 20.0 and logs when p is null; 0.0 when p->RebAI (+0xfc) -- the 'accommodated' case; otherwise min( float32(|float32(StrategyServer::IdealSuit(p->server /*+0x8*/, species) - clamp(suit, 0, 20))|) , p->SuitTol /*+0xb4*/ ). The ideal is the SERVER's per-species baseline (the save's ISsu array), NOT the player's own IdealSuit field", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "PopGrowthDeltaHelper", "addr": "0x00537140", "convention": "cdecl", "prototype": "int64 (ServerPlayer* p, int groupType, int species, float suit, float factor, int64 pop) // `add esp,0x1c`, 7 dwords of arguments, ends 0x005371ca. Returns 0 unless pop > 0; g = PopGrowthFraction(p, groupType, species, suit, factor); returns 0 when g == 0 (an fucomp against zero, so a NaN g also proceeds); d = ftoi64(g x pop); returns d, or exactly 1 when d == 0 and g is strictly positive, or 0 when d == 0 and g is negative", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "PopTypeTableStaticInit", "addr": "0x009abe20", "convention": "cdecl", "prototype": "void () // the CRT static initialiser for the population-type table at 0x00b104e8, and THE ONLY WRITER of the group-ceiling field. It sets every row's +0x28 to 0xffffffff and +0x2c to 0x7fffffff -- the int64 INT64_MAX -- along with +0x00/+0x04/+0x08/+0x0c to -1/1.0/-1/-1 and the float columns to 1.0. InitPopTypeTable 0x00535ca0 runs later and overwrites +0x00 through +0x20 but NEVER touches +0x24/+0x28/+0x2c. So MaxPopGeneric's group-ceiling clamp is present, always enabled, and always a no-op; a reader who opens only InitPopTypeTable sees zero there and would cap every carrying capacity in the game at nothing", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "ShipCarriedPopIncomePerShip", "addr": "0x0081f8c0", "convention": "thiscall", "prototype": "int (StarShip* sh) // returns 0 unless design->+0xb8 & 0x04000000 (the carried-population bit lane B6 named); otherwise sums GroupIncome over the ship's `hsp` Population at ship+0x9c for group types 0 and 1. Body read to the second GroupIncome call only", "status": "unverified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "g_SavingsInterestRate", "addr": "0x009e31c0", "convention": "data", "prototype": "double 0.009999999776482582 = (double)0.01f -- the savings-interest multiplier ComputeBudget uses at 0x008631ce, then truncated by _ftol2. It is a WIDENED FLOAT, not the exact decimal, and the difference is not cosmetic: a treasury of exactly 50,000 earns 499, not 500. That one money was the whole residual on the human's Sav after civilian growth landed", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "g_DebtInterestRate", "addr": "0x009ed188", "convention": "data", "prototype": "double 0.15000000596046448 = (double)0.15f -- the debt-interest multiplier ComputeBudget uses at 0x008631b0. The same constant lane E1 already carries, negated, as the bankruptcy-elimination divisor", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "g_FlaggedSystemGrowthFactor", "addr": "0x00a1b000", "convention": "data", "prototype": "float 1.5 -- the per-call growth factor PopGrowthDelta 0x00748100 substitutes for 1.0 when the system's GFlags (+0xdc) carries the owner's PlyrIdx bit. It is a BONUS, not a penalty. UNEXERCISED: GFlags is 0 for the growing player on every corpus system", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" }, { "name": "g_PopTypeCivilianGrowthMod", "addr": "0x009e5ac0", "convention": "data", "prototype": "float 0.25 -- POPTYPE[1]+0x04, the civilian growth column, read out of InitPopTypeTable's six-register fxch rotation. Independently re-derived this lane; lane N's whole table is reproduced (imperial +0x04 = 1.0 from 0x009e2ea0's neighbours, civilian +0x10/+0x14 = 0.33f from 0x009f8d48, civilian +0x1c = 0.5, civilian +0x20 = 2.0 from 0x009e5840)", "status": "verified", "source": "findings/subsystems/population-growth.md (lane G3 2026-09-08)" } ], "_note": "ServerSystem_GrowCivilianPops_G3 dropped 2026-09-08: same address 0x00754220 as addresses.json's ServerSystem_GrowCivilianPops. A lane-suffixed alias of an existing entry is a fork of the vocabulary, not a new fact - if the prototype needed correcting, correct the entry. G3's real end finding (0x00754b59, past Ghidra's reported end) stands in findings/subsystems/population-growth.md. Dropped 2026-09-08 as same-address duplicates, agreement recorded: ServerSystem_MaxPopGeneric_G3 -> ServerSystem_MaxPopGeneric (addresses.json); Ship_RepairCost_G3 -> Ship_RepairCost (lane-c3.json); Ship_ApplyRepair_G3 -> Ship_ApplyRepair (lane-c3.json); ServerSystem_ShipCarriedPopIncome -> SystemRepairDemandForOwner (lane-c3.json)." }