make recipe selection and buffers of smelter and reprocessing plant behave like other buildings, except that a recipe may be chosen automatically

This commit is contained in:
2026-08-12 23:24:31 +02:00
parent 4ee6438405
commit 9bbade2420
18 changed files with 363 additions and 335 deletions

View File

@@ -27,7 +27,7 @@ struct ItemProduction
//
// What counts as available differs by building type, because only Miner and Assembler
// recipes are individually unlocked (REQ-LOCK-UI-RECIPE): those are filtered by the
// unlock state, while a Smelter's or Reprocessing Plant's implicit recipes
// unlock state, while a Smelter's or Reprocessing Plant's recipes
// (REQ-BLD-SMELTER, REQ-BLD-REPROCESSING) are filtered by whether their building is
// unlocked yet (REQ-LOCK-BUILDING) -- there is no sense in naming a path through a
// plant the player cannot place.

View File

@@ -50,7 +50,13 @@ std::vector<RecipeSelectionOption> buildRecipeSelectionOptions(
BuildingType type, Simulation& sim, const GameConfig& config)
{
std::vector<RecipeSelectionOption> options;
options.push_back({std::string(), QObject::tr("(None)"), RecipeLineRow::Spec()});
// The clearing option. On a building that picks its own recipe it does not leave the
// building idle but hands it back to that selection, and says so
// (REQ-UI-SELECT-OPTIONS, REQ-BLD-AUTO-RECIPE).
options.push_back({std::string(),
isAutoRecipeBuildingType(type) ? QObject::tr("(Auto)")
: QObject::tr("(None)"),
RecipeLineRow::Spec()});
if (type == BuildingType::Shipyard)
{

View File

@@ -1,75 +0,0 @@
#include "AutoProductionContent.h"
#include "Building.h"
#include "BuildingTarget.h"
#include "GameConfig.h"
#include "ProductionRules.h"
AutoProductionContent::AutoProductionContent(const SelectionContext& context,
const SelectionRequest& request,
QWidget* parent)
: BufferedBuildingContent(context, request.buildings.front(), parent)
{
}
BufferedBuildingContent::CycleInfo AutoProductionContent::getCycleInfo(
const BuildingTarget& target) const
{
CycleInfo info;
// An auto-recipe building always runs an implicit recipe (REQ-BLD-SMELTER,
// REQ-BLD-REPROCESSING), so its production section is always shown -- but only a
// running cycle names a recipe, so while it is idle there is no cycle to describe.
info.runsProduction = true;
if (target.building)
{
// What the building handles at all, so its buffers are not blank whenever it
// happens to be between cycles (REQ-UI-SINGLE-SELECTION). This is the same union
// of every recipe of its type that the simulation sized the buffers over, and
// the locked ones among them are dropped when the card lists them.
for (const RecipeDef* recipe :
gatherCandidateRecipes(*getContext().config, *target.building))
{
for (const RecipeIngredient& ingredient : recipe->inputs)
{
info.handledInputs.push_back(ingredient.item);
}
for (const RecipeOutput& output : recipe->outputs)
{
info.handledOutputs.push_back(output.item);
}
}
}
// Which recipe describes the cycle: the one running, or -- between cycles -- the one
// that ran last. Dropping it while idle would take the summary row and the chips'
// per-cycle amounts away and bring them back with every cycle, resizing the card in
// step with the building's status (REQ-UI-RECIPE-SUMMARY). Only a building that has
// never run has nothing to describe.
if (target.building && target.building->production.has_value())
{
m_lastRecipeId = target.building->production->recipeId;
}
if (!target.building || m_lastRecipeId.empty())
{
return info;
}
const RecipeDef* recipe =
getContext().config->recipes.findRecipeDef(m_lastRecipeId, target.type);
if (!recipe)
{
return info;
}
for (const RecipeIngredient& ingredient : recipe->inputs)
{
info.perCycleInputs[ingredient.item] = ingredient.amount;
}
for (const RecipeOutput& output : recipe->outputs)
{
info.perCycleOutputs[output.item] = output.amount;
}
info.durationSeconds = recipe->durationSeconds;
return info;
}

View File

@@ -1,29 +0,0 @@
#pragma once
#include <string>
#include "BufferedBuildingContent.h"
#include "SelectionContentFactory.h"
// The card for a Smelter or a Reprocessing Plant (REQ-UI-SELECTION-CONTENT). Both
// auto-process whatever they receive and have no player-facing recipe selection
// (REQ-BLD-SMELTER, REQ-BLD-REPROCESSING), so the card has no configuration group at
// all; its cycle is whichever recipe is in production, or the last one that was.
class AutoProductionContent : public BufferedBuildingContent
{
Q_OBJECT
public:
AutoProductionContent(const SelectionContext& context,
const SelectionRequest& request, QWidget* parent = nullptr);
protected:
CycleInfo getCycleInfo(const BuildingTarget& target) const override;
private:
// The recipe last seen in production, which keeps describing the cycle while the
// building sits between cycles (REQ-UI-RECIPE-SUMMARY). Mutable because it is a
// record of what getCycleInfo() has observed rather than state of its own: the card
// shows the same thing whether or not it has been asked before.
mutable std::string m_lastRecipeId;
};

View File

@@ -20,7 +20,6 @@ SET(HDRS
${CMAKE_CURRENT_SOURCE_DIR}/ClearBeltControl.h
${CMAKE_CURRENT_SOURCE_DIR}/BufferedBuildingContent.h
${CMAKE_CURRENT_SOURCE_DIR}/RecipeProductionContent.h
${CMAKE_CURRENT_SOURCE_DIR}/AutoProductionContent.h
${CMAKE_CURRENT_SOURCE_DIR}/ShipyardContent.h
${CMAKE_CURRENT_SOURCE_DIR}/StorageContent.h
${CMAKE_CURRENT_SOURCE_DIR}/HqContent.h
@@ -55,7 +54,6 @@ SET(SRCS
${CMAKE_CURRENT_SOURCE_DIR}/ClearBeltControl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/BufferedBuildingContent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/RecipeProductionContent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/AutoProductionContent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ShipyardContent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/StorageContent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/HqContent.cpp

View File

@@ -1,6 +1,5 @@
#include "SelectionContentFactory.h"
#include "AutoProductionContent.h"
#include "BeltContent.h"
#include "DebrisContent.h"
#include "FactoryQueries.h"
@@ -27,10 +26,9 @@ SelectionContentKind getKindForType(BuildingType type)
{
case BuildingType::Miner:
case BuildingType::Assembler:
return SelectionContentKind::RecipeProduction;
case BuildingType::Smelter:
case BuildingType::ReprocessingPlant:
return SelectionContentKind::AutoProduction;
return SelectionContentKind::RecipeProduction;
case BuildingType::Shipyard:
return SelectionContentKind::Shipyard;
case BuildingType::SalvageBay:
@@ -160,8 +158,6 @@ SelectionContent* createContent(const ContentKey& key, const SelectionRequest& r
return nullptr;
case SelectionContentKind::RecipeProduction:
return new RecipeProductionContent(context, request, parent);
case SelectionContentKind::AutoProduction:
return new AutoProductionContent(context, request, parent);
case SelectionContentKind::Shipyard:
return new ShipyardContent(context, request, parent);
case SelectionContentKind::Storage:

View File

@@ -31,8 +31,7 @@ struct SelectionRequest
enum class SelectionContentKind
{
None, // nothing selected: the panel hides itself entirely
RecipeProduction, // Miner, Assembler
AutoProduction, // Smelter, Reprocessing Plant
RecipeProduction, // Miner, Assembler, Smelter, Reprocessing Plant
Shipyard,
Storage, // Salvage Bay
Hq,