#pragma once #include #include #include #include #include "BuildingType.h" // One entry in [[recipe]].inputs — amount units of a named item consumed per // production cycle (REQ-MAT-CYCLE). struct RecipeIngredient { std::string item; int amount; }; // One item produced by an output group -- amount units of a named item // (REQ-MAT-OUTPUT-GROUP). struct RecipeOutput { std::string item; int amount; }; // One possible result of a production cycle: the items it yields, produced together, and // the weight this group is picked with among the recipe's groups (REQ-MAT-OUTPUT-GROUP). // A recipe with a single group always produces it, so the weight is meaningful only where // there are several -- which is the only difference between what used to be called a // deterministic and a probabilistic recipe. struct RecipeOutputGroup { std::vector items; std::optional probability; }; struct RecipeDef { std::string id; // Unique recipe id; used by UI for selection. BuildingType building; // Which BuildingType can run this recipe. std::vector inputs; // Never empty: one group is the ordinary recipe (REQ-MAT-OUTPUT-GROUP). std::vector outputGroups; double durationSeconds; // Assembler only. When true, this recipe is available from game start // regardless of the implicit item graph — used for base recipes that no // schematic's materials reach (e.g. building blocks). See REQ-LOCK-IMPLICIT. // Otherwise an assembler recipe is either explicitly gated (granted by an // unlock group, REQ-LOCK-EXPLICIT) or implicitly gated via the item graph. bool unlockedAtStart = false; }; // Every distinct item any group of this recipe can produce, in config order. Most callers // only want to know what a recipe can make at all -- which items it has buffers for, which // recipes produce an item -- and not which group yields what. inline std::vector getProducibleItems(const RecipeDef& recipe) { std::vector items; for (const RecipeOutputGroup& group : recipe.outputGroups) { for (const RecipeOutput& out : group.items) { if (std::find(items.begin(), items.end(), out.item) == items.end()) { items.push_back(out.item); } } } return items; } // True when some group of this recipe yields the given item. inline bool producesItem(const RecipeDef& recipe, const std::string& itemId) { for (const RecipeOutputGroup& group : recipe.outputGroups) { for (const RecipeOutput& out : group.items) { if (out.item == itemId) { return true; } } } return false; } struct RecipesConfig { std::vector recipes; // Returns the definition for the given recipe id, or nullptr if the id has // no entry in recipes.toml. const RecipeDef* findRecipeDef(const std::string& id) const { for (const RecipeDef& recipe : recipes) { if (recipe.id == id) { return &recipe; } } return nullptr; } // Same, but additionally requires the recipe to belong to the given building // type — recipe ids are only unique per building type. const RecipeDef* findRecipeDef(const std::string& id, BuildingType building) const { for (const RecipeDef& recipe : recipes) { if (recipe.id == id && recipe.building == building) { return &recipe; } } return nullptr; } };