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
This commit is contained in:
@@ -348,7 +348,6 @@ deconstruct_tint = "#ff000033" # deconstruct-mode hover tint
|
||||
selection_rect = "#00ff00" # box-drag selection rectangle (REQ-UI-MULTI-SELECT)
|
||||
tile_highlight = "#ffffff22" # tile under cursor
|
||||
selected_outline = "#ffff00" # outline drawn around currently-selected building(s)
|
||||
copy_config = "#33ccff66" # copy-settings eligible-target tint + copy/paste flash (REQ-BLD-COPY-CONFIG-FEEDBACK)
|
||||
locked_asteroid = "#0000007f" # tint over the asteroid left of the buildable edge (not yet unlocked by expansion)
|
||||
modal_dim = "#00000099" # semi-transparent black dim behind modal dialogs/menus (REQ-UI-MODAL-DIM)
|
||||
tunnel_preview = "#00ff0055" # tunnel connection preview: matched end + tiles between (REQ-BLD-TUNNEL-MODE)
|
||||
|
||||
@@ -333,7 +333,7 @@ The game world is drawn into a single `GameWorldView` widget that inherits `QOpe
|
||||
|
||||
The drawing itself lives in `WorldRenderer`, not in the widget. `paintGL` is a call sequence: build the frame's `WorldCoordinates`, hand the renderer a `WorldRenderFrame`, then draw the screen-anchored chrome. The split is the world-space / screen-space line, and it is exact: the renderer draws everything positioned in tiles, while everything positioned in pixels — the pause and deconstruct vignettes, the replay overlay, the debug stats panel — stays with the widget. A useful consequence is that the renderer draws no translatable text at all (its text is config-driven glyphs, ASCII port arrows, and numbers), so it needs no `tr()` and no tie to the meta-object system.
|
||||
|
||||
`WorldRenderFrame` is what makes the renderer independent of the widget. The renderer reads the simulation directly, but everything else it draws is interaction state the widget owns — the selection, the active build mode, live beams, the copy-settings feedback, the box-select rectangle. Those are gathered into the frame each `paintGL` and passed by reference, so the renderer keeps no copy that a later click could invalidate. The renderer knows nothing about input: the widget resolves clicks and hit-tests, and the renderer only draws the result.
|
||||
`WorldRenderFrame` is what makes the renderer independent of the widget. The renderer reads the simulation directly, but everything else it draws is interaction state the widget owns — the selection, the active build mode, live beams, the box-select rectangle. Those are gathered into the frame each `paintGL` and passed by reference, so the renderer keeps no copy that a later click could invalidate. The renderer knows nothing about input: the widget resolves clicks and hit-tests, and the renderer only draws the result.
|
||||
|
||||
### Render Loop
|
||||
|
||||
|
||||
@@ -147,12 +147,6 @@ Any ship, module, building, or assembler recipe id that appears in no unlock gro
|
||||
- REQ-BLD-DECONSTRUCT-CLICK: While in deconstruct mode (REQ-UI-HOTKEYS, REQ-UI-DECONSTRUCT-BUTTON), left-clicking a placed factory building or construction site in the game world marks it for demolition, following the rules of REQ-BLD-DECONSTRUCT: a fully-built building is added to the deconstruction queue (REQ-BLD-DECON-QUEUE), and a construction site is removed instantly with the full refund. Left-clicking a fully-built building that is **already in the deconstruction queue** instead removes it from the queue (un-queues it, REQ-BLD-DECON-QUEUE), with no refund; repeated clicks on the same building therefore alternate between queueing and un-queueing it. Clicking a building that cannot be deconstructed (the HQ or a player defence station, per REQ-BLD-DECONSTRUCT), or clicking empty world space, has no effect. Deconstruct mode stays active after each action so the player can continue without re-entering the mode; it is exited via the Q toggle (REQ-UI-HOTKEYS) or the Deconstruct button (REQ-UI-DECONSTRUCT-BUTTON).
|
||||
- REQ-BLD-DECONSTRUCT-BOX: While in deconstruct mode (REQ-UI-HOTKEYS, REQ-UI-DECONSTRUCT-BUTTON), the player can click and drag a selection box in the game world. A selection rectangle is drawn while dragging, using the same box-drag gesture and coverage semantics as the multi-select box (REQ-UI-MULTI-SELECT). On mouse up, following the rules of REQ-BLD-DECONSTRUCT: every construction site covered by the box is removed instantly with the full refund; and among the fully-built deconstructible buildings covered by the box, if **all** of them are already in the deconstruction queue they are all removed from it (un-queued, REQ-BLD-DECON-QUEUE), otherwise every covered building not yet in the queue is added to the deconstruction queue (already-queued ones stay). Buildings that cannot be deconstructed (the HQ and player defence stations, per REQ-BLD-DECONSTRUCT) are excluded from the box demolition; ships and defence stations are never affected.
|
||||
- REQ-BLD-SITE-CONFIG: A construction site — a building that has been placed but is still queued or under construction (REQ-BLD-QUEUE) — can be selected and configured exactly like the equivalent operational building, before it finishes building. Whatever configuration the building type supports is available on the site: the recipe for a Miner or Assembler (REQ-UI-SELECT-BUTTON), the produced-ship schematic and its module layout for a Shipyard (REQ-UI-SELECT-BUTTON, REQ-MOD-UI-PREVIEW, REQ-MOD-UI-DIALOG), and the output filters for a Splitter (REQ-BLD-SPLITTER) — all set through the same Selected Building Panel controls (REQ-UI-CONFIG-INLINE). Only currently unlocked recipes and schematics are offered, exactly as for operational buildings (REQ-LOCK-UI-RECIPE, REQ-LOCK-UI-SCHEMATIC, REQ-LOCK-UI-SPLITTER). The configuration is stored on the construction site and carries over unchanged when construction completes, so the building becomes operational already configured. A construction site has no input/output buffers and runs no production cycle, so the buffer and production-progress portions of the panel (REQ-UI-SINGLE-SELECTION, REQ-UI-PRODUCTION-PROGRESS) are not shown for it; only its construction progress (REQ-UI-CONSTRUCTION-PROGRESS) and its configuration controls appear. (Blueprint placement already applies a stored recipe or schematic to a construction site on placement per REQ-UI-BLUEPRINT-PLACE; this requirement additionally lets the player set or change that configuration directly on an existing site.)
|
||||
- REQ-BLD-COPY-CONFIG: **Copy building settings (hold Shift).** While the Shift key is held, the player can copy one building's settings onto other buildings of the same type, so several identical machines can be set up without opening each one's panel. This gesture is available only in the default selection mode; while a builder, blueprint placement, or deconstruct mode is active it is disabled, so it never clashes with placement or demolition clicks.
|
||||
- **Shift + right-click** a building copies its current settings into a temporary cache, along with the building's type. The settings copied are whatever that building type supports: the selected recipe (Miner, Assembler), the selected schematic together with its module layout (Shipyard), or the two output filters (Splitter, REQ-BLD-SPLITTER). Copying succeeds only when there is something to copy — a Miner or Assembler with a recipe selected, a Shipyard with a schematic selected, or any Splitter (whose output filters, even when empty/accept-all, always constitute valid settings). Shift + right-clicking a configurable building with nothing yet selected, a building type that has no settings at all (Smelter, Reprocessing Plant, Salvage Bay, belt/tunnel tiles, the HQ), or empty world space, has no effect and leaves any existing cache unchanged.
|
||||
- **Shift + left-click** a building of the **same type** as the cached one applies the cached settings to it, exactly as if the player had made that selection through the selected building panel — with the same effects as a normal selection change (buffer clearing per REQ-MAT-INPUT-BUFFER and REQ-MAT-OUTPUT-BUFFER, and, for a Shipyard, in-progress cycle cancellation per REQ-BLD-SHIPYARD). This can be repeated on any number of same-type buildings while Shift stays held. Shift + left-clicking a building of a different type than the cached one, any building while the cache is empty, or empty world space, has no effect.
|
||||
- Both operational buildings and construction sites take part as source and target (REQ-BLD-SITE-CONFIG); settings applied to a construction site carry over unchanged when it finishes building.
|
||||
- **Releasing Shift clears the temporary cache.** It is never persisted and does not survive Shift being released; the next copy starts fresh.
|
||||
- Because the cached settings were already valid on a same-type source building, they remain valid and available on the target (a selected recipe/schematic stays unlocked per REQ-LOCK-UI-RECIPE and REQ-LOCK-UI-SCHEMATIC; splitter filter item types stay unlocked per REQ-LOCK-UI-SPLITTER).
|
||||
|
||||
## Building Types
|
||||
|
||||
@@ -323,7 +317,7 @@ Any ship, module, building, or assembler recipe id that appears in no unlock gro
|
||||
|
||||
- REQ-MOD-UI-EMPTY-PULSE: While a module is selected for placement in the layout configuration dialog (REQ-MOD-UI-DIALOG), the empty buildable cells of the layout grid pulse smoothly around their normal fill shade, oscillating between a slightly darker and a slightly brighter shade at approximately 1 Hz (one full cycle per second), to draw the player's attention to where the module can be placed. All empty buildable cells pulse in phase. When no module is selected for placement (including remove mode), empty buildable cells render at their normal static shade. Non-buildable cells and cells occupied by a placed module do not pulse.
|
||||
|
||||
- REQ-MOD-UI-AUTO-DIALOG: When the player selects a schematic for a shipyard (operational building or construction site) through the schematic selection dialog (REQ-UI-SELECT-BUTTON), and the chosen schematic **differs** from the shipyard's current schematic, the layout configuration dialog (REQ-MOD-UI-DIALOG) opens automatically and immediately once the selection dialog closes — exactly as if the player had then clicked "Configure". Re-selecting the schematic already set does not reopen the dialog. This auto-open applies only to the manual schematic selection dialog; schematic changes applied via the copy-settings gesture (REQ-BLD-COPY-CONFIG) or blueprint placement (REQ-UI-BLUEPRINT-PLACE) do **not** auto-open the dialog. The player may still cancel the auto-opened dialog (REQ-MOD-UI-DIALOG), which leaves the newly selected schematic in place with its default empty layout; the "Configure" button (REQ-MOD-UI-PREVIEW) remains available to open the dialog again later.
|
||||
- REQ-MOD-UI-AUTO-DIALOG: When the player selects a schematic for a shipyard (operational building or construction site) through the schematic selection dialog (REQ-UI-SELECT-BUTTON), and the chosen schematic **differs** from the shipyard's current schematic, the layout configuration dialog (REQ-MOD-UI-DIALOG) opens automatically and immediately once the selection dialog closes — exactly as if the player had then clicked "Configure". Re-selecting the schematic already set does not reopen the dialog. This auto-open applies only to the manual schematic selection dialog; a schematic applied by blueprint placement (REQ-UI-BLUEPRINT-PLACE) does **not** auto-open the dialog. The player may still cancel the auto-opened dialog (REQ-MOD-UI-DIALOG), which leaves the newly selected schematic in place with its default empty layout; the "Configure" button (REQ-MOD-UI-PREVIEW) remains available to open the dialog again later.
|
||||
|
||||
- REQ-MOD-UI-MODULE-TOOLTIP: Each module selection button in the layout configuration dialog (REQ-MOD-UI-DIALOG) shows a hover tooltip with the descriptive text defined for that module type in `modules.toml` (the optional per-module tooltip field). If a module type defines no tooltip text, its button shows no tooltip. The "Remove" button is not a module type and has no config-defined tooltip.
|
||||
|
||||
|
||||
@@ -157,9 +157,9 @@ Blueprint captureBlueprintFromSelection(const Simulation& sim,
|
||||
building.type = e.type;
|
||||
building.rotation = e.rotation;
|
||||
building.offset = e.anchor - center;
|
||||
// Recipe / schematic / layout / splitter-filter capture is shared with the
|
||||
// copy-settings gesture (REQ-BLD-COPY-CONFIG) via readBuildingConfig, which
|
||||
// handles operational buildings and construction sites alike.
|
||||
// Recipe / schematic / layout / splitter-filter capture goes through
|
||||
// readBuildingConfig, which handles operational buildings and construction
|
||||
// sites alike.
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(sim, e.id);
|
||||
if (config.has_value())
|
||||
{
|
||||
|
||||
@@ -15,8 +15,8 @@ struct BuildingsConfig;
|
||||
|
||||
// The user-configurable settings of a single building or construction site: the
|
||||
// selected recipe / ship schematic, the shipyard module layout, and (for
|
||||
// splitters) the two output filters. Shared by the copy-settings gesture
|
||||
// (REQ-BLD-COPY-CONFIG) and blueprint capture (REQ-UI-BLUEPRINT-STORAGE).
|
||||
// splitters) the two output filters. This is what blueprint capture records per
|
||||
// constituent building (REQ-UI-BLUEPRINT-STORAGE).
|
||||
struct BuildingConfig
|
||||
{
|
||||
BuildingType type = BuildingType::Miner;
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#include "SimulationTestAccess.h"
|
||||
#include "TestConfig.h"
|
||||
|
||||
// readBuildingConfig underpins the copy-settings gesture (REQ-BLD-COPY-CONFIG):
|
||||
// it extracts a building's recipe / schematic / layout / splitter filters so they
|
||||
// can be stamped onto a same-type building. It reads operational buildings and
|
||||
// construction sites alike.
|
||||
// readBuildingConfig underpins blueprint capture (REQ-UI-BLUEPRINT-STORAGE): it
|
||||
// extracts a building's recipe / schematic / layout / splitter filters so they can be
|
||||
// stored in the blueprint and reapplied on placement. It reads operational buildings
|
||||
// and construction sites alike.
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -60,7 +60,7 @@ const ShipDef* findAvailableSchematic(const GameConfig& cfg)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("readBuildingConfig returns a miner's selected recipe", "[copyconfig]")
|
||||
TEST_CASE("readBuildingConfig returns a miner's selected recipe", "[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
Simulation sim(loadTestConfig(), 7);
|
||||
@@ -78,7 +78,7 @@ TEST_CASE("readBuildingConfig returns a miner's selected recipe", "[copyconfig]"
|
||||
}
|
||||
|
||||
TEST_CASE("readBuildingConfig leaves recipe unset when nothing is selected",
|
||||
"[copyconfig]")
|
||||
"[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
Simulation sim(loadTestConfig(), 7);
|
||||
@@ -93,7 +93,7 @@ TEST_CASE("readBuildingConfig leaves recipe unset when nothing is selected",
|
||||
}
|
||||
|
||||
TEST_CASE("readBuildingConfig returns a shipyard's schematic and layout",
|
||||
"[copyconfig]")
|
||||
"[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
Simulation sim(loadTestConfig(), 7);
|
||||
@@ -113,7 +113,7 @@ TEST_CASE("readBuildingConfig returns a shipyard's schematic and layout",
|
||||
CHECK(config->shipLayout.has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("readBuildingConfig reads a queued construction site", "[copyconfig]")
|
||||
TEST_CASE("readBuildingConfig reads a queued construction site", "[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
Simulation sim(loadTestConfig(), 7);
|
||||
@@ -134,7 +134,7 @@ TEST_CASE("readBuildingConfig reads a queued construction site", "[copyconfig]")
|
||||
CHECK(*config->recipeId == "mine_iron_ore");
|
||||
}
|
||||
|
||||
TEST_CASE("readBuildingConfig returns nullopt for an unknown id", "[copyconfig]")
|
||||
TEST_CASE("readBuildingConfig returns nullopt for an unknown id", "[blueprint]")
|
||||
{
|
||||
Simulation sim(loadTestConfig(), 7);
|
||||
CHECK_FALSE(readBuildingConfig(sim, kInvalidBuildingId).has_value());
|
||||
|
||||
@@ -269,20 +269,6 @@ void GameWorldView::onFrame()
|
||||
pruneDespawnedDebris();
|
||||
pruneDespawnedActors();
|
||||
|
||||
// Expire copy/paste flashes. Lifetime is wall-clock (the frame delta), so the
|
||||
// flash plays for a fixed real duration regardless of game speed, including
|
||||
// while the game is paused (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
if (!m_copyConfigFlashes.empty())
|
||||
{
|
||||
std::vector<CopyConfigFlash> live;
|
||||
for (CopyConfigFlash flash : m_copyConfigFlashes)
|
||||
{
|
||||
flash.remainingMs -= elapsed;
|
||||
if (flash.remainingMs > 0) { live.push_back(flash); }
|
||||
}
|
||||
m_copyConfigFlashes = std::move(live);
|
||||
}
|
||||
|
||||
// Apply held scroll
|
||||
{
|
||||
const bool viewMoved =
|
||||
@@ -414,9 +400,8 @@ void GameWorldView::paintGL()
|
||||
|
||||
WorldRenderFrame GameWorldView::makeRenderFrame() const
|
||||
{
|
||||
return WorldRenderFrame{m_selection, m_buildMode, m_activeBeams, m_copiedConfig,
|
||||
m_copyConfigFlashes, m_boxSelecting, m_boxStartTile,
|
||||
m_boxCurrentTile, m_debugDraw};
|
||||
return WorldRenderFrame{m_selection, m_buildMode, m_activeBeams, m_boxSelecting,
|
||||
m_boxStartTile, m_boxCurrentTile, m_debugDraw};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1026,10 +1011,6 @@ void GameWorldView::keyReleaseEvent(QKeyEvent* event)
|
||||
return;
|
||||
}
|
||||
if (m_inputMapper.handleKeyRelease(event)) { return; }
|
||||
// Releasing Shift discards the copied building settings (REQ-BLD-COPY-CONFIG).
|
||||
// Stays here rather than moving to the input mapper: Shift is the modifier of a
|
||||
// mouse gesture, not a keyboard action of its own.
|
||||
if (event->key() == Qt::Key_Shift) { m_copiedConfig.reset(); }
|
||||
QOpenGLWidget::keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
@@ -1061,15 +1042,6 @@ void GameWorldView::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
m_buildMode.exitCurrentMode();
|
||||
}
|
||||
else if (event->modifiers() & Qt::ShiftModifier)
|
||||
{
|
||||
// Shift + right-click copies a building's settings, but only in the
|
||||
// default selection mode (REQ-BLD-COPY-CONFIG).
|
||||
const QPoint tile = coordinates.widgetToTile(event->pos());
|
||||
std::optional<BuildingId> id = buildingAtTile(tile);
|
||||
if (!id.has_value()) { id = siteAtTile(tile); }
|
||||
if (id.has_value()) { copyConfigFrom(*id); }
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1105,20 +1077,6 @@ void GameWorldView::mousePressEvent(QMouseEvent* event)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shift + left-click applies the copied settings to a same-type building
|
||||
// (REQ-BLD-COPY-CONFIG). Consumes the click so it does not change the
|
||||
// selection. Only active in the default selection mode.
|
||||
if ((event->modifiers() & Qt::ShiftModifier) && m_copiedConfig.has_value())
|
||||
{
|
||||
std::optional<BuildingId> id = buildingAtTile(tile);
|
||||
if (!id.has_value()) { id = siteAtTile(tile); }
|
||||
if (id.has_value())
|
||||
{
|
||||
pasteConfigTo(*id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const bool ctrl = (event->modifiers() & Qt::ControlModifier) != 0;
|
||||
|
||||
// Only a click that hit nothing starts a box drag. Starting one on a hit
|
||||
@@ -1375,85 +1333,6 @@ void GameWorldView::rotateGhost(bool clockwise)
|
||||
}
|
||||
}
|
||||
|
||||
void GameWorldView::copyConfigFrom(BuildingId id)
|
||||
{
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(*m_sim, id);
|
||||
if (!config.has_value()) { return; }
|
||||
|
||||
// Only cache when there is something to copy: a selected recipe / schematic
|
||||
// (Miner, Assembler, Shipyard) or any Splitter (empty filters = accept-all).
|
||||
// Building types with no settings (Smelter, Reprocessing Plant, Salvage Bay,
|
||||
// belts / tunnels, HQ) leave any existing cache untouched (REQ-BLD-COPY-CONFIG).
|
||||
if (!config->recipeId.has_value() && !config->isSplitter) { return; }
|
||||
|
||||
m_copiedConfig = config;
|
||||
m_copyConfigFlashes.push_back({ id, kCopyFlashDurationMs });
|
||||
}
|
||||
|
||||
void GameWorldView::pasteConfigTo(BuildingId id)
|
||||
{
|
||||
if (!m_copiedConfig.has_value()) { return; }
|
||||
|
||||
const std::optional<BuildingConfig> target = readBuildingConfig(*m_sim, id);
|
||||
if (!target.has_value() || target->type != m_copiedConfig->type) { return; }
|
||||
|
||||
// The paste applies below; flash the target to confirm (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
m_copyConfigFlashes.push_back({ id, kCopyFlashDurationMs });
|
||||
|
||||
const BuildingConfig& source = *m_copiedConfig;
|
||||
|
||||
// The cached settings were valid on a same-type source building, so they are
|
||||
// valid and available on the target: unlock state is global, so a selected
|
||||
// recipe / schematic (REQ-LOCK-UI-RECIPE, REQ-LOCK-UI-SCHEMATIC) and splitter
|
||||
// filter item types (REQ-LOCK-UI-SPLITTER) remain unlocked. Applying reuses the
|
||||
// same configuration commands as the selected building panel, inheriting their
|
||||
// buffer-clearing and mid-cycle-cancel semantics (REQ-MAT-INPUT-BUFFER,
|
||||
// REQ-MAT-OUTPUT-BUFFER, REQ-BLD-SHIPYARD).
|
||||
if (source.isSplitter)
|
||||
{
|
||||
// Operational splitters are configured by tile; sites by BuildingId
|
||||
// (mirrors SelectedBuildingPanel::onSplitterFilterChanged).
|
||||
if (const Building* building = findBuilding(m_sim->getFactoryState(), id))
|
||||
{
|
||||
std::shared_ptr<SetSplitterFiltersCommand> command =
|
||||
std::make_shared<SetSplitterFiltersCommand>();
|
||||
command->tile = building->anchor;
|
||||
command->filterA = source.splitterFilterA;
|
||||
command->filterB = source.splitterFilterB;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::shared_ptr<SetSiteSplitterFiltersCommand> command =
|
||||
std::make_shared<SetSiteSplitterFiltersCommand>();
|
||||
command->id = id;
|
||||
command->filterA = source.splitterFilterA;
|
||||
command->filterB = source.splitterFilterB;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.recipeId.has_value())
|
||||
{
|
||||
std::shared_ptr<SetRecipeCommand> command = std::make_shared<SetRecipeCommand>();
|
||||
command->id = id;
|
||||
command->recipeId = *source.recipeId;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
|
||||
// For a shipyard the schematic (above) must be applied before its module
|
||||
// layout, so both commands drain in order at the next tick boundary.
|
||||
if (source.type == BuildingType::Shipyard && source.shipLayout.has_value())
|
||||
{
|
||||
std::shared_ptr<SetShipLayoutCommand> command =
|
||||
std::make_shared<SetShipLayoutCommand>();
|
||||
command->id = id;
|
||||
command->layout = *source.shipLayout;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
double GameWorldView::getGameSpeed() const
|
||||
{
|
||||
return m_gameSpeedMultiplier;
|
||||
@@ -1484,8 +1363,6 @@ void GameWorldView::resetForNewGame()
|
||||
m_activeBeams.clear();
|
||||
m_schematicChoiceShown = false;
|
||||
m_selection.clearAll();
|
||||
m_copiedConfig = std::nullopt;
|
||||
m_copyConfigFlashes.clear();
|
||||
m_boxSelecting = false;
|
||||
m_camera.reset();
|
||||
// Drops any key still held across the restart, which also republishes the pan
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "Blueprint.h"
|
||||
#include "BuildModeController.h"
|
||||
#include "BuildingConfig.h"
|
||||
#include "BlueprintModeExitedEvent.h"
|
||||
#include "BlueprintPlacementRequestedEvent.h"
|
||||
#include "BuilderModeExitedEvent.h"
|
||||
@@ -217,12 +216,6 @@ private:
|
||||
// Enqueues placements and rotate-in-place commands for the resolved path.
|
||||
void applyBeltDragPath();
|
||||
|
||||
// Copy-settings gesture (REQ-BLD-COPY-CONFIG): Shift+right-click copies a
|
||||
// building's configuration into m_copiedConfig; Shift+left-click applies it to
|
||||
// another building of the same type via the existing configuration commands.
|
||||
void copyConfigFrom(BuildingId id);
|
||||
void pasteConfigTo(BuildingId id);
|
||||
|
||||
// Turns the ghost and refreshes everything that depends on its facing: placement
|
||||
// validity, the tunnel completion match, and an in-progress belt drag's path.
|
||||
// The mode transitions themselves live on m_buildMode.
|
||||
@@ -271,17 +264,6 @@ private:
|
||||
// and to resolve the tunnel ghost sub-tile (REQ-BLD-TUNNEL-MODE).
|
||||
QVector2D m_cursorWorldPos;
|
||||
|
||||
// Temporary cache for the copy-settings gesture (REQ-BLD-COPY-CONFIG); held
|
||||
// only while Shift is down and cleared on Shift release.
|
||||
std::optional<BuildingConfig> m_copiedConfig;
|
||||
|
||||
// Brief outline flash shown on a building when settings are copied from it or
|
||||
// pasted onto it (REQ-BLD-COPY-CONFIG-FEEDBACK). remainingMs counts down in
|
||||
// wall-clock time so the flash plays at a fixed length regardless of game speed
|
||||
// (and while paused).
|
||||
std::vector<CopyConfigFlash> m_copyConfigFlashes;
|
||||
static constexpr qint64 kCopyFlashDurationMs = 300;
|
||||
|
||||
bool m_debugDraw;
|
||||
|
||||
// Owns the selection across all three categories and the rules for moving
|
||||
|
||||
@@ -48,7 +48,6 @@ struct OverlayVisuals
|
||||
QColor selectionRect;
|
||||
QColor tileHighlight;
|
||||
QColor selectedOutline;
|
||||
QColor copyConfig;
|
||||
QColor lockedAsteroid;
|
||||
QColor modalDim;
|
||||
QColor tunnelPreview; // tunnel connection preview highlight (REQ-BLD-TUNNEL-MODE)
|
||||
|
||||
@@ -224,7 +224,6 @@ VisualsConfig VisualsLoader::load(const std::string& path)
|
||||
cfg.overlays.selectionRect = parseColor(requireString(ov, "selection_rect", "overlays"), "overlays.selection_rect");
|
||||
cfg.overlays.tileHighlight = parseColor(requireString(ov, "tile_highlight", "overlays"), "overlays.tile_highlight");
|
||||
cfg.overlays.selectedOutline = parseColor(requireString(ov, "selected_outline", "overlays"), "overlays.selected_outline");
|
||||
cfg.overlays.copyConfig = parseColor(requireString(ov, "copy_config", "overlays"), "overlays.copy_config");
|
||||
cfg.overlays.lockedAsteroid = parseColor(requireString(ov, "locked_asteroid", "overlays"), "overlays.locked_asteroid");
|
||||
cfg.overlays.modalDim = parseColor(requireString(ov, "modal_dim", "overlays"), "overlays.modal_dim");
|
||||
cfg.overlays.tunnelPreview = parseColor(requireString(ov, "tunnel_preview", "overlays"), "overlays.tunnel_preview");
|
||||
|
||||
@@ -129,7 +129,6 @@ void WorldRenderer::render(QPainter& painter, const WorldCoordinates& coordinate
|
||||
// into the port and stay visible while crossing directly between two touching
|
||||
// buildings (REQ-MAT-OUTPUT-EMERGE, REQ-MAT-INPUT-INTAKE, REQ-MAT-DIRECT-COUPLE).
|
||||
drawPortItems(painter, coordinates, frame);
|
||||
drawCopyConfigFeedback(painter, coordinates, frame);
|
||||
drawStations(painter, coordinates, frame);
|
||||
drawBeltItems(painter, coordinates, frame);
|
||||
drawDebris(painter, coordinates, frame);
|
||||
@@ -505,44 +504,6 @@ void WorldRenderer::drawSelectionHighlights(QPainter& painter, const WorldCoordi
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderer::drawCopyConfigFeedback(QPainter& painter, const WorldCoordinates& coordinates,
|
||||
const WorldRenderFrame& frame)
|
||||
{
|
||||
const QColor color = m_visuals.overlays.copyConfig;
|
||||
|
||||
// Eligible-target tint: while a configuration is cached, every same-type
|
||||
// building and site (the source included) is a valid paste target and is
|
||||
// washed in the copy-settings color (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
if (frame.copiedConfig.has_value())
|
||||
{
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(color);
|
||||
const BuildingType type = frame.copiedConfig->type;
|
||||
for (const Building& b : getAllBuildings(m_sim.getFactoryState()))
|
||||
{
|
||||
if (b.type != type) { continue; }
|
||||
const std::optional<QRectF> rect = footprintWidgetRect(coordinates, b.id);
|
||||
if (rect.has_value()) { painter.drawRect(*rect); }
|
||||
}
|
||||
for (const ConstructionSite& s : getAllSites(m_sim.getFactoryState()))
|
||||
{
|
||||
if (s.type != type) { continue; }
|
||||
const std::optional<QRectF> rect = footprintWidgetRect(coordinates, s.id);
|
||||
if (rect.has_value()) { painter.drawRect(*rect); }
|
||||
}
|
||||
}
|
||||
|
||||
// Copy / paste flashes: a brief outline in the same color, drawn like the
|
||||
// selection outline (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
painter.setPen(QPen(color, 2));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
for (const CopyConfigFlash& flash : frame.copyConfigFlashes)
|
||||
{
|
||||
const std::optional<QRectF> rect = footprintWidgetRect(coordinates, flash.id);
|
||||
if (rect.has_value()) { painter.drawRect(rect->adjusted(-1, -1, 1, 1)); }
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderer::drawPortItems(QPainter& painter, const WorldCoordinates& coordinates,
|
||||
const WorldRenderFrame& /*frame*/)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include "BeamFiredEvent.h"
|
||||
#include "BuildModeController.h"
|
||||
#include "BuildingConfig.h"
|
||||
#include "BuildingId.h"
|
||||
#include "BuildingType.h"
|
||||
#include "Rotation.h"
|
||||
@@ -36,31 +35,19 @@ struct ActiveBeam
|
||||
QVector2D targetOffset;
|
||||
};
|
||||
|
||||
// Brief outline flash shown on a building when settings are copied from it or
|
||||
// pasted onto it (REQ-BLD-COPY-CONFIG-FEEDBACK). remainingMs counts down in
|
||||
// wall-clock time so the flash plays at a fixed length regardless of game speed
|
||||
// (and while paused).
|
||||
struct CopyConfigFlash
|
||||
{
|
||||
BuildingId id;
|
||||
qint64 remainingMs;
|
||||
};
|
||||
|
||||
// Everything the renderer draws that the simulation does not know about: what the
|
||||
// player has selected, which build mode is active, and the other transient bits of
|
||||
// interaction state the view owns. Assembled fresh each frame and passed by
|
||||
// reference, so the renderer holds no copy that could go stale.
|
||||
struct WorldRenderFrame
|
||||
{
|
||||
const SelectionController& selection;
|
||||
const BuildModeController& buildMode;
|
||||
const std::vector<ActiveBeam>& beams;
|
||||
const std::optional<BuildingConfig>& copiedConfig;
|
||||
const std::vector<CopyConfigFlash>& copyConfigFlashes;
|
||||
bool isBoxSelecting;
|
||||
QPoint boxStartTile;
|
||||
QPoint boxCurrentTile;
|
||||
bool isDebugDrawEnabled;
|
||||
const SelectionController& selection;
|
||||
const BuildModeController& buildMode;
|
||||
const std::vector<ActiveBeam>& beams;
|
||||
bool isBoxSelecting;
|
||||
QPoint boxStartTile;
|
||||
QPoint boxCurrentTile;
|
||||
bool isDebugDrawEnabled;
|
||||
};
|
||||
|
||||
// Draws the game world: terrain, buildings, items, ships, effects and the build
|
||||
@@ -94,8 +81,6 @@ private:
|
||||
const WorldRenderFrame& frame);
|
||||
void drawSelectionHighlights(QPainter& painter, const WorldCoordinates& coordinates,
|
||||
const WorldRenderFrame& frame);
|
||||
void drawCopyConfigFeedback(QPainter& painter, const WorldCoordinates& coordinates,
|
||||
const WorldRenderFrame& frame);
|
||||
void drawPortItems(QPainter& painter, const WorldCoordinates& coordinates,
|
||||
const WorldRenderFrame& frame);
|
||||
void drawStations(QPainter& painter, const WorldCoordinates& coordinates,
|
||||
@@ -145,8 +130,7 @@ private:
|
||||
const QColor& fill) const;
|
||||
|
||||
// Widget-space rectangle covering a building or construction site's footprint,
|
||||
// or nullopt if the id resolves to neither. Shared by the selection highlight
|
||||
// and the copy-settings feedback (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
// or nullopt if the id resolves to neither. Used by the selection highlight.
|
||||
std::optional<QRectF> footprintWidgetRect(const WorldCoordinates& coordinates,
|
||||
BuildingId id) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user