#pragma once #include #include #include #include #include #include #include #include #include #include #include "BeltSystem.h" #include "Building.h" #include "BuildingType.h" #include "EntityId.h" #include "GameConfig.h" #include "Rotation.h" #include "Tick.h" // Manages building placement, construction queuing, and the per-tick // production loop (belt→building pull, production, building→belt push). // Belt and Splitter types are forwarded to BeltSystem rather than stored // as Building instances. class BuildingSystem { public: BuildingSystem(const GameConfig& config, BeltSystem& belts, std::function allocateId, std::function addBuildingBlocks, std::mt19937& rng); // -- Placement / demolish ------------------------------------------------ // Returns the new entity id. Belt and Splitter register with BeltSystem // directly; other types enter the construction queue. EntityId place(BuildingType type, QPoint anchor, Rotation rotation, Tick currentTick); // Remove a building or construction site by id. Returns the refund in // building blocks (floor(cost * refundPercentage / 100)). Returns 0 for // unknown ids. int demolish(EntityId id); // Set the recipe (or blueprint id for shipyard) on a building or queued // construction site. Clears both buffers on an operational building. void setRecipe(EntityId id, const std::string& recipeId); // -- Tick hooks (called from Simulation::tick in the documented order) --- void tickConstruction(Tick currentTick); void tickBeltPull(); void tickProduction(Tick currentTick); void tickBeltPush(); // -- Queries ------------------------------------------------------------- struct BeltTileInfo { EntityId id; QPoint tile; BuildingType type; // Belt or Splitter }; const Building* findBuilding(EntityId id) const; const ConstructionSite* findSite(EntityId id) const; std::vector allBuildings() const; std::vector allSites() const; std::vector allBeltTiles() const; bool isTileOccupied(QPoint tile) const; // Find nearest operational building of the given type; nullptr if none. const Building* findNearestBuilding(QVector2D worldPos, BuildingType type) const; // Place one "scrap" item into a SalvageBay's output buffer. // Returns false if bay not found, wrong type, or output buffer is full. bool deliverScrapToSalvageBay(EntityId bayId); // Increase a building's HP by amount, clamped to maxHp. void healBuilding(EntityId id, float amount); // Reduce a building's HP by amount; hp may go below 0 (step 9 processes deaths). void damageBuilding(EntityId id, float amount); // 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. EntityId placeImmediate(BuildingType type, const std::vector& surfaceMask, QPoint anchor, Rotation rotation, float hp, float maxHp); // Remove an operational building by id without refund (used for deaths). // Returns true if found and removed. bool removeBuilding(EntityId id); // Set the weapon component on an already-placed defence station. void initStationWeapon(EntityId id, const StationWeapon& weapon); // Mutable iteration over all operational buildings (used by CombatSystem). void forEachBuilding(std::function fn); private: struct BeltEntry { QPoint tile; BuildingType type; // Belt or Splitter }; const BuildingDef* findBuildingDef(BuildingType type) const; const RecipeDef* findRecipe(const std::string& id, BuildingType type) const; void initBuffers(Building& b, const RecipeDef& recipe) const; std::vector computeInputPorts(const Building& b) const; std::vector rollReprocessingOutput(const RecipeDef& recipe); const GameConfig& m_config; BeltSystem& m_belts; std::function m_allocateId; std::function m_addBuildingBlocks; std::mt19937& m_rng; std::vector m_buildings; std::deque m_constructionQueue; std::map m_beltEntities; // Maps every occupied body-cell coordinate to the entity that owns it. std::map, EntityId> m_tileOccupancy; };