The second chain ComputeBudget needs. The output term (lane N) is a system's
OUTPUT; what the budget and the bankruptcy limits sum is its MONEY, which runs
that total through TradePointsToMoney. Read instruction by instruction, every
range disassembled to the next function start.
The income law is not the output law with a different constant: money per head
is typeIncomeMod / 14000 -- no 1.8, no 500000 -- and it truncates TWICE per
(group, species) row, once inside the per-row term and once after the morale and
addiction factors. Summing the species first and truncating once is wrong on any
colony with more than one species.
The multiplier that was "not on the wire" (formula-gaps.md Q3) resolves to a
three-row table the executable BUILDS IN CODE from .rdata float literals, exactly
like lane N's pop-type table: {int id; float ai[3]; float other[3]}, selected per
player by is-AI && !NPC. Every corpus save carries aidf == 1, whose AI income
column is 1.1f -- the x1.1 the BnkEl oracle measured. Its other two columns are a
fleet-maintenance divisor and a research multiplier, both of which already had a
home in BudgetInputs and no source.
Verified against the 25-record BnkEl oracle, which inverts the stored limit to
the true sum and therefore needs no VM: 6/25 before, 25/25 after. Falsified three
ways -- moving the AI flag off the AI players costs 11 records, moving it onto the
humans costs the other 11, and forcing one species' resource pair on all of them
costs every Zuul record.
app: T31 UpdateBankruptcyLimits now runs the whole roll-up and self-checks it
every run against the BnkEl the input save already carries -- 8 of 8 players on
turn1-state with --ai-player 1. It stays blocked on two things that are not the
formula: ServerPlayer+0xf9 (is this player AI?) is a game-setup input the save
does not carry, and BnkPr needs BANKRUPTCY_PROTECTION_LIMIT_FACTOR from the data
files. Committing it closes nothing on the reference pair -- the limits move
because the CIVILIAN POPULATION grows and that growth is not committed -- so
measured with --commit-blocked=T31 --ai-player 1: 0 closed, 0 REGRESSED, i.e.
209 -> 204 and 108 -> 103 unchanged.
P01 is NOT unblocked, and the roadmap's item 1 was wrong about that: ComputeBudget
takes its per-system money from ComputeOutput with the system's OWN rate sliders,
not from ComputeMaxIncome. Only its projected mode uses the max-income form. On
the turn path the repair pass runs and the unspent-industry and
unspent-terraforming cascades into the money channel are live, so the proof that
both are zero does not apply. The catalog text says so now.
Two things the 25/25 does NOT cover, and they are labelled in the code: the
suitability money cost is multiplied by zero on every corpus colony (all sit at
their species' ideal), and the slave, addiction, morale, station and
capacity-surplus branches are unexercised.
88 lines
4 KiB
C++
88 lines
4 KiB
C++
// Tuning constants the strategic formulas read at runtime.
|
|
//
|
|
// The game loads these as `KEY value` lines from its data files (StrategyVars.txt,
|
|
// globals.txt, ...). The data file is authoritative; the executable only carries fallback
|
|
// defaults, several of which differ from the shipped file. Nothing in this module
|
|
// hard-codes a shipped value: callers fill a TuningTable (from the parsed data files) and
|
|
// pass it in. Field names are the config keys verbatim so a loader can map by name.
|
|
//
|
|
// All values default to zero so a test can set only the keys a formula reads.
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
|
|
namespace sots::sim {
|
|
|
|
struct TuningTable {
|
|
// ---- trade (StrategyVars) ----
|
|
double TRADE_ROUTE_REQ_CIVPOPULATION = 0; // civilians per supported route
|
|
double TRADE_ROUTE_REQ_IMPPOPULATION = 0; // imperials per supported route
|
|
int TRADE_ROUTE_STARTUP_TURNS = 0; // routes younger than this pay a flat amount
|
|
int TRADE_ROUTE_STARTUP_INCOME = 0;
|
|
int TRADE_ROUTE_MIN_INCOME = 0;
|
|
int TRADE_ROUTE_MAX_FREIGHTERS = 0; // freighter cap per route
|
|
int TRADE_ROUTE_INCOME_PERFREIGHTER_CRQ = 0;
|
|
int TRADE_ROUTE_INCOME_PERFREIGHTER_CR = 0;
|
|
int TRADE_ROUTE_INCOME_PERFREIGHTER_DE = 0;
|
|
double TRADE_ROUTE_OWNERS_SHARE = 0; // owner's share of a route's income (0..1)
|
|
double STATION_BONUS_TRADE_INCOME = 0; // per trade station at the system
|
|
double ADDICTION_TRADE_MOD = 0; // applied when the partner is addicted
|
|
|
|
// ---- bankruptcy ----
|
|
int BANKRUPTCY_ELIMINATION_TURNS = 0;
|
|
double BANKRUPTCY_PROTECTION_LIMIT_FACTOR = 0; // debt floor = -factor x max income
|
|
|
|
// ---- population ----
|
|
double POPULATION_GROWTH_MOD = 0;
|
|
double POPULATION_GROWTH_EXP = 0;
|
|
double INDSYS_IMPERIAL_POPULATION_MOD = 0;
|
|
|
|
// ---- slaves ----
|
|
double SLAVES_DEATH_RATE = 0;
|
|
double SLAVES_DEATH_RATE_BYHAZARD = 0;
|
|
double SLAVES_DEATH_RATE_BYOUTPUT = 0;
|
|
std::int64_t SLAVES_MIN_DEATHS = 0;
|
|
std::int64_t SLAVES_MAX_DEATHS = -1; // -1 = no upper clamp
|
|
// The slave row of the per-population-type table. The imperial and civilian rows of
|
|
// that table are built from literals inside the executable; only these three are read
|
|
// from the data files, through the loader's pointer slots.
|
|
double SLAVES_OUTPUT_MOD = 0;
|
|
double SLAVES_INCOME_MOD = 0;
|
|
double SLAVES_REPAIR_MOD = 0;
|
|
|
|
// ---- system bonus (long-held stable colonies) ----
|
|
int SYSTEMBONUS_MINTURNS = 0;
|
|
double SYSTEMBONUS_POPBONUS = 0;
|
|
double SYSTEMBONUS_POPBONUS_HOME = 0;
|
|
double SYSTEMBONUS_POPBONUS_INC = 0;
|
|
double SYSTEMBONUS_INFRABONUS = 0;
|
|
double SYSTEMBONUS_INFRABONUS_HOME = 0;
|
|
double SYSTEMBONUS_INFRABONUS_INC = 0;
|
|
|
|
// ---- output modifiers ----
|
|
double STATION_BONUS_IMPERIAL_OUTPUT = 0;
|
|
double STATION_BONUS_SHIPCON = 0;
|
|
double MORALE_INCREASE_OUTPUT = 0;
|
|
double MORALE_INCREASE_OUTPUT_MOD = 0;
|
|
double MORALE_DECREASE_OUTPUT = 0;
|
|
double MORALE_DECREASE_OUTPUT_MOD = 0;
|
|
double ADDICTION_OUTPUT_MOD = 0;
|
|
// The income counterpart, read through its own pointer slot (0x00aeca48 -> 0x00aeca44).
|
|
// It is applied per (group, species) row inside the population income term, not to the
|
|
// system total the way the output modifier is. Its file-image value is 0.9f, the same
|
|
// as ADDICTION_OUTPUT_MOD's, but they are two independent keys.
|
|
double ADDICTION_INCOME_MOD = 0;
|
|
|
|
// ---- movement (globals) ----
|
|
double STUTTER_SYSTEM_INFLUENCE_RADIUS = 0;
|
|
double STUTTER_MIN_SPEED = 0;
|
|
double STUTTER_MAX_SPEED = 0;
|
|
|
|
// ---- gates (read by the tech-effect layer) ----
|
|
// Both are integers in the executable: the tech callback does a signed integer max
|
|
// against the player's PrGtTrf word, not a float compare (B2, read from the branch).
|
|
int PERGATETRAFFIC_DRV_TpGate = 0; // per-gate traffic capacity granted by the gate tech
|
|
int PERGATETRAFFIC_DRV_GatAmp = 0; // ... and by the amplifier tech (the higher wins)
|
|
};
|
|
|
|
} // namespace sots::sim
|