Files
dota_factory/src/lib/sim/FactoryState.h
Malte Langkabel e7bfd91054 number the buildings from the factory that holds them
The building-id counter was the last piece of factory data living on Simulation
behind a callback: every construction site, every building, and every tile a
station entity claims took its id from a std::function BuildingSystem held, which
the arena and three test fixtures each had to supply.

It moves into FactoryState as nextBuildingId, handed out by
allocateBuildingId(state) in FactoryQueries beside the other operations over the
state. BuildingSystem's callback is gone; so are Simulation::allocateBuildingId
and ArenaSimulation::allocateBuildingId, whose remaining callers now allocate
from the state directly.

Checksum order is untouched: Simulation folds the counter where it always did.

What is left on BuildingSystem is the config, the belts, the RNG, and two
callbacks that reach genuinely outside the factory -- spawning a finished ship
into the entity model, and testing an output group against the unlock state.
Neither is factory data, so this is where the migration stops.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
2026-08-19 17:31:48 +02:00

89 lines
4.2 KiB
C++

#pragma once
#include <deque>
#include <vector>
#include "Building.h"
#include "GameConfig.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 Simulation (and by ArenaSimulation in the balancing tool), not by the
// systems that operate on it. Every system that touches the factory —
// BuildingSystem, ConstructionSystem, DeconstructionSystem — takes it as an argument
// and holds none of it, so each is stateless over the world it works on. Reading it
// needs no system at all: the queries are free functions over this struct
// (FactoryQueries.h, PlacementRules.h, ProductionRules.h).
//
// What is not here yet is the data those systems still reach back into Simulation
// for through callbacks: the building-id counter and the global building block stock,
// both of which are factory data living outside the factory's state.
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;
// Current buildable asteroid width, the left bound for placement. Grows as the
// player buys expansions (REQ-EXP-UNLOCK). Deliberately not checksummed: it is
// derived from config and Simulation's expansion count, which is folded already.
// Seeded from config by BuildingSystem's constructor.
int asteroidWidth_tiles = 0;
// Next id to hand out for a building, a construction site, or a tile claimed by a
// station entity. Strictly increasing and never reused, so an id names one thing for
// the whole run -- which is what lets a command reference a building across a replay
// (docs/replay_design.md). Allocated through allocateBuildingId (FactoryQueries.h).
BuildingId nextBuildingId = 1;
// The global building block stock (REQ-HQ-STARTING-BLOCKS, REQ-HQ-BELT-INPUT):
// what placement spends, what deconstruction refunds, and what blocks delivered to
// the HQ add to. Factory data, so it lives with the factory rather than being
// reached through a callback by every system that credits it.
//
// Folded into the checksum by Simulation, in the place it has always occupied
// (docs/replay_design.md).
int buildingBlocksStock = 0;
};
// A fresh factory for a new run: nothing built, the asteroid bound and the starting
// block stock seeded from config. Every owner of a FactoryState creates it this way —
// the bound has no sensible default without the config, so a default-constructed state
// would refuse every placement on the asteroid, and the player would start with nothing
// to build from.
inline FactoryState makeFactoryState(const GameConfig& config)
{
FactoryState state;
state.asteroidWidth_tiles = config.world.regions.asteroidWidth_tiles;
state.buildingBlocksStock = config.world.startingBuildingBlocks;
return state;
}