From e39b81eb22fea372dcfe88b4593cc04edf9050a2 Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Tue, 4 Aug 2026 20:39:34 +0200 Subject: [PATCH] gather the factory's world data into FactoryState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BuildingSystem is the one system in the codebase that owns the world data it operates on. The ecs/system/ classes already do the opposite — AiSystem and SalvagerSystem hold config and their own scratch, and take EntityAdmin and the other systems as tick arguments — so this is bringing the outlier in line, not inventing a pattern. Phase 1 of that: the buildings vector, both work queues and the tile grid move into a FactoryState struct, still owned by BuildingSystem. Every method reaches through m_state. The public API is untouched, so none of the 33 files that reference BuildingSystem needed a change. DeconstructionEntry moves out of BuildingSystem's private section into FactoryState.h, since the queue that holds it lives there now. m_asteroidWidth_tiles stays on the system: it is not checksummed and is a cached placement bound derived from config and the expansion count, not factory data. The intent is for Simulation to own FactoryState and pass it into the tick methods, leaving the systems stateless over it. FactoryState.h notes explicitly that this is a data/behaviour split and not a step toward putting buildings in the entity model, which architecture.md rules out. Verified with a golden-checksum capture before and after — all four sample ticks identical. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG --- src/lib/sim/BuildingSystem.cpp | 154 ++++++++++++++++----------------- src/lib/sim/BuildingSystem.h | 25 ++---- src/lib/sim/CMakeLists.txt | 1 + src/lib/sim/FactoryState.h | 45 ++++++++++ 4 files changed, 128 insertions(+), 97 deletions(-) create mode 100644 src/lib/sim/FactoryState.h diff --git a/src/lib/sim/BuildingSystem.cpp b/src/lib/sim/BuildingSystem.cpp index 647505a..124408d 100644 --- a/src/lib/sim/BuildingSystem.cpp +++ b/src/lib/sim/BuildingSystem.cpp @@ -308,7 +308,7 @@ std::optional BuildingSystem::place(BuildingType type, QPoint anchor for (const QPoint& cell : mask.bodyCells) { const QPoint absCell = anchor + cell; - m_grid.occupy(absCell, id); + m_state.grid.occupy(absCell, id); } // Build construction site. @@ -323,13 +323,13 @@ std::optional BuildingSystem::place(BuildingType type, QPoint anchor site.bodyCells.push_back(anchor + cell); } - if (m_constructionQueue.empty()) + if (m_state.constructionQueue.empty()) { site.completesAt = currentTick + secondsToTicks(def->constructionTimeSeconds); } // else: completesAt remains 0 (queued, not yet started). - m_constructionQueue.push_back(std::move(site)); + m_state.constructionQueue.push_back(std::move(site)); return id; } @@ -405,15 +405,15 @@ int BuildingSystem::deconstruct(BuildingId id, Tick currentTick) { // Construction site? Removed instantly with the full refund; never queued // for deconstruction (REQ-BLD-DECONSTRUCT). - for (std::deque::iterator it = m_constructionQueue.begin(); - it != m_constructionQueue.end(); + for (std::deque::iterator it = m_state.constructionQueue.begin(); + it != m_state.constructionQueue.end(); ++it) { if (it->id == id) { const BuildingDef* def = m_config.buildings.findBuildingDef(it->type); - m_grid.release(it->bodyCells); - m_constructionQueue.erase(it); + m_state.grid.release(it->bodyCells); + m_state.constructionQueue.erase(it); if (def) { return def->cost; @@ -425,7 +425,7 @@ int BuildingSystem::deconstruct(BuildingId id, Tick currentTick) // Operational building? Append it to the deconstruction queue rather than // removing it now; the partial refund is credited on completion in // tickDeconstruction (REQ-BLD-DECON-QUEUE). - for (Building& building : m_buildings) + for (Building& building : m_state.buildings) { if (building.id != id) { continue; } if (building.queuedForDeconstruction) { return 0; } // already queued @@ -453,8 +453,8 @@ int BuildingSystem::deconstruct(BuildingId id, Tick currentTick) m_belts.removeTile(building.anchor); } - const bool wasEmpty = m_deconstructionQueue.empty(); - m_deconstructionQueue.push_back(std::move(entry)); + const bool wasEmpty = m_state.deconstructionQueue.empty(); + m_state.deconstructionQueue.push_back(std::move(entry)); if (wasEmpty) { startFrontDeconstruction(currentTick); @@ -467,8 +467,8 @@ int BuildingSystem::deconstruct(BuildingId id, Tick currentTick) void BuildingSystem::startFrontDeconstruction(Tick currentTick) { - if (m_deconstructionQueue.empty()) { return; } - DeconstructionEntry& front = m_deconstructionQueue.front(); + if (m_state.deconstructionQueue.empty()) { return; } + DeconstructionEntry& front = m_state.deconstructionQueue.front(); if (front.completesAt == 0) { front.completesAt = @@ -483,7 +483,7 @@ void BuildingSystem::startFrontDeconstruction(Tick currentTick) void BuildingSystem::setRecipe(BuildingId id, const std::string& recipeId) { // Construction site: store recipe for when building completes. - for (ConstructionSite& site : m_constructionQueue) + for (ConstructionSite& site : m_state.constructionQueue) { if (site.id == id) { @@ -506,7 +506,7 @@ void BuildingSystem::setRecipe(BuildingId id, const std::string& recipeId) } // Operational building: clear buffers and re-init. - for (Building& building : m_buildings) + for (Building& building : m_state.buildings) { if (building.id == id) { @@ -558,7 +558,7 @@ void BuildingSystem::setRecipe(BuildingId id, const std::string& recipeId) void BuildingSystem::setShipLayout(BuildingId id, const ShipLayoutConfig& layout) { - for (ConstructionSite& site : m_constructionQueue) + for (ConstructionSite& site : m_state.constructionQueue) { if (site.id == id) { @@ -567,7 +567,7 @@ void BuildingSystem::setShipLayout(BuildingId id, const ShipLayoutConfig& layout } } - for (Building& building : m_buildings) + for (Building& building : m_state.buildings) { if (building.id == id) { @@ -594,7 +594,7 @@ void BuildingSystem::setShipLayout(BuildingId id, const ShipLayoutConfig& layout std::optional BuildingSystem::getSiteSplitterInfo(BuildingId id) const { - for (const ConstructionSite& site : m_constructionQueue) + for (const ConstructionSite& site : m_state.constructionQueue) { if (site.id != id) { continue; } if (site.type != BuildingType::Splitter) { return std::nullopt; } @@ -618,7 +618,7 @@ void BuildingSystem::setSiteSplitterFilters(BuildingId id, const std::vector& filterA, const std::vector& filterB) { - for (ConstructionSite& site : m_constructionQueue) + for (ConstructionSite& site : m_state.constructionQueue) { if (site.id == id && site.type == BuildingType::Splitter) { @@ -636,12 +636,12 @@ void BuildingSystem::setSiteSplitterFilters(BuildingId id, void BuildingSystem::tickConstruction(Tick currentTick) { TRACE(); - if (m_constructionQueue.empty()) + if (m_state.constructionQueue.empty()) { return; } - ConstructionSite& front = m_constructionQueue.front(); + ConstructionSite& front = m_state.constructionQueue.front(); // Guard: if somehow the front site was never started, start it now. if (front.completesAt == 0) @@ -719,18 +719,18 @@ void BuildingSystem::tickConstruction(Tick currentTick) // filters configured while under construction carry over (REQ-BLD-SITE-CONFIG). reregisterBeltTile(building, front.splitterFilterA, front.splitterFilterB); - m_buildings.push_back(std::move(building)); + m_state.buildings.push_back(std::move(building)); - m_constructionQueue.pop_front(); + m_state.constructionQueue.pop_front(); // Start next queued site if present. - if (!m_constructionQueue.empty() && m_constructionQueue.front().completesAt == 0) + if (!m_state.constructionQueue.empty() && m_state.constructionQueue.front().completesAt == 0) { const BuildingDef* nextDef = - m_config.buildings.findBuildingDef(m_constructionQueue.front().type); + m_config.buildings.findBuildingDef(m_state.constructionQueue.front().type); if (nextDef) { - m_constructionQueue.front().completesAt = + m_state.constructionQueue.front().completesAt = currentTick + secondsToTicks(nextDef->constructionTimeSeconds); } } @@ -767,12 +767,12 @@ void BuildingSystem::reregisterBeltTile(const Building& building, void BuildingSystem::tickDeconstruction(Tick currentTick) { TRACE(); - if (m_deconstructionQueue.empty()) + if (m_state.deconstructionQueue.empty()) { return; } - DeconstructionEntry& front = m_deconstructionQueue.front(); + DeconstructionEntry& front = m_state.deconstructionQueue.front(); // Guard: if the front entry's timer was never started, start it now. if (front.completesAt == 0) @@ -789,15 +789,15 @@ void BuildingSystem::tickDeconstruction(Tick currentTick) // Remove the building from the world and credit its refund (REQ-BLD-DECONSTRUCT). // Belt/tunnel/splitter tiles were already unregistered when the building was // queued (see deconstruct), so only tile occupancy and the record remain. - for (std::vector::iterator it = m_buildings.begin(); - it != m_buildings.end(); + for (std::vector::iterator it = m_state.buildings.begin(); + it != m_state.buildings.end(); ++it) { if (it->id != front.id) { continue; } const BuildingDef* def = m_config.buildings.findBuildingDef(it->type); - m_grid.release(it->bodyCells); - m_buildings.erase(it); + m_state.grid.release(it->bodyCells); + m_state.buildings.erase(it); if (def) { m_addBuildingBlocks(def->cost * m_config.world.refundPercentage / 100); @@ -805,7 +805,7 @@ void BuildingSystem::tickDeconstruction(Tick currentTick) break; } - m_deconstructionQueue.pop_front(); + m_state.deconstructionQueue.pop_front(); // Start the next queued deconstruction, if any. startFrontDeconstruction(currentTick); @@ -813,8 +813,8 @@ void BuildingSystem::tickDeconstruction(Tick currentTick) void BuildingSystem::cancelDeconstruction(BuildingId id) { - for (std::deque::iterator it = m_deconstructionQueue.begin(); - it != m_deconstructionQueue.end(); + for (std::deque::iterator it = m_state.deconstructionQueue.begin(); + it != m_state.deconstructionQueue.end(); ++it) { if (it->id != id) { continue; } @@ -828,7 +828,7 @@ void BuildingSystem::cancelDeconstruction(BuildingId id) reregisterBeltTile(*building, it->splitterFilterA, it->splitterFilterB); } - m_deconstructionQueue.erase(it); + m_state.deconstructionQueue.erase(it); // If the running front was removed, the new front (completesAt == 0) has // its timer started by the next tickDeconstruction guard. return; @@ -848,7 +848,7 @@ void BuildingSystem::tickBeltPull() // (REQ-GW-BELT-SPEED, REQ-MAT-INPUT-INTAKE). const double progressPerTick = m_belts.getProgressPerTick_tpt(); - for (Building& building : m_buildings) + for (Building& building : m_state.buildings) { // A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE). if (building.queuedForDeconstruction) { continue; } @@ -932,7 +932,7 @@ bool BuildingSystem::tryDirectCoupleDeposit(BuildingId producerId, const Port& outputPort, const Item& item) { - const std::optional ownerId = m_grid.findOwner(outputPort.tile); + const std::optional ownerId = m_state.grid.findOwner(outputPort.tile); if (!ownerId.has_value() || *ownerId == producerId) { return false; @@ -966,7 +966,7 @@ bool BuildingSystem::tryDirectCoupleDeposit(BuildingId producerId, void BuildingSystem::tickProduction(Tick currentTick) { TRACE(); - for (Building& building : m_buildings) + for (Building& building : m_state.buildings) { // A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE). if (building.queuedForDeconstruction) { continue; } @@ -1069,7 +1069,7 @@ void BuildingSystem::tickProduction(Tick currentTick) void BuildingSystem::tickShipyardProduction(Tick currentTick) { TRACE(); - for (Building& building : m_buildings) + for (Building& building : m_state.buildings) { // A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE). if (building.queuedForDeconstruction) { continue; } @@ -1169,7 +1169,7 @@ void BuildingSystem::tickOutputBelts() // same speed as real belts (REQ-GW-BELT-SPEED, REQ-MAT-OUTPUT-EMERGE). const double progressPerTick = m_belts.getProgressPerTick_tpt(); - for (Building& building : m_buildings) + for (Building& building : m_state.buildings) { // A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE). if (building.queuedForDeconstruction) { continue; } @@ -1215,7 +1215,7 @@ void BuildingSystem::tickOutputBelts() void BuildingSystem::forEachEmergingItem( const std::function& visit) const { - for (const Building& building : m_buildings) + for (const Building& building : m_state.buildings) { for (std::size_t p = 0; p < building.outputPorts.size(); ++p) { @@ -1237,7 +1237,7 @@ void BuildingSystem::forEachEmergingItem( void BuildingSystem::forEachIncomingItem( const std::function& visit) const { - for (const Building& building : m_buildings) + for (const Building& building : m_state.buildings) { for (std::size_t p = 0; p < building.inputPorts.size(); ++p) { @@ -1262,7 +1262,7 @@ void BuildingSystem::forEachIncomingItem( const Building* BuildingSystem::findBuilding(BuildingId id) const { - for (const Building& building : m_buildings) + for (const Building& building : m_state.buildings) { if (building.id == id) { @@ -1274,7 +1274,7 @@ const Building* BuildingSystem::findBuilding(BuildingId id) const Building* BuildingSystem::findBuildingMutable(BuildingId id) { - for (Building& building : m_buildings) + for (Building& building : m_state.buildings) { if (building.id == id) { @@ -1286,7 +1286,7 @@ Building* BuildingSystem::findBuildingMutable(BuildingId id) const ConstructionSite* BuildingSystem::findSite(BuildingId id) const { - for (const ConstructionSite& site : m_constructionQueue) + for (const ConstructionSite& site : m_state.constructionQueue) { if (site.id == id) { @@ -1298,13 +1298,13 @@ const ConstructionSite* BuildingSystem::findSite(BuildingId id) const std::vector BuildingSystem::getAllBuildings() const { - return m_buildings; + return m_state.buildings; } std::vector BuildingSystem::getAllSites() const { - return std::vector(m_constructionQueue.begin(), - m_constructionQueue.end()); + return std::vector(m_state.constructionQueue.begin(), + m_state.constructionQueue.end()); } namespace @@ -1328,7 +1328,7 @@ bool isProductionBuildingType(BuildingType type) int BuildingSystem::getProductionBuildingCount() const { int count = 0; - for (const Building& b : m_buildings) + for (const Building& b : m_state.buildings) { if (isProductionBuildingType(b.type)) { ++count; } } @@ -1338,7 +1338,7 @@ int BuildingSystem::getProductionBuildingCount() const int BuildingSystem::getActiveProductionBuildingCount() const { int count = 0; - for (const Building& b : m_buildings) + for (const Building& b : m_state.buildings) { if (isProductionBuildingType(b.type) && b.production.has_value()) { ++count; } } @@ -1489,7 +1489,7 @@ BuildingSystem::getProductionStatus(const Building& building) const std::vector BuildingSystem::getAllBeltTiles() const { std::vector result; - for (const Building& b : m_buildings) + for (const Building& b : m_state.buildings) { if (b.type != BuildingType::Belt && b.type != BuildingType::Splitter) { @@ -1520,7 +1520,7 @@ std::vector BuildingSystem::getAllBeltTiles() cons bool BuildingSystem::isTileOccupied(QPoint tile) const { - return m_grid.isOccupied(tile); + return m_state.grid.isOccupied(tile); } std::optional BuildingSystem::findRotateInPlaceTarget( @@ -1541,13 +1541,13 @@ std::optional BuildingSystem::findRotateInPlaceTarget( // All body cells must be occupied by the same entity. const QPoint firstAbs = anchor + mask.bodyCells[0]; - const std::optional firstOwner = m_grid.findOwner(firstAbs); + const std::optional firstOwner = m_state.grid.findOwner(firstAbs); if (!firstOwner.has_value()) { return std::nullopt; } const BuildingId candidateId = *firstOwner; for (const QPoint& rel : mask.bodyCells) { - const std::optional owner = m_grid.findOwner(anchor + rel); + const std::optional owner = m_state.grid.findOwner(anchor + rel); if (!owner.has_value() || *owner != candidateId) { return std::nullopt; @@ -1555,14 +1555,14 @@ std::optional BuildingSystem::findRotateInPlaceTarget( } // Verify the candidate is the same building type with the same cell count. - for (const ConstructionSite& site : m_constructionQueue) + for (const ConstructionSite& site : m_state.constructionQueue) { if (site.id != candidateId) { continue; } if (site.type != type) { return std::nullopt; } if (site.bodyCells.size() != mask.bodyCells.size()) { return std::nullopt; } return candidateId; } - for (const Building& b : m_buildings) + for (const Building& b : m_state.buildings) { if (b.id != candidateId) { continue; } if (b.type != type) { return std::nullopt; } @@ -1576,7 +1576,7 @@ std::optional BuildingSystem::findRotateInPlaceTarget( void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation) { // Construction site path — just update rotation; no ports to recompute. - for (ConstructionSite& site : m_constructionQueue) + for (ConstructionSite& site : m_state.constructionQueue) { if (site.id == id) { @@ -1586,7 +1586,7 @@ void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation) } // Operational building path. - for (Building& b : m_buildings) + for (Building& b : m_state.buildings) { if (b.id != id) { continue; } @@ -1643,7 +1643,7 @@ const Building* BuildingSystem::findNearestBuilding(QVector2D worldPos, { const Building* best = nullptr; float bestDist = std::numeric_limits::max(); - for (const Building& b : m_buildings) + for (const Building& b : m_state.buildings) { if (b.type != type) { @@ -1664,7 +1664,7 @@ const Building* BuildingSystem::findNearestBuilding(QVector2D worldPos, bool BuildingSystem::deliverScrapToSalvageBay(BuildingId bayId) { Building* bay = nullptr; - for (Building& b : m_buildings) + for (Building& b : m_state.buildings) { if (b.id == bayId) { @@ -1708,7 +1708,7 @@ BuildingId BuildingSystem::placeImmediate(BuildingType type, { const QPoint absCell = anchor + cell; building.bodyCells.push_back(absCell); - m_grid.occupy(absCell, id); + m_state.grid.occupy(absCell, id); } for (const Port& port : mask.outputPorts) { @@ -1726,14 +1726,14 @@ BuildingId BuildingSystem::placeImmediate(BuildingType type, initSalvageBayBuffer(building); } - m_buildings.push_back(std::move(building)); + m_state.buildings.push_back(std::move(building)); return id; } bool BuildingSystem::removeBuilding(BuildingId id) { - for (std::vector::iterator it = m_buildings.begin(); - it != m_buildings.end(); + for (std::vector::iterator it = m_state.buildings.begin(); + it != m_state.buildings.end(); ++it) { if (it->id == id) @@ -1743,8 +1743,8 @@ bool BuildingSystem::removeBuilding(BuildingId id) { m_belts.removeTile(it->anchor); } - m_grid.release(it->bodyCells); - m_buildings.erase(it); + m_state.grid.release(it->bodyCells); + m_state.buildings.erase(it); return true; } } @@ -1753,7 +1753,7 @@ bool BuildingSystem::removeBuilding(BuildingId id) void BuildingSystem::forEachBuilding(std::function fn) { - for (Building& b : m_buildings) + for (Building& b : m_state.buildings) { fn(b); } @@ -1762,12 +1762,12 @@ void BuildingSystem::forEachBuilding(std::function fn) void BuildingSystem::registerTileOccupancy(const std::vector& cells, BuildingId ownerPlaceholder) { - m_grid.occupy(cells, ownerPlaceholder); + m_state.grid.occupy(cells, ownerPlaceholder); } void BuildingSystem::unregisterTileOccupancy(const std::vector& cells) { - m_grid.release(cells); + m_state.grid.release(cells); } namespace @@ -1801,10 +1801,10 @@ void appendInputBuffer(Hasher& hasher, const InputBuffer& buffer) void BuildingSystem::appendChecksum(Hasher& hasher) const { - // m_buildings keeps a stable, deterministic order (append on build, swap-free + // m_state.buildings keeps a stable, deterministic order (append on build, swap-free // erase aside — both runs perform identical operations, so order matches). - hasher.append(m_buildings.size()); - for (const Building& b : m_buildings) + hasher.append(m_state.buildings.size()); + for (const Building& b : m_state.buildings) { hasher.append(b.id); hasher.append(b.anchor); @@ -1847,8 +1847,8 @@ void BuildingSystem::appendChecksum(Hasher& hasher) const hasher.append(b.queuedForDeconstruction); } - hasher.append(m_constructionQueue.size()); - for (const ConstructionSite& s : m_constructionQueue) + hasher.append(m_state.constructionQueue.size()); + for (const ConstructionSite& s : m_state.constructionQueue) { hasher.append(s.id); hasher.append(s.anchor); @@ -1865,8 +1865,8 @@ void BuildingSystem::appendChecksum(Hasher& hasher) const for (const ItemType& type : s.splitterFilterB) { hasher.append(type.id); } } - hasher.append(m_deconstructionQueue.size()); - for (const DeconstructionEntry& e : m_deconstructionQueue) + hasher.append(m_state.deconstructionQueue.size()); + for (const DeconstructionEntry& e : m_state.deconstructionQueue) { hasher.append(e.id); hasher.append(e.completesAt); @@ -1876,5 +1876,5 @@ void BuildingSystem::appendChecksum(Hasher& hasher) const for (const ItemType& type : e.splitterFilterB) { hasher.append(type.id); } } - m_grid.appendChecksum(hasher); + m_state.grid.appendChecksum(hasher); } diff --git a/src/lib/sim/BuildingSystem.h b/src/lib/sim/BuildingSystem.h index 88cc228..3a70f0c 100644 --- a/src/lib/sim/BuildingSystem.h +++ b/src/lib/sim/BuildingSystem.h @@ -15,7 +15,7 @@ #include "BeltSystem.h" #include "Building.h" -#include "BuildingGrid.h" +#include "FactoryState.h" #include "BuildingType.h" #include "BuildingId.h" #include "GameConfig.h" @@ -294,23 +294,8 @@ private: std::mt19937& m_rng; int m_asteroidWidth_tiles; - std::vector m_buildings; - std::deque m_constructionQueue; - - // 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; - }; - std::deque m_deconstructionQueue; - - // The authority on which building owns which tile; every placement and removal - // path claims and releases its body cells here. - BuildingGrid m_grid; + // The factory's world data — buildings, queued work, tile ownership. Held here + // for now; the intent is for Simulation to own it and pass it into the tick + // methods, leaving this system stateless over it (see FactoryState.h). + FactoryState m_state; }; diff --git a/src/lib/sim/CMakeLists.txt b/src/lib/sim/CMakeLists.txt index c0b8fcd..6c2f0ed 100644 --- a/src/lib/sim/CMakeLists.txt +++ b/src/lib/sim/CMakeLists.txt @@ -13,6 +13,7 @@ SET(HDRS ${CMAKE_CURRENT_SOURCE_DIR}/Building.h ${CMAKE_CURRENT_SOURCE_DIR}/BuildingConfig.h ${CMAKE_CURRENT_SOURCE_DIR}/BuildingGrid.h + ${CMAKE_CURRENT_SOURCE_DIR}/FactoryState.h ${CMAKE_CURRENT_SOURCE_DIR}/BuildingSystem.h ${CMAKE_CURRENT_SOURCE_DIR}/EntityHitTest.h ${CMAKE_CURRENT_SOURCE_DIR}/ShipLayout.h diff --git a/src/lib/sim/FactoryState.h b/src/lib/sim/FactoryState.h new file mode 100644 index 0000000..3624076 --- /dev/null +++ b/src/lib/sim/FactoryState.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include "Building.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 BuildingSystem for now. The intent is to hand ownership to Simulation +// and pass this into the tick methods, so the systems become stateless over it. +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; +};