getAllDebrisInfo and collectOne only ever touched EntityAdmin — DebrisSystem
holds nothing else — so they become free functions over the registry. That lets
AiSystem, SalvagerSystem and SalvageScrapEvaluator drop their DebrisSystem&
parameters entirely; SalvagerSystem already held the admin, and the other two
were handed it alongside.
No system in lib/ecs/system takes another system now. Every tick signature names
the data it works on: the registry, the factory state, or both.
DebrisSystem keeps spawn, tickDespawn and consume — the first two are genuine
tick behaviour rather than lookups.
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
The member reference is gone. All 23 methods that read or write the factory now
take FactoryState& (const for the two item walks and the checksum fold), so a
BuildingSystem is no longer bound to one state and its signatures say which data
each call touches. It holds only config, belts, rng and the callbacks — the same
shape as AiSystem and CombatSystem.
This completes what phase 2 set out to do; the ownership move landed earlier, but
the systems kept reaching the data through a member until the queries were off
them.
Seeding the asteroid bound moved with the state, and that broke four tests: the
fixtures build their own FactoryState, which defaulted the bound to 0 and refused
every placement on the asteroid. Rather than fix the four call sites, makeFactoryState()
now creates a run's state from the config, and Simulation, ArenaSimulation and the
test fixtures all use it — there is one place that knows what a fresh factory
looks like.
Verified with a golden-checksum capture before and after — all four sample ticks
identical — and by re-running the declaration/definition check over the header.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
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
The parameter was already commented out in the definition — combat resolution
never touched it. Removing it also removes the last reference to BuildingSystem
from CombatSystem, so the forward declaration goes too.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
Ten queries that read nothing but FactoryState become free functions in
FactoryQueries.h; the BuildingSystem methods stay as one-line forwards, so no
existing caller moves yet.
That lets the AI path drop its dependency on the system entirely. AiSystem,
SalvagerSystem, DeliverScrapEvaluator and DeliverScrapExecutor took a
BuildingSystem& purely to call findBuilding, findNearestBuilding and
deliverScrapToSalvageBay — all three are state-pure — so they now take
FactoryState& and say what they actually read. Four forward declarations of
BuildingSystem go with them.
No facade: the queries are plain free functions over the data. A facade was
considered to spare the ~180 UI call sites, but the AI needed only the data and
would have been given GameConfig it has no use for.
isProductionBuildingType moves to BuildingType.h beside isAutoRecipeBuildingType
and isBeltSubsystemType rather than being copied into the new file.
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
Simulation (and ArenaSimulation in the balancing tool) now owns the factory's
world data; BuildingSystem holds a reference to it. This is what lets the systems
that operate on the data be handed the same state — phase 3's construction and
deconstruction systems, and later the ecs/system/ classes that today take a
BuildingSystem& only to query it.
reset() clears the state alongside m_admin and m_beltSystem, matching how the
subsystems were already rebuilt from scratch.
Falls short of the tick-argument form I sketched: BuildingSystem still reaches
the data through a member reference rather than a parameter. Making it truly
stateless means the const query surface has to find the data some other way, and
that surface is large — findBuilding alone has 64 call sites, with findSite,
getAllBuildings, getAllSites, isTileOccupied and the rest behind it. Doing that
needs a queries facade behind Simulation::getBuildings() so the callers do not
all move, which is its own decision rather than a side effect of this one.
The constructor gains a parameter, so the four owners and the three test fixtures
that build a BuildingSystem directly are updated; the 33 files that only use one
are untouched.
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