migrate every factory query off BuildingSystem onto the free functions

The eleven forwarding members added last commit are gone; callers now read the
data directly through FactoryQueries.h. Simulation and ArenaSimulation expose
getFactoryState() so the UI, the balancing view and the tests can reach it.

isQueuedForDeconstruction joined the free functions along the way — it only
reaches findBuilding, so it was state-pure too.

No facade was introduced. The chained form was the reason one looked attractive,
but rewriting sim.getBuildings().findBuilding(id) to findBuilding(sim.getFactoryState(), id)
turned out to be mechanical, and the result says which data is read rather than
which system happens to own it.

BuildingSystem.cpp is down to 1735 lines and no longer answers questions about
the factory — it only changes it. What remains on it are the mutators, the tick
phases, and the queries that also need GameConfig.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
This commit is contained in:
2026-08-04 21:52:49 +02:00
parent 58e173ad5b
commit df7f60c898
20 changed files with 212 additions and 241 deletions

View File

@@ -1,4 +1,5 @@
#include "BuildingConfig.h"
#include "FactoryQueries.h"
#include <algorithm>
#include <climits>
@@ -26,8 +27,8 @@ struct SelectedBuilding
// (the HQ and defence stations, per REQ-UI-BLUEPRINT-CREATE).
std::optional<SelectedBuilding> resolvePlaceable(const Simulation& sim, BuildingId id)
{
const Building* building = sim.getBuildings().findBuilding(id);
const ConstructionSite* site = building ? nullptr : sim.getBuildings().findSite(id);
const Building* building = findBuilding(sim.getFactoryState(), id);
const ConstructionSite* site = building ? nullptr : findSite(sim.getFactoryState(), id);
if (!building && !site)
{
return std::nullopt;
@@ -52,8 +53,8 @@ std::optional<SelectedBuilding> resolvePlaceable(const Simulation& sim, Building
std::optional<BuildingConfig> readBuildingConfig(const Simulation& sim, BuildingId id)
{
const Building* building = sim.getBuildings().findBuilding(id);
const ConstructionSite* site = building ? nullptr : sim.getBuildings().findSite(id);
const Building* building = findBuilding(sim.getFactoryState(), id);
const ConstructionSite* site = building ? nullptr : findSite(sim.getFactoryState(), id);
if (!building && !site)
{
return std::nullopt;

View File

@@ -241,11 +241,11 @@ std::vector<Port> BuildingSystem::computeInputPorts(
std::vector<Port> BuildingSystem::getInputPorts(BuildingId id) const
{
if (const Building* building = findBuilding(id))
if (const Building* building = findBuilding(m_state, id))
{
return building->inputPorts;
}
if (const ConstructionSite* site = findSite(id))
if (const ConstructionSite* site = findSite(m_state, id))
{
// A site stores no ports; derive its output ports from the mask (absolute)
// and run the same input-edge scan (REQ-BLD-BELT-DRAG, REQ-MAT-INPUT-PORTS).
@@ -825,7 +825,7 @@ void BuildingSystem::cancelDeconstruction(BuildingId id)
// Resume operation: clear the flag and re-register belt/tunnel/splitter
// tiles that were unregistered at enqueue (which re-pairs tunnels,
// REQ-BLD-TUNNEL-PAIR). Deconstruction progress is discarded; no refund.
if (Building* building = findBuildingMutable(id))
if (Building* building = findBuilding(m_state, id))
{
building->queuedForDeconstruction = false;
reregisterBeltTile(*building, it->splitterFilterA, it->splitterFilterB);
@@ -838,12 +838,6 @@ void BuildingSystem::cancelDeconstruction(BuildingId id)
}
}
bool BuildingSystem::isQueuedForDeconstruction(BuildingId id) const
{
const Building* building = findBuilding(id);
return building && building->queuedForDeconstruction;
}
void BuildingSystem::tickBeltPull()
{
TRACE();
@@ -941,7 +935,7 @@ bool BuildingSystem::tryDirectCoupleDeposit(BuildingId producerId,
return false;
}
Building* consumer = findBuildingMutable(*ownerId);
Building* consumer = findBuilding(m_state, *ownerId);
if (!consumer)
{
return false; // an unbuilt construction site, or not an operational building
@@ -1263,41 +1257,6 @@ void BuildingSystem::forEachIncomingItem(
// Queries
// ---------------------------------------------------------------------------
const Building* BuildingSystem::findBuilding(BuildingId id) const
{
return ::findBuilding(m_state, id);
}
Building* BuildingSystem::findBuildingMutable(BuildingId id)
{
return ::findBuilding(m_state, id);
}
const ConstructionSite* BuildingSystem::findSite(BuildingId id) const
{
return ::findSite(m_state, id);
}
std::vector<Building> BuildingSystem::getAllBuildings() const
{
return ::getAllBuildings(m_state);
}
std::vector<ConstructionSite> BuildingSystem::getAllSites() const
{
return ::getAllSites(m_state);
}
int BuildingSystem::getProductionBuildingCount() const
{
return ::getProductionBuildingCount(m_state);
}
int BuildingSystem::getActiveProductionBuildingCount() const
{
return ::getActiveProductionBuildingCount(m_state);
}
std::vector<const RecipeDef*>
BuildingSystem::gatherCandidateRecipes(const Building& b) const
{
@@ -1471,11 +1430,6 @@ std::vector<BuildingSystem::BeltTileInfo> BuildingSystem::getAllBeltTiles() cons
return result;
}
bool BuildingSystem::isTileOccupied(QPoint tile) const
{
return ::isTileOccupied(m_state, tile);
}
std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
BuildingType type, QPoint anchor, Rotation rot) const
{
@@ -1591,17 +1545,6 @@ void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation)
}
}
const Building* BuildingSystem::findNearestBuilding(QVector2D worldPos,
BuildingType type) const
{
return ::findNearestBuilding(m_state, worldPos, type);
}
bool BuildingSystem::deliverScrapToSalvageBay(BuildingId bayId)
{
return ::deliverScrapToSalvageBay(m_state, bayId);
}
BuildingId BuildingSystem::placeImmediate(BuildingType type,
const std::vector<std::string>& surfaceMask,
QPoint anchor, Rotation rotation)

View File

@@ -94,7 +94,6 @@ public:
void cancelDeconstruction(BuildingId id);
// True if the building is currently in the deconstruction queue.
bool isQueuedForDeconstruction(BuildingId id) const;
// Set the recipe (or schematic id for shipyard) on a building or queued
// construction site. Clears both buffers on an operational building.
@@ -140,18 +139,12 @@ public:
Rotation directionB; // Splitter: second output; Belt: same as directionA
};
const Building* findBuilding(BuildingId id) const;
const ConstructionSite* findSite(BuildingId id) const;
std::vector<Building> getAllBuildings() const;
std::vector<ConstructionSite> getAllSites() const;
// REQ-UI-DEBUG-OVERLAY "Max Factory Production": count of completed
// (operational) Miner/Smelter/Assembler/ReprocessingPlant/Shipyard buildings.
int getProductionBuildingCount() const;
// REQ-UI-DEBUG-OVERLAY "Current Factory Production": subset of the above
// that currently has an active production cycle.
int getActiveProductionBuildingCount() const;
// Production state for the UI status light (REQ-UI-STATUS-LIGHT). Returns
// nullopt for building types that show no light (belts, splitters, tunnels,
@@ -201,7 +194,6 @@ public:
// 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).
@@ -234,7 +226,6 @@ private:
const std::vector<ItemType>& splitterFilterA,
const std::vector<ItemType>& splitterFilterB);
Building* findBuildingMutable(BuildingId id);
// True if the consumer would accept `type` at the given input port right now:
// it is a required input (or a building block for the HQ), the reservation-aware
// buffer has room, and the input belt entry is free (REQ-MAT-INPUT-INTAKE).

View File

@@ -77,6 +77,12 @@ bool isTileOccupied(const FactoryState& state, QPoint tile)
return state.grid.isOccupied(tile);
}
bool isQueuedForDeconstruction(const FactoryState& state, BuildingId id)
{
const Building* building = findBuilding(state, id);
return building && building->queuedForDeconstruction;
}
const Building* findNearestBuilding(const FactoryState& state, QVector2D worldPos,
BuildingType type)
{

View File

@@ -39,6 +39,9 @@ int getActiveProductionBuildingCount(const FactoryState& state);
bool isTileOccupied(const FactoryState& state, QPoint tile);
// True while the building is in the deconstruction queue (REQ-BLD-DECON-QUEUE).
bool isQueuedForDeconstruction(const FactoryState& state, BuildingId id);
// The nearest building of the given type to a world position, or nullptr when
// none exists. Distance is measured to the building's footprint centre.
const Building* findNearestBuilding(const FactoryState& state, QVector2D worldPos,

View File

@@ -1,5 +1,7 @@
#include "Simulation.h"
#include "FactoryQueries.h"
#include <algorithm>
#include <cassert>
#include <cmath>
@@ -811,12 +813,12 @@ double Simulation::getThreatAccumulationRate() const
double Simulation::getMaxFactoryProductionThreatRate() const
{
return static_cast<double>(m_buildingSystem->getProductionBuildingCount());
return static_cast<double>(getProductionBuildingCount(m_factoryState));
}
double Simulation::getCurrentFactoryProductionThreatRate() const
{
return static_cast<double>(m_buildingSystem->getActiveProductionBuildingCount());
return static_cast<double>(getActiveProductionBuildingCount(m_factoryState));
}
int Simulation::getBossWaveCounter() const
@@ -895,6 +897,11 @@ BuildingSystem& Simulation::getBuildingsMutable()
return *m_buildingSystem;
}
const FactoryState& Simulation::getFactoryState() const
{
return m_factoryState;
}
const BuildingSystem& Simulation::getBuildings() const
{
return *m_buildingSystem;

View File

@@ -119,6 +119,9 @@ public:
// chokepoint) or, in tests, SimulationTestAccess — so production code cannot
// mutate the factory outside the recorded command path (docs/replay_design.md).
const BuildingSystem& getBuildings() const;
// The factory's world data, for the free queries in FactoryQueries.h.
const FactoryState& getFactoryState() const;
const BeltSystem& getBelts() const;
ShipSystem& getShips();
const ShipSystem& getShips() const;