separate what buildings are from what flows through them
BuildingSystem was four unrelated jobs in one class: building lifecycle, building configuration, the per-tick material flow, and (until last commit) the checksum. The flow was the odd one out -- it is what the RNG, the ship spawner and the unlock test were held for, none of which placement, rotation or demolition has any business reaching. Tick steps 3 to 5 move to a new ProductionSystem: tickBeltPull, tickProduction, tickShipyardProduction, tickOutputBelts, their five private helpers and rollOutputGroup. The cut is clean in both directions -- nothing in the block called a topology or configuration method, and nothing outside it called the helpers -- so the bodies move verbatim; a scripted comparison against the old file confirms all nine differ only by class qualifier, the m_belts -> belts rename, and the two added parameters. (Seven em dashes in comments became `--`; the new file is ASCII, as the guidelines require.) Belts arrive per tick rather than being held, matching ConstructionSystem, and only the two methods that touch them take the parameter. BuildingSystem is left holding the config and the belts, and its constructor takes exactly those two. The arena constructs no ProductionSystem at all: it stages ships directly and never runs a factory. Determinism rests on the RNG stream: step 4's weighted output-group pick is the factory's only draw, so the four calls must keep their order and position in Simulation::tick. They do, and the tick-order section now says why, since no test can catch a reordering here. 913 lines of BuildingSystem.cpp become 444 there and 483 in ProductionSystem.cpp. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
This commit is contained in:
@@ -4,13 +4,11 @@
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
#include <QVector2D>
|
||||
|
||||
#include "BeltSystem.h"
|
||||
@@ -19,7 +17,6 @@
|
||||
#include "BuildingBuffers.h"
|
||||
#include "DeconstructionSystem.h"
|
||||
#include "PlacementRules.h"
|
||||
#include "ProductionRules.h"
|
||||
#include "BuildingType.h"
|
||||
#include "BuildingId.h"
|
||||
#include "GameConfig.h"
|
||||
@@ -29,19 +26,17 @@
|
||||
#include "ShipsConfig.h"
|
||||
#include "Tick.h"
|
||||
|
||||
// Manages building placement, construction queuing, and the per-tick
|
||||
// production loop (belt→building pull, production, building→belt push).
|
||||
// All types including Belt and Splitter are stored as Building instances;
|
||||
// BeltSystem owns the per-tile simulation data (item slots, flow).
|
||||
// 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:
|
||||
BuildingSystem(const GameConfig& config,
|
||||
BeltSystem& belts,
|
||||
std::function<void(const std::string&, QVector2D,
|
||||
const std::optional<ShipLayoutConfig>&)> spawnShip,
|
||||
std::function<bool(const std::string&)> isItemUnlocked,
|
||||
std::mt19937& rng);
|
||||
BuildingSystem(const GameConfig& config, BeltSystem& belts);
|
||||
|
||||
// -- Placement / deconstruct ------------------------------------------------
|
||||
// Returns the new entity id, or nullopt if the placement falls outside the
|
||||
@@ -92,17 +87,9 @@ public:
|
||||
const std::vector<ItemType>& filterA,
|
||||
const std::vector<ItemType>& filterB);
|
||||
|
||||
// -- Tick hooks (called from Simulation::tick in the documented order) ---
|
||||
// Advances every building's virtual input belts, delivers what arrives into the
|
||||
// input buffers (into the global block stock for the HQ), and takes what the
|
||||
// adjacent real belts offer (REQ-MAT-INPUT-INTAKE, REQ-HQ-BELT-INPUT).
|
||||
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(FactoryState& state);
|
||||
// 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
|
||||
@@ -133,46 +120,11 @@ public:
|
||||
void forEachBuilding(FactoryState& state, std::function<void(Building&)> fn);
|
||||
|
||||
private:
|
||||
// Selects a recipe for an auto-recipe building that has none, from a material being
|
||||
// offered to it at one of its input ports (REQ-BLD-AUTO-RECIPE). No-op for every
|
||||
// other building, for one that already holds a recipe, and for a material none of
|
||||
// its recipes consumes. Called from both intake paths -- the belt pull and the
|
||||
// direct coupling -- since either can be where the first material arrives.
|
||||
void selectAutoRecipeIfUnset(Building& building,
|
||||
const ItemType& offered);
|
||||
// True if the consumer would accept `type` at the given input port right now:
|
||||
// it is a required input (or a building block for the HQ), the reservation-aware
|
||||
// buffer has room, and the input belt entry is free (REQ-MAT-INPUT-INTAKE).
|
||||
bool canAcceptInput(const Building& consumer,
|
||||
std::size_t inputPortIndex,
|
||||
const ItemType& type) const;
|
||||
// Places an accepted item onto the consumer's input belt at progress 0.0,
|
||||
// reserving a per-material buffer slot (REQ-MAT-INPUT-INTAKE).
|
||||
void depositToInputBelt(Building& consumer,
|
||||
std::size_t inputPortIndex,
|
||||
const Item& item);
|
||||
// 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(FactoryState& state, BuildingId producerId,
|
||||
const Port& outputPort,
|
||||
const Item& item);
|
||||
|
||||
// What one cycle of this recipe produces: the items of its one output group
|
||||
// (REQ-MAT-OUTPUT-GROUP). Where the recipe has several, one is picked by weight from
|
||||
// those currently eligible (REQ-LOCK-OUTPUT-POOL) and the result is empty if none is;
|
||||
// where it has one, that group is returned with no draw and no eligibility test.
|
||||
std::vector<Item> rollOutputGroup(const RecipeDef& recipe);
|
||||
|
||||
// No world data among these: the factory arrives per call (FactoryState.h). What is
|
||||
// left are the immutable config, the transport layer, the shared RNG, and two
|
||||
// callbacks into what this system genuinely cannot reach -- the entity model a
|
||||
// finished ship is spawned into, and the unlock state an output group is tested
|
||||
// against. Neither is factory data, so neither belongs in the state.
|
||||
const GameConfig& m_config;
|
||||
BeltSystem& m_belts;
|
||||
std::function<void(const std::string&, QVector2D,
|
||||
const std::optional<ShipLayoutConfig>&)> m_spawnShip;
|
||||
std::function<bool(const std::string&)> m_isItemUnlocked;
|
||||
std::mt19937& m_rng;
|
||||
// No world data here: the factory arrives per call (FactoryState.h). The config says
|
||||
// what a building costs, occupies and can run; the belts are what a placed, rotated or
|
||||
// demolished belt tile must be registered with and unregistered from. Nothing else --
|
||||
// no RNG and no callbacks, those having gone to ProductionSystem with the material
|
||||
// flow that needed them.
|
||||
const GameConfig& m_config;
|
||||
BeltSystem& m_belts;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user