make BuildingSystem stateless: FactoryState becomes a parameter
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
This commit is contained in:
@@ -50,6 +50,7 @@ Simulation::Simulation(GameConfig config, unsigned int seed)
|
||||
{
|
||||
m_currentEnemyStationEntities[0] = entt::null;
|
||||
m_currentEnemyStationEntities[1] = entt::null;
|
||||
m_factoryState = makeFactoryState(m_config);
|
||||
|
||||
initializeSubsystems();
|
||||
|
||||
@@ -97,7 +98,7 @@ void Simulation::reset(unsigned int seed)
|
||||
m_pendingSchematicChoices.clear();
|
||||
|
||||
m_admin.clear();
|
||||
m_factoryState = FactoryState{};
|
||||
m_factoryState = makeFactoryState(m_config);
|
||||
m_beltSystem = BeltSystem(m_config.world.beltSpeed_tps);
|
||||
initializeSubsystems();
|
||||
|
||||
@@ -109,7 +110,6 @@ void Simulation::initializeSubsystems()
|
||||
{
|
||||
m_buildingSystem = std::make_unique<BuildingSystem>(
|
||||
m_config,
|
||||
m_factoryState,
|
||||
m_beltSystem,
|
||||
[this]() { return allocateBuildingId(); },
|
||||
[this](int amount) { m_buildingBlocksStock += amount; },
|
||||
@@ -153,15 +153,15 @@ void Simulation::apply(const Command& command)
|
||||
const BuildingId id = *placed;
|
||||
if (c.recipeId.has_value())
|
||||
{
|
||||
m_buildingSystem->setRecipe(id, *c.recipeId);
|
||||
m_buildingSystem->setRecipe(m_factoryState, id, *c.recipeId);
|
||||
}
|
||||
if (c.shipLayout.has_value())
|
||||
{
|
||||
m_buildingSystem->setShipLayout(id, *c.shipLayout);
|
||||
m_buildingSystem->setShipLayout(m_factoryState, id, *c.shipLayout);
|
||||
}
|
||||
if (c.hasSplitterFilters)
|
||||
{
|
||||
m_buildingSystem->setSiteSplitterFilters(id, c.splitterFilterA, c.splitterFilterB);
|
||||
m_buildingSystem->setSiteSplitterFilters(m_factoryState, id, c.splitterFilterA, c.splitterFilterB);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -174,26 +174,26 @@ void Simulation::apply(const Command& command)
|
||||
case CommandKind::RotateInPlace:
|
||||
{
|
||||
const RotateInPlaceCommand& c = static_cast<const RotateInPlaceCommand&>(command);
|
||||
m_buildingSystem->rotateInPlace(*c.id, c.newRotation);
|
||||
m_buildingSystem->rotateInPlace(m_factoryState, *c.id, c.newRotation);
|
||||
break;
|
||||
}
|
||||
case CommandKind::SetRecipe:
|
||||
{
|
||||
const SetRecipeCommand& c = static_cast<const SetRecipeCommand&>(command);
|
||||
m_buildingSystem->setRecipe(*c.id, c.recipeId);
|
||||
m_buildingSystem->setRecipe(m_factoryState, *c.id, c.recipeId);
|
||||
break;
|
||||
}
|
||||
case CommandKind::SetShipLayout:
|
||||
{
|
||||
const SetShipLayoutCommand& c = static_cast<const SetShipLayoutCommand&>(command);
|
||||
m_buildingSystem->setShipLayout(*c.id, c.layout);
|
||||
m_buildingSystem->setShipLayout(m_factoryState, *c.id, c.layout);
|
||||
break;
|
||||
}
|
||||
case CommandKind::SetSiteSplitterFilters:
|
||||
{
|
||||
const SetSiteSplitterFiltersCommand& c =
|
||||
static_cast<const SetSiteSplitterFiltersCommand&>(command);
|
||||
m_buildingSystem->setSiteSplitterFilters(*c.id, c.filterA, c.filterB);
|
||||
m_buildingSystem->setSiteSplitterFilters(m_factoryState, *c.id, c.filterA, c.filterB);
|
||||
break;
|
||||
}
|
||||
case CommandKind::SetSplitterFilters:
|
||||
@@ -243,12 +243,12 @@ void Simulation::tick()
|
||||
m_waveSystem->tickThreatAccumulation();
|
||||
|
||||
// Construction + production pipeline
|
||||
m_buildingSystem->tickConstruction(m_currentTick);
|
||||
m_buildingSystem->tickDeconstruction(m_currentTick); // parallel to construction
|
||||
m_buildingSystem->tickBeltPull(); // step 3
|
||||
m_buildingSystem->tickProduction(m_currentTick); // step 4
|
||||
m_buildingSystem->tickShipyardProduction(m_currentTick); // step 4b
|
||||
m_buildingSystem->tickOutputBelts(); // step 5
|
||||
m_buildingSystem->tickConstruction(m_factoryState, m_currentTick);
|
||||
m_buildingSystem->tickDeconstruction(m_factoryState, m_currentTick); // parallel to construction
|
||||
m_buildingSystem->tickBeltPull(m_factoryState); // step 3
|
||||
m_buildingSystem->tickProduction(m_factoryState, m_currentTick); // step 4
|
||||
m_buildingSystem->tickShipyardProduction(m_factoryState, m_currentTick); // step 4b
|
||||
m_buildingSystem->tickOutputBelts(m_factoryState); // step 5
|
||||
m_beltSystem.tick(); // step 6
|
||||
|
||||
// Step 7: ship behavior systems (movement arbitration via intent priority)
|
||||
@@ -306,8 +306,7 @@ void Simulation::placeInitialStructures()
|
||||
(m_config.world.heightTiles - hqParsed.footprint.height()) / 2;
|
||||
const float hqHp =
|
||||
static_cast<float>(m_config.stations.hq.hpFormula.evaluate(0.0));
|
||||
m_hqBuildingId = m_buildingSystem->placeImmediate(
|
||||
BuildingType::Hq,
|
||||
m_hqBuildingId = m_buildingSystem->placeImmediate(m_factoryState, BuildingType::Hq,
|
||||
m_config.stations.hq.surfaceMask,
|
||||
QPoint(hqAnchorX, hqAnchorY),
|
||||
Rotation::East);
|
||||
@@ -356,7 +355,7 @@ void Simulation::placeInitialStructures()
|
||||
m_admin.addComponent<ModuleOwnerComponent>(wChild,
|
||||
ModuleOwnerComponent{m_playerStation1Entity});
|
||||
}
|
||||
m_buildingSystem->registerTileOccupancy(absCells, allocateBuildingId());
|
||||
m_buildingSystem->registerTileOccupancy(m_factoryState, absCells, allocateBuildingId());
|
||||
}
|
||||
{
|
||||
const QPoint anchor(psAnchorX, ps2Y);
|
||||
@@ -373,7 +372,7 @@ void Simulation::placeInitialStructures()
|
||||
m_admin.addComponent<ModuleOwnerComponent>(wChild,
|
||||
ModuleOwnerComponent{m_playerStation2Entity});
|
||||
}
|
||||
m_buildingSystem->registerTileOccupancy(absCells, allocateBuildingId());
|
||||
m_buildingSystem->registerTileOccupancy(m_factoryState, absCells, allocateBuildingId());
|
||||
}
|
||||
|
||||
// Rally point: center of the player defence stations' X column, world vertical midpoint.
|
||||
@@ -428,7 +427,7 @@ void Simulation::placeEnemyStationSet(int generation)
|
||||
m_admin.addComponent<ModuleOwnerComponent>(wChild,
|
||||
ModuleOwnerComponent{m_currentEnemyStationEntities[0]});
|
||||
}
|
||||
m_buildingSystem->registerTileOccupancy(absCells, allocateBuildingId());
|
||||
m_buildingSystem->registerTileOccupancy(m_factoryState, absCells, allocateBuildingId());
|
||||
}
|
||||
{
|
||||
const QPoint anchor(anchorX, y2);
|
||||
@@ -445,7 +444,7 @@ void Simulation::placeEnemyStationSet(int generation)
|
||||
m_admin.addComponent<ModuleOwnerComponent>(wChild,
|
||||
ModuleOwnerComponent{m_currentEnemyStationEntities[1]});
|
||||
}
|
||||
m_buildingSystem->registerTileOccupancy(absCells, allocateBuildingId());
|
||||
m_buildingSystem->registerTileOccupancy(m_factoryState, absCells, allocateBuildingId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +518,7 @@ void Simulation::tickDeathsAndLoot()
|
||||
{
|
||||
m_debrisSystem->spawn(pos.value, scrap, despawnAt);
|
||||
}
|
||||
m_buildingSystem->unregisterTileOccupancy(sb.bodyCells);
|
||||
m_buildingSystem->unregisterTileOccupancy(m_factoryState, sb.bodyCells);
|
||||
{
|
||||
std::vector<entt::entity> stationChildren;
|
||||
m_admin.forEach<ModuleOwnerComponent>(
|
||||
@@ -671,7 +670,7 @@ unsigned long long Simulation::computeStateChecksum() const
|
||||
m_unlockState.appendChecksum(hasher);
|
||||
|
||||
// Subsystems contribute their own state.
|
||||
m_buildingSystem->appendChecksum(hasher);
|
||||
m_buildingSystem->appendChecksum(m_factoryState, hasher);
|
||||
m_beltSystem.appendChecksum(hasher);
|
||||
|
||||
// ECS component state. View iteration order is a pure function of the
|
||||
@@ -784,7 +783,7 @@ void Simulation::tryExpandAsteroid()
|
||||
}
|
||||
m_buildingBlocksStock -= cost;
|
||||
++m_expansionsPurchased;
|
||||
m_buildingSystem->setAsteroidWidth_tiles(getCurrentAsteroidWidth_tiles());
|
||||
m_buildingSystem->setAsteroidWidth_tiles(m_factoryState, getCurrentAsteroidWidth_tiles());
|
||||
}
|
||||
|
||||
bool Simulation::isGameOver() const
|
||||
@@ -880,17 +879,17 @@ std::optional<BuildingId> Simulation::tryPlaceBuilding(BuildingType type, QPoint
|
||||
return std::nullopt;
|
||||
}
|
||||
m_buildingBlocksStock -= cost;
|
||||
return m_buildingSystem->place(type, anchor, rotation, m_currentTick);
|
||||
return m_buildingSystem->place(m_factoryState, type, anchor, rotation, m_currentTick);
|
||||
}
|
||||
|
||||
void Simulation::deconstruct(BuildingId id)
|
||||
{
|
||||
m_buildingBlocksStock += m_buildingSystem->deconstruct(id, m_currentTick);
|
||||
m_buildingBlocksStock += m_buildingSystem->deconstruct(m_factoryState, id, m_currentTick);
|
||||
}
|
||||
|
||||
void Simulation::cancelDeconstruction(BuildingId id)
|
||||
{
|
||||
m_buildingSystem->cancelDeconstruction(id);
|
||||
m_buildingSystem->cancelDeconstruction(m_factoryState, id);
|
||||
}
|
||||
|
||||
BuildingSystem& Simulation::getBuildingsMutable()
|
||||
|
||||
Reference in New Issue
Block a user