Clip each side of the pause vignette to a trapezoid so the four gradient
strips meet along the corner diagonals instead of overlapping, which was
compositing two 50% blacks into darker, oddly-shaded corners.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc
Draw a black gradient border (50% alpha at the viewport edges fading to
transparent toward the center) while the game is paused (speed 0x), making
the paused state hard to miss.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc
UI widgets hold the Simulation and read authoritative values via getters on
demand; state-change events are refresh signals, not carriers of truth. This
codifies the pattern used by BuildButtonGrid and avoids stale-cache bugs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc
BuildButtonGrid now holds the Simulation and reads the building block stock
on demand, subscribing to BuildingBlocksChangedEvent itself instead of being
driven through MainWindow. When the currently selected build tool can no
longer be afforded, builder mode is exited so the button does not stay
selected or linger enabled after exit. m_activeIndex is now a std::optional.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc
The Game Over, Win, and escape-menu Restart paths did not rebase the
wall-clock time source. Time spent with one of those dialogs open
accumulated in the frame timer and, on the first frame after the reset,
was converted into ticks on the fresh simulation, fast-forwarding the new
run by the dialog-open duration.
resetForNewGame() now restarts m_frameTimer and resets m_tickDriver so a
new run starts from a clean time base. The drained-ResetCommand path
(Game Over / escape) also samples elapsed before the reset applies, so
onFrame() now skips the tick advance on the frame it resets, discarding
that stale delta.
Adds a TickDriver regression test for the invariant the fix relies on.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc
Rename bare-noun value accessors to start with "get", per the coding
guideline that a getter's name should begin with "get". Covers simple
nullary accessors across the simulation, balancing, and UI layers
(e.g. currentTick -> getCurrentTick, config -> getConfig,
buildings/belts/ships/scraps/admin, threatLevel, artifactCount,
tilePx, viewportRect, Hasher::value -> getValue, Formula::source ->
getSource).
Left untouched by design: boolean predicates (is*/has*/can*),
verb-named lookups/computations (find*/compute*/peek*/create*),
coordinate transforms, and existing set*-prefixed setters. Test-only
helpers and std/toml++ accessors (optional::value, parse_error::source)
were deliberately not renamed.
Builds clean; all 406 test cases (3222 assertions) pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc