let the smelter and the plant hold one recipe like everyone else
Their buffers were unioned over every recipe of their type, so a single smelter accepted all four ores at once, held three kinds of ingot, and ran whichever recipe happened to be satisfiable. Four recipes coexisted in one building. They now hold exactly one recipe, sized and cleared like any other building's, with one addition: while none is set, the first material offered at an input port that one of their recipes consumes selects it. That hook sits at both intake paths -- the belt pull and the direct coupling -- because a building that accepts nothing would otherwise leave a coupled producer stuck at its port for good. Ports are walked in order and config order breaks a tie, so the choice is deterministic. Once set the recipe never changes on its own, so a material belonging to another of its recipes is simply refused. Changing it is the player's, through the ordinary dialog, which clears the buffers and thereby also frees a building left holding part of a cycle nothing feeds any more; the dialog's clearing option reads (Auto) there, since it returns the building to selecting its own. Falling out of this: gatherCandidateRecipes collapses to getSelectedRecipe, so tickProduction loses its candidate loop; initAutoBuffers and its union are gone; these buildings can now be grey; and AutoProductionContent is deleted, its two reasons for existing (the buffer-chip union and the "recipe it ran last" fallback) having been artefacts of holding no recipe. Also makes them configurable for blueprint purposes -- they carry a recipe now, so a blueprint of one has something to transfer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user