Auto-process smelter and reprocessing plant (no recipe selection)

This commit is contained in:
2026-07-12 21:35:23 +02:00
parent ad3e73fdd8
commit dd7c997816
4 changed files with 354 additions and 92 deletions

View File

@@ -78,6 +78,25 @@ bool isProductionBuilding(BuildingType type)
|| type == BuildingType::Shipyard;
}
// Buildings that expose a player recipe/schematic selection control
// (REQ-UI-SELECT-BUTTON): Miner ore type, Assembler recipe, Shipyard schematic.
// The Smelter and Reprocessing Plant auto-process and offer no selection
// (REQ-BLD-SMELTER, REQ-BLD-REPROCESSING).
bool hasRecipeSelection(BuildingType type)
{
return type == BuildingType::Miner
|| type == BuildingType::Assembler
|| type == BuildingType::Shipyard;
}
// Auto-recipe buildings have no selected recipe; their production is driven by
// whatever inputs they receive.
bool isAutoRecipeBuilding(BuildingType type)
{
return type == BuildingType::Smelter
|| type == BuildingType::ReprocessingPlant;
}
bool isBeltLike(BuildingType type)
{
return type == BuildingType::Belt || type == BuildingType::Splitter
@@ -265,7 +284,7 @@ void SelectedBuildingPanel::buildSingle(BuildingId id)
m_titleLabel->show();
m_buffersLabel->show();
if (isProductionBuilding(type))
if (hasRecipeSelection(type))
{
const std::vector<RecipeSelectionOption> options =
buildRecipeSelectionOptions(type, *m_sim, *m_config);
@@ -383,6 +402,21 @@ void SelectedBuildingPanel::refreshBuffers(const Building* b)
? findShipDef(b->recipeId)
: nullptr;
// Auto-recipe buildings (Smelter, Reprocessing Plant) have no selected
// recipe; while a cycle runs, resolve the recipe actually in production so
// the cycle time and progress can be shown (REQ-UI-PRODUCTION-PROGRESS).
if (!recipe && isAutoRecipeBuilding(b->type) && b->production.has_value())
{
for (const RecipeDef& r : m_config->recipes.recipes)
{
if (r.id == b->production->recipeId && r.building == b->type)
{
recipe = &r;
break;
}
}
}
QString bufText;
if (!b->inputBuffer.counts.empty())
@@ -469,41 +503,51 @@ void SelectedBuildingPanel::refreshBuffers(const Building* b)
}
}
if (isProductionBuilding(b->type) && (recipe || shipDef))
if (isProductionBuilding(b->type)
&& (recipe || shipDef || isAutoRecipeBuilding(b->type)))
{
double durationSeconds = recipe
? recipe->durationSeconds
: shipDef->schematic.productionTimeSeconds;
if (shipDef && b->shipLayout.has_value())
if (recipe || shipDef)
{
for (const PlacedModule& pm : b->shipLayout->placedModules)
double durationSeconds = recipe
? recipe->durationSeconds
: shipDef->schematic.productionTimeSeconds;
if (shipDef && b->shipLayout.has_value())
{
for (const ModuleDef& modDef : m_config->modules.modules)
for (const PlacedModule& pm : b->shipLayout->placedModules)
{
if (modDef.id == pm.moduleId)
for (const ModuleDef& modDef : m_config->modules.modules)
{
durationSeconds += modDef.productionTimeSeconds;
break;
if (modDef.id == pm.moduleId)
{
durationSeconds += modDef.productionTimeSeconds;
break;
}
}
}
}
}
bufText += tr("Cycle: %1 s\n").arg(durationSeconds, 0, 'f', 1);
bufText += tr("Cycle: %1 s\n").arg(durationSeconds, 0, 'f', 1);
if (b->production.has_value())
{
const Tick cycleTicks = secondsToTicks(durationSeconds);
const Tick completesAt = b->production->completesAt;
const Tick currentTick = m_sim->currentTick();
const Tick elapsed = currentTick - (completesAt - cycleTicks);
const int pct = static_cast<int>(
std::max(Tick(0), std::min(cycleTicks, elapsed)) * 100 / cycleTicks);
bufText += tr("Progress: %1%\n").arg(pct);
if (b->production.has_value())
{
const Tick cycleTicks = secondsToTicks(durationSeconds);
const Tick completesAt = b->production->completesAt;
const Tick currentTick = m_sim->currentTick();
const Tick elapsed = currentTick - (completesAt - cycleTicks);
const int pct = static_cast<int>(
std::max(Tick(0), std::min(cycleTicks, elapsed)) * 100 / cycleTicks);
bufText += tr("Progress: %1%\n").arg(pct);
}
else
{
bufText += tr("Progress: idle\n");
}
}
else
{
// Auto-recipe building with no active cycle: no single recipe to
// show a cycle time for.
bufText += tr("Progress: idle\n");
}
}