From e110e7e413a6ee639c1c8b55990cd12f01e1fde9 Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Thu, 9 Jul 2026 20:25:15 +0200 Subject: [PATCH] Add T hotkey for temporary blueprint from selection --- docs/requirements.md | 3 +++ src/lib/eventsystem/event/CMakeLists.txt | 1 + .../event/TemporaryBlueprintRequestedEvent.h | 9 +++++++++ src/ui/BlueprintPanel.cpp | 14 ++++++++++++++ src/ui/BlueprintPanel.h | 5 ++++- src/ui/GameWorldView.cpp | 8 ++++++++ 6 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/lib/eventsystem/event/TemporaryBlueprintRequestedEvent.h diff --git a/docs/requirements.md b/docs/requirements.md index b23b69b..b15ab3c 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -421,6 +421,7 @@ The screen is divided into two columns: a main column (75% width) containing the - **A / D** — scroll the view left / right (REQ-UI-SCROLL). - **Q** — context-sensitive. If a build mode is active (builder mode or blueprint placement mode), pressing Q exits it. Otherwise, pressing Q toggles demolish mode: it enters demolish mode if inactive, or exits demolish mode if already active. (See also REQ-UI-DEMOLISH-BUTTON for the equivalent button.) - **R / Shift+R** — in builder mode, rotate the ghost counter-clockwise / clockwise (REQ-BLD-ROTATE). + - **T** — create a temporary blueprint from the current selection and enter its placement mode (REQ-UI-BLUEPRINT-TEMP). - **Escape** — opens the escape menu (REQ-UI-GAME-MENU). - **Build mode selection** — pressing a build hotkey activates builder mode for the corresponding building type, equivalent to clicking its build button (REQ-BLD-BUILDER-MODE): - **1** — Belt, **2** — Splitter, **3** — Tunnel Entry, **4** — Tunnel Exit. @@ -475,6 +476,8 @@ The screen is divided into two columns: a main column (75% width) containing the - REQ-UI-BLUEPRINT-CREATE: The "Create Blueprint" button is enabled only when at least one player-placeable building (i.e. a building with a button in the build button grid) is currently selected; non-player-placeable buildings (HQ, defence stations) in the selection do not count toward this condition. When clicked, a modal dialog appears prompting the player to enter a name. The dialog has Confirm and Cancel buttons. Clicking Cancel closes the dialog with no effect. Clicking Confirm with a non-empty name creates a blueprint from the current selection, silently excluding any non-player-placeable buildings, and appends its button to the blueprint list. +- REQ-UI-BLUEPRINT-TEMP: Pressing the **T** key (REQ-UI-HOTKEYS) creates a **temporary blueprint** from the current selection and immediately enters blueprint placement mode for it, without opening the naming dialog. It has effect only when at least one player-placeable building is currently selected — the same condition as REQ-UI-BLUEPRINT-CREATE; pressing T with an empty selection, or a selection containing only non-player-placeable buildings (HQ, defence stations), does nothing. Entering this mode replaces any currently active build, blueprint placement, or demolish mode. The temporary blueprint is captured exactly as a saved blueprint (REQ-UI-BLUEPRINT-STORAGE), silently excluding any non-player-placeable buildings from the selection, but it is never named, never shown in the blueprint panel (REQ-UI-BLUEPRINT-PANEL), and never persisted to `blueprints.toml` (REQ-UI-BLUEPRINT-SAVE). Placement behaves identically to a saved blueprint's placement mode (REQ-UI-BLUEPRINT-MODE, REQ-UI-BLUEPRINT-PLACE): a ghost is rendered per building, R / Shift+R rotate the entire constellation, placement follows the same per-building validity and total-cost rules, and after a successful placement the mode stays active so the blueprint can be placed again. Right-clicking in the game world exits placement mode, at which point the temporary blueprint is discarded. + - REQ-UI-BLUEPRINT-STORAGE: A blueprint stores its name and, for each building in the selection, the building type, its rotation, its tile offset (integer dx, dy) from the center of the bounding box of all selected buildings' footprints, and — where applicable — the selected recipe ID (miners and assemblers) or schematic ID (shipyards), and for splitters the two output filters (each a list of item types; an empty list means accept-all), at the time of capture. If no recipe or schematic was selected at capture time, none is stored; for a splitter with no filters set, no filter lists are stored. This structure maps directly to a TOML representation (e.g. one `[[building]]` array entry per constituent building, with the splitter filters as `filter_a`/`filter_b` arrays of item-type ids). - REQ-UI-BLUEPRINT-BUTTON: Each blueprint entry consists of a blueprint button and a dedicated delete icon ("×") placed to the right of the button. The blueprint button displays the blueprint name and, below it, the total building block cost of the blueprint (sum of the individual costs of all constituent buildings). A blueprint button is disabled when the player cannot afford the total cost. Clicking an enabled blueprint button enters blueprint placement mode for that blueprint. The delete icon is always enabled regardless of whether the player can afford the blueprint. diff --git a/src/lib/eventsystem/event/CMakeLists.txt b/src/lib/eventsystem/event/CMakeLists.txt index ab7f2ff..327abab 100644 --- a/src/lib/eventsystem/event/CMakeLists.txt +++ b/src/lib/eventsystem/event/CMakeLists.txt @@ -22,6 +22,7 @@ SET(HDRS ${CMAKE_CURRENT_SOURCE_DIR}/DemolishModeToggleRequestedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/BlueprintPlacementRequestedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/ExitBlueprintModeRequestedEvent.h + ${CMAKE_CURRENT_SOURCE_DIR}/TemporaryBlueprintRequestedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/SpeedChangeRequestedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/LayoutDialogRequestedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/RecipeSelectionRequestedEvent.h diff --git a/src/lib/eventsystem/event/TemporaryBlueprintRequestedEvent.h b/src/lib/eventsystem/event/TemporaryBlueprintRequestedEvent.h new file mode 100644 index 0000000..4c89ce5 --- /dev/null +++ b/src/lib/eventsystem/event/TemporaryBlueprintRequestedEvent.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Event.h" + +// Emitted when the player presses the temporary-blueprint hotkey (REQ-UI-BLUEPRINT-TEMP). +// Carries no payload: the blueprint is built from the current selection by the receiver. +class TemporaryBlueprintRequestedEvent : public Event +{ +}; diff --git a/src/ui/BlueprintPanel.cpp b/src/ui/BlueprintPanel.cpp index 8b57cea..b3d984a 100644 --- a/src/ui/BlueprintPanel.cpp +++ b/src/ui/BlueprintPanel.cpp @@ -339,3 +339,17 @@ void BlueprintPanel::handleEvent(std::shared_ptr { clearActiveBlueprintButton(); } + +void BlueprintPanel::handleEvent(std::shared_ptr /*event*/) +{ + // Temporary blueprint (REQ-UI-BLUEPRINT-TEMP): build from the current selection and + // enter placement mode without adding it to the list or persisting it. If nothing + // player-placeable is selected, do nothing. + Blueprint bp = createBlueprintFromSelection(); + if (bp.buildings.empty()) { return; } + + // No saved blueprint is active while a temporary one is being placed. + clearActiveBlueprintButton(); + EventManager::getInstance()->sendEventImmediately( + std::make_shared(std::move(bp))); +} diff --git a/src/ui/BlueprintPanel.h b/src/ui/BlueprintPanel.h index bf16846..bbb03b9 100644 --- a/src/ui/BlueprintPanel.h +++ b/src/ui/BlueprintPanel.h @@ -11,6 +11,7 @@ #include "EventHandler.h" #include "GameConfig.h" #include "SelectionChangedEvent.h" +#include "TemporaryBlueprintRequestedEvent.h" #include "Tick.h" class Simulation; @@ -21,7 +22,8 @@ class QVBoxLayout; class BlueprintPanel : public QWidget, public CombinedEventHandler + BlueprintModeExitedEvent, + TemporaryBlueprintRequestedEvent> { Q_OBJECT @@ -33,6 +35,7 @@ private: void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; + void handleEvent(std::shared_ptr event) override; private slots: void onCreateClicked(); diff --git a/src/ui/GameWorldView.cpp b/src/ui/GameWorldView.cpp index c583796..1a4bc20 100644 --- a/src/ui/GameWorldView.cpp +++ b/src/ui/GameWorldView.cpp @@ -56,6 +56,7 @@ #include "EscapeMenuRequestedEvent.h" #include "TracePrintRequestedEvent.h" #include "BuildHotkeyPressedEvent.h" +#include "TemporaryBlueprintRequestedEvent.h" #include "BossWaveUpdatedEvent.h" #include "BuilderModeExitedEvent.h" #include "BlueprintModeExitedEvent.h" @@ -1582,6 +1583,13 @@ void GameWorldView::keyPressEvent(QKeyEvent* event) else if (m_blueprintMode.has_value()) { exitBlueprintMode(); } else { toggleDemolishMode(); } break; + case Qt::Key_T: + // Request a temporary blueprint from the current selection (REQ-UI-BLUEPRINT-TEMP). + // The BlueprintPanel owns the selection and blueprint-capture logic; it decides + // whether anything placeable is selected and drives placement mode from there. + EventManager::getInstance()->sendEventImmediately( + std::make_shared()); + break; case Qt::Key_Escape: EventManager::getInstance()->sendEventImmediately( std::make_shared());