Use std::optional instead of sentinel values for absent data

Replace sentinel values that signalled "no value" with std::optional
across simulation, UI, and balancing code:

- BuildingId references: DeliverScrapBehavior::deliveryBay,
  Simulation::m_hqBuildingId, the return types of BuildingSystem::place
  and Simulation::tryPlaceBuilding (previously kInvalidBuildingId on
  failure), GameWorldView building/site-at-tile lookups and demolish
  hover, SelectedBuildingPanel::m_singleBuildingId.
- Command id fields: the five id-carrying commands now hold
  optional<BuildingId>; CommandSerializer and Simulation::apply updated.
  The serialized replay format is unchanged.
- Index sentinels: BlueprintPanel::m_activeIndex,
  BalancingWindow::m_inspectedArenaIndex, ArenaSimulation winnerTeam,
  and the moduleIndex grid cells in ShipLayoutDialog/ShipLayoutPreview.
  ShipLayoutDialog::m_activeModuleIndex was a tri-state, so it is
  modelled as bool m_removeMode + optional<int>.

Building/ConstructionSite id defaults keep kInvalidBuildingId (identity,
not an absent reference); entt::null and config -1 domain values are
left as-is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc
This commit is contained in:
2026-07-20 07:36:34 +02:00
parent b708e1b29d
commit 388d354e51
32 changed files with 228 additions and 209 deletions

View File

@@ -131,7 +131,6 @@ SelectedBuildingPanel::SelectedBuildingPanel(Simulation* sim,
: QWidget(parent)
, m_sim(sim)
, m_config(config)
, m_singleBuildingId(kInvalidBuildingId)
, m_splitterTile(0, 0)
{
m_layout = new QVBoxLayout(this);
@@ -170,10 +169,10 @@ SelectedBuildingPanel::SelectedBuildingPanel(Simulation* sim,
connect(m_clearBeltBtn, &QPushButton::clicked,
this, &SelectedBuildingPanel::onClearBelt);
connect(m_configureLayoutBtn, &QPushButton::clicked, this, [this]() {
if (m_singleBuildingId != kInvalidBuildingId)
if (m_singleBuildingId.has_value())
{
EventManager::getInstance()->sendEventImmediately(
std::make_shared<LayoutDialogRequestedEvent>(m_singleBuildingId));
std::make_shared<LayoutDialogRequestedEvent>(*m_singleBuildingId));
}
});
connect(m_filterAList, &QListWidget::itemChanged,
@@ -257,7 +256,7 @@ void SelectedBuildingPanel::hideAllWidgets()
void SelectedBuildingPanel::clearContent()
{
m_singleBuildingId = kInvalidBuildingId;
m_singleBuildingId = std::nullopt;
hideAllWidgets();
}
@@ -672,8 +671,8 @@ void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason)
return;
}
if (m_singleBuildingId == kInvalidBuildingId) { return; }
const Building* b = m_sim->getBuildings().findBuilding(m_singleBuildingId);
if (!m_singleBuildingId.has_value()) { return; }
const Building* b = m_sim->getBuildings().findBuilding(*m_singleBuildingId);
if (b)
{
if (m_titleLabel->text().startsWith(tr("(Building) ")))
@@ -686,7 +685,7 @@ void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason)
}
return;
}
const ConstructionSite* s = m_sim->getBuildings().findSite(m_singleBuildingId);
const ConstructionSite* s = m_sim->getBuildings().findSite(*m_singleBuildingId);
if (s)
{
// A periodic tick only advances construction progress, so update just the
@@ -708,7 +707,7 @@ void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason)
void SelectedBuildingPanel::buildMulti(const std::vector<BuildingId>& ids)
{
m_singleBuildingId = kInvalidBuildingId;
m_singleBuildingId = std::nullopt;
m_recipeSelectButton->hide();
m_clearBeltBtn->hide();
m_filterALabel->hide();
@@ -764,7 +763,7 @@ void SelectedBuildingPanel::buildMulti(const std::vector<BuildingId>& ids)
void SelectedBuildingPanel::onSelectRecipeClicked()
{
if (m_singleBuildingId == kInvalidBuildingId)
if (!m_singleBuildingId.has_value())
{
return;
}
@@ -775,7 +774,7 @@ void SelectedBuildingPanel::onSelectRecipeClicked()
// refreshBuffers() path picks up the new schematic (and shows the layout
// preview + Configure Layout button) once the command has been applied.
EventManager::getInstance()->sendEventImmediately(
std::make_shared<RecipeSelectionRequestedEvent>(m_singleBuildingId));
std::make_shared<RecipeSelectionRequestedEvent>(*m_singleBuildingId));
rebuild();
}
@@ -825,7 +824,7 @@ void SelectedBuildingPanel::buildSplitterFilters(
void SelectedBuildingPanel::onSplitterFilterChanged()
{
if (m_singleBuildingId == kInvalidBuildingId)
if (!m_singleBuildingId.has_value())
{
return;
}
@@ -848,7 +847,7 @@ void SelectedBuildingPanel::onSplitterFilterChanged()
{
std::shared_ptr<SetSiteSplitterFiltersCommand> command =
std::make_shared<SetSiteSplitterFiltersCommand>();
command->id = m_singleBuildingId;
command->id = *m_singleBuildingId;
command->filterA = collectFilter(m_filterAList);
command->filterB = collectFilter(m_filterBList);
EventManager::getInstance()->sendEventImmediately(