145 lines
6.3 KiB
C++
145 lines
6.3 KiB
C++
#pragma once
|
|
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <random>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <QPoint>
|
|
#include <QVector2D>
|
|
|
|
#include "BeltSystem.h"
|
|
#include "Building.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"
|
|
|
|
// Manages building placement, construction queuing, and the per-tick
|
|
// production loop (belt→building pull, production, building→belt push).
|
|
// All types including Belt and Splitter are stored as Building instances;
|
|
// BeltSystem owns the per-tile simulation data (item slots, flow).
|
|
class BuildingSystem
|
|
{
|
|
public:
|
|
BuildingSystem(const GameConfig& config,
|
|
BeltSystem& belts,
|
|
std::function<BuildingId()> allocateBuildingId,
|
|
std::function<void(int)> addBuildingBlocks,
|
|
std::function<void(const std::string&, QVector2D,
|
|
const std::optional<ShipLayoutConfig>&)> spawnShip,
|
|
std::mt19937& rng);
|
|
|
|
// -- Placement / demolish ------------------------------------------------
|
|
// Returns the new entity id. Belt and Splitter register with BeltSystem
|
|
// directly; other types enter the construction queue.
|
|
BuildingId 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(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(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(BuildingId id, const ShipLayoutConfig& layout);
|
|
|
|
// -- Tick hooks (called from Simulation::tick in the documented order) ---
|
|
void tickConstruction(Tick currentTick);
|
|
void tickBeltPull();
|
|
void tickProduction(Tick currentTick);
|
|
void tickShipyardProduction(Tick currentTick);
|
|
void tickBeltPush();
|
|
|
|
// -- Queries -------------------------------------------------------------
|
|
struct BeltTileInfo
|
|
{
|
|
BuildingId buildingId;
|
|
QPoint tile;
|
|
BuildingType type; // Belt or Splitter
|
|
Rotation directionA; // Belt: its direction; Splitter: first output
|
|
Rotation directionB; // Splitter: second output; Belt: same as directionA
|
|
};
|
|
|
|
const Building* findBuilding(BuildingId id) const;
|
|
const ConstructionSite* findSite(BuildingId id) const;
|
|
std::vector<Building> allBuildings() const;
|
|
std::vector<ConstructionSite> allSites() const;
|
|
std::vector<BeltTileInfo> allBeltTiles() const;
|
|
bool isTileOccupied(QPoint tile) const;
|
|
|
|
// Returns the entity id of the building or construction site whose footprint
|
|
// exactly coincides with the ghost (type, anchor, rot) and is of the same
|
|
// building type. Returns nullopt otherwise.
|
|
std::optional<BuildingId> findRotateInPlaceTarget(BuildingType type,
|
|
QPoint anchor,
|
|
Rotation rot) const;
|
|
|
|
// 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(BuildingId id, Rotation newRotation);
|
|
|
|
// Find nearest operational building of the given type; nullptr if none.
|
|
const Building* findNearestBuilding(QVector2D worldPos, BuildingType type) const;
|
|
|
|
// Register / unregister tile occupancy for ECS station entities.
|
|
void registerTileOccupancy(const std::vector<QPoint>& cells, BuildingId ownerPlaceholder);
|
|
void unregisterTileOccupancy(const std::vector<QPoint>& cells);
|
|
|
|
// 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(BuildingId bayId);
|
|
|
|
// 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(BuildingType type,
|
|
const std::vector<std::string>& surfaceMask,
|
|
QPoint anchor, Rotation rotation);
|
|
|
|
// Remove an operational building by id without refund (used for deaths).
|
|
// Returns true if found and removed.
|
|
bool removeBuilding(BuildingId id);
|
|
|
|
// Mutable iteration over all operational buildings.
|
|
void forEachBuilding(std::function<void(Building&)> fn);
|
|
|
|
private:
|
|
const BuildingDef* findBuildingDef(BuildingType type) const;
|
|
const RecipeDef* findRecipe(const std::string& id, BuildingType type) const;
|
|
const ShipDef* findShipDef(const std::string& id) const;
|
|
const ModuleDef* findModuleDef(const std::string& id) const;
|
|
void initBuffers(Building& b, const RecipeDef& recipe) const;
|
|
void initShipyardBuffers(Building& b) const;
|
|
std::vector<Port> computeInputPorts(const Building& b) const;
|
|
std::vector<Item> rollReprocessingOutput(const RecipeDef& recipe);
|
|
|
|
const GameConfig& m_config;
|
|
BeltSystem& m_belts;
|
|
std::function<BuildingId()> m_allocateBuildingId;
|
|
std::function<void(int)> m_addBuildingBlocks;
|
|
std::function<void(const std::string&, QVector2D,
|
|
const std::optional<ShipLayoutConfig>&)> m_spawnShip;
|
|
std::mt19937& m_rng;
|
|
|
|
std::vector<Building> m_buildings;
|
|
std::deque<ConstructionSite> m_constructionQueue;
|
|
|
|
// Maps every occupied body-cell coordinate to the entity that owns it.
|
|
std::map<std::pair<int, int>, BuildingId> m_tileOccupancy;
|
|
};
|