extract the production rules as free functions over config and building

gatherCandidateRecipes, recipeInputsAvailable, computeShipyardRequiredMaterials,
hasInputsToStart and getProductionStatus read no factory state — they answer
"what can this building produce, and can it start" from the config and the
Building alone. They move to ProductionRules.h as free functions, and the
ProductionStatus enum goes with them since it is that group's return type.

Two of the five are pure in their arguments; the other three need GameConfig
through gatherCandidateRecipes and computeShipyardRequiredMaterials, so config is
a parameter rather than the group being split across two headers.

Only two callers outside BuildingSystem existed — the status light in
GameWorldView and one test lambda — so this is nearly all internal.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
This commit is contained in:
2026-08-04 22:17:01 +02:00
parent d1b688f45e
commit 3272431353
7 changed files with 199 additions and 158 deletions

View File

@@ -16,6 +16,7 @@
#include "BeltSystem.h"
#include "Building.h"
#include "FactoryState.h"
#include "ProductionRules.h"
#include "BuildingType.h"
#include "BuildingId.h"
#include "GameConfig.h"
@@ -27,18 +28,6 @@
class Hasher;
// Production state of a building for the UI status light (REQ-UI-STATUS-LIGHT).
// The simulation owns the classification so it stays in sync with the
// production-cycle predicates (REQ-MAT-CYCLE); the UI maps each value to a fill
// color.
enum class ProductionStatus
{
Unconfigured, // no recipe/schematic selected (grey)
Producing, // a production cycle is active (green)
Starved, // idle: a required input is missing / Salvage Bay empty (red)
Blocked, // idle: output buffer full, inputs otherwise present (yellow)
};
// 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;
@@ -150,7 +139,6 @@ public:
// nullopt for building types that show no light (belts, splitters, tunnels,
// HQ, defence stations). The Salvage Bay is a two-state special case:
// Producing while its output buffer holds scrap, Starved when empty.
std::optional<ProductionStatus> getProductionStatus(const Building& building) const;
std::vector<BeltTileInfo> getAllBeltTiles() const;
// Visits every item currently emerging from a building output port on its
@@ -245,17 +233,12 @@ private:
// building (Smelter, Reprocessing Plant) offers every recipe of its type with
// inputs; other buildings offer only their selected recipe. Shared by
// tickProduction and the status classifier (REQ-MAT-CYCLE, REQ-UI-STATUS-LIGHT).
std::vector<const RecipeDef*> gatherCandidateRecipes(const Building& b) const;
// True if every input of `recipe` is present in `b`'s input buffers in the
// required per-cycle amount (REQ-MAT-CYCLE input check).
bool recipeInputsAvailable(const Building& b,
const RecipeDef& recipe) const;
// Combined base + module materials a shipyard needs per ship (REQ-BLD-SHIPYARD).
std::map<std::string, int> computeShipyardRequiredMaterials(const Building& b) const;
// True if the building currently has all inputs/materials to start a cycle
// (ignoring output-buffer space); drives the Starved/Blocked distinction of
// the status light (REQ-UI-STATUS-LIGHT).
bool hasInputsToStart(const Building& b) const;
void initBuffers(Building& b, const RecipeDef& recipe) const;
// Buffers for an auto-recipe building (Smelter, Reprocessing Plant): input