Implements REQ-MAT-OUTPUT-GROUP. A recipe had two shapes -- outputs produced together, or outputs of which exactly one happened -- and every rule over them was written twice, selected by `building == ReprocessingPlant`: sizing a buffer, deciding whether a cycle fits, resolving what a cycle makes, costing an item. RecipeDef now holds output groups, each a weight and a list of items, and a cycle yields exactly one group. One group is the ordinary recipe, so the old two cases are the same shape with one and with several, and all four rules collapse to one expression apiece with no building-type test left. rollReprocessingOutput becomes rollOutputGroup, where a single group returns without drawing or testing eligibility. That early-out is load-bearing twice over. Drawing there would consume entropy for every ordinary recipe and shift every later random outcome; and eligibility must not apply either, since implicit unlocking is demand-derived, so an ordinary recipe's output can be producible while nothing yet calls for it -- testing it would stop the building producing rather than gate a drop. Past the early-out a group is eligible only when all of its items are unlocked, being produced whole. Threat follows the recipe's shape rather than the building, and the per-unit value now divides by the group's amount as well as its odds. That moves no number today: every item resolved through this path has amount 1, which is why the threat expectations are untouched. Config keeps `outputs = [...]` as the single-group form, so only the two reprocessing recipes change shape. The recipe summary gains "/" between groups and keeps "+" within one, which also fixes the plant reading as though a cycle produced all of its items at once. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
83 lines
4.2 KiB
C++
83 lines
4.2 KiB
C++
#pragma once
|
|
|
|
#include <map>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#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<std::string, int> 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<std::string, int> computeShipyardRequiredMaterials(
|
|
const GameConfig& config, const std::string& recipeId,
|
|
const std::optional<ShipLayoutConfig>& 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<ShipLayoutConfig>& 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<ProductionStatus> getProductionStatus(const GameConfig& config,
|
|
const Building& building);
|