Four MainWindow sites hand-rolled snapshot speed / setGameSpeed(0) / modal /
restore + resetFrameTimer, with the restore duplicated on early-return paths.
ModalPauseScope does it via RAII, with restore()/release() for the two sites
that must restore early or not at all.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GH8ZMRY3vhxxXcaUxBqxkk
The three restart paths each repeated the same config + visuals reload with
its own try/catch and error dialog. Only that shared part is extracted; each
site keeps its own follow-up (ResetCommand vs. direct Simulation::reset) and
its own error-path cleanup.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GH8ZMRY3vhxxXcaUxBqxkk
Four separate caches rasterized the same item SVGs, one of them rebuilt on
every recipe-dialog open. MainWindow now owns one cache and hands a
non-owning pointer to HeaderBar, BuildButtonGrid, GameWorldView and
RecipeSelectionDialog.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GH8ZMRY3vhxxXcaUxBqxkk
HeaderBar was the only panel caching event payloads as truth. It now holds
a const Simulation* and re-reads getBuildingBlocksStock() /
getCurrentExpansionCost() in the handlers, per the "events are refresh
signals" rule.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GH8ZMRY3vhxxXcaUxBqxkk
Add deterministic record/playback for a run.
Recording captures `(seed, config hash, ordered tick-tagged commands)` and re-simulates on playback — no state snapshots. `DotaFactory.exe --replay <file>` re-plays a recorded run view-only with manual speed/pause.
Reviewed-on: #4
Co-authored-by: Malte Langkabel <malte.langkabel@gmail.com>
Co-committed-by: Malte Langkabel <malte.langkabel@gmail.com>