diff --git a/findings/subsystems/strategic-turn-internals.md b/findings/subsystems/strategic-turn-internals.md index 6a71763..f0700c7 100644 --- a/findings/subsystems/strategic-turn-internals.md +++ b/findings/subsystems/strategic-turn-internals.md @@ -117,6 +117,24 @@ if (ResT) researchAlloc.push_back({ResT, [21]}); [9] = researchMoney - [13] [14] savingsGiven = min(max(remaining,0), savAid) net = [1]+[2]+[3]+[4]+[5]+[6] - [7]-[8]-[9]-[10]-[11]-[12]-[13]-[14] ``` +**Corrections from the B1 live trace (2026-09-08, sots-engine `docs/B1.md`, 4,623 traced + +4,437 compared calls on `ref-turn2.sav`):** + +- The out parameter is **`int[22]`, not `int[25]`**. It is a `Budget` object: + `{ int slot[22]; std::vector<{Tech* node, int points}> researchAlloc @+0x58; int overBudget @+0x64 }`. + The three words after slot 21 are the vector's `{first, last, end}`; they grow by exactly one + 8-byte element for a player with `ResT` and stay `{0,0,0}` otherwise. (What was read as + "[22]..[24]" is that header.) +- **`[9] researchMoneyKept` is written inside the `if (ResT)` branch**, together with the + `researchAlloc.push_back`. A player with no research target still gets `[16] researchMoney` + and `[17]/[21]` research points, but `[9]` stays 0 and the money is never subtracted from the + net. Independent evidence: player 0 of the reference save has no `ResT`, and its End Turn + moves `Sav` 289,688 -> 532,369, i.e. `+[1] 239,785 +[5] 2,896` with nothing deducted. +- `GetDifficultyMods` rows, measured: **AI (`isAI && !NPC`) = {maintenance divisor 3, research + multiplier 1.5}; human/NPC = {1, 1}** for this game (AI Normal, econ/research 100 %). + `[8] = Maint / ftol(divisor)` confirmed at 500->166, 1000->333 (AI) and 1000->1000 (human row). +- `p+0xf9` is the AI gate as described: player 7 is `isAI && NPC` and takes the human row. + So the "budget sliders" are `ResRate` (research share of net) and the `Nexp` entries; construction is not a slider — it is whatever the build queues demand up to `avail`. The UI pie chart (`PIECHARTCOLOR_INCOME/COST_FLEETS/COST_RESEARCH/ COST_RESEARCH_AID/COST_FLEETS_QUEUED/COST_FLEETS_TOTAL`) is drawn from this same array. diff --git a/ghidra/generated/sots_addresses.h b/ghidra/generated/sots_addresses.h index e619ed7..5bb917e 100644 --- a/ghidra/generated/sots_addresses.h +++ b/ghidra/generated/sots_addresses.h @@ -1,5 +1,5 @@ // GENERATED — do not edit. Facts about Sword of the Stars.exe (GOG 1.8.1). -// Source: sots-re ghidra/addresses.json @ 73f5f1e, generated 2026-09-07 by tools/gen_addresses.py +// Source: sots-re ghidra/addresses.json @ 1e7428d, generated 2026-09-07 by tools/gen_addresses.py // Runtime address = (uintptr_t)GetModuleHandle(NULL) + RVA (the exe is ASLR-relocated). #pragma once #include