Replace recipe/schematic dropdowns with button and selection dialog

This commit is contained in:
2026-06-22 12:17:42 +02:00
parent c43225b6fa
commit c7218c7c1e
10 changed files with 318 additions and 50 deletions

View File

@@ -247,7 +247,7 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
### Module UI
- REQ-MOD-UI-PREVIEW: When a schematic is selected in a shipyard's selected building panel, a small non-interactive **ship layout preview** widget is shown below the schematic dropdown. The preview renders the ship's layout grid at a reduced scale: buildable cells without a module are shown as white, non-buildable cells are shown as black, and cells occupied by a module are shown in that module's `fill_color` with the module's `glyph` character. Below the preview, a "Configure" button is shown.
- REQ-MOD-UI-PREVIEW: When a schematic is selected in a shipyard's selected building panel, a small non-interactive **ship layout preview** widget is shown below the schematic selection button (REQ-UI-SELECT-BUTTON). The preview renders the ship's layout grid at a reduced scale: buildable cells without a module are shown as white, non-buildable cells are shown as black, and cells occupied by a module are shown in that module's `fill_color` with the module's `glyph` character. Below the preview, a "Configure" button is shown.
- REQ-MOD-UI-DIALOG: Clicking the "Configure" button opens the **layout configuration dialog** as a modal. While the dialog is open, the game is paused (speed set to 0×). On close, the game speed is restored to what it was before the dialog was opened.
The dialog contains:
@@ -318,7 +318,7 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
For a **ship or module schematic**: if the player does not yet have the schematic, it is unlocked (ship schematics unlock the corresponding shipyard selection; module schematics unlock the module type for placement in the layout configuration dialog (REQ-MOD-UI-DIALOG)). If the player already has it, the schematic's `player_production_level` is incremented by 1 — for ship schematics, subsequent ships of that type are produced at a higher level; for module schematics, all instances of that module type use the higher level in their stat formulas.
For an **assembler recipe schematic**: the recipe is explicitly unlocked and becomes available in the assembler recipe-selection dropdown (subject to REQ-LOCK-UI-RECIPE). The schematic is removed from the drop pool permanently (REQ-LOCK-EXPLICIT). The implicit unlock set is recomputed (REQ-LOCK-IMPLICIT).
For an **assembler recipe schematic**: the recipe is explicitly unlocked and becomes available in the assembler recipe-selection dialog (subject to REQ-LOCK-UI-RECIPE). The schematic is removed from the drop pool permanently (REQ-LOCK-EXPLICIT). The implicit unlock set is recomputed (REQ-LOCK-IMPLICIT).
## Progression & Locking
@@ -326,15 +326,15 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
- REQ-LOCK-IMPLICIT: Item types and miner/assembler recipes are **implicitly** unlocked or locked based on the current set of unlocked ship, module, and assembler recipe schematics. The implicit unlock set is recomputed whenever any schematic changes lock state (on Restart or after REQ-DEF-SCHEMATIC-DROP). Computation:
1. Start with the union of: (a) all item types listed in `materials` across all currently unlocked ship schematics and all currently unlocked module schematics, and (b) the output item type of every currently explicitly unlocked assembler recipe schematic (REQ-LOCK-EXPLICIT).
2. For each item type in the current set: for every recipe (miner, smelter, or assembler) that produces it — skipping any assembler recipe schematic that defines `unlock_at_station_level` and is not yet explicitly unlocked — add each of that recipe's input item types to the set. If the recipe is a miner recipe or an assembler recipe that does not define `unlock_at_station_level`, mark it as implicitly unlocked. Explicitly unlocked assembler recipe schematics are available in the assembler dropdown by virtue of REQ-LOCK-EXPLICIT; their inputs are also added to the implicit set in this step.
2. For each item type in the current set: for every recipe (miner, smelter, or assembler) that produces it — skipping any assembler recipe schematic that defines `unlock_at_station_level` and is not yet explicitly unlocked — add each of that recipe's input item types to the set. If the recipe is a miner recipe or an assembler recipe that does not define `unlock_at_station_level`, mark it as implicitly unlocked. Explicitly unlocked assembler recipe schematics are available in the assembler recipe-selection dialog by virtue of REQ-LOCK-EXPLICIT; their inputs are also added to the implicit set in this step.
3. Repeat step 2 until no new item types are added.
Item types and miner/assembler recipes not reached by this process (and not explicitly unlocked) are locked. Smelter recipes participate in the traversal to propagate unlocking to their inputs but are never themselves shown in any UI dropdown.
- REQ-LOCK-REPROCESSING-POOL: The pool of possible outputs for a Reprocessing Plant cycle (REQ-BLD-REPROCESSING) is restricted to item types that are currently implicitly unlocked (REQ-LOCK-IMPLICIT). Weights are renormalized over the eligible outputs. If no eligible outputs remain, the Reprocessing Plant cannot start a production cycle.
- REQ-LOCK-UI-RECIPE: Locked miner ore-type recipes and assembler recipes are not shown in their respective recipe-selection dropdowns.
- REQ-LOCK-UI-RECIPE: Locked miner ore-type recipes and assembler recipes are not shown in their respective recipe-selection dialogs (REQ-UI-SELECT-BUTTON).
- REQ-LOCK-UI-SCHEMATIC: Locked ship schematics are not shown in the shipyard's schematic-selection dropdown.
- REQ-LOCK-UI-SCHEMATIC: Locked ship schematics are not shown in the shipyard's schematic-selection dialog (REQ-UI-SELECT-BUTTON).
- REQ-LOCK-UI-SPLITTER: Item types that are not implicitly unlocked are excluded from splitter filter dropdowns (REQ-BLD-SPLITTER).
@@ -434,7 +434,11 @@ The screen is divided into two columns: a main column (75% width) containing the
- REQ-UI-PRODUCTION-PROGRESS: For buildings that produce items or ships (miner, smelter, assembler, reprocessing plant, shipyard), the selected building panel also shows: (a) the cycle time of the currently selected recipe or schematic in seconds, and (b) the completion percentage of the active production cycle as an integer (e.g. `42%`), or the text `idle` when no production cycle is active. When no recipe or schematic is selected, neither the cycle time nor the progress indicator is shown.
- REQ-UI-MULTI-SELECT: The player selects multiple buildings by box-drag or by Ctrl+clicking individual buildings to add or remove them from the selection.
- REQ-UI-MULTI-SELECTION: When multiple buildings are selected, the panel shows how many of each building type are selected. No per-building detail is shown.
- REQ-UI-CONFIG-INLINE: Recipe, schematic, ship stance, and target priority configuration for a selected building is shown and changed inline within this panel. For shipyards, the panel additionally shows the ship layout preview and "Configure" button below the schematic dropdown (REQ-MOD-UI-PREVIEW).
- REQ-UI-CONFIG-INLINE: Recipe, schematic, ship stance, and target priority configuration for a selected building is shown within this panel. Ship stance and target priority are selected and changed inline. Recipe selection (miner, assembler) and schematic selection (shipyard) use the selection button and dialog (REQ-UI-SELECT-BUTTON) rather than an inline control. For shipyards, the panel additionally shows the ship layout preview and "Configure" button below the schematic selection button (REQ-MOD-UI-PREVIEW).
- REQ-UI-SELECT-BUTTON: **Recipe and schematic selection control.** Recipe selection (Miner ore type, Assembler recipe) and schematic selection (Shipyard) are each presented in the selected building panel as a single **selection button** whose caption is the name of the currently selected recipe or schematic, or a placeholder ("Select recipe" / "Select schematic") when none is selected. Clicking the button opens a modal **selection dialog** that pauses the game (speed set to 0×; on close, the speed is restored to what it was before the dialog was opened). The dialog contains a grid of option buttons, one per selectable option — only options that are currently unlocked are shown (REQ-LOCK-UI-RECIPE for recipes, REQ-LOCK-UI-SCHEMATIC for schematics). Hovering an option button shows the selection info tooltip (REQ-UI-SELECT-TOOLTIP). Clicking an option button selects that recipe/schematic, closes the dialog, and updates the selection button's caption in the selected building panel. The dialog can be dismissed without changing the current selection (e.g. closing it without clicking an option). Selecting a new recipe or schematic has the same effects as before (REQ-MAT-INPUT-BUFFER, REQ-MAT-OUTPUT-BUFFER, REQ-BLD-SHIPYARD).
- REQ-UI-SELECT-TOOLTIP: **Selection info tooltip.** Hovering an option button in the selection dialog (REQ-UI-SELECT-BUTTON), and hovering the selection button in the selected building panel when a selection is set, displays an info tooltip:
- For a **recipe** (Miner or Assembler): the recipe name; the name and quantity of each input item (no inputs are listed for miner recipes, which consume nothing); the completion time (`duration_seconds`); and the name and quantity of the produced output item.
- For a **ship schematic** (Shipyard): the ship's `display_name`; the name and quantity of each base required material (`[ship.schematic].materials`, excluding any module contributions); the base production time (`[ship.schematic].production_time_seconds`); and "Produces: 1 <ship display name>".
- REQ-UI-BELT-CLEAR: When one or more belt, splitter, tunnel entry, or tunnel exit tiles are selected, the panel shows a "Clear" button that removes all items from the selected tiles. Clearing a tunnel entry or exit also discards all items currently in transit through that tunnel (REQ-BLD-TUNNEL-TRANSIT). This can be used to resolve stalled belts, splitters, and tunnels.
- REQ-UI-ENTITY-CLICK-SELECT: The player can click any ship (player or enemy) or any defence station (player or enemy) in the game world to select it. Clicking a ship or defence station clears any existing selection and establishes a single-entity selection containing only that entity. Ships and defence stations cannot participate in multi-select together with buildings. Clicking empty world space (no building, ship, or defence station) clears the selection.
- REQ-UI-SHIP-STATS-PANEL: When a single ship is selected (REQ-UI-ENTITY-CLICK-SELECT), the selected building panel shows a **ship stats panel**. The panel structure mirrors REQ-MOD-UI-STATS-PANEL but reflects the ship's actual live state: stats are computed at the ship's actual level with its installed modules per REQ-MOD-STAT-CALC. The panel always shows all hull stats: HP (current / maximum), max linear speed, sensor range, main acceleration, maneuvering acceleration, angular acceleration, and max rotation speed. In addition, capability module summaries are shown conditioned on which module types are installed, using the same aggregation rules as REQ-MOD-UI-STATS-PANEL: weapons (combined DPS, maximum range), salvage (combined collection rate, maximum range), and repair (combined repair rate, maximum range), each section appearing only if at least one instance of that module type is installed. While debug draw mode is active (REQ-UI-DEBUG-DRAW), the panel additionally shows the ship's derived threat cost (REQ-MOD-THREAT).

View File

@@ -20,6 +20,7 @@ SET(HDRS
${CMAKE_CURRENT_SOURCE_DIR}/ExitBlueprintModeRequestedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/SpeedChangeRequestedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/LayoutDialogRequestedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/RecipeSelectionRequestedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/InspectWindowClosedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/ArenaStartRequestedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/ArenaInspectRequestedEvent.h

View File

@@ -0,0 +1,12 @@
#pragma once
#include "BuildingId.h"
#include "Event.h"
class RecipeSelectionRequestedEvent : public Event
{
public:
explicit RecipeSelectionRequestedEvent(BuildingId buildingId)
: buildingId(buildingId) {}
const BuildingId buildingId;
};

View File

@@ -12,6 +12,7 @@ SET(HDRS
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutPreview.h
${CMAKE_CURRENT_SOURCE_DIR}/ShipStatsPanel.h
${CMAKE_CURRENT_SOURCE_DIR}/SchematicChoiceDialog.h
${CMAKE_CURRENT_SOURCE_DIR}/RecipeSelectionDialog.h
PARENT_SCOPE
)
@@ -28,5 +29,6 @@ SET(SRCS
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutPreview.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ShipStatsPanel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/SchematicChoiceDialog.cpp
${CMAKE_CURRENT_SOURCE_DIR}/RecipeSelectionDialog.cpp
PARENT_SCOPE
)

View File

@@ -17,6 +17,7 @@
#include "BuildingSystem.h"
#include "ConfigLoader.h"
#include "GameWorldView.h"
#include "RecipeSelectionDialog.h"
#include "SchematicChoiceDialog.h"
#include "HeaderBar.h"
#include "SelectedBuildingPanel.h"
@@ -232,6 +233,35 @@ void MainWindow::handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> e
m_gameWorldView->resetFrameTimer();
}
void MainWindow::handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent> event)
{
const double prevSpeed = m_gameWorldView->gameSpeed();
m_gameWorldView->setGameSpeed(0.0);
const Building* b = m_sim->buildings().findBuilding(event->buildingId);
if (!b)
{
m_gameWorldView->setGameSpeed(prevSpeed);
m_gameWorldView->resetFrameTimer();
return;
}
const std::vector<RecipeSelectionOption> options =
buildRecipeSelectionOptions(*b, *m_sim, m_sim->config());
const QString title = (b->type == BuildingType::Shipyard)
? tr("Select Schematic")
: tr("Select Recipe");
RecipeSelectionDialog dialog(options, title, this);
if (dialog.exec() == QDialog::Accepted && dialog.getChosenId().has_value())
{
m_sim->buildings().setRecipe(event->buildingId, *dialog.getChosenId());
}
m_gameWorldView->setGameSpeed(prevSpeed);
m_gameWorldView->resetFrameTimer();
}
void MainWindow::handleEvent(std::shared_ptr<const GameOverEvent> /*event*/)
{
const Tick tick = m_sim->currentTick();

View File

@@ -11,6 +11,7 @@
#include "EventHandler.h"
#include "GameOverEvent.h"
#include "LayoutDialogRequestedEvent.h"
#include "RecipeSelectionRequestedEvent.h"
#include "SchematicChoicesAvailableEvent.h"
#include "ShipLayoutBlueprint.h"
#include "Tick.h"
@@ -30,7 +31,8 @@ class MainWindow : public QWidget,
SchematicChoicesAvailableEvent,
GameOverEvent,
EscapeMenuRequestedEvent,
LayoutDialogRequestedEvent>
LayoutDialogRequestedEvent,
RecipeSelectionRequestedEvent>
{
Q_OBJECT
@@ -48,6 +50,7 @@ private:
void handleEvent(std::shared_ptr<const GameOverEvent> event) override;
void handleEvent(std::shared_ptr<const EscapeMenuRequestedEvent> event) override;
void handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> event) override;
void handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent> event) override;
void layoutPanels();
private:

View File

@@ -0,0 +1,172 @@
#include "RecipeSelectionDialog.h"
#include <cstddef>
#include <QGridLayout>
#include <QPushButton>
#include <QStringList>
#include <QVBoxLayout>
#include "Building.h"
#include "BuildingType.h"
#include "DisplayName.h"
#include "GameConfig.h"
#include "RecipesConfig.h"
#include "ShipsConfig.h"
#include "Simulation.h"
namespace
{
QString itemLine(const std::string& itemId, int amount)
{
return QStringLiteral(" ")
+ QString::fromStdString(toDisplayName(itemId))
+ QStringLiteral(" ×") + QString::number(amount);
}
QString recipeTooltip(const RecipeDef& recipe)
{
QStringList lines;
lines << QObject::tr("Recipe: %1")
.arg(QString::fromStdString(toDisplayName(recipe.id)));
if (recipe.inputs.empty())
{
lines << QObject::tr("Inputs: none");
}
else
{
lines << QObject::tr("Inputs:");
for (const RecipeIngredient& ingredient : recipe.inputs)
{
lines << itemLine(ingredient.item, ingredient.amount);
}
}
lines << QObject::tr("Completion time: %1 s").arg(recipe.durationSeconds);
if (!recipe.outputs.empty())
{
lines << QObject::tr("Produces:");
for (const RecipeOutput& output : recipe.outputs)
{
lines << itemLine(output.item, output.amount);
}
}
return lines.join('\n');
}
QString shipTooltip(const ShipDef& def)
{
const QString name = QString::fromStdString(toDisplayName(def.id));
QStringList lines;
lines << QObject::tr("Ship: %1").arg(name);
if (def.schematic.materials.empty())
{
lines << QObject::tr("Materials: none");
}
else
{
lines << QObject::tr("Materials:");
for (const RecipeIngredient& material : def.schematic.materials)
{
lines << itemLine(material.item, material.amount);
}
}
lines << QObject::tr("Completion time: %1 s")
.arg(def.schematic.productionTimeSeconds);
lines << QObject::tr("Produces: 1 %1").arg(name);
return lines.join('\n');
}
} // namespace
std::vector<RecipeSelectionOption> buildRecipeSelectionOptions(
const Building& building, Simulation& sim, const GameConfig& config)
{
std::vector<RecipeSelectionOption> options;
options.push_back({std::string(), QObject::tr("(None)"), QString()});
if (building.type == BuildingType::Shipyard)
{
for (const ShipDef& def : config.ships.ships)
{
if (!sim.isSchematicUnlocked(def.id)) { continue; }
options.push_back({def.id,
QString::fromStdString(toDisplayName(def.id)),
shipTooltip(def)});
}
}
else
{
for (const RecipeDef& recipe : config.recipes.recipes)
{
if (recipe.building != building.type) { continue; }
if ((building.type == BuildingType::Miner
|| building.type == BuildingType::Assembler)
&& !sim.isRecipeUnlocked(recipe.id))
{
continue;
}
options.push_back({recipe.id,
QString::fromStdString(toDisplayName(recipe.id)),
recipeTooltip(recipe)});
}
}
return options;
}
RecipeSelectionDialog::RecipeSelectionDialog(
const std::vector<RecipeSelectionOption>& options,
const QString& title, QWidget* parent)
: QDialog(parent)
{
setWindowTitle(title);
setModal(true);
QVBoxLayout* mainLayout = new QVBoxLayout(this);
QGridLayout* grid = new QGridLayout();
mainLayout->addLayout(grid);
const int columnCount = 2;
for (int i = 0; i < static_cast<int>(options.size()); ++i)
{
const RecipeSelectionOption& option = options[static_cast<std::size_t>(i)];
QPushButton* button = new QPushButton(option.caption, this);
if (!option.tooltip.isEmpty())
{
button->setToolTip(option.tooltip);
}
button->setMinimumHeight(40);
grid->addWidget(button, i / columnCount, i % columnCount);
m_optionIds.push_back(option.id);
const int index = i;
connect(button, &QPushButton::clicked, this, [this, index]()
{
onOptionClicked(index);
});
}
}
std::optional<std::string> RecipeSelectionDialog::getChosenId() const
{
return m_chosenId;
}
void RecipeSelectionDialog::onOptionClicked(int index)
{
if (index >= 0 && index < static_cast<int>(m_optionIds.size()))
{
m_chosenId = m_optionIds[static_cast<std::size_t>(index)];
}
accept();
}

View File

@@ -0,0 +1,50 @@
#pragma once
#include <optional>
#include <string>
#include <vector>
#include <QDialog>
#include <QString>
struct Building;
struct GameConfig;
class Simulation;
class QPushButton;
// One selectable entry in the recipe/schematic selection dialog
// (REQ-UI-SELECT-BUTTON). The "(None)" entry uses an empty id.
struct RecipeSelectionOption
{
std::string id;
QString caption;
QString tooltip;
};
// Builds the lock-aware list of selectable options for a production building,
// with display captions and info tooltips (REQ-UI-SELECT-BUTTON,
// REQ-UI-SELECT-TOOLTIP). The first entry is always a "(None)" option with an
// empty id. Shared by the selection dialog and the panel selection button so
// both render identical captions and tooltips.
std::vector<RecipeSelectionOption> buildRecipeSelectionOptions(
const Building& building, Simulation& sim, const GameConfig& config);
// Modal dialog showing a grid of option buttons (REQ-UI-SELECT-BUTTON). The
// game is paused by the caller while it is open. Clicking an option selects it
// and closes the dialog; dismissing it (close/Esc) leaves no choice.
class RecipeSelectionDialog : public QDialog
{
Q_OBJECT
public:
RecipeSelectionDialog(const std::vector<RecipeSelectionOption>& options,
const QString& title, QWidget* parent = nullptr);
std::optional<std::string> getChosenId() const;
private:
void onOptionClicked(int index);
std::vector<std::string> m_optionIds;
std::optional<std::string> m_chosenId;
};

View File

@@ -6,7 +6,6 @@
#include <set>
#include <string>
#include <QComboBox>
#include <QLabel>
#include <QListWidget>
#include <QPushButton>
@@ -32,6 +31,8 @@
#include "ItemType.h"
#include "LayoutDialogRequestedEvent.h"
#include "ModulesConfig.h"
#include "RecipeSelectionDialog.h"
#include "RecipeSelectionRequestedEvent.h"
#include "Rotation.h"
#include "ShipLayoutPreview.h"
#include "Simulation.h"
@@ -110,7 +111,7 @@ SelectedBuildingPanel::SelectedBuildingPanel(Simulation* sim,
m_layout->setAlignment(Qt::AlignTop);
m_titleLabel = new QLabel(this);
m_recipeCombo = new QComboBox(this);
m_recipeSelectButton = new QPushButton(this);
m_clearBeltBtn = new QPushButton(tr("Clear Items"), this);
m_filterALabel = new QLabel(this);
m_filterAList = new QListWidget(this);
@@ -125,7 +126,7 @@ SelectedBuildingPanel::SelectedBuildingPanel(Simulation* sim,
m_filterBList->setMaximumHeight(100);
m_layout->addWidget(m_titleLabel);
m_layout->addWidget(m_recipeCombo);
m_layout->addWidget(m_recipeSelectButton);
m_layout->addWidget(m_layoutPreview);
m_layout->addWidget(m_configureLayoutBtn);
m_layout->addWidget(m_clearBeltBtn);
@@ -135,8 +136,8 @@ SelectedBuildingPanel::SelectedBuildingPanel(Simulation* sim,
m_layout->addWidget(m_filterBList);
m_layout->addWidget(m_buffersLabel);
connect(m_recipeCombo, qOverload<int>(&QComboBox::currentIndexChanged),
this, &SelectedBuildingPanel::onRecipeChanged);
connect(m_recipeSelectButton, &QPushButton::clicked,
this, &SelectedBuildingPanel::onSelectRecipeClicked);
connect(m_clearBeltBtn, &QPushButton::clicked,
this, &SelectedBuildingPanel::onClearBelt);
connect(m_configureLayoutBtn, &QPushButton::clicked, this, [this]() {
@@ -206,7 +207,7 @@ void SelectedBuildingPanel::rebuild()
void SelectedBuildingPanel::hideAllWidgets()
{
m_titleLabel->hide();
m_recipeCombo->hide();
m_recipeSelectButton->hide();
m_layoutPreview->hide();
m_configureLayoutBtn->hide();
m_clearBeltBtn->hide();
@@ -285,43 +286,33 @@ void SelectedBuildingPanel::buildSingle(BuildingId id)
if (isProductionBuilding(b->type))
{
m_recipeCombo->blockSignals(true);
m_recipeCombo->clear();
const std::vector<RecipeSelectionOption> options =
buildRecipeSelectionOptions(*b, *m_sim, *m_config);
m_recipeCombo->addItem(tr("(none)"), QString());
if (b->type == BuildingType::Shipyard)
const RecipeSelectionOption* current = nullptr;
for (const RecipeSelectionOption& option : options)
{
for (const ShipDef& def : m_config->ships.ships)
if (option.id == b->recipeId)
{
if (m_sim->isSchematicUnlocked(def.id))
{
m_recipeCombo->addItem(
QString::fromStdString(def.id),
QString::fromStdString(def.id));
}
current = &option;
break;
}
}
if (current && !current->id.empty())
{
m_recipeSelectButton->setText(current->caption);
m_recipeSelectButton->setToolTip(current->tooltip);
}
else
{
for (const RecipeDef& recipe : m_config->recipes.recipes)
{
if (recipe.building != b->type) { continue; }
if ((b->type == BuildingType::Miner || b->type == BuildingType::Assembler)
&& !m_sim->isRecipeUnlocked(recipe.id))
{
continue;
}
m_recipeCombo->addItem(
QString::fromStdString(recipe.id),
QString::fromStdString(recipe.id));
}
const QString placeholder = (b->type == BuildingType::Shipyard)
? tr("Select schematic")
: tr("Select recipe");
m_recipeSelectButton->setText(placeholder);
m_recipeSelectButton->setToolTip(QString());
}
const int currentIdx = m_recipeCombo->findData(QString::fromStdString(b->recipeId));
m_recipeCombo->setCurrentIndex(currentIdx >= 0 ? currentIdx : 0);
m_recipeCombo->blockSignals(false);
m_recipeCombo->show();
m_recipeSelectButton->show();
if (b->type == BuildingType::Shipyard && !b->recipeId.empty())
{
@@ -352,7 +343,7 @@ void SelectedBuildingPanel::buildSingle(BuildingId id)
}
else
{
m_recipeCombo->hide();
m_recipeSelectButton->hide();
m_layoutPreview->hide();
m_configureLayoutBtn->hide();
}
@@ -582,7 +573,7 @@ void SelectedBuildingPanel::handleEvent(std::shared_ptr<const TickAdvancedEvent>
void SelectedBuildingPanel::buildMulti(const std::vector<BuildingId>& ids)
{
m_singleBuildingId = kInvalidBuildingId;
m_recipeCombo->hide();
m_recipeSelectButton->hide();
m_clearBeltBtn->hide();
m_filterALabel->hide();
m_filterAList->hide();
@@ -626,14 +617,18 @@ void SelectedBuildingPanel::buildMulti(const std::vector<BuildingId>& ids)
}
}
void SelectedBuildingPanel::onRecipeChanged(int comboIndex)
void SelectedBuildingPanel::onSelectRecipeClicked()
{
if (m_singleBuildingId == kInvalidBuildingId)
{
return;
}
const QString recipeId = m_recipeCombo->itemData(comboIndex).toString();
m_sim->buildings().setRecipe(m_singleBuildingId, recipeId.toStdString());
// The emit is synchronous: MainWindow pauses the game, runs the modal
// selection dialog, applies the chosen recipe/schematic, and restores the
// speed before this returns. rebuild() then refreshes the button caption,
// tooltip, preview, and buffers for the new selection.
EventManager::getInstance()->sendEventImmediately(
std::make_shared<RecipeSelectionRequestedEvent>(m_singleBuildingId));
rebuild();
}

View File

@@ -26,7 +26,6 @@ class Simulation;
class ShipLayoutPreview;
class ShipStatsPanel;
class QLabel;
class QComboBox;
class QListWidget;
class QPushButton;
class QVBoxLayout;
@@ -51,7 +50,7 @@ private:
void handleEvent(std::shared_ptr<const DebugDrawToggledEvent> event) override;
private slots:
void onRecipeChanged(int comboIndex);
void onSelectRecipeClicked();
void onClearBelt();
void onSplitterFilterChanged();
@@ -75,7 +74,7 @@ private:
QVBoxLayout* m_layout;
QLabel* m_titleLabel;
QComboBox* m_recipeCombo;
QPushButton* m_recipeSelectButton;
QPushButton* m_clearBeltBtn;
QLabel* m_filterALabel;
QListWidget* m_filterAList;