diff --git a/docs/requirements.md b/docs/requirements.md index d3de231..0c39011 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -493,7 +493,7 @@ The screen is divided into two columns: a main column (75% width) containing the - 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 ". - 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. A plain click on a ship or defence station makes it the sole selection, clearing any previous selection. Ships and defence stations can be multi-selected — by Ctrl+clicking individual actors to add or remove them, or by box-drag (REQ-UI-MULTI-SELECT) — and can be selected together with scrap piles and with one another in a single field selection (REQ-UI-SELECTION-CATEGORIES), freely mixing player and enemy actors. Actors cannot be selected together with buildings: selecting a ship or defence station clears any building selection, and selecting a building clears the actors (buildings win). Clicking a scrap pile adds to or establishes a field selection (REQ-UI-SCRAP-CLICK-SELECT). Clicking empty world space (no building, ship, defence station, or scrap pile) clears the selection. -- REQ-UI-SHIP-STATS-PANEL: When exactly one ship is selected (REQ-UI-ENTITY-CLICK-SELECT) — whether or not scrap piles are also selected — 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 from 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). +- REQ-UI-SHIP-STATS-PANEL: When exactly one ship is selected (REQ-UI-ENTITY-CLICK-SELECT) and no scrap is selected, the selected building panel shows a **ship stats panel**. (If scrap is also selected, the panel shows the compact count summary instead, per REQ-UI-FIELD-MULTI-SELECTION.) The panel structure mirrors REQ-MOD-UI-STATS-PANEL but reflects the ship's actual live state: stats are computed from 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). - REQ-UI-SHIP-BEHAVIOR: The ship stats panel (REQ-UI-SHIP-STATS-PANEL) additionally displays the selected ship's **current behavior** — a single label naming the top-priority behavior currently governing the ship's navigation, as resolved by the fixed-priority behavior arbitration. Only the winning behavior is named; lower-priority behaviors that are suppressed are not shown, and neither are the salvage/repair cycles that run regardless of the active behavior (REQ-SHP-SALVAGE, REQ-SHP-REPAIR). The label updates live as the ship's behavior changes, and it is always shown (independent of debug draw mode, unlike the threat-cost line of REQ-UI-SHIP-STATS-PANEL). This applies to both player and enemy ships (REQ-UI-ENTITY-CLICK-SELECT); enemy ships only ever show **Engaging** or **Advancing**. The behavior labels (all wrapped in `tr()`) are: - **Retreating** — the ship is retreating (REQ-SHP-RETREAT). - **Engaging** — the ship is engaging a combat target (player: REQ-SHP-COMBAT; enemy: REQ-SHP-ENEMY-AI). @@ -502,11 +502,11 @@ The screen is divided into two columns: a main column (75% width) containing the - **Rallying** — the ship is moving to or orbiting the rally point (REQ-SHP-RALLY). - **Standby** — the ship is holding with its fleet (REQ-SHP-STANDBY). - **Advancing** — the ship is executing the baseline forward advance with no higher-priority behavior active (player: REQ-SHP-COMBAT advance toward the enemy; enemy: REQ-SHP-ENEMY-AI advance toward the asteroid). -- REQ-UI-STATION-STATS-PANEL: When exactly one defence station is selected (REQ-UI-ENTITY-CLICK-SELECT) — whether or not scrap piles are also selected — the selected building panel shows a **station stats panel** displaying the station's stats computed at its current level: HP (current / maximum), damage, range, and fire rate. -- REQ-UI-FIELD-MULTI-SELECTION: When more than one actor (ship or defence station) is selected, the selected building panel shows a **summary** of the selected actors instead of the single-actor stats panels (REQ-UI-SHIP-STATS-PANEL, REQ-UI-STATION-STATS-PANEL): a count per actor type — ships grouped by schematic display name and defence stations counted as a group, distinguishing player from enemy. No per-actor detail is shown. When exactly one actor is selected, its full single-actor stats panel is shown instead. In either case, if scrap piles are also part of the field selection (REQ-UI-SELECTION-CATEGORIES) their total is appended below (REQ-UI-SCRAP-PANEL). Building selections use REQ-UI-SINGLE-SELECTION / REQ-UI-MULTI-SELECTION instead. +- REQ-UI-STATION-STATS-PANEL: When exactly one defence station is selected (REQ-UI-ENTITY-CLICK-SELECT) and no scrap is selected, the selected building panel shows a **station stats panel** displaying the station's stats computed at its current level: HP (current / maximum), damage, range, and fire rate. (If scrap is also selected, the panel shows the compact count summary instead, per REQ-UI-FIELD-MULTI-SELECTION.) +- REQ-UI-FIELD-MULTI-SELECTION: The full single-actor stats panel (REQ-UI-SHIP-STATS-PANEL, REQ-UI-STATION-STATS-PANEL) is shown only when the field selection holds exactly one actor and no scrap. Whenever the selection holds more than one object — multiple actors, or a single actor together with scrap — the panel shows a **compact summary** instead: a count per actor type, one line per type rendered as " x " (the same `x`-count notation as the recipe tooltip and the building multi-selection, REQ-UI-MULTI-SELECTION). Ships are grouped by schematic display name and defence stations as a group, distinguishing player from enemy. No per-actor detail and no total-actor-count header are shown (consistent with the building panel). If scrap piles are also part of the field selection (REQ-UI-SELECTION-CATEGORIES) their total is appended as a final line of the same summary (REQ-UI-SCRAP-PANEL), so all lines share uniform spacing. Building selections use REQ-UI-SINGLE-SELECTION / REQ-UI-MULTI-SELECTION instead. - REQ-UI-SCRAP-CLICK-SELECT: The player can click any scrap pile (REQ-RES-SCRAP-DROP) in the game world to select it. Scrap piles are field objects (REQ-UI-SELECTION-CATEGORIES) and can be selected together with ships and defence stations, but not with buildings. A plain click on a scrap pile makes it the sole selection, clearing any previous selection; selecting a building clears any scrap (buildings win), and selecting a scrap pile clears any building selection. Hit-testing prefers a building over a coincident actor or scrap pile, and an actor (ship or defence station) over a coincident scrap pile: a scrap pile is selected only when no building or actor is under the cursor. A selected scrap pile that despawns or is fully collected (REQ-RES-SCRAP-DROP) is removed from the selection; if no selected object remains, the panel becomes empty (REQ-UI-EMPTY-SELECTION). - REQ-UI-SCRAP-MULTI-SELECT: Multiple scrap piles can be selected by box-drag or by Ctrl+clicking individual piles to add or remove them, mirroring building multi-select (REQ-UI-MULTI-SELECT). Scrap shares the field-object category with ships and defence stations (REQ-UI-SELECTION-CATEGORIES), so a field selection may hold scrap piles and actors together. Ctrl+clicking a scrap pile while a field selection is active adds or removes that pile within the same selection; Ctrl+clicking a scrap pile while a building selection is active first clears the buildings and begins a field selection (buildings win). Conversely, selecting a building while a field selection is active clears it. Box-drag disambiguation follows REQ-UI-MULTI-SELECT (a box covering any building selects buildings; a box covering no building selects the ships, defence stations, and scrap piles within it). -- REQ-UI-SCRAP-PANEL: When one or more scrap piles are selected, the selected building panel shows the **total remaining scrap amount** across all selected piles — the sum of the piles' current remaining amounts (REQ-RES-SCRAP-DROP), e.g. "Scrap: 47". The same summed-amount display is used whether one pile or many are selected; no per-pile detail and no pile count are shown. The displayed total updates as selected piles are partially collected or despawn (REQ-UI-SCRAP-CLICK-SELECT). When actors are also selected, this scrap total is shown in addition to the single-actor stats panel or the actor summary (REQ-UI-SHIP-STATS-PANEL, REQ-UI-STATION-STATS-PANEL, REQ-UI-FIELD-MULTI-SELECTION). +- REQ-UI-SCRAP-PANEL: When one or more scrap piles are selected, the selected building panel shows the **total remaining scrap amount** across all selected piles — the sum of the piles' current remaining amounts (REQ-RES-SCRAP-DROP), e.g. "Scrap: 47". The same summed-amount display is used whether one pile or many are selected; no per-pile detail and no pile count are shown. The displayed total updates as selected piles are partially collected or despawn (REQ-UI-SCRAP-CLICK-SELECT). When actors are also selected, this scrap total is shown as an additional line of the actor count summary rather than alongside a single-actor stats panel (REQ-UI-FIELD-MULTI-SELECTION). ### Build Button Grid diff --git a/src/ui/SelectedBuildingPanel.cpp b/src/ui/SelectedBuildingPanel.cpp index f416da4..111e3ef 100644 --- a/src/ui/SelectedBuildingPanel.cpp +++ b/src/ui/SelectedBuildingPanel.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "BeltSystem.h" @@ -668,14 +669,22 @@ void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason) { if (!m_selectedEntities.empty() || !m_selectedScrap.empty()) { - // Field selection: keep the live single-actor stats current, and refresh the - // scrap total, which shrinks live as piles are collected or despawn + // Field selection. Keep the live values current: the single-actor stats panel, + // the standalone scrap total, or the count summary (whose scrap line shrinks as + // piles are collected) — matching the layout chosen by buildFieldSelection() // (REQ-UI-SHIP-STATS-PANEL, REQ-UI-SCRAP-PANEL). - refreshEntityStats(); - if (!m_selectedScrap.empty()) + if (m_selectedEntities.size() == 1 && m_selectedScrap.empty()) + { + refreshEntityStats(); + } + else if (m_selectedEntities.empty()) { refreshScrapTotal(); } + else + { + buildEntitySummary(); + } return; } @@ -745,7 +754,7 @@ void SelectedBuildingPanel::buildMulti(const std::vector& ids) QString text; for (const std::pair& entry : counts) { - text += buildingTypeName(entry.first) + ": " + text += buildingTypeName(entry.first) + " x " + QString::number(entry.second) + "\n"; if (isBeltLike(entry.first)) { @@ -944,17 +953,13 @@ void SelectedBuildingPanel::buildFieldSelection() EntityAdmin& admin = m_sim->getAdmin(); - // Actor section: single-actor stats panel, multi-actor summary, or nothing. - if (m_selectedEntities.empty()) - { - m_entityTitleLabel->hide(); - m_entityStatsPanel->hide(); - m_stationStatsLabel->hide(); - m_entitySummaryLabel->hide(); - } - else if (m_selectedEntities.size() == 1) + // Full single-actor stats are shown only for a lone actor with no scrap. As soon as + // the selection holds more than one object (multiple actors, or an actor plus scrap), + // the panel switches to the compact count summary (REQ-UI-FIELD-MULTI-SELECTION). + if (m_selectedEntities.size() == 1 && m_selectedScrap.empty()) { m_entitySummaryLabel->hide(); + m_scrapLabel->hide(); const entt::entity entity = m_selectedEntities.front(); if (admin.isValid(entity) && admin.hasAll(entity)) { @@ -970,25 +975,26 @@ void SelectedBuildingPanel::buildFieldSelection() m_entityStatsPanel->hide(); m_stationStatsLabel->hide(); } - } - else - { - m_entityTitleLabel->hide(); - m_entityStatsPanel->hide(); - m_stationStatsLabel->hide(); - buildEntitySummary(); + return; } - // Scrap section, appended below the actor section (REQ-UI-SCRAP-PANEL). - if (!m_selectedScrap.empty()) + m_entityTitleLabel->hide(); + m_entityStatsPanel->hide(); + m_stationStatsLabel->hide(); + + if (m_selectedEntities.empty()) { + // Scrap only: a single "Scrap: N" line. + m_entitySummaryLabel->hide(); refreshScrapTotal(); m_scrapLabel->show(); + return; } - else - { - m_scrapLabel->hide(); - } + + // Actor counts, with the scrap total appended into the same label so every line + // shares the same spacing. + m_scrapLabel->hide(); + buildEntitySummary(); } void SelectedBuildingPanel::buildEntitySummary() @@ -1000,7 +1006,6 @@ void SelectedBuildingPanel::buildEntitySummary() std::vector keys; std::map counts; std::map labels; - int shown = 0; for (entt::entity entity : m_selectedEntities) { @@ -1034,15 +1039,22 @@ void SelectedBuildingPanel::buildEntitySummary() labels[key] = label; } counts[key] += 1; - ++shown; } - QString text = tr("%1 selected").arg(shown); + // One " x " line per group (matching the recipe tooltip and the building + // multi-selection). No total-count header, consistent with the building panel. The + // scrap total, when present, is appended as another line in the same label so the + // line spacing is uniform (REQ-UI-FIELD-MULTI-SELECTION, REQ-UI-SCRAP-PANEL). + QStringList lines; for (const QString& key : keys) { - text += tr("\n%1 ×%2").arg(labels[key]).arg(counts[key]); + lines << tr("%1 x %2").arg(labels[key]).arg(counts[key]); } - m_entitySummaryLabel->setText(text); + if (!m_selectedScrap.empty()) + { + lines << scrapTotalText(); + } + m_entitySummaryLabel->setText(lines.join('\n')); m_entitySummaryLabel->show(); } @@ -1173,7 +1185,7 @@ void SelectedBuildingPanel::handleEvent( buildFieldSelection(); } -void SelectedBuildingPanel::refreshScrapTotal() +QString SelectedBuildingPanel::scrapTotalText() const { // Sum the remaining amounts of the still-living selected piles (REQ-UI-SCRAP-PANEL). int total = 0; @@ -1185,7 +1197,12 @@ void SelectedBuildingPanel::refreshScrapTotal() total += info.amount; } } - m_scrapLabel->setText(tr("Scrap: %1").arg(total)); + return tr("Scrap: %1").arg(total); +} + +void SelectedBuildingPanel::refreshScrapTotal() +{ + m_scrapLabel->setText(scrapTotalText()); } void SelectedBuildingPanel::handleEvent(std::shared_ptr event) diff --git a/src/ui/SelectedBuildingPanel.h b/src/ui/SelectedBuildingPanel.h index a0893c7..71052db 100644 --- a/src/ui/SelectedBuildingPanel.h +++ b/src/ui/SelectedBuildingPanel.h @@ -81,6 +81,8 @@ private: void buildSingle(BuildingId id); void buildMulti(const std::vector& ids); void refreshScrapTotal(); + // "Scrap: N" for the summed remaining amount of the selected piles (REQ-UI-SCRAP-PANEL). + QString scrapTotalText() const; void refreshBuffers(const Building* b); void refreshSiteProgress(const ConstructionSite* s); void updateShipyardLayoutWidgets(BuildingType type,