143 lines
4.5 KiB
C++
143 lines
4.5 KiB
C++
#include "ProductionRules.h"
|
|
|
|
#include "BuildingType.h"
|
|
#include "ItemType.h"
|
|
#include "ModulesConfig.h"
|
|
#include "ShipsConfig.h"
|
|
|
|
std::vector<const RecipeDef*>
|
|
gatherCandidateRecipes(const GameConfig& config, const Building& b)
|
|
{
|
|
std::vector<const RecipeDef*> candidates;
|
|
if (isAutoRecipeBuildingType(b.type))
|
|
{
|
|
for (const RecipeDef& r : config.recipes.recipes)
|
|
{
|
|
if (r.building == b.type && !r.inputs.empty())
|
|
{
|
|
candidates.push_back(&r);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const RecipeDef* recipe = config.recipes.findRecipeDef(b.recipeId, b.type);
|
|
if (recipe)
|
|
{
|
|
candidates.push_back(recipe);
|
|
}
|
|
}
|
|
return candidates;
|
|
}
|
|
bool recipeInputsAvailable(const Building& b, const RecipeDef& recipe)
|
|
{
|
|
for (const RecipeIngredient& ing : recipe.inputs)
|
|
{
|
|
const std::map<ItemType, int>::const_iterator it =
|
|
b.inputBuffer.counts.find(ItemType{ing.item});
|
|
const int have = (it != b.inputBuffer.counts.end()) ? it->second : 0;
|
|
if (have < ing.amount)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
std::map<std::string, int>
|
|
computeShipyardRequiredMaterials(const GameConfig& config, const Building& b)
|
|
{
|
|
std::map<std::string, int> requiredMaterials;
|
|
const ShipDef* shipDef = config.ships.findShipDef(b.recipeId);
|
|
if (!shipDef)
|
|
{
|
|
return requiredMaterials;
|
|
}
|
|
for (const RecipeIngredient& ing : shipDef->schematic.materials)
|
|
{
|
|
requiredMaterials[ing.item] += ing.amount;
|
|
}
|
|
if (b.shipLayout.has_value())
|
|
{
|
|
for (const PlacedModule& pm : b.shipLayout->placedModules)
|
|
{
|
|
const ModuleDef* modDef = config.modules.findModuleDef(pm.moduleId);
|
|
if (!modDef)
|
|
{
|
|
continue;
|
|
}
|
|
for (const RecipeIngredient& ing : modDef->materials)
|
|
{
|
|
requiredMaterials[ing.item] += ing.amount;
|
|
}
|
|
}
|
|
}
|
|
return requiredMaterials;
|
|
}
|
|
bool hasInputsToStart(const GameConfig& config, const Building& b)
|
|
{
|
|
if (b.type == BuildingType::Shipyard)
|
|
{
|
|
const std::map<std::string, int> required =
|
|
computeShipyardRequiredMaterials(config, b);
|
|
for (const std::pair<const std::string, int>& req : required)
|
|
{
|
|
const std::map<ItemType, int>::const_iterator it =
|
|
b.inputBuffer.counts.find(ItemType{req.first});
|
|
const int have = (it != b.inputBuffer.counts.end()) ? it->second : 0;
|
|
if (have < req.second)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Recipe buildings: startable if any candidate recipe's inputs are satisfied.
|
|
// A Miner recipe has no inputs, so an idle Miner is always startable and its
|
|
// only idle reason is a full output buffer.
|
|
for (const RecipeDef* recipe : gatherCandidateRecipes(config, b))
|
|
{
|
|
if (recipeInputsAvailable(b, *recipe))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
std::optional<ProductionStatus>
|
|
getProductionStatus(const GameConfig& config, const Building& building)
|
|
{
|
|
// Salvage Bay has no recipe or production cycle (REQ-BLD-SALVAGE-BAY): it is
|
|
// "producing" while it holds scrap to push out, and starved when empty.
|
|
if (building.type == BuildingType::SalvageBay)
|
|
{
|
|
return building.getOutputItemCount() >= 1 ? ProductionStatus::Producing
|
|
: ProductionStatus::Starved;
|
|
}
|
|
|
|
// Only the five recipe/cycle production types show a status light besides the
|
|
// Salvage Bay; belts, splitters, tunnels, HQ, and stations show none.
|
|
if (!isProductionBuildingType(building.type))
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Grey only applies to player-configured types; auto-recipe buildings
|
|
// (Smelter, Reprocessing Plant) always run an implicit recipe.
|
|
if (!isAutoRecipeBuildingType(building.type) && building.recipeId.empty())
|
|
{
|
|
return ProductionStatus::Unconfigured;
|
|
}
|
|
|
|
if (building.production.has_value())
|
|
{
|
|
return ProductionStatus::Producing;
|
|
}
|
|
|
|
// Idle: missing inputs (red) take precedence over a full output buffer
|
|
// (yellow). If inputs are present yet the building is idle, the only remaining
|
|
// reason it could not start a cycle is a full output buffer (REQ-MAT-CYCLE).
|
|
return hasInputsToStart(config, building) ? ProductionStatus::Blocked
|
|
: ProductionStatus::Starved;
|
|
}
|