diff --git a/src/ui/GameWorldView.cpp b/src/ui/GameWorldView.cpp index 692b73f..1abd308 100644 --- a/src/ui/GameWorldView.cpp +++ b/src/ui/GameWorldView.cpp @@ -570,6 +570,41 @@ BuildingId GameWorldView::siteAtTile(QPoint tile) const } +std::vector GameWorldView::buildingsInBox(QPoint cornerA, QPoint cornerB) const +{ + const int x0 = std::min(cornerA.x(), cornerB.x()); + const int y0 = std::min(cornerA.y(), cornerB.y()); + const int x1 = std::max(cornerA.x(), cornerB.x()); + const int y1 = std::max(cornerA.y(), cornerB.y()); + + std::vector ids; + for (const Building& b : m_sim->buildings().allBuildings()) + { + for (const QPoint& cell : b.bodyCells) + { + if (cell.x() >= x0 && cell.x() <= x1 + && cell.y() >= y0 && cell.y() <= y1) + { + ids.push_back(b.id); + break; + } + } + } + for (const ConstructionSite& s : m_sim->buildings().allSites()) + { + for (const QPoint& cell : s.bodyCells) + { + if (cell.x() >= x0 && cell.x() <= x1 + && cell.y() >= y0 && cell.y() <= y1) + { + ids.push_back(s.id); + break; + } + } + } + return ids; +} + std::optional GameWorldView::entityPosition(entt::entity entity) const { if (!m_sim->admin().isValid(entity) || !m_sim->admin().hasAll(entity)) @@ -1236,8 +1271,28 @@ void GameWorldView::drawOverlays(QPainter& painter) } } - // Demolish hover tint - if (m_demolishMode && m_demolishHoverBuildingId != kInvalidBuildingId) + // Demolish tint: while dragging a demolish box, tint every covered + // building/site (REQ-BLD-DEMOLISH-BOX); otherwise tint the hovered one. + if (m_demolishMode && m_boxSelecting) + { + for (BuildingId id : buildingsInBox(m_boxStartTile, m_boxCurrentTile)) + { + const Building* b = m_sim->buildings().findBuilding(id); + if (b && b->type == BuildingType::Hq) { continue; } + const std::vector* cells = nullptr; + const ConstructionSite* s = nullptr; + if (b) { cells = &b->bodyCells; } + else if ((s = m_sim->buildings().findSite(id))) { cells = &s->bodyCells; } + if (cells) + { + for (const QPoint& cell : *cells) + { + painter.fillRect(tileRect(cell), m_visuals->overlays.demolishTint); + } + } + } + } + else if (m_demolishMode && m_demolishHoverBuildingId != kInvalidBuildingId) { const Building* b = m_sim->buildings().findBuilding(m_demolishHoverBuildingId); if (b) @@ -1531,24 +1586,11 @@ void GameWorldView::mousePressEvent(QMouseEvent* event) } else if (m_demolishMode) { - BuildingId hovered = buildingAtTile(tile); - if (hovered == kInvalidBuildingId) - { - hovered = siteAtTile(tile); - } - if (hovered != kInvalidBuildingId) - { - const Building* b = m_sim->buildings().findBuilding(hovered); - const bool isProtected = b && b->type == BuildingType::Hq; - if (!isProtected) - { - std::shared_ptr command = - std::make_shared(); - command->id = hovered; - enqueueCommand(command); - m_demolishHoverBuildingId = kInvalidBuildingId; - } - } + // Start a demolish box drag; a plain click resolves as a 1x1 box on + // release (REQ-BLD-DEMOLISH-CLICK, REQ-BLD-DEMOLISH-BOX). + m_boxSelecting = true; + m_boxStartTile = tile; + m_boxCurrentTile = tile; } else { @@ -1636,6 +1678,7 @@ void GameWorldView::mouseMoveEvent(QMouseEvent* event) else if (m_demolishMode) { m_demolishHoverBuildingId = buildingAtTile(tile); + if (m_boxSelecting) { m_boxCurrentTile = tile; } } else if (m_boxSelecting) { @@ -1657,44 +1700,33 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event) { m_boxSelecting = false; - const int x0 = std::min(m_boxStartTile.x(), m_boxCurrentTile.x()); - const int y0 = std::min(m_boxStartTile.y(), m_boxCurrentTile.y()); - const int x1 = std::max(m_boxStartTile.x(), m_boxCurrentTile.x()); - const int y1 = std::max(m_boxStartTile.y(), m_boxCurrentTile.y()); + const std::vector boxIds = + buildingsInBox(m_boxStartTile, m_boxCurrentTile); - std::vector boxSel; - for (const Building& b : m_sim->buildings().allBuildings()) + if (m_demolishMode) { - for (const QPoint& cell : b.bodyCells) + // Demolish every covered building/site; the HQ is protected + // (REQ-BLD-DEMOLISH, REQ-BLD-DEMOLISH-BOX). + for (BuildingId id : boxIds) { - if (cell.x() >= x0 && cell.x() <= x1 - && cell.y() >= y0 && cell.y() <= y1) - { - boxSel.push_back(b.id); - break; - } - } - } - for (const ConstructionSite& s : m_sim->buildings().allSites()) - { - for (const QPoint& cell : s.bodyCells) - { - if (cell.x() >= x0 && cell.x() <= x1 - && cell.y() >= y0 && cell.y() <= y1) - { - boxSel.push_back(s.id); - break; - } + const Building* b = m_sim->buildings().findBuilding(id); + if (b && b->type == BuildingType::Hq) { continue; } + std::shared_ptr command = + std::make_shared(); + command->id = id; + enqueueCommand(command); } + m_demolishHoverBuildingId = kInvalidBuildingId; + return; } if (!(event->modifiers() & Qt::ControlModifier)) { - m_selectedBuildingIds = boxSel; + m_selectedBuildingIds = boxIds; } else { - for (BuildingId id : boxSel) + for (BuildingId id : boxIds) { bool found = false; for (BuildingId sel : m_selectedBuildingIds) diff --git a/src/ui/GameWorldView.h b/src/ui/GameWorldView.h index b8dc0c7..22cde0a 100644 --- a/src/ui/GameWorldView.h +++ b/src/ui/GameWorldView.h @@ -143,6 +143,9 @@ private: const BuildingDef* findBuildingDef(BuildingType type) const; BuildingId buildingAtTile(QPoint tile) const; BuildingId siteAtTile(QPoint tile) const; + // Ids of all buildings and construction sites whose footprint intersects + // the tile box spanned by the two (unordered) corner tiles. + std::vector buildingsInBox(QPoint cornerA, QPoint cornerB) const; QVector2D widgetToWorld(QPoint widgetPt) const; void drawPortGlyph(QPainter& painter, QPoint bodyTile,