#pragma once #include #include #include #include #include "Building.h" #include "GameConfig.h" #include "RecipesConfig.h" // 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) }; // The rules deciding what a building can produce and whether it can start. // Pure functions of the config and the building itself — they read no factory // state, so they are free functions rather than BuildingSystem members. // The recipe this building runs, or null when it has none selected. Every building type // holds exactly one, a Smelter and a Reprocessing Plant included -- they only differ in // how theirs first gets set (REQ-BLD-AUTO-RECIPE). const RecipeDef* getSelectedRecipe(const GameConfig& config, const Building& b); // The recipe an auto-recipe building adopts when this material is offered to it while it // has none: the first recipe of its type, in config order, that consumes the material // (REQ-BLD-AUTO-RECIPE). Null when no recipe of the type takes it. const RecipeDef* findAutoRecipeFor(const GameConfig& config, BuildingType type, const ItemType& item); // True when the building's input buffer holds every ingredient the recipe needs. bool recipeInputsAvailable(const Building& b, const RecipeDef& recipe); // Total materials a shipyard needs for its schematic plus its placed modules // (REQ-BLD-SHIPYARD), keyed by item id. std::map computeShipyardRequiredMaterials(const GameConfig& config, const Building& b); // The same sum over a stored configuration rather than an operational building, so a // construction site's schematic can be costed before it is built (REQ-BLD-SITE-CONFIG). std::map computeShipyardRequiredMaterials( const GameConfig& config, const std::string& recipeId, const std::optional& shipLayout); // The ship's base production time plus that of every module in the configured layout // (REQ-MOD-PRODUCTION-TIME), over a stored configuration as above. double computeShipyardProductionTimeSeconds( const GameConfig& config, const std::string& recipeId, const std::optional& shipLayout); // True when a production cycle could start right now, ignoring output-buffer space. bool hasInputsToStart(const GameConfig& config, const Building& b); // True when the building can take `itemCount` more items of `type` beside what it // already holds of it. An emerging item has not left the building yet and so still // counts against that material's capacity (REQ-MAT-OUTPUT-EMERGE, REQ-MAT-OUTPUT-BUFFER). bool outputBufferHasRoom(const Building& b, const ItemType& type, int itemCount); // True when every one of the recipe's output groups would fit -- the gate a cycle has to // pass before it may start (REQ-MAT-CYCLE, REQ-MAT-OUTPUT-GROUP). With a single group that // is simply that group. With several the pick is committed the moment the cycle starts, so // every outcome must fit: testing all of them rather than the picked one is what keeps a // stalled output belt from biasing the distribution. bool recipeOutputsFit(const Building& b, const RecipeDef& recipe); // True when a production cycle could actually start right now: some candidate recipe // has its inputs *and* passes recipeOutputsFit (REQ-MAT-CYCLE). Stricter than // hasInputsToStart, which looks at the input buffers alone. bool canStartCycle(const GameConfig& config, const Building& b); // Status light for a building, or nullopt for types that show none — belts, // splitters, tunnels, HQ and defence stations (REQ-UI-STATUS-LIGHT). std::optional getProductionStatus(const GameConfig& config, const Building& building);