gather the factory's world data into FactoryState

This commit is contained in:
2026-08-05 06:43:30 +02:00
parent 60cc187d92
commit 0edea5d961
4 changed files with 128 additions and 97 deletions

View File

@@ -308,7 +308,7 @@ std::optional<BuildingId> BuildingSystem::place(BuildingType type, QPoint anchor
for (const QPoint& cell : mask.bodyCells) for (const QPoint& cell : mask.bodyCells)
{ {
const QPoint absCell = anchor + cell; const QPoint absCell = anchor + cell;
m_grid.occupy(absCell, id); m_state.grid.occupy(absCell, id);
} }
// Build construction site. // Build construction site.
@@ -323,13 +323,13 @@ std::optional<BuildingId> BuildingSystem::place(BuildingType type, QPoint anchor
site.bodyCells.push_back(anchor + cell); site.bodyCells.push_back(anchor + cell);
} }
if (m_constructionQueue.empty()) if (m_state.constructionQueue.empty())
{ {
site.completesAt = currentTick + secondsToTicks(def->constructionTimeSeconds); site.completesAt = currentTick + secondsToTicks(def->constructionTimeSeconds);
} }
// else: completesAt remains 0 (queued, not yet started). // 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; return id;
} }
@@ -405,15 +405,15 @@ int BuildingSystem::deconstruct(BuildingId id, Tick currentTick)
{ {
// Construction site? Removed instantly with the full refund; never queued // Construction site? Removed instantly with the full refund; never queued
// for deconstruction (REQ-BLD-DECONSTRUCT). // for deconstruction (REQ-BLD-DECONSTRUCT).
for (std::deque<ConstructionSite>::iterator it = m_constructionQueue.begin(); for (std::deque<ConstructionSite>::iterator it = m_state.constructionQueue.begin();
it != m_constructionQueue.end(); it != m_state.constructionQueue.end();
++it) ++it)
{ {
if (it->id == id) if (it->id == id)
{ {
const BuildingDef* def = m_config.buildings.findBuildingDef(it->type); const BuildingDef* def = m_config.buildings.findBuildingDef(it->type);
m_grid.release(it->bodyCells); m_state.grid.release(it->bodyCells);
m_constructionQueue.erase(it); m_state.constructionQueue.erase(it);
if (def) if (def)
{ {
return def->cost; return def->cost;
@@ -425,7 +425,7 @@ int BuildingSystem::deconstruct(BuildingId id, Tick currentTick)
// Operational building? Append it to the deconstruction queue rather than // Operational building? Append it to the deconstruction queue rather than
// removing it now; the partial refund is credited on completion in // removing it now; the partial refund is credited on completion in
// tickDeconstruction (REQ-BLD-DECON-QUEUE). // tickDeconstruction (REQ-BLD-DECON-QUEUE).
for (Building& building : m_buildings) for (Building& building : m_state.buildings)
{ {
if (building.id != id) { continue; } if (building.id != id) { continue; }
if (building.queuedForDeconstruction) { return 0; } // already queued if (building.queuedForDeconstruction) { return 0; } // already queued
@@ -453,8 +453,8 @@ int BuildingSystem::deconstruct(BuildingId id, Tick currentTick)
m_belts.removeTile(building.anchor); m_belts.removeTile(building.anchor);
} }
const bool wasEmpty = m_deconstructionQueue.empty(); const bool wasEmpty = m_state.deconstructionQueue.empty();
m_deconstructionQueue.push_back(std::move(entry)); m_state.deconstructionQueue.push_back(std::move(entry));
if (wasEmpty) if (wasEmpty)
{ {
startFrontDeconstruction(currentTick); startFrontDeconstruction(currentTick);
@@ -467,8 +467,8 @@ int BuildingSystem::deconstruct(BuildingId id, Tick currentTick)
void BuildingSystem::startFrontDeconstruction(Tick currentTick) void BuildingSystem::startFrontDeconstruction(Tick currentTick)
{ {
if (m_deconstructionQueue.empty()) { return; } if (m_state.deconstructionQueue.empty()) { return; }
DeconstructionEntry& front = m_deconstructionQueue.front(); DeconstructionEntry& front = m_state.deconstructionQueue.front();
if (front.completesAt == 0) if (front.completesAt == 0)
{ {
front.completesAt = front.completesAt =
@@ -483,7 +483,7 @@ void BuildingSystem::startFrontDeconstruction(Tick currentTick)
void BuildingSystem::setRecipe(BuildingId id, const std::string& recipeId) void BuildingSystem::setRecipe(BuildingId id, const std::string& recipeId)
{ {
// Construction site: store recipe for when building completes. // Construction site: store recipe for when building completes.
for (ConstructionSite& site : m_constructionQueue) for (ConstructionSite& site : m_state.constructionQueue)
{ {
if (site.id == id) if (site.id == id)
{ {
@@ -506,7 +506,7 @@ void BuildingSystem::setRecipe(BuildingId id, const std::string& recipeId)
} }
// Operational building: clear buffers and re-init. // Operational building: clear buffers and re-init.
for (Building& building : m_buildings) for (Building& building : m_state.buildings)
{ {
if (building.id == id) 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) void BuildingSystem::setShipLayout(BuildingId id, const ShipLayoutConfig& layout)
{ {
for (ConstructionSite& site : m_constructionQueue) for (ConstructionSite& site : m_state.constructionQueue)
{ {
if (site.id == id) 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) if (building.id == id)
{ {
@@ -594,7 +594,7 @@ void BuildingSystem::setShipLayout(BuildingId id, const ShipLayoutConfig& layout
std::optional<BeltSystem::SplitterInfo> std::optional<BeltSystem::SplitterInfo>
BuildingSystem::getSiteSplitterInfo(BuildingId id) const 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.id != id) { continue; }
if (site.type != BuildingType::Splitter) { return std::nullopt; } if (site.type != BuildingType::Splitter) { return std::nullopt; }
@@ -618,7 +618,7 @@ void BuildingSystem::setSiteSplitterFilters(BuildingId id,
const std::vector<ItemType>& filterA, const std::vector<ItemType>& filterA,
const std::vector<ItemType>& filterB) const std::vector<ItemType>& filterB)
{ {
for (ConstructionSite& site : m_constructionQueue) for (ConstructionSite& site : m_state.constructionQueue)
{ {
if (site.id == id && site.type == BuildingType::Splitter) if (site.id == id && site.type == BuildingType::Splitter)
{ {
@@ -636,12 +636,12 @@ void BuildingSystem::setSiteSplitterFilters(BuildingId id,
void BuildingSystem::tickConstruction(Tick currentTick) void BuildingSystem::tickConstruction(Tick currentTick)
{ {
TRACE(); TRACE();
if (m_constructionQueue.empty()) if (m_state.constructionQueue.empty())
{ {
return; return;
} }
ConstructionSite& front = m_constructionQueue.front(); ConstructionSite& front = m_state.constructionQueue.front();
// Guard: if somehow the front site was never started, start it now. // Guard: if somehow the front site was never started, start it now.
if (front.completesAt == 0) if (front.completesAt == 0)
@@ -719,18 +719,18 @@ void BuildingSystem::tickConstruction(Tick currentTick)
// filters configured while under construction carry over (REQ-BLD-SITE-CONFIG). // filters configured while under construction carry over (REQ-BLD-SITE-CONFIG).
reregisterBeltTile(building, front.splitterFilterA, front.splitterFilterB); 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. // 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 = const BuildingDef* nextDef =
m_config.buildings.findBuildingDef(m_constructionQueue.front().type); m_config.buildings.findBuildingDef(m_state.constructionQueue.front().type);
if (nextDef) if (nextDef)
{ {
m_constructionQueue.front().completesAt = m_state.constructionQueue.front().completesAt =
currentTick + secondsToTicks(nextDef->constructionTimeSeconds); currentTick + secondsToTicks(nextDef->constructionTimeSeconds);
} }
} }
@@ -767,12 +767,12 @@ void BuildingSystem::reregisterBeltTile(const Building& building,
void BuildingSystem::tickDeconstruction(Tick currentTick) void BuildingSystem::tickDeconstruction(Tick currentTick)
{ {
TRACE(); TRACE();
if (m_deconstructionQueue.empty()) if (m_state.deconstructionQueue.empty())
{ {
return; 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. // Guard: if the front entry's timer was never started, start it now.
if (front.completesAt == 0) 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). // Remove the building from the world and credit its refund (REQ-BLD-DECONSTRUCT).
// Belt/tunnel/splitter tiles were already unregistered when the building was // Belt/tunnel/splitter tiles were already unregistered when the building was
// queued (see deconstruct), so only tile occupancy and the record remain. // queued (see deconstruct), so only tile occupancy and the record remain.
for (std::vector<Building>::iterator it = m_buildings.begin(); for (std::vector<Building>::iterator it = m_state.buildings.begin();
it != m_buildings.end(); it != m_state.buildings.end();
++it) ++it)
{ {
if (it->id != front.id) { continue; } if (it->id != front.id) { continue; }
const BuildingDef* def = m_config.buildings.findBuildingDef(it->type); const BuildingDef* def = m_config.buildings.findBuildingDef(it->type);
m_grid.release(it->bodyCells); m_state.grid.release(it->bodyCells);
m_buildings.erase(it); m_state.buildings.erase(it);
if (def) if (def)
{ {
m_addBuildingBlocks(def->cost * m_config.world.refundPercentage / 100); m_addBuildingBlocks(def->cost * m_config.world.refundPercentage / 100);
@@ -805,7 +805,7 @@ void BuildingSystem::tickDeconstruction(Tick currentTick)
break; break;
} }
m_deconstructionQueue.pop_front(); m_state.deconstructionQueue.pop_front();
// Start the next queued deconstruction, if any. // Start the next queued deconstruction, if any.
startFrontDeconstruction(currentTick); startFrontDeconstruction(currentTick);
@@ -813,8 +813,8 @@ void BuildingSystem::tickDeconstruction(Tick currentTick)
void BuildingSystem::cancelDeconstruction(BuildingId id) void BuildingSystem::cancelDeconstruction(BuildingId id)
{ {
for (std::deque<DeconstructionEntry>::iterator it = m_deconstructionQueue.begin(); for (std::deque<DeconstructionEntry>::iterator it = m_state.deconstructionQueue.begin();
it != m_deconstructionQueue.end(); it != m_state.deconstructionQueue.end();
++it) ++it)
{ {
if (it->id != id) { continue; } if (it->id != id) { continue; }
@@ -828,7 +828,7 @@ void BuildingSystem::cancelDeconstruction(BuildingId id)
reregisterBeltTile(*building, it->splitterFilterA, it->splitterFilterB); 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 // If the running front was removed, the new front (completesAt == 0) has
// its timer started by the next tickDeconstruction guard. // its timer started by the next tickDeconstruction guard.
return; return;
@@ -848,7 +848,7 @@ void BuildingSystem::tickBeltPull()
// (REQ-GW-BELT-SPEED, REQ-MAT-INPUT-INTAKE). // (REQ-GW-BELT-SPEED, REQ-MAT-INPUT-INTAKE).
const double progressPerTick = m_belts.getProgressPerTick_tpt(); 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). // A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE).
if (building.queuedForDeconstruction) { continue; } if (building.queuedForDeconstruction) { continue; }
@@ -932,7 +932,7 @@ bool BuildingSystem::tryDirectCoupleDeposit(BuildingId producerId,
const Port& outputPort, const Port& outputPort,
const Item& item) const Item& item)
{ {
const std::optional<BuildingId> ownerId = m_grid.findOwner(outputPort.tile); const std::optional<BuildingId> ownerId = m_state.grid.findOwner(outputPort.tile);
if (!ownerId.has_value() || *ownerId == producerId) if (!ownerId.has_value() || *ownerId == producerId)
{ {
return false; return false;
@@ -966,7 +966,7 @@ bool BuildingSystem::tryDirectCoupleDeposit(BuildingId producerId,
void BuildingSystem::tickProduction(Tick currentTick) void BuildingSystem::tickProduction(Tick currentTick)
{ {
TRACE(); TRACE();
for (Building& building : m_buildings) for (Building& building : m_state.buildings)
{ {
// A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE). // A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE).
if (building.queuedForDeconstruction) { continue; } if (building.queuedForDeconstruction) { continue; }
@@ -1069,7 +1069,7 @@ void BuildingSystem::tickProduction(Tick currentTick)
void BuildingSystem::tickShipyardProduction(Tick currentTick) void BuildingSystem::tickShipyardProduction(Tick currentTick)
{ {
TRACE(); TRACE();
for (Building& building : m_buildings) for (Building& building : m_state.buildings)
{ {
// A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE). // A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE).
if (building.queuedForDeconstruction) { continue; } if (building.queuedForDeconstruction) { continue; }
@@ -1169,7 +1169,7 @@ void BuildingSystem::tickOutputBelts()
// same speed as real belts (REQ-GW-BELT-SPEED, REQ-MAT-OUTPUT-EMERGE). // same speed as real belts (REQ-GW-BELT-SPEED, REQ-MAT-OUTPUT-EMERGE).
const double progressPerTick = m_belts.getProgressPerTick_tpt(); 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). // A building queued for deconstruction stops operating (REQ-BLD-DECON-QUEUE).
if (building.queuedForDeconstruction) { continue; } if (building.queuedForDeconstruction) { continue; }
@@ -1215,7 +1215,7 @@ void BuildingSystem::tickOutputBelts()
void BuildingSystem::forEachEmergingItem( void BuildingSystem::forEachEmergingItem(
const std::function<void(const ItemType&, QPointF)>& visit) const const std::function<void(const ItemType&, QPointF)>& 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) for (std::size_t p = 0; p < building.outputPorts.size(); ++p)
{ {
@@ -1237,7 +1237,7 @@ void BuildingSystem::forEachEmergingItem(
void BuildingSystem::forEachIncomingItem( void BuildingSystem::forEachIncomingItem(
const std::function<void(const ItemType&, QPointF)>& visit) const const std::function<void(const ItemType&, QPointF)>& 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) 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 const Building* BuildingSystem::findBuilding(BuildingId id) const
{ {
for (const Building& building : m_buildings) for (const Building& building : m_state.buildings)
{ {
if (building.id == id) if (building.id == id)
{ {
@@ -1274,7 +1274,7 @@ const Building* BuildingSystem::findBuilding(BuildingId id) const
Building* BuildingSystem::findBuildingMutable(BuildingId id) Building* BuildingSystem::findBuildingMutable(BuildingId id)
{ {
for (Building& building : m_buildings) for (Building& building : m_state.buildings)
{ {
if (building.id == id) if (building.id == id)
{ {
@@ -1286,7 +1286,7 @@ Building* BuildingSystem::findBuildingMutable(BuildingId id)
const ConstructionSite* BuildingSystem::findSite(BuildingId id) const const ConstructionSite* BuildingSystem::findSite(BuildingId id) const
{ {
for (const ConstructionSite& site : m_constructionQueue) for (const ConstructionSite& site : m_state.constructionQueue)
{ {
if (site.id == id) if (site.id == id)
{ {
@@ -1298,13 +1298,13 @@ const ConstructionSite* BuildingSystem::findSite(BuildingId id) const
std::vector<Building> BuildingSystem::getAllBuildings() const std::vector<Building> BuildingSystem::getAllBuildings() const
{ {
return m_buildings; return m_state.buildings;
} }
std::vector<ConstructionSite> BuildingSystem::getAllSites() const std::vector<ConstructionSite> BuildingSystem::getAllSites() const
{ {
return std::vector<ConstructionSite>(m_constructionQueue.begin(), return std::vector<ConstructionSite>(m_state.constructionQueue.begin(),
m_constructionQueue.end()); m_state.constructionQueue.end());
} }
namespace namespace
@@ -1328,7 +1328,7 @@ bool isProductionBuildingType(BuildingType type)
int BuildingSystem::getProductionBuildingCount() const int BuildingSystem::getProductionBuildingCount() const
{ {
int count = 0; int count = 0;
for (const Building& b : m_buildings) for (const Building& b : m_state.buildings)
{ {
if (isProductionBuildingType(b.type)) { ++count; } if (isProductionBuildingType(b.type)) { ++count; }
} }
@@ -1338,7 +1338,7 @@ int BuildingSystem::getProductionBuildingCount() const
int BuildingSystem::getActiveProductionBuildingCount() const int BuildingSystem::getActiveProductionBuildingCount() const
{ {
int count = 0; 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; } if (isProductionBuildingType(b.type) && b.production.has_value()) { ++count; }
} }
@@ -1489,7 +1489,7 @@ BuildingSystem::getProductionStatus(const Building& building) const
std::vector<BuildingSystem::BeltTileInfo> BuildingSystem::getAllBeltTiles() const std::vector<BuildingSystem::BeltTileInfo> BuildingSystem::getAllBeltTiles() const
{ {
std::vector<BeltTileInfo> result; std::vector<BeltTileInfo> result;
for (const Building& b : m_buildings) for (const Building& b : m_state.buildings)
{ {
if (b.type != BuildingType::Belt && b.type != BuildingType::Splitter) if (b.type != BuildingType::Belt && b.type != BuildingType::Splitter)
{ {
@@ -1520,7 +1520,7 @@ std::vector<BuildingSystem::BeltTileInfo> BuildingSystem::getAllBeltTiles() cons
bool BuildingSystem::isTileOccupied(QPoint tile) const bool BuildingSystem::isTileOccupied(QPoint tile) const
{ {
return m_grid.isOccupied(tile); return m_state.grid.isOccupied(tile);
} }
std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget( std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
@@ -1541,13 +1541,13 @@ std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
// All body cells must be occupied by the same entity. // All body cells must be occupied by the same entity.
const QPoint firstAbs = anchor + mask.bodyCells[0]; const QPoint firstAbs = anchor + mask.bodyCells[0];
const std::optional<BuildingId> firstOwner = m_grid.findOwner(firstAbs); const std::optional<BuildingId> firstOwner = m_state.grid.findOwner(firstAbs);
if (!firstOwner.has_value()) { return std::nullopt; } if (!firstOwner.has_value()) { return std::nullopt; }
const BuildingId candidateId = *firstOwner; const BuildingId candidateId = *firstOwner;
for (const QPoint& rel : mask.bodyCells) for (const QPoint& rel : mask.bodyCells)
{ {
const std::optional<BuildingId> owner = m_grid.findOwner(anchor + rel); const std::optional<BuildingId> owner = m_state.grid.findOwner(anchor + rel);
if (!owner.has_value() || *owner != candidateId) if (!owner.has_value() || *owner != candidateId)
{ {
return std::nullopt; return std::nullopt;
@@ -1555,14 +1555,14 @@ std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
} }
// Verify the candidate is the same building type with the same cell count. // 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.id != candidateId) { continue; }
if (site.type != type) { return std::nullopt; } if (site.type != type) { return std::nullopt; }
if (site.bodyCells.size() != mask.bodyCells.size()) { return std::nullopt; } if (site.bodyCells.size() != mask.bodyCells.size()) { return std::nullopt; }
return candidateId; return candidateId;
} }
for (const Building& b : m_buildings) for (const Building& b : m_state.buildings)
{ {
if (b.id != candidateId) { continue; } if (b.id != candidateId) { continue; }
if (b.type != type) { return std::nullopt; } if (b.type != type) { return std::nullopt; }
@@ -1576,7 +1576,7 @@ std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation) void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation)
{ {
// Construction site path — just update rotation; no ports to recompute. // 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) if (site.id == id)
{ {
@@ -1586,7 +1586,7 @@ void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation)
} }
// Operational building path. // Operational building path.
for (Building& b : m_buildings) for (Building& b : m_state.buildings)
{ {
if (b.id != id) { continue; } if (b.id != id) { continue; }
@@ -1643,7 +1643,7 @@ const Building* BuildingSystem::findNearestBuilding(QVector2D worldPos,
{ {
const Building* best = nullptr; const Building* best = nullptr;
float bestDist = std::numeric_limits<float>::max(); float bestDist = std::numeric_limits<float>::max();
for (const Building& b : m_buildings) for (const Building& b : m_state.buildings)
{ {
if (b.type != type) if (b.type != type)
{ {
@@ -1664,7 +1664,7 @@ const Building* BuildingSystem::findNearestBuilding(QVector2D worldPos,
bool BuildingSystem::deliverScrapToSalvageBay(BuildingId bayId) bool BuildingSystem::deliverScrapToSalvageBay(BuildingId bayId)
{ {
Building* bay = nullptr; Building* bay = nullptr;
for (Building& b : m_buildings) for (Building& b : m_state.buildings)
{ {
if (b.id == bayId) if (b.id == bayId)
{ {
@@ -1708,7 +1708,7 @@ BuildingId BuildingSystem::placeImmediate(BuildingType type,
{ {
const QPoint absCell = anchor + cell; const QPoint absCell = anchor + cell;
building.bodyCells.push_back(absCell); building.bodyCells.push_back(absCell);
m_grid.occupy(absCell, id); m_state.grid.occupy(absCell, id);
} }
for (const Port& port : mask.outputPorts) for (const Port& port : mask.outputPorts)
{ {
@@ -1726,14 +1726,14 @@ BuildingId BuildingSystem::placeImmediate(BuildingType type,
initSalvageBayBuffer(building); initSalvageBayBuffer(building);
} }
m_buildings.push_back(std::move(building)); m_state.buildings.push_back(std::move(building));
return id; return id;
} }
bool BuildingSystem::removeBuilding(BuildingId id) bool BuildingSystem::removeBuilding(BuildingId id)
{ {
for (std::vector<Building>::iterator it = m_buildings.begin(); for (std::vector<Building>::iterator it = m_state.buildings.begin();
it != m_buildings.end(); it != m_state.buildings.end();
++it) ++it)
{ {
if (it->id == id) if (it->id == id)
@@ -1743,8 +1743,8 @@ bool BuildingSystem::removeBuilding(BuildingId id)
{ {
m_belts.removeTile(it->anchor); m_belts.removeTile(it->anchor);
} }
m_grid.release(it->bodyCells); m_state.grid.release(it->bodyCells);
m_buildings.erase(it); m_state.buildings.erase(it);
return true; return true;
} }
} }
@@ -1753,7 +1753,7 @@ bool BuildingSystem::removeBuilding(BuildingId id)
void BuildingSystem::forEachBuilding(std::function<void(Building&)> fn) void BuildingSystem::forEachBuilding(std::function<void(Building&)> fn)
{ {
for (Building& b : m_buildings) for (Building& b : m_state.buildings)
{ {
fn(b); fn(b);
} }
@@ -1762,12 +1762,12 @@ void BuildingSystem::forEachBuilding(std::function<void(Building&)> fn)
void BuildingSystem::registerTileOccupancy(const std::vector<QPoint>& cells, void BuildingSystem::registerTileOccupancy(const std::vector<QPoint>& cells,
BuildingId ownerPlaceholder) BuildingId ownerPlaceholder)
{ {
m_grid.occupy(cells, ownerPlaceholder); m_state.grid.occupy(cells, ownerPlaceholder);
} }
void BuildingSystem::unregisterTileOccupancy(const std::vector<QPoint>& cells) void BuildingSystem::unregisterTileOccupancy(const std::vector<QPoint>& cells)
{ {
m_grid.release(cells); m_state.grid.release(cells);
} }
namespace namespace
@@ -1801,10 +1801,10 @@ void appendInputBuffer(Hasher& hasher, const InputBuffer& buffer)
void BuildingSystem::appendChecksum(Hasher& hasher) const 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). // erase aside — both runs perform identical operations, so order matches).
hasher.append(m_buildings.size()); hasher.append(m_state.buildings.size());
for (const Building& b : m_buildings) for (const Building& b : m_state.buildings)
{ {
hasher.append(b.id); hasher.append(b.id);
hasher.append(b.anchor); hasher.append(b.anchor);
@@ -1847,8 +1847,8 @@ void BuildingSystem::appendChecksum(Hasher& hasher) const
hasher.append(b.queuedForDeconstruction); hasher.append(b.queuedForDeconstruction);
} }
hasher.append(m_constructionQueue.size()); hasher.append(m_state.constructionQueue.size());
for (const ConstructionSite& s : m_constructionQueue) for (const ConstructionSite& s : m_state.constructionQueue)
{ {
hasher.append(s.id); hasher.append(s.id);
hasher.append(s.anchor); hasher.append(s.anchor);
@@ -1865,8 +1865,8 @@ void BuildingSystem::appendChecksum(Hasher& hasher) const
for (const ItemType& type : s.splitterFilterB) { hasher.append(type.id); } for (const ItemType& type : s.splitterFilterB) { hasher.append(type.id); }
} }
hasher.append(m_deconstructionQueue.size()); hasher.append(m_state.deconstructionQueue.size());
for (const DeconstructionEntry& e : m_deconstructionQueue) for (const DeconstructionEntry& e : m_state.deconstructionQueue)
{ {
hasher.append(e.id); hasher.append(e.id);
hasher.append(e.completesAt); hasher.append(e.completesAt);
@@ -1876,5 +1876,5 @@ void BuildingSystem::appendChecksum(Hasher& hasher) const
for (const ItemType& type : e.splitterFilterB) { hasher.append(type.id); } for (const ItemType& type : e.splitterFilterB) { hasher.append(type.id); }
} }
m_grid.appendChecksum(hasher); m_state.grid.appendChecksum(hasher);
} }

View File

@@ -15,7 +15,7 @@
#include "BeltSystem.h" #include "BeltSystem.h"
#include "Building.h" #include "Building.h"
#include "BuildingGrid.h" #include "FactoryState.h"
#include "BuildingType.h" #include "BuildingType.h"
#include "BuildingId.h" #include "BuildingId.h"
#include "GameConfig.h" #include "GameConfig.h"
@@ -294,23 +294,8 @@ private:
std::mt19937& m_rng; std::mt19937& m_rng;
int m_asteroidWidth_tiles; int m_asteroidWidth_tiles;
std::vector<Building> m_buildings; // The factory's world data — buildings, queued work, tile ownership. Held here
std::deque<ConstructionSite> m_constructionQueue; // 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).
// One pending demolition of a fully-built building (REQ-BLD-DECON-QUEUE). FactoryState m_state;
// 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<ItemType> splitterFilterA;
std::vector<ItemType> splitterFilterB;
};
std::deque<DeconstructionEntry> 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;
}; };

View File

@@ -13,6 +13,7 @@ SET(HDRS
${CMAKE_CURRENT_SOURCE_DIR}/Building.h ${CMAKE_CURRENT_SOURCE_DIR}/Building.h
${CMAKE_CURRENT_SOURCE_DIR}/BuildingConfig.h ${CMAKE_CURRENT_SOURCE_DIR}/BuildingConfig.h
${CMAKE_CURRENT_SOURCE_DIR}/BuildingGrid.h ${CMAKE_CURRENT_SOURCE_DIR}/BuildingGrid.h
${CMAKE_CURRENT_SOURCE_DIR}/FactoryState.h
${CMAKE_CURRENT_SOURCE_DIR}/BuildingSystem.h ${CMAKE_CURRENT_SOURCE_DIR}/BuildingSystem.h
${CMAKE_CURRENT_SOURCE_DIR}/EntityHitTest.h ${CMAKE_CURRENT_SOURCE_DIR}/EntityHitTest.h
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayout.h ${CMAKE_CURRENT_SOURCE_DIR}/ShipLayout.h

View File

@@ -0,0 +1,45 @@
#pragma once
#include <deque>
#include <vector>
#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<ItemType> splitterFilterA;
std::vector<ItemType> 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<Building> buildings;
std::deque<ConstructionSite> constructionQueue;
std::deque<DeconstructionEntry> deconstructionQueue;
// The authority on which building owns which tile; every placement and removal
// path claims and releases its body cells here.
BuildingGrid grid;
};