#pragma once #include #include #include #include #include #include #include #include #include #include "BeltSystem.h" #include "Building.h" #include "FactoryState.h" #include "BuildingBuffers.h" #include "DeconstructionSystem.h" #include "PlacementRules.h" #include "BuildingType.h" #include "BuildingId.h" #include "GameConfig.h" #include "Rotation.h" #include "ModulesConfig.h" #include "ShipLayout.h" #include "ShipsConfig.h" #include "Tick.h" // What buildings are: placing them, demolishing them, rotating them, and configuring // what they will run. What flows through them once they stand -- intake, production // cycles, output -- belongs to ProductionSystem, which took the RNG, the ship spawner and // the unlock test with it. // // All types including Belt and Splitter are stored as Building instances; BeltSystem owns // the per-tile simulation data (item slots, flow). class BuildingSystem { public: explicit BuildingSystem(const GameConfig& config); // -- Placement / deconstruct ------------------------------------------------ // Returns the new entity id, or nullopt if the placement falls outside the // world bounds (vertical extent and asteroid left edge). Belt and Splitter // register with BeltSystem directly; other types enter the construction // queue. Terrain type (A vs S) is NOT checked here so that tests can stage // arbitrary layouts; the player-facing entry point // (Simulation::tryPlaceBuilding) enforces the full rule via isPlacementValid. std::optional place(FactoryState& state, BuildingType type, QPoint anchor, Rotation rotation, Tick currentTick); // Sets the current buildable asteroid width in tiles. Grows the left // placement bound as the player unlocks asteroid expansions (REQ-EXP-UNLOCK). // Defaults to world.regions.asteroid_width_tiles at construction. void setAsteroidWidth_tiles(FactoryState& state, int widthTiles) const { state.asteroidWidth_tiles = widthTiles; } // Mark a building or construction site for demolition (REQ-BLD-DECONSTRUCT). // A construction site is removed instantly and its full cost credited back to the // block stock. A fully-built building is instead appended to the deconstruction // queue (REQ-BLD-DECON-QUEUE) and stops operating at once; its (partial) refund is // credited later, on completion, by DeconstructionSystem. No-op for unknown ids and // for a building already queued. void deconstruct(FactoryState& state, BeltSystem& belts, BuildingId id, Tick currentTick); // Take a building back out of the deconstruction queue before it is removed // (REQ-BLD-DECON-QUEUE). Clears its queued flag and resumes operation // (re-registering belt/tunnel/splitter tiles); discards deconstruction // progress and credits no refund. No-op if the id is not queued. void cancelDeconstruction(FactoryState& state, BeltSystem& belts, BuildingId id); // Set the recipe (or schematic id for shipyard) on a building or queued // construction site. Clears both buffers on an operational building. void setRecipe(FactoryState& state, BuildingId id, const std::string& recipeId); // Set the module layout for a shipyard. Cancels in-progress production // (materials discarded) and reinitializes input buffers (REQ-BLD-SHIPYARD). void setShipLayout(FactoryState& state, BuildingId id, const ShipLayoutConfig& layout); // Splitter filter configuration for a queued/under-construction Splitter // site (REQ-BLD-SITE-CONFIG). Operational splitters are configured through // BeltSystem by tile; these mirror that for sites, which are not yet // registered with BeltSystem. getSiteSplitterInfo returns the site's two // output directions (derived from its surface mask) and stored filters, or // nullopt if the id is not a Splitter site. The stored filters are applied // to BeltSystem when the splitter finishes building (tickConstruction). void setSiteSplitterFilters(FactoryState& state, BuildingId id, const std::vector& filterA, const std::vector& filterB); // This system has no tick hook of its own: a building placed, configured or // demolished is a player action, not something that advances every tick // (ProductionSystem, ConstructionSystem, DeconstructionSystem tick instead). // This system answers no queries: reading the factory needs none, the queries being // free functions over the state (FactoryQueries.h), the placement rules // (PlacementRules.h) and the production rules (ProductionRules.h). What is left here // mutates. // Rotate an existing building or construction site to newRotation in place. // For belt-type operational buildings, re-registers with BeltSystem (items // currently on the tile are discarded by BeltSystem::removeTile). void rotateInPlace(FactoryState& state, BeltSystem& belts, BuildingId id, Rotation newRotation); // Register / unregister tile occupancy for ECS station entities. void registerTileOccupancy(FactoryState& state, const std::vector& cells, BuildingId ownerPlaceholder); void unregisterTileOccupancy(FactoryState& state, const std::vector& cells); // Bypass the construction queue and create a fully-operational Building // immediately. Used for pre-placed structures (HQ, defence stations). // surfaceMask comes from the relevant config struct. BuildingId placeImmediate(FactoryState& state, BuildingType type, const std::vector& surfaceMask, QPoint anchor, Rotation rotation); // Mutable iteration over all operational buildings. void forEachBuilding(FactoryState& state, std::function fn); private: // The config alone: what a building costs, occupies and can run. No world data -- // the factory and the transport layer both arrive per call, as they do for // ConstructionSystem and ProductionSystem (FactoryState.h) -- and no RNG or callbacks, // those having gone to ProductionSystem with the material flow that needed them. const GameConfig& m_config; };