rekey the temporary blueprint to C, add V to re-place it
This commit is contained in:
@@ -495,7 +495,8 @@ 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 deconstruct mode: it enters deconstruct mode if inactive, or exits deconstruct mode if already active. (See also REQ-UI-DECONSTRUCT-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).
|
||||
- **C** — create a temporary blueprint from the current selection and enter its placement mode (REQ-UI-BLUEPRINT-TEMP). Has effect only when at least one player-placeable building is selected; otherwise it does nothing.
|
||||
- **V** — re-enter placement mode for the last temporary blueprint created with C (REQ-UI-BLUEPRINT-TEMP). Does nothing when no temporary blueprint exists.
|
||||
- **Ctrl+C** — save the current selection as a named blueprint: opens the blueprint save dialog (REQ-UI-BLUEPRINT-CREATE). Has effect only when at least one player-placeable building is selected; otherwise it does nothing.
|
||||
- **Ctrl+V** — opens the blueprint selection dialog (REQ-UI-BLUEPRINT-DIALOG), from which a saved blueprint is picked for placement. It is available whenever the game is being played, regardless of the current selection or of which build mode is active, and opens the dialog even when no blueprints are saved yet.
|
||||
- **Escape** — opens the escape menu (REQ-UI-GAME-MENU). While a blueprint dialog is open, Escape closes that dialog instead (REQ-UI-BLUEPRINT-DIALOG).
|
||||
@@ -567,7 +568,7 @@ The screen is divided into two columns: a main column (75% width) containing the
|
||||
|
||||
### Blueprints
|
||||
|
||||
Blueprints occupy no permanent screen space. They are saved with **Ctrl+C** from the current selection (REQ-UI-BLUEPRINT-CREATE) and picked for placement from the blueprint selection dialog, opened with **Ctrl+V** (REQ-UI-BLUEPRINT-DIALOG). There is no blueprint panel in the side panel column (REQ-UI-PANEL-COLUMN). (The ship layout blueprint panel of the layout configuration dialog, REQ-MOD-UI-BLUEPRINT-PANEL, is a separate feature and is unaffected.)
|
||||
Blueprints occupy no permanent screen space. They are saved with **Ctrl+C** from the current selection (REQ-UI-BLUEPRINT-CREATE) and picked for placement from the blueprint selection dialog, opened with **Ctrl+V** (REQ-UI-BLUEPRINT-DIALOG). The unmodified **C** and **V** keys are the throwaway counterparts of the same two gestures: they capture and re-place a single unnamed temporary blueprint that is never saved and never listed (REQ-UI-BLUEPRINT-TEMP). There is no blueprint panel in the side panel column (REQ-UI-PANEL-COLUMN). (The ship layout blueprint panel of the layout configuration dialog, REQ-MOD-UI-BLUEPRINT-PANEL, is a separate feature and is unaffected.)
|
||||
|
||||
- REQ-UI-BLUEPRINT-CREATE: Pressing **Ctrl+C** (REQ-UI-HOTKEYS) opens the modal **blueprint save dialog**, which pauses the simulation and dims the game window (REQ-UI-MODAL-DIM). It has effect only when at least one player-placeable building (i.e. a building with a button in the build button bar) is currently selected; non-player-placeable buildings (HQ, defence stations) in the selection do not count toward this condition, and pressing Ctrl+C with an empty selection or a selection of only non-player-placeable buildings does nothing (no dialog opens). A selected player-placeable building may be either an operational building or a construction site (a building placed but not yet fully built, REQ-BLD-SITE-CONFIG); both count toward this condition and are captured identically (REQ-UI-BLUEPRINT-STORAGE). The dialog prompts the player to enter a name and has Confirm and Cancel buttons. Clicking Cancel — or pressing Escape, or closing the dialog — closes it with no effect and does not open the blueprint selection dialog. Clicking Confirm with a non-empty name creates a blueprint from the current selection, silently excluding any non-player-placeable buildings, appends it to the blueprint list, closes the save dialog, and immediately opens the blueprint selection dialog (REQ-UI-BLUEPRINT-DIALOG) showing the new blueprint among the others.
|
||||
|
||||
@@ -592,7 +593,11 @@ Blueprints occupy no permanent screen space. They are saved with **Ctrl+C** from
|
||||
+------------------------------------------------------+
|
||||
```
|
||||
|
||||
- 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 deconstruct 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 selection dialog (REQ-UI-BLUEPRINT-DIALOG), 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-TEMP: Pressing the **C** 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 C with an empty selection, or a selection containing only non-player-placeable buildings (HQ, defence stations), does nothing at all, and in particular leaves any existing temporary blueprint in place. Entering this mode replaces any currently active build, blueprint placement, or deconstruct 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 selection dialog (REQ-UI-BLUEPRINT-DIALOG), 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; unlike the mode, the temporary blueprint itself survives, so it can be entered again with V.
|
||||
|
||||
Pressing the **V** key re-enters blueprint placement mode for the temporary blueprint, capturing nothing new: it is independent of the current selection, can be pressed any number of times, and yields exactly the mode described above. Like C it replaces any currently active build, blueprint placement, or deconstruct mode, and it does not test whether the player can currently afford the blueprint — cost is enforced at placement (REQ-UI-BLUEPRINT-PLACE), consistently with C. Pressing V when no temporary blueprint exists does nothing: no mode is entered and any currently active mode is left untouched.
|
||||
|
||||
There is at most one temporary blueprint at a time; pressing C replaces the previous one. It is held only in memory for the current run: it is discarded when the application closes and when the simulation is restarted from the escape menu (REQ-UI-GAME-MENU), after which V does nothing until C captures a new one.
|
||||
|
||||
- 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. A source building may be either an operational building or a construction site (REQ-BLD-SITE-CONFIG); a construction site is captured identically, storing whatever configuration it currently holds and never any buffer or construction-progress state. 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).
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SchematicChoicesAvailableEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SelectionChangedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameOverEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameResetEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WinEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ArtifactCountChangedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/UnlockedBuildingsChangedEvent.h
|
||||
@@ -31,7 +32,8 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DeconstructModeToggleRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintPlacementRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ExitBlueprintModeRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TemporaryBlueprintRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TemporaryBlueprintCaptureRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TemporaryBlueprintPlaceRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SpeedChangeRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/LayoutDialogRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/RecipeSelectionRequestedEvent.h
|
||||
|
||||
13
src/lib/eventsystem/event/GameResetEvent.h
Normal file
13
src/lib/eventsystem/event/GameResetEvent.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
// Emitted once per restart, after a queued ResetCommand has been applied and the view has
|
||||
// been reset for the new run (REQ-UI-GAME-MENU). It lets presentation state that does not
|
||||
// live in the simulation -- e.g. the temporary blueprint, REQ-UI-BLUEPRINT-TEMP -- be
|
||||
// dropped for the fresh run. Sent from GameWorldView::resetForNewGame(), which is where a
|
||||
// restart actually lands: the escape menu, game over, and win dialogs only enqueue the
|
||||
// command.
|
||||
class GameResetEvent : public Event
|
||||
{
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
// Emitted when the player presses C to capture a temporary blueprint (REQ-UI-BLUEPRINT-TEMP).
|
||||
// Carries no payload: the blueprint is built from the current selection by the receiver.
|
||||
class TemporaryBlueprintCaptureRequestedEvent : public Event
|
||||
{
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
// Emitted when the player presses V to re-enter placement mode for the temporary blueprint
|
||||
// captured with C (REQ-UI-BLUEPRINT-TEMP). Carries no payload: the receiver holds the
|
||||
// blueprint, and nothing is captured from the current selection.
|
||||
class TemporaryBlueprintPlaceRequestedEvent : public Event
|
||||
{
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
#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
|
||||
{
|
||||
};
|
||||
@@ -116,22 +116,48 @@ void BlueprintLibrary::handleEvent(std::shared_ptr<const SelectionChangedEvent>
|
||||
|
||||
void BlueprintLibrary::handleEvent(std::shared_ptr<const BlueprintModeExitedEvent> /*event*/)
|
||||
{
|
||||
// Only the saved-blueprint index is cleared. A temporary blueprint deliberately
|
||||
// survives its placement mode so V can re-enter it (REQ-UI-BLUEPRINT-TEMP).
|
||||
m_activeIndex = std::nullopt;
|
||||
}
|
||||
|
||||
void BlueprintLibrary::handleEvent(
|
||||
std::shared_ptr<const TemporaryBlueprintRequestedEvent> /*event*/)
|
||||
std::shared_ptr<const TemporaryBlueprintCaptureRequestedEvent> /*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.
|
||||
// C (REQ-UI-BLUEPRINT-TEMP): capture the current selection and enter placement mode
|
||||
// for it, without naming it, listing it, or persisting it. If nothing player-placeable
|
||||
// is selected, do nothing at all -- in particular, keep the previous temporary
|
||||
// blueprint, which V can still place.
|
||||
Blueprint blueprint = createBlueprintFromSelection();
|
||||
if (blueprint.buildings.empty()) { return; }
|
||||
|
||||
m_temporaryBlueprint = std::move(blueprint);
|
||||
// No saved blueprint is active while a temporary one is being placed.
|
||||
m_activeIndex = std::nullopt;
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<BlueprintPlacementRequestedEvent>(std::move(blueprint)));
|
||||
std::make_shared<BlueprintPlacementRequestedEvent>(*m_temporaryBlueprint));
|
||||
}
|
||||
|
||||
void BlueprintLibrary::handleEvent(
|
||||
std::shared_ptr<const TemporaryBlueprintPlaceRequestedEvent> /*event*/)
|
||||
{
|
||||
// V (REQ-UI-BLUEPRINT-TEMP): re-enter placement mode for the temporary blueprint,
|
||||
// capturing nothing. With none captured, nothing happens -- no mode is entered and
|
||||
// whichever mode is active is left alone. Affordability is not checked here, matching
|
||||
// C; cost is enforced at placement (REQ-UI-BLUEPRINT-PLACE). The blueprint is copied,
|
||||
// not moved: it stays available for the next V.
|
||||
if (!m_temporaryBlueprint.has_value()) { return; }
|
||||
|
||||
m_activeIndex = std::nullopt;
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<BlueprintPlacementRequestedEvent>(*m_temporaryBlueprint));
|
||||
}
|
||||
|
||||
void BlueprintLibrary::handleEvent(std::shared_ptr<const GameResetEvent> /*event*/)
|
||||
{
|
||||
// A restart begins a new run, and the temporary blueprint belongs to the old one
|
||||
// (REQ-UI-BLUEPRINT-TEMP). The saved blueprints are not run state and stay.
|
||||
m_temporaryBlueprint = std::nullopt;
|
||||
}
|
||||
|
||||
Blueprint BlueprintLibrary::createBlueprintFromSelection() const
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
#include "BuildingId.h"
|
||||
#include "EventHandler.h"
|
||||
#include "GameConfig.h"
|
||||
#include "GameResetEvent.h"
|
||||
#include "SelectionChangedEvent.h"
|
||||
#include "TemporaryBlueprintRequestedEvent.h"
|
||||
#include "TemporaryBlueprintCaptureRequestedEvent.h"
|
||||
#include "TemporaryBlueprintPlaceRequestedEvent.h"
|
||||
|
||||
class Simulation;
|
||||
class QWidget;
|
||||
@@ -24,9 +26,12 @@ class QWidget;
|
||||
// (REQ-UI-BLUEPRINT-DIALOG), and the modal dialogs that present them must be driven from
|
||||
// MainWindow, the only widget that can pause the game (ModalPauseScope) and raise the dim
|
||||
// overlay. This class is the model those dialogs read and mutate.
|
||||
class BlueprintLibrary : public CombinedEventHandler<SelectionChangedEvent,
|
||||
class BlueprintLibrary
|
||||
: public CombinedEventHandler<SelectionChangedEvent,
|
||||
BlueprintModeExitedEvent,
|
||||
TemporaryBlueprintRequestedEvent>
|
||||
TemporaryBlueprintCaptureRequestedEvent,
|
||||
TemporaryBlueprintPlaceRequestedEvent,
|
||||
GameResetEvent>
|
||||
{
|
||||
public:
|
||||
// dialogParent parents the load-failure message box (REQ-UI-BLUEPRINT-LOAD) and is
|
||||
@@ -67,7 +72,11 @@ public:
|
||||
private:
|
||||
void handleEvent(std::shared_ptr<const SelectionChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const BlueprintModeExitedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const TemporaryBlueprintRequestedEvent> event) override;
|
||||
void handleEvent(
|
||||
std::shared_ptr<const TemporaryBlueprintCaptureRequestedEvent> event) override;
|
||||
void handleEvent(
|
||||
std::shared_ptr<const TemporaryBlueprintPlaceRequestedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const GameResetEvent> event) override;
|
||||
|
||||
Blueprint createBlueprintFromSelection() const;
|
||||
void loadFromDisk();
|
||||
@@ -83,4 +92,10 @@ private:
|
||||
// Index of the blueprint currently in placement mode, so deleting it can exit that
|
||||
// mode (REQ-UI-BLUEPRINT-DELETE). nullopt = no saved blueprint is being placed.
|
||||
std::optional<int> m_activeIndex;
|
||||
// The one unnamed blueprint captured with C and re-placed with V
|
||||
// (REQ-UI-BLUEPRINT-TEMP). Deliberately kept out of m_blueprints: it is never named,
|
||||
// never listed in the selection dialog, and never written to blueprints.toml. It
|
||||
// outlives its placement mode, so V can re-enter it. nullopt = none captured since
|
||||
// startup or the last restart.
|
||||
std::optional<Blueprint> m_temporaryBlueprint;
|
||||
};
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
#include "EscapeMenuRequestedEvent.h"
|
||||
#include "TracePrintRequestedEvent.h"
|
||||
#include "BuildHotkeyPressedEvent.h"
|
||||
#include "TemporaryBlueprintRequestedEvent.h"
|
||||
#include "GameResetEvent.h"
|
||||
#include "BossWaveUpdatedEvent.h"
|
||||
#include "BuilderModeExitedEvent.h"
|
||||
#include "BlueprintModeExitedEvent.h"
|
||||
@@ -1502,6 +1502,10 @@ void GameWorldView::resetForNewGame()
|
||||
m_lastArtifactCount = -1;
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<SelectionChangedEvent>(std::vector<BuildingId>{}));
|
||||
// The one place a restart actually lands (the menu, game over and win dialogs only
|
||||
// enqueue the command), so it is where presentation state belonging to the finished
|
||||
// run is dropped -- e.g. the temporary blueprint (REQ-UI-BLUEPRINT-TEMP).
|
||||
EventManager::getInstance()->sendEventImmediately(std::make_shared<GameResetEvent>());
|
||||
setGameSpeed(1.0);
|
||||
// Rebase the wall-clock time source so a fresh run starts from a clean time
|
||||
// base. Without this, wall time accumulated while a modal (Game Over, Win, or
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#include "PanDirectionChangedEvent.h"
|
||||
#include "PauseToggleRequestedEvent.h"
|
||||
#include "SpeedStepRequestedEvent.h"
|
||||
#include "TemporaryBlueprintRequestedEvent.h"
|
||||
#include "TemporaryBlueprintCaptureRequestedEvent.h"
|
||||
#include "TemporaryBlueprintPlaceRequestedEvent.h"
|
||||
#include "TracePrintRequestedEvent.h"
|
||||
|
||||
namespace
|
||||
@@ -100,9 +101,10 @@ bool InputMapper::handleKeyPress(QKeyEvent* event)
|
||||
}
|
||||
|
||||
// Blueprint chords (REQ-UI-HOTKEYS). Checked ahead of the plain-key switch below,
|
||||
// which binds bare A/D/W/S/R/Q/T and must not fire on a Ctrl chord. Both requests
|
||||
// are decided by MainWindow, the only widget that can pause the game and dim the
|
||||
// window for a modal.
|
||||
// which binds bare A/D/W/S/R/Q/C/V and must not fire on a Ctrl chord -- bare C and V
|
||||
// are the temporary-blueprint counterparts of these two (REQ-UI-BLUEPRINT-TEMP). Both
|
||||
// requests are decided by MainWindow, the only widget that can pause the game and dim
|
||||
// the window for a modal.
|
||||
if ((event->modifiers() & Qt::ControlModifier) != 0)
|
||||
{
|
||||
switch (event->key())
|
||||
@@ -152,12 +154,18 @@ bool InputMapper::handleKeyPress(QKeyEvent* event)
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<ModeCancelRequestedEvent>());
|
||||
return true;
|
||||
case Qt::Key_T:
|
||||
// Request a temporary blueprint from the current selection (REQ-UI-BLUEPRINT-TEMP).
|
||||
case Qt::Key_C:
|
||||
// Capture a temporary blueprint from the current selection (REQ-UI-BLUEPRINT-TEMP).
|
||||
// The BlueprintLibrary 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<TemporaryBlueprintRequestedEvent>());
|
||||
std::make_shared<TemporaryBlueprintCaptureRequestedEvent>());
|
||||
return true;
|
||||
case Qt::Key_V:
|
||||
// Re-enter placement mode for the temporary blueprint captured with C, if there is
|
||||
// one (REQ-UI-BLUEPRINT-TEMP). The library holds it; nothing is captured here.
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<TemporaryBlueprintPlaceRequestedEvent>());
|
||||
return true;
|
||||
case Qt::Key_F3:
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
|
||||
Reference in New Issue
Block a user