The last system holding a piece of the world as a member. ConstructionSystem and ProductionSystem take the transport layer as a tick argument; BuildingSystem kept a BeltSystem& from construction, so the same object arrived two different ways depending on which system you were reading. Three methods need it -- deconstruct, cancelDeconstruction and rotateInPlace, all of which register or unregister a belt tile -- and they now take it after the state, in the argument order the other systems use. The constructor is down to the config alone. CombatSystemTest's fixture kept a BeltSystem only to pass it here, so that goes too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
127 lines
6.3 KiB
C++
127 lines
6.3 KiB
C++
#pragma once
|
|
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <QPoint>
|
|
#include <QVector2D>
|
|
|
|
#include "BeltSystem.h"
|
|
#include "Building.h"
|
|
#include "FactoryState.h"
|
|
#include "BuildingBuffers.h"
|
|
#include "DeconstructionSystem.h"
|
|
#include "PlacementRules.h"
|
|
#include "BuildingType.h"
|
|
#include "BuildingId.h"
|
|
#include "GameConfig.h"
|
|
#include "Rotation.h"
|
|
#include "ModulesConfig.h"
|
|
#include "ShipLayout.h"
|
|
#include "ShipsConfig.h"
|
|
#include "Tick.h"
|
|
|
|
// What buildings are: placing them, demolishing them, rotating them, and configuring
|
|
// what they will run. What flows through them once they stand -- intake, production
|
|
// cycles, output -- belongs to ProductionSystem, which took the RNG, the ship spawner and
|
|
// the unlock test with it.
|
|
//
|
|
// All types including Belt and Splitter are stored as Building instances; BeltSystem owns
|
|
// the per-tile simulation data (item slots, flow).
|
|
class BuildingSystem
|
|
{
|
|
public:
|
|
explicit BuildingSystem(const GameConfig& config);
|
|
|
|
// -- Placement / deconstruct ------------------------------------------------
|
|
// Returns the new entity id, or nullopt if the placement falls outside the
|
|
// world bounds (vertical extent and asteroid left edge). Belt and Splitter
|
|
// register with BeltSystem directly; other types enter the construction
|
|
// 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(FactoryState& state, BuildingType type, QPoint anchor, Rotation rotation,
|
|
Tick currentTick);
|
|
|
|
// 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(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 its full cost credited back to the
|
|
// block stock. A fully-built building is instead appended to the deconstruction
|
|
// queue (REQ-BLD-DECON-QUEUE) and stops operating at once; its (partial) refund is
|
|
// credited later, on completion, by DeconstructionSystem. No-op for unknown ids and
|
|
// for a building already queued.
|
|
void deconstruct(FactoryState& state, BeltSystem& belts, 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(FactoryState& state, BeltSystem& belts, BuildingId id);
|
|
|
|
// Set the recipe (or schematic id for shipyard) on a building or queued
|
|
// construction site. Clears both buffers on an operational building.
|
|
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(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
|
|
// BeltSystem by tile; these mirror that for sites, which are not yet
|
|
// registered with BeltSystem. getSiteSplitterInfo returns the site's two
|
|
// 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(FactoryState& state, BuildingId id,
|
|
const std::vector<ItemType>& filterA,
|
|
const std::vector<ItemType>& filterB);
|
|
|
|
// This system has no tick hook of its own: a building placed, configured or
|
|
// demolished is a player action, not something that advances every tick
|
|
// (ProductionSystem, ConstructionSystem, DeconstructionSystem tick instead).
|
|
|
|
// This system answers no queries: reading the factory needs none, the queries being
|
|
// free functions over the state (FactoryQueries.h), the placement rules
|
|
// (PlacementRules.h) and the production rules (ProductionRules.h). What is left here
|
|
// mutates.
|
|
|
|
// 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(FactoryState& state, BeltSystem& belts, BuildingId id,
|
|
Rotation newRotation);
|
|
|
|
// Register / unregister tile occupancy for ECS station entities.
|
|
void registerTileOccupancy(FactoryState& state, const std::vector<QPoint>& cells, BuildingId ownerPlaceholder);
|
|
void unregisterTileOccupancy(FactoryState& state, const std::vector<QPoint>& cells);
|
|
|
|
// 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(FactoryState& state, BuildingType type,
|
|
const std::vector<std::string>& surfaceMask,
|
|
QPoint anchor, Rotation rotation);
|
|
|
|
// Mutable iteration over all operational buildings.
|
|
void forEachBuilding(FactoryState& state, std::function<void(Building&)> fn);
|
|
|
|
private:
|
|
// The config alone: what a building costs, occupies and can run. No world data --
|
|
// the factory and the transport layer both arrive per call, as they do for
|
|
// ConstructionSystem and ProductionSystem (FactoryState.h) -- and no RNG or callbacks,
|
|
// those having gone to ProductionSystem with the material flow that needed them.
|
|
const GameConfig& m_config;
|
|
};
|