#pragma once #include #include #include "Building.h" #include "GameConfig.h" #include "BuildingGrid.h" #include "BuildingId.h" #include "ItemType.h" #include "Tick.h" // One pending demolition of a fully-built building (REQ-BLD-DECON-QUEUE). // completesAt == 0 means "queued but its timer has not started yet" // (mirrors ConstructionSite). For a Splitter, the filters it had are captured // here so cancelDeconstruction can restore them on re-registration. struct DeconstructionEntry { BuildingId id = kInvalidBuildingId; Tick completesAt = 0; std::vector splitterFilterA; std::vector splitterFilterB; }; // The factory's world data: every building, the work queued on them, and the // tile ownership index. This is the buildings-side counterpart to EntityAdmin — // data with no behaviour of its own beyond what BuildingGrid encapsulates. // // Buildings deliberately stay a plain vector rather than becoming EnTT entities // (see docs/architecture.md). Separating this data from the systems that operate // on it is not a step toward putting them in the entity model; it is the same // data/behaviour split the ecs/system/ classes already follow, where world data // arrives as a tick argument instead of being owned by the system. // // Owned by Simulation (and by ArenaSimulation in the balancing tool), not by the // systems that operate on it. BuildingSystem holds a reference. The remaining step // is to pass this into the tick methods instead, so the systems become stateless // over it — that one is gated on the query surface, which today reaches the data // through BuildingSystem's ~180 const call sites. struct FactoryState { std::vector buildings; std::deque constructionQueue; std::deque deconstructionQueue; // The authority on which building owns which tile; every placement and removal // path claims and releases its body cells here. BuildingGrid grid; // Current buildable asteroid width, the left bound for placement. Grows as the // player buys expansions (REQ-EXP-UNLOCK). Deliberately not checksummed: it is // derived from config and Simulation's expansion count, which is folded already. // Seeded from config by BuildingSystem's constructor. int asteroidWidth_tiles = 0; }; // A fresh factory for a new run: nothing built, and the asteroid bound seeded from // config. Every owner of a FactoryState creates it this way — the bound has no // sensible default without the config, so a default-constructed state would refuse // every placement on the asteroid. inline FactoryState makeFactoryState(const GameConfig& config) { FactoryState state; state.asteroidWidth_tiles = config.world.regions.asteroidWidth_tiles; return state; }