implement copy building settings with shift + click gesture
This commit is contained in:
@@ -1617,6 +1617,8 @@ void GameWorldView::keyReleaseEvent(QKeyEvent* event)
|
||||
}
|
||||
if (event->key() == Qt::Key_A) { m_scrollLeft = false; }
|
||||
if (event->key() == Qt::Key_D) { m_scrollRight = false; }
|
||||
// Releasing Shift discards the copied building settings (REQ-BLD-COPY-CONFIG).
|
||||
if (event->key() == Qt::Key_Shift) { m_copiedConfig.reset(); }
|
||||
QOpenGLWidget::keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
@@ -1629,6 +1631,15 @@ void GameWorldView::mousePressEvent(QMouseEvent* event)
|
||||
if (m_builderType.has_value()) { exitBuilderMode(); }
|
||||
else if (m_blueprintMode.has_value()) { exitBlueprintMode(); }
|
||||
else if (m_demolishMode) { toggleDemolishMode(); }
|
||||
else if (event->modifiers() & Qt::ShiftModifier)
|
||||
{
|
||||
// Shift + right-click copies a building's settings, but only in the
|
||||
// default selection mode (REQ-BLD-COPY-CONFIG).
|
||||
const QPoint tile = widgetToTile(event->pos());
|
||||
BuildingId id = buildingAtTile(tile);
|
||||
if (id == kInvalidBuildingId) { id = siteAtTile(tile); }
|
||||
if (id != kInvalidBuildingId) { copyConfigFrom(id); }
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1663,6 +1674,20 @@ void GameWorldView::mousePressEvent(QMouseEvent* event)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shift + left-click applies the copied settings to a same-type building
|
||||
// (REQ-BLD-COPY-CONFIG). Consumes the click so it does not change the
|
||||
// selection. Only active in the default selection mode.
|
||||
if ((event->modifiers() & Qt::ShiftModifier) && m_copiedConfig.has_value())
|
||||
{
|
||||
BuildingId id = buildingAtTile(tile);
|
||||
if (id == kInvalidBuildingId) { id = siteAtTile(tile); }
|
||||
if (id != kInvalidBuildingId)
|
||||
{
|
||||
pasteConfigTo(id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const QVector2D worldPos = widgetToWorld(event->pos());
|
||||
const entt::entity hitEntity = entityAtWorldPos(m_sim->admin(), worldPos);
|
||||
|
||||
@@ -1868,6 +1893,81 @@ void GameWorldView::rotateGhost(bool clockwise)
|
||||
}
|
||||
}
|
||||
|
||||
void GameWorldView::copyConfigFrom(BuildingId id)
|
||||
{
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(*m_sim, id);
|
||||
if (!config.has_value()) { return; }
|
||||
|
||||
// Only cache when there is something to copy: a selected recipe / schematic
|
||||
// (Miner, Assembler, Shipyard) or any Splitter (empty filters = accept-all).
|
||||
// Building types with no settings (Smelter, Reprocessing Plant, Salvage Bay,
|
||||
// belts / tunnels, HQ) leave any existing cache untouched (REQ-BLD-COPY-CONFIG).
|
||||
if (!config->recipeId.has_value() && !config->isSplitter) { return; }
|
||||
|
||||
m_copiedConfig = config;
|
||||
}
|
||||
|
||||
void GameWorldView::pasteConfigTo(BuildingId id)
|
||||
{
|
||||
if (!m_copiedConfig.has_value()) { return; }
|
||||
|
||||
const std::optional<BuildingConfig> target = readBuildingConfig(*m_sim, id);
|
||||
if (!target.has_value() || target->type != m_copiedConfig->type) { return; }
|
||||
|
||||
const BuildingConfig& source = *m_copiedConfig;
|
||||
|
||||
// The cached settings were valid on a same-type source building, so they are
|
||||
// valid and available on the target: unlock state is global, so a selected
|
||||
// recipe / schematic (REQ-LOCK-UI-RECIPE, REQ-LOCK-UI-SCHEMATIC) and splitter
|
||||
// filter item types (REQ-LOCK-UI-SPLITTER) remain unlocked. Applying reuses the
|
||||
// same configuration commands as the selected building panel, inheriting their
|
||||
// buffer-clearing and mid-cycle-cancel semantics (REQ-MAT-INPUT-BUFFER,
|
||||
// REQ-MAT-OUTPUT-BUFFER, REQ-BLD-SHIPYARD).
|
||||
if (source.isSplitter)
|
||||
{
|
||||
// Operational splitters are configured by tile; sites by BuildingId
|
||||
// (mirrors SelectedBuildingPanel::onSplitterFilterChanged).
|
||||
if (const Building* building = m_sim->buildings().findBuilding(id))
|
||||
{
|
||||
std::shared_ptr<SetSplitterFiltersCommand> command =
|
||||
std::make_shared<SetSplitterFiltersCommand>();
|
||||
command->tile = building->anchor;
|
||||
command->filterA = source.splitterFilterA;
|
||||
command->filterB = source.splitterFilterB;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::shared_ptr<SetSiteSplitterFiltersCommand> command =
|
||||
std::make_shared<SetSiteSplitterFiltersCommand>();
|
||||
command->id = id;
|
||||
command->filterA = source.splitterFilterA;
|
||||
command->filterB = source.splitterFilterB;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.recipeId.has_value())
|
||||
{
|
||||
std::shared_ptr<SetRecipeCommand> command = std::make_shared<SetRecipeCommand>();
|
||||
command->id = id;
|
||||
command->recipeId = *source.recipeId;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
|
||||
// For a shipyard the schematic (above) must be applied before its module
|
||||
// layout, so both commands drain in order at the next tick boundary.
|
||||
if (source.type == BuildingType::Shipyard && source.shipLayout.has_value())
|
||||
{
|
||||
std::shared_ptr<SetShipLayoutCommand> command =
|
||||
std::make_shared<SetShipLayoutCommand>();
|
||||
command->id = id;
|
||||
command->layout = *source.shipLayout;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
void GameWorldView::enterBuilderMode(BuildingType type)
|
||||
{
|
||||
m_builderType = type;
|
||||
@@ -1940,6 +2040,7 @@ void GameWorldView::resetForNewGame()
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<DemolishModeChangedEvent>(false));
|
||||
m_selectedBuildingIds.clear();
|
||||
m_copiedConfig = std::nullopt;
|
||||
m_boxSelecting = false;
|
||||
m_scrollXTiles = 0.0f;
|
||||
m_scrollLeft = false;
|
||||
|
||||
Reference in New Issue
Block a user