make BuildingSystem stateless: FactoryState becomes a parameter

This commit is contained in:
2026-08-05 06:50:11 +02:00
parent d87d063b10
commit 114a43b205
15 changed files with 401 additions and 395 deletions

View File

@@ -37,7 +37,6 @@ class BuildingSystem
{
public:
BuildingSystem(const GameConfig& config,
FactoryState& state,
BeltSystem& belts,
std::function<BuildingId()> allocateBuildingId,
std::function<void(int)> addBuildingBlocks,
@@ -53,7 +52,7 @@ public:
// queue. Terrain type (A vs S) is NOT checked here so that tests can stage
// arbitrary layouts; the player-facing entry point
// (Simulation::tryPlaceBuilding) enforces the full rule via isPlacementValid.
std::optional<BuildingId> place(BuildingType type, QPoint anchor, Rotation rotation,
std::optional<BuildingId> place(FactoryState& state, BuildingType type, QPoint anchor, Rotation rotation,
Tick currentTick);
// Returns true if the placement satisfies REQ-BLD-PLACE-VALID terrain and
@@ -65,7 +64,8 @@ public:
// Sets the current buildable asteroid width in tiles. Grows the left
// placement bound as the player unlocks asteroid expansions (REQ-EXP-UNLOCK).
// Defaults to world.regions.asteroid_width_tiles at construction.
void setAsteroidWidth_tiles(int widthTiles) { m_state.asteroidWidth_tiles = widthTiles; }
void setAsteroidWidth_tiles(FactoryState& state, int widthTiles) const
{ state.asteroidWidth_tiles = widthTiles; }
// Mark a building or construction site for demolition (REQ-BLD-DECONSTRUCT).
// A construction site is removed instantly and the full cost is returned.
@@ -73,23 +73,23 @@ public:
// (REQ-BLD-DECON-QUEUE) and stops operating at once; its (partial) refund is
// credited later, on completion in tickDeconstruction, so this returns 0 for
// it. Returns 0 for unknown ids and for a building already queued.
int deconstruct(BuildingId id, Tick currentTick);
int deconstruct(FactoryState& state, BuildingId id, Tick currentTick);
// Take a building back out of the deconstruction queue before it is removed
// (REQ-BLD-DECON-QUEUE). Clears its queued flag and resumes operation
// (re-registering belt/tunnel/splitter tiles); discards deconstruction
// progress and credits no refund. No-op if the id is not queued.
void cancelDeconstruction(BuildingId id);
void cancelDeconstruction(FactoryState& state, BuildingId id);
// True if the building is currently in the deconstruction queue.
// Set the recipe (or schematic id for shipyard) on a building or queued
// construction site. Clears both buffers on an operational building.
void setRecipe(BuildingId id, const std::string& recipeId);
void setRecipe(FactoryState& state, BuildingId id, const std::string& recipeId);
// Set the module layout for a shipyard. Cancels in-progress production
// (materials discarded) and reinitializes input buffers (REQ-BLD-SHIPYARD).
void setShipLayout(BuildingId id, const ShipLayoutConfig& layout);
void setShipLayout(FactoryState& state, BuildingId id, const ShipLayoutConfig& layout);
// Splitter filter configuration for a queued/under-construction Splitter
// site (REQ-BLD-SITE-CONFIG). Operational splitters are configured through
@@ -98,23 +98,23 @@ public:
// output directions (derived from its surface mask) and stored filters, or
// nullopt if the id is not a Splitter site. The stored filters are applied
// to BeltSystem when the splitter finishes building (tickConstruction).
void setSiteSplitterFilters(BuildingId id,
void setSiteSplitterFilters(FactoryState& state, BuildingId id,
const std::vector<ItemType>& filterA,
const std::vector<ItemType>& filterB);
// -- Tick hooks (called from Simulation::tick in the documented order) ---
void tickConstruction(Tick currentTick);
void tickConstruction(FactoryState& state, Tick currentTick);
// Advances the deconstruction queue (REQ-BLD-DECON-QUEUE): one building at a
// time, in parallel with tickConstruction. Removes the front building and
// credits its refund when its timer elapses.
void tickDeconstruction(Tick currentTick);
void tickBeltPull();
void tickProduction(Tick currentTick);
void tickShipyardProduction(Tick currentTick);
void tickDeconstruction(FactoryState& state, Tick currentTick);
void tickBeltPull(FactoryState& state);
void tickProduction(FactoryState& state, Tick currentTick);
void tickShipyardProduction(FactoryState& state, Tick currentTick);
// Advances each building's virtual output belts, hands finished items off onto
// the adjacent real belt, and feeds new buffered items into them
// (REQ-MAT-OUTPUT-EMERGE).
void tickOutputBelts();
void tickOutputBelts(FactoryState& state);
// -- Queries -------------------------------------------------------------
@@ -134,19 +134,19 @@ public:
// virtual output belt (REQ-MAT-OUTPUT-EMERGE), passing the item type and its
// world-space centre (in tile units). Least-progressed first (drawn bottom) so
// callers can paint in visit order (REQ-GW-TILE-SIZE ordering).
void forEachEmergingItem(
void forEachEmergingItem(const FactoryState& state,
const std::function<void(const ItemType&, QPointF)>& visit) const;
// Visits every item currently travelling inward on a building input port's
// virtual input belt (REQ-MAT-INPUT-INTAKE), passing the item type and its
// world-space centre (in tile units). Least-progressed first (drawn bottom).
void forEachIncomingItem(
void forEachIncomingItem(const FactoryState& state,
const std::function<void(const ItemType&, QPointF)>& visit) const;
// Rotate an existing building or construction site to newRotation in place.
// For belt-type operational buildings, re-registers with BeltSystem (items
// currently on the tile are discarded by BeltSystem::removeTile).
void rotateInPlace(BuildingId id, Rotation newRotation);
void rotateInPlace(FactoryState& state, BuildingId id, Rotation newRotation);
// Input-capable adjacent tiles for a building or construction site
@@ -155,8 +155,8 @@ public:
// the target. Output-port edges are excluded. Empty for an unknown id.
// Register / unregister tile occupancy for ECS station entities.
void registerTileOccupancy(const std::vector<QPoint>& cells, BuildingId ownerPlaceholder);
void unregisterTileOccupancy(const std::vector<QPoint>& cells);
void registerTileOccupancy(FactoryState& state, const std::vector<QPoint>& cells, BuildingId ownerPlaceholder);
void unregisterTileOccupancy(FactoryState& state, const std::vector<QPoint>& cells);
// Place one "scrap" item into a SalvageBay's output buffer.
// Returns false if bay not found, wrong type, or output buffer is full.
@@ -164,26 +164,26 @@ public:
// Bypass the construction queue and create a fully-operational Building
// immediately. Used for pre-placed structures (HQ, defence stations).
// surfaceMask comes from the relevant config struct.
BuildingId placeImmediate(BuildingType type,
BuildingId placeImmediate(FactoryState& state, BuildingType type,
const std::vector<std::string>& surfaceMask,
QPoint anchor, Rotation rotation);
// Remove an operational building by id without refund (used for deaths).
// Returns true if found and removed.
bool removeBuilding(BuildingId id);
bool removeBuilding(FactoryState& state, BuildingId id);
// Mutable iteration over all operational buildings.
void forEachBuilding(std::function<void(Building&)> fn);
void forEachBuilding(FactoryState& state, std::function<void(Building&)> fn);
// -- Determinism ---------------------------------------------------------
// Folds all building, construction-site, and tile-occupancy state into the
// hasher in deterministic order (see docs/replay_design.md).
void appendChecksum(Hasher& hasher) const;
void appendChecksum(const FactoryState& state, Hasher& hasher) const;
private:
// Starts the front deconstruction-queue entry's timer if not yet started
// (mirrors how tickConstruction starts a queued construction site).
void startFrontDeconstruction(Tick currentTick);
void startFrontDeconstruction(FactoryState& state, Tick currentTick);
// Registers a belt/splitter/tunnel building's tile with the belt subsystem
// (on construction completion, or when un-queuing a deconstruction). No-op for
@@ -206,7 +206,7 @@ private:
// Attempts to hand an emerging output item straight into a directly adjacent
// building whose input edge meets the producer's output port (REQ-MAT-DIRECT-COUPLE).
// Returns true if the item was accepted onto the consumer's input belt.
bool tryDirectCoupleDeposit(BuildingId producerId,
bool tryDirectCoupleDeposit(FactoryState& state, BuildingId producerId,
const Port& outputPort,
const Item& item);
@@ -233,9 +233,6 @@ private:
const GameConfig& m_config;
// The factory's world data — buildings, queued work, tile ownership. Owned by
// Simulation, not by this system (see FactoryState.h).
FactoryState& m_state;
BeltSystem& m_belts;
std::function<BuildingId()> m_allocateBuildingId;