Commit Graph

400 Commits

Author SHA1 Message Date
3cf35b669e implement blueprint compatible overlap and configuration transfer
Implements the two rules specified in 128bf81, which restore the
copy-settings capability removed in 648241b as a property of blueprint
placement: press C on one configured building, then click same-type
buildings to stamp its settings across the factory.

The ghost colour and the click path already shared one predicate,
canPlaceBuilding, which is what kept preview and outcome from disagreeing.
The new rules make that answer four-valued, so the shared predicate becomes
a shared classifier: resolveBlueprintGhost in PlacementRules returns
PlaceNew / CompatibleOverlap / Transfer / Invalid, and both callers switch
on it. Putting it in lib rather than in the view is the whole testability
story -- src/ui is off the test include path.

findRotateInPlaceTarget is now the tunnel guard plus a shared
findCoincidingSameTypeBuilding core, so the two rules cannot drift apart;
its existing tests pass untouched. Blueprint placement no longer emits
RotateInPlaceCommand at all. Builder mode and the belt drag are unchanged.

transferConfigTo sends every field unconditionally, so a field the
blueprint stores nothing for clears the target's rather than leaving it.
The simulation's unchanged-value guards absorb the no-op case, which is
what keeps clicking an already-matching building free of buffer and
production-progress loss.

drawBuildingGhost's bool valid becomes GhostTint{Normal,Invalid,Transfer};
the transfer colour is taken at full RGB like the invalid one so it does
not double-dim against the ghost opacity. visuals.toml regains the overlay
colour under the name config_transfer, with its VisualsConfig field and
loader line -- overlay keys are mandatory, so the three move together.

The six new resolveBlueprintGhost cases failed on first run because the
test buildings were anchored in space: BuildingSystem::place skips the
terrain rules, resolveBlueprintGhost applies them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 19:20:34 +02:00
250da8c9aa make re-applying an unchanged ship layout a no-op
setRecipe has always returned early when the recipe is unchanged, so a
redundant selection does not reset buffers. setShipLayout had no such
guard: it cancelled the production cycle and wiped every buffer on any
call, including one that set the layout the shipyard already had. That
already contradicted REQ-BLD-SHIPYARD, which cancels a cycle only when the
player "confirms a layout change", and it becomes load-bearing for the
blueprint configuration transfer of REQ-UI-BLUEPRINT-TRANSFER, which is
meant to be clicked repeatedly onto matching shipyards.

REQ-MAT-INPUT-BUFFER now states the rule generally: setting a
configuration to the value it already holds changes nothing, whatever
path applies it.

An unset layout counts as an empty one. The two are equivalent for
buffers, production, and the spawned ship -- the spawn path already
converts nullopt to an empty layout deliberately -- so clearing an
already-unconfigured shipyard is a no-op too. That is the case a transfer
from a layout-less source produces. The only place the two differ is the
has_value() bit in StateChecksum, and record and replay take the same
branch, so determinism is unaffected.

Layout equality is order-sensitive by choice: reporting a change that is
not one costs a buffer reset, while missing a real change would leave a
shipyard building the wrong ship.

BuildingConfigTest's shipyard case used an empty layout as shorthand for
"a layout is set", which is now a no-op and left nothing to read back. It
configures a real module instead and checks it round-trips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 17:37:57 +02:00
128bf81467 re-cover copy-settings through single-building blueprints
The Shift copy gesture removed in 648241b comes back as a property of
blueprint placement rather than a gesture of its own, reusing the C / V
shortcuts: capture one configured building with C, then click same-type
buildings to stamp its settings onto them.

REQ-UI-BLUEPRINT-TRANSFER (new): a single-building blueprint of a
configurable type (Miner, Assembler, Shipyard, Splitter) whose ghost
exactly coincides with a same-type building is a transfer target, drawn in
a distinct colour. Clicking mirrors the source's settings onto it -- placing
nothing, charging nothing, rotating nothing. The mirror includes the
absence of a setting, so a splitter captured without filters clears the
target's. Target rotation is irrelevant, since a transfer never rotates.

REQ-UI-BLUEPRINT-OVERLAP (new): blueprint placement no longer rotates
anything in place, so REQ-BLD-ROTATE-IN-PLACE is now builder-mode only.
Instead a ghost coinciding with a same-type, same-rotation building is a
compatible overlap: valid, ordinary ghost colour, and that building is left
untouched while the rest of the constellation is placed around it. Without
this a blueprint dropped over a partial copy of itself would be blocked
outright, since placement is all-or-nothing. Tunnels are included -- their
rotate-in-place exception existed only because rotation was involved.

REQ-MAT-INPUT-BUFFER gains the rule that setting a configuration to the
value it already holds is a no-op, whatever path applies it. setRecipe
already behaves this way; setShipLayout does not, and clearing a shipyard
on an identical layout already contradicted REQ-BLD-SHIPYARD's "confirms a
layout change". The transfer inherits the rule per field, so re-clicking
already-matching buildings costs no buffers or construction progress.

Also fixes a stale claim in REQ-BLD-ROTATE-IN-PLACE that a coinciding
tunnel ghost is "skipped" in blueprint mode; it has always been invalid.

Requirements only; no code implements this yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 17:13:44 +02:00
648241ba2a remove the Shift copy-building-settings gesture
REQ-BLD-COPY-CONFIG is dropped: Shift+right-click no longer caches a
building's recipe / schematic+layout / splitter filters, Shift+left-click
no longer stamps them onto same-type buildings, and the cyan eligible-
target tint and copy/paste flashes go with them. Shift+left-click now
falls through to normal selection, which needs no code of its own -- only
Ctrl is meaningful to selection (REQ-UI-MULTI-SELECT), so dropping the
branch that consumed the click is enough.

readBuildingConfig stays. It had two callers, and the other one is
captureBlueprintFromSelection, which needs it to record each blueprint
building's configuration (REQ-UI-BLUEPRINT-STORAGE). Its five tests stay
too, retagged [blueprint] and rewritten to name the caller that is left.

The visuals.toml copy_config colour is removed together with its
VisualsConfig field and its loader line: overlay keys are mandatory, so
those three have to move as one or startup aborts.

REQ-BLD-COPY-CONFIG-FEEDBACK, cited by four of these files and by the TOML
comment, never existed in requirements.md; deleting the citations retires
a dangling id.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 16:21:53 +02:00
51c6a4a567 bind the temporary blueprint to C and V
Follows f9c654b, which rekeyed REQ-UI-BLUEPRINT-TEMP: C captures a
temporary blueprint from the selection and enters its placement mode, V
re-enters that mode later without capturing anything, and T is unbound.
The plain keys are now the throwaway counterparts of Ctrl+C / Ctrl+V.

V forces the blueprint to outlive its placement mode, so
BlueprintModeExitedEvent no longer drops it -- it now clears only the
saved-blueprint index. BlueprintLibrary holds the one temporary blueprint
in an optional beside the saved list, which keeps it out of the selection
dialog and out of blueprints.toml by construction.

Restarting must discard it, and nothing announced a restart. GameResetEvent
does, emitted from GameWorldView::resetForNewGame(): the escape menu, game
over and win dialogs only enqueue a ResetCommand, so that is the single
point where a restart has actually been applied. The alternative, calling
into the library from all three dialogs, would triplicate the call and
couple MainWindow to the library's internals.

TemporaryBlueprintRequestedEvent is renamed to
TemporaryBlueprintCaptureRequestedEvent -- with two temp-blueprint requests
the old name no longer says which. Also drops a dead include of it from
GameWorldView, unused since the library was split out of BlueprintPanel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 15:11:49 +02:00
f9c654b148 rekey the temporary blueprint to C, add V to re-place it
REQ-UI-BLUEPRINT-TEMP moves off T onto C, mirroring the Ctrl+C / Ctrl+V
pair it is the throwaway counterpart of. V re-enters placement mode for
the last temporary blueprint without capturing anything new, and does
nothing when none exists.

This forces a lifetime change: the blueprint can no longer be discarded
when placement mode is left, or V would only ever work between a C press
and the first right-click. It now survives mode exit, is replaced only by
the next C, and is discarded on application close and on Restart.

Requirements only; the code still binds T and still discards on exit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 14:30:51 +02:00
a86cbd3fbd move blueprints out of the sidebar into Ctrl+C / Ctrl+V dialogs
The blueprint panel is gone; the side panel column now holds the selected
building panel alone at full height. Saving is Ctrl+C on the current
selection, and picking one for placement is a new frameless modal card grid
opened with Ctrl+V, or handed to directly after a confirmed save.

BlueprintPanel sheds its widget half and becomes BlueprintLibrary: the list,
its disk round-trip, capture, and the T hotkey, with no widget of its own.
MainWindow owns it and drives both dialogs, because ModalPauseScope needs a
GameWorldView and only MainWindow has one. Both handlers hold a single pause
scope and a single dim scope across the save-to-select handoff, so the dim
does not blink and the simulation is not resumed in between.

The two facts a card derives -- the per-type contents summary and the plain
cost total -- go into lib/sim next to captureBlueprintFromSelection so they
can be unit tested; src/ui is off the test include path. They are kept apart
from GameWorldView's placement total, which excludes locked types and
rotate-in-place targets and is a different number by design.

A card's delete icon is a sibling of the card body rather than one of its
children: Qt disables a widget's children along with it, and the delete has
to stay live on an unaffordable card.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 11:29:48 +02:00
d2d93347ad move blueprints out of the sidebar into Ctrl+C / Ctrl+V dialogs
The blueprint panel leaves the side panel column, which now holds the
selected building panel alone at full height. Saving a blueprint moves to
Ctrl+C on the current selection, and picking one for placement moves to a
new modal blueprint selection dialog opened with Ctrl+V (and directly after
a confirmed save).

REQ-UI-BLUEPRINT-PANEL is retired in favour of REQ-UI-BLUEPRINT-DIALOG, and
REQ-UI-BLUEPRINT-BUTTON becomes REQ-UI-BLUEPRINT-CARD. Neither id is cited
from code. REQ-UI-BLUEPRINT-CREATE keeps its id -- its six code citations
are about which buildings a capture counts, which is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 10:20:55 +02:00
af410ee4de make the build hotkey badge legible
The shift glyph U+21E7 was not covered by the UI font and rendered as an
unrecognizable substitute, and the badge sat two points below the button font,
which made it hard to read even where the glyph was fine. Use U+2191, a plain
single-stroke arrow the standard fonts do carry, and draw the badge at the full
button font in bold. It stays dimmed, so it still reads as secondary to the cost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 08:27:00 +02:00
8f6815f414 add the ASCII-only source rule and the visual-verification note to CLAUDE.md
Two things learned the hard way while moving the build buttons. A shift glyph
written as a literal would have been mangled by MSVC 2017, which does not read
the sources as UTF-8 by default, and a PowerShell Get-Content/Set-Content
round-trip re-encoded a source file with a BOM and double-encoded em-dashes.

Screen-capturing the app window returns a blank client area because the capture
cannot read the QOpenGLWidget surface, which is easy to misread as a broken UI;
visual checks belong to the user.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 08:19:46 +02:00
57ab63202f float the build buttons as a bar over the game world
Implements the REQ-UI-BUILD-BAR rewrite: the build buttons leave the side
panel column for a widget floating at the bottom center of the game world
view, as one non-wrapping row. BuildButtonGrid is renamed BuildButtonBar and
its QGridLayout becomes a QHBoxLayout; the side panel column is left with two
equal-height panels.

Buttons become icon-only. Each face is composed into a single pixmap per
QIcon mode - hotkey badge in the top-left corner, chip icon centered, cost
and block icon below - because a QPushButton holds only one icon. That
replaces the custom-painted BuildButton and lets Qt grey an unaffordable
button by swapping the pixmap. The building name moves into the tooltip,
which now always leads with it, and a missing chip SVG falls back to the name
in the chip's place.

The badge labels come from InputMapper, searched out of the same table the
key handler uses so a badge cannot claim a key that does nothing. The
Deconstruct button has no cost, so it shows its name there instead, and sits
last behind a gap.

Parenting the bar to MainWindow makes creation order the stacking order: it
lands above the world view and its vignettes and below the modal dim, and Qt
routes mouse events to it rather than the world, which is the whole input
clause of REQ-UI-BUILD-BAR at no cost.

Requirements are amended for the two things the mockup added: the hotkey
badge and the Deconstruct caption.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 08:12:38 +02:00
dbabc3a563 float the build buttons as a horizontal bar over the game world
Renames REQ-UI-BUILD-GRID to REQ-UI-BUILD-BAR: the build buttons move out
of the side panel column into a widget floating at the bottom center of the
game world view, as a single non-wrapping row. Buttons become icon-only with
a cost, so the building name moves into the hover tooltip and the missing-icon
fallback becomes a name caption. Deconstruct is the last entry, separated by
a gap. The side panel column is now two equal-height panels.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-06 07:09:40 +02:00
f39fe9a118 move the debug stats panel out of the renderer 2026-08-05 22:14:55 +02:00
e5dcb9de5f extract WorldRenderer 2026-08-05 22:14:55 +02:00
dc83add5c6 move two placement queries out of the view into PlacementRules 2026-08-05 22:14:54 +02:00
3a1951559d share the world shapes both views (game and balancing) draw identically 2026-08-05 22:14:54 +02:00
26d7448492 extract BuildModeController, fixing a silent blueprint exit 2026-08-05 22:14:54 +02:00
202f583067 extract SelectionController 2026-08-05 22:14:54 +02:00
d5ab44b9bf move the remaining hotkeys into the InputMapper 2026-08-05 22:14:53 +02:00
bb1ffab8fc fix a bug where view continues to pan when window lost focus while panning 2026-08-05 22:14:53 +02:00
e1445fe508 move the already-event-driven hotkeys into the InputMapper 2026-08-05 22:14:53 +02:00
caa810f66d move pan input into an InputMapper 2026-08-05 22:14:53 +02:00
f6df95abb2 extract the scroll position into WorldCamera 2026-08-05 22:14:52 +02:00
fa9dbd62ad use WorldCoordinates in ArenaView too 2026-08-05 22:14:52 +02:00
2af09d9eb1 extract the world<->widget transform into WorldCoordinates 2026-08-05 22:14:52 +02:00
4f7fdb8a4c add tone, critique and class layout rules to CLAUDE.md
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
2026-08-05 22:14:37 +02:00
949937d2c2 re-use PlacementFixture in BuildingTests 2026-08-05 07:53:08 +02:00
4c166bf47f depend on the registry instead of DebrisSystem in the AI path 2026-08-05 07:25:59 +02:00
60260540cd make deconstruction its own system 2026-08-05 07:10:46 +02:00
1f4503176b make construction its own system (extracted from BuildingSystem) 2026-08-05 06:57:12 +02:00
fd85e8e10a free the buffer setup and belt registration from BuildingSystem 2026-08-05 06:55:13 +02:00
114a43b205 make BuildingSystem stateless: FactoryState becomes a parameter 2026-08-05 06:50:11 +02:00
d87d063b10 move the placement rules and the config-dependent queries off BuildingSystem 2026-08-05 06:49:49 +02:00
537597c854 delete the unused getAllBeltTiles and BeltTileInfo 2026-08-05 06:49:28 +02:00
9c3be0fbd0 extract the production rules as free functions over config and building 2026-08-05 06:49:15 +02:00
58b94223f7 migrate every factory query off BuildingSystem onto the free functions 2026-08-05 06:46:13 +02:00
1fb63cce4e move the asteroid width bound into FactoryState 2026-08-05 06:45:33 +02:00
0b7e94b4e4 drop CombatSystem's unused BuildingSystem parameter 2026-08-05 06:45:16 +02:00
0408336cf9 depend on factory data instead of BuildingSystem in the AI path 2026-08-05 06:44:49 +02:00
46932e4abf move FactoryState ownership out of BuildingSystem to Simulation 2026-08-05 06:43:52 +02:00
0edea5d961 gather the factory's world data into FactoryState 2026-08-05 06:43:30 +02:00
60cc187d92 add BuildingGrid to manage tile occupancy 2026-08-04 18:26:01 +02:00
3990351a16 share BuildingSystem's free functions instead of copying them 2026-08-04 18:24:38 +02:00
bd344e4fbe add FieldSelectionPanel for extracting the ships/stations/debris selection 2026-08-04 18:23:28 +02:00
64c344c3a3 correct the belt subsystem interface description in architecture.md 2026-08-04 18:12:02 +02:00
02c7fed9b4 allow "auto" for named local lambdas and iterator types via claude.md 2026-08-04 18:11:37 +02:00
5d4a975384 cover unlock state in the determinism tests 2026-08-04 18:11:03 +02:00
475df0e5fd extract unlock state from Simulation to UnlockState class 2026-08-04 18:10:43 +02:00
61634f6fd2 move the shared TOML helpers into the utility namespace to avoid name collisions 2026-08-04 18:05:52 +02:00
c8ff7da345 extract load methods into their own files 2026-08-04 18:05:27 +02:00