#pragma once #include #include #include #include "Blueprint.h" #include "BlueprintModeExitedEvent.h" #include "BuildingId.h" #include "EventHandler.h" #include "GameConfig.h" #include "GameResetEvent.h" #include "SelectionChangedEvent.h" #include "TemporaryBlueprintCaptureRequestedEvent.h" #include "TemporaryBlueprintPlaceRequestedEvent.h" class Simulation; class QWidget; // The player's saved blueprints: the list itself, its persistence (REQ-UI-BLUEPRINT-SAVE, // REQ-UI-BLUEPRINT-LOAD), capture from the current selection (REQ-UI-BLUEPRINT-CREATE, // REQ-UI-BLUEPRINT-TEMP), and entry into blueprint placement mode. // // Deliberately not a widget: blueprints have no permanent place on screen any more // (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 { public: // dialogParent parents the load-failure message box (REQ-UI-BLUEPRINT-LOAD) and is // not owned. BlueprintLibrary(Simulation* sim, const GameConfig* config, QWidget* dialogParent); ~BlueprintLibrary(); // True when the current selection holds at least one player-placeable building or // construction site -- the condition under which Ctrl+C has any effect // (REQ-UI-BLUEPRINT-CREATE). bool getCanCaptureSelection() const; // True once C has captured something -- the condition under which V does anything // (REQ-UI-BLUEPRINT-TEMP), and so the condition under which the controls panel // offers it (REQ-UI-CONTROLS-ACCURACY). bool getHasTemporaryBlueprint() const; // Captures the current selection under the given name and appends it to the list. // Silently does nothing when nothing player-placeable is selected. void saveSelectionAs(const QString& name); const std::vector& getBlueprints() const; // The blueprint's contents line, ready for its card: one " x " entry // per building type, comma-separated, ordered by summarizeBlueprintContents // (REQ-UI-BLUEPRINT-CARD). QString getContentsSummary(int index) const; // Sum of the constituent buildings' placement costs (REQ-UI-BLUEPRINT-CARD). int getCost(int index) const; // Whether the player can currently afford the blueprint's total cost, which is what // enables or greys out its card (REQ-UI-BLUEPRINT-CARD). bool getCanAfford(int index) const; // Removes the blueprint, exiting blueprint placement mode if it was the active one // (REQ-UI-BLUEPRINT-DELETE). void remove(int index); // Enters blueprint placement mode for the blueprint (REQ-UI-BLUEPRINT-MODE). void beginPlacement(int index); 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; void handleEvent(std::shared_ptr event) override; Blueprint createBlueprintFromSelection() const; void loadFromDisk(); void saveToDisk() const; // The simulation is the single source of truth for the block stock and the // selection; the change events are only refresh signals. Simulation* m_sim; const GameConfig* m_config; QWidget* m_dialogParent; std::vector m_selectedBuildingIds; std::vector m_blueprints; // 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 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 m_temporaryBlueprint; };