show accumulated threat for teams in balancing target
This commit is contained in:
@@ -497,7 +497,7 @@ A separate executable target (`balancing`) that links against `lib` but contains
|
|||||||
- REQ-BAL-UI-WINDOW: On startup the tool displays a window containing a "Reload Config" button and a "Start All" button at the top (in that order, left to right), followed by a scrollable vertical list of arena widgets, one per arena defined in `balancing.toml`. Simulations do not start automatically on startup. All buttons and controls in the main window are disabled while an arena is being inspected (REQ-BAL-UI-INSPECT).
|
- REQ-BAL-UI-WINDOW: On startup the tool displays a window containing a "Reload Config" button and a "Start All" button at the top (in that order, left to right), followed by a scrollable vertical list of arena widgets, one per arena defined in `balancing.toml`. Simulations do not start automatically on startup. All buttons and controls in the main window are disabled while an arena is being inspected (REQ-BAL-UI-INSPECT).
|
||||||
- REQ-BAL-UI-RELOAD: The "Reload Config" button reloads all config files from disk (`balancing.toml`, `ships.toml`, `stations.toml`), stops any running simulations, and replaces the arena widget list with freshly created widgets from the reloaded config. The button is disabled while any arena simulation is currently running.
|
- REQ-BAL-UI-RELOAD: The "Reload Config" button reloads all config files from disk (`balancing.toml`, `ships.toml`, `stations.toml`), stops any running simulations, and replaces the arena widget list with freshly created widgets from the reloaded config. The button is disabled while any arena simulation is currently running.
|
||||||
- REQ-BAL-UI-START-ALL: The "Start All" button is placed above the scrollable arena list, to the right of the "Reload Config" button. Clicking it starts (or restarts) the simulation for every arena that is not currently running. The button is disabled when all arenas are currently running.
|
- REQ-BAL-UI-START-ALL: The "Start All" button is placed above the scrollable arena list, to the right of the "Reload Config" button. Clicking it starts (or restarts) the simulation for every arena that is not currently running. The button is disabled when all arenas are currently running.
|
||||||
- REQ-BAL-UI-WIDGET: Each arena widget displays the arena name, an "Inspect" button (to the right of the arena name), and two columns (one per team). Each column shows the team name as a header, followed by a list of entries. The HQ is always the first entry in each column. Below the HQ, ship types are listed, followed by defence stations (if any). Each entry uses the format `surviving/total TypeName Llevel` — for example `2/3 Fighter L5` or `1/1 HQ L1`. The surviving count updates live as the simulation progresses. When the fight ends, the winning team's name header is prefixed with `[WON]`.
|
- REQ-BAL-UI-WIDGET: Each arena widget displays the arena name, an "Inspect" button (to the right of the arena name), and two columns (one per team). Each column shows the team name as a header, then directly below the header the team's **accumulated threat level** — the sum, across the team's configured ship entries, of each entry's `count` multiplied by the threat cost (REQ-MOD-THREAT) of one ship of that entry computed from its level-independent module layout. Only ships contribute; the HQ and defence stations are excluded. This value is static: it is computed once from the full configured roster and does not change as ships are destroyed during the fight. Below the threat level, the column shows a list of entries. The HQ is always the first entry in each column. Below the HQ, ship types are listed, followed by defence stations (if any). Each entry uses the format `surviving/total TypeName Llevel` — for example `2/3 Fighter L5` or `1/1 HQ L1`. The surviving count updates live as the simulation progresses. When the fight ends, the winning team's name header is prefixed with `[WON]`.
|
||||||
- REQ-BAL-UI-WIDGET-START: Each arena widget contains a "Start" button that starts the simulation for that arena. The button is disabled while the arena's simulation is running. When a finished arena's Start button is clicked, a fresh simulation is created and started (the widget resets to initial unit counts, the border returns to blue, and the previous results are replaced).
|
- REQ-BAL-UI-WIDGET-START: Each arena widget contains a "Start" button that starts the simulation for that arena. The button is disabled while the arena's simulation is running. When a finished arena's Start button is clicked, a fresh simulation is created and started (the widget resets to initial unit counts, the border returns to blue, and the previous results are replaced).
|
||||||
- REQ-BAL-UI-WIDGET-BORDER: Each arena widget has a colored border indicating its state: grey when not yet started, blue while its simulation is running, and green when the fight has ended.
|
- REQ-BAL-UI-WIDGET-BORDER: Each arena widget has a colored border indicating its state: grey when not yet started, blue while its simulation is running, and green when the fight has ended.
|
||||||
- REQ-BAL-UI-INSPECT: Clicking an arena widget's "Inspect" button opens a new inspect window for that arena. Any previously open inspect window is closed first (its arena's simulation is aborted and its widget border returns to grey). The inspected arena is restarted with a fresh simulation that runs at controllable game speed with full rendering (REQ-BAL-SIM-SPEED). The arena widget updates live during inspection (surviving counts, border color, `[WON]` prefix) as it does for non-inspected arenas. Only one inspect window may be open at a time.
|
- REQ-BAL-UI-INSPECT: Clicking an arena widget's "Inspect" button opens a new inspect window for that arena. Any previously open inspect window is closed first (its arena's simulation is aborted and its widget border returns to grey). The inspected arena is restarted with a fresh simulation that runs at controllable game speed with full rendering (REQ-BAL-SIM-SPEED). The arena widget updates live during inspection (surviving counts, border color, `[WON]` prefix) as it does for non-inspected arenas. Only one inspect window may be open at a time.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "StationBodyComponent.h"
|
#include "StationBodyComponent.h"
|
||||||
#include "StationsConfig.h"
|
#include "StationsConfig.h"
|
||||||
#include "SurfaceMask.h"
|
#include "SurfaceMask.h"
|
||||||
|
#include "ThreatCostCalculator.h"
|
||||||
#include "WeaponComponent.h"
|
#include "WeaponComponent.h"
|
||||||
|
|
||||||
ArenaSimulation::ArenaSimulation(const GameConfig& gameConfig,
|
ArenaSimulation::ArenaSimulation(const GameConfig& gameConfig,
|
||||||
@@ -64,6 +65,24 @@ ArenaSimulation::ArenaSimulation(const GameConfig& gameConfig,
|
|||||||
m_salvagerSystem = std::make_unique<SalvagerSystem>(m_admin);
|
m_salvagerSystem = std::make_unique<SalvagerSystem>(m_admin);
|
||||||
m_repairSystem = std::make_unique<RepairSystem>(m_admin);
|
m_repairSystem = std::make_unique<RepairSystem>(m_admin);
|
||||||
|
|
||||||
|
// Static accumulated threat per team: sum of count * per-ship threat cost
|
||||||
|
// (REQ-MOD-THREAT) over the configured ship roster. Ships only; HQ and
|
||||||
|
// defence stations are excluded. Level-independent, so computed once here.
|
||||||
|
for (int ti = 0; ti < 2; ++ti)
|
||||||
|
{
|
||||||
|
double teamThreat = 0.0;
|
||||||
|
for (const ArenaShipEntry& shipEntry : m_arenaConfig.teams[ti].ships)
|
||||||
|
{
|
||||||
|
const std::vector<PlacedModule>& modules = shipEntry.layout
|
||||||
|
? shipEntry.layout->placedModules
|
||||||
|
: std::vector<PlacedModule>{};
|
||||||
|
const double shipThreat = calculateShipThreatCost(
|
||||||
|
m_gameConfig.threatCosts, m_gameConfig, shipEntry.schematicId, modules);
|
||||||
|
teamThreat += shipThreat * shipEntry.count;
|
||||||
|
}
|
||||||
|
m_teamThreat[ti] = teamThreat;
|
||||||
|
}
|
||||||
|
|
||||||
placeStructures();
|
placeStructures();
|
||||||
spawnShips();
|
spawnShips();
|
||||||
|
|
||||||
@@ -460,6 +479,7 @@ void ArenaSimulation::updateStatus()
|
|||||||
{
|
{
|
||||||
ArenaStatus::TeamStatus& teamStatus = newStatus.teams[ti];
|
ArenaStatus::TeamStatus& teamStatus = newStatus.teams[ti];
|
||||||
teamStatus.name = m_arenaConfig.teams[ti].name;
|
teamStatus.name = m_arenaConfig.teams[ti].name;
|
||||||
|
teamStatus.threatLevel = m_teamThreat[ti];
|
||||||
|
|
||||||
// HQ entry (always first).
|
// HQ entry (always first).
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ struct ArenaStatus
|
|||||||
struct TeamStatus
|
struct TeamStatus
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
|
double threatLevel = 0.0; // accumulated threat of the team's configured ships
|
||||||
std::vector<Entry> entries; // HQ first, then ships, then stations
|
std::vector<Entry> entries; // HQ first, then ships, then stations
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -108,6 +109,9 @@ private:
|
|||||||
int m_winnerTeam;
|
int m_winnerTeam;
|
||||||
std::atomic<bool> m_stopRequested;
|
std::atomic<bool> m_stopRequested;
|
||||||
|
|
||||||
|
// Static accumulated threat per team, computed once from the configured roster.
|
||||||
|
double m_teamThreat[2] = {0.0, 0.0};
|
||||||
|
|
||||||
std::vector<WeaponFiredEvent> m_weaponFiredEvents;
|
std::vector<WeaponFiredEvent> m_weaponFiredEvents;
|
||||||
|
|
||||||
mutable std::mutex m_statusMutex;
|
mutable std::mutex m_statusMutex;
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ void ArenaWidget::buildLayout(const std::string& arenaName)
|
|||||||
headerFont.setBold(true);
|
headerFont.setBold(true);
|
||||||
m_team1Header->setFont(headerFont);
|
m_team1Header->setFont(headerFont);
|
||||||
team1Layout->addWidget(m_team1Header);
|
team1Layout->addWidget(m_team1Header);
|
||||||
|
m_team1Threat = new QLabel(this);
|
||||||
|
team1Layout->addWidget(m_team1Threat);
|
||||||
m_team1Content = new QLabel(this);
|
m_team1Content = new QLabel(this);
|
||||||
team1Layout->addWidget(m_team1Content);
|
team1Layout->addWidget(m_team1Content);
|
||||||
team1Layout->addStretch();
|
team1Layout->addStretch();
|
||||||
@@ -71,6 +73,8 @@ void ArenaWidget::buildLayout(const std::string& arenaName)
|
|||||||
m_team2Header = new QLabel(this);
|
m_team2Header = new QLabel(this);
|
||||||
m_team2Header->setFont(headerFont);
|
m_team2Header->setFont(headerFont);
|
||||||
team2Layout->addWidget(m_team2Header);
|
team2Layout->addWidget(m_team2Header);
|
||||||
|
m_team2Threat = new QLabel(this);
|
||||||
|
team2Layout->addWidget(m_team2Threat);
|
||||||
m_team2Content = new QLabel(this);
|
m_team2Content = new QLabel(this);
|
||||||
team2Layout->addWidget(m_team2Content);
|
team2Layout->addWidget(m_team2Content);
|
||||||
team2Layout->addStretch();
|
team2Layout->addStretch();
|
||||||
@@ -101,6 +105,7 @@ void ArenaWidget::updateStatus(const ArenaStatus& status)
|
|||||||
{
|
{
|
||||||
const ArenaStatus::TeamStatus& team = status.teams[ti];
|
const ArenaStatus::TeamStatus& team = status.teams[ti];
|
||||||
QLabel* header = (ti == 0) ? m_team1Header : m_team2Header;
|
QLabel* header = (ti == 0) ? m_team1Header : m_team2Header;
|
||||||
|
QLabel* threat = (ti == 0) ? m_team1Threat : m_team2Threat;
|
||||||
QLabel* content = (ti == 0) ? m_team1Content : m_team2Content;
|
QLabel* content = (ti == 0) ? m_team1Content : m_team2Content;
|
||||||
|
|
||||||
if (status.finished && status.winnerTeam == ti)
|
if (status.finished && status.winnerTeam == ti)
|
||||||
@@ -112,6 +117,8 @@ void ArenaWidget::updateStatus(const ArenaStatus& status)
|
|||||||
header->setText(QString::fromStdString(team.name));
|
header->setText(QString::fromStdString(team.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
threat->setText(tr("Threat: %1").arg(QString::number(team.threatLevel, 'f', 0)));
|
||||||
|
|
||||||
QString lines;
|
QString lines;
|
||||||
for (const ArenaStatus::Entry& entry : team.entries)
|
for (const ArenaStatus::Entry& entry : team.entries)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ private:
|
|||||||
QLabel* m_titleLabel;
|
QLabel* m_titleLabel;
|
||||||
QLabel* m_team1Header;
|
QLabel* m_team1Header;
|
||||||
QLabel* m_team2Header;
|
QLabel* m_team2Header;
|
||||||
|
QLabel* m_team1Threat;
|
||||||
|
QLabel* m_team2Threat;
|
||||||
QLabel* m_team1Content;
|
QLabel* m_team1Content;
|
||||||
QLabel* m_team2Content;
|
QLabel* m_team2Content;
|
||||||
QPushButton* m_inspectButton;
|
QPushButton* m_inspectButton;
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ InspectWindow::InspectWindow(ArenaSimulation* sim, const GameConfig* config,
|
|||||||
headerFont.setBold(true);
|
headerFont.setBold(true);
|
||||||
m_team1Header->setFont(headerFont);
|
m_team1Header->setFont(headerFont);
|
||||||
team1Layout->addWidget(m_team1Header);
|
team1Layout->addWidget(m_team1Header);
|
||||||
|
m_team1Threat = new QLabel(infoPanel);
|
||||||
|
team1Layout->addWidget(m_team1Threat);
|
||||||
m_team1Content = new QLabel(infoPanel);
|
m_team1Content = new QLabel(infoPanel);
|
||||||
team1Layout->addWidget(m_team1Content);
|
team1Layout->addWidget(m_team1Content);
|
||||||
team1Layout->addStretch();
|
team1Layout->addStretch();
|
||||||
@@ -100,6 +102,8 @@ InspectWindow::InspectWindow(ArenaSimulation* sim, const GameConfig* config,
|
|||||||
m_team2Header = new QLabel(infoPanel);
|
m_team2Header = new QLabel(infoPanel);
|
||||||
m_team2Header->setFont(headerFont);
|
m_team2Header->setFont(headerFont);
|
||||||
team2Layout->addWidget(m_team2Header);
|
team2Layout->addWidget(m_team2Header);
|
||||||
|
m_team2Threat = new QLabel(infoPanel);
|
||||||
|
team2Layout->addWidget(m_team2Threat);
|
||||||
m_team2Content = new QLabel(infoPanel);
|
m_team2Content = new QLabel(infoPanel);
|
||||||
team2Layout->addWidget(m_team2Content);
|
team2Layout->addWidget(m_team2Content);
|
||||||
team2Layout->addStretch();
|
team2Layout->addStretch();
|
||||||
@@ -198,6 +202,7 @@ void InspectWindow::updateInfoPanel(const ArenaStatus& status)
|
|||||||
{
|
{
|
||||||
const ArenaStatus::TeamStatus& team = status.teams[ti];
|
const ArenaStatus::TeamStatus& team = status.teams[ti];
|
||||||
QLabel* header = (ti == 0) ? m_team1Header : m_team2Header;
|
QLabel* header = (ti == 0) ? m_team1Header : m_team2Header;
|
||||||
|
QLabel* threat = (ti == 0) ? m_team1Threat : m_team2Threat;
|
||||||
QLabel* content = (ti == 0) ? m_team1Content : m_team2Content;
|
QLabel* content = (ti == 0) ? m_team1Content : m_team2Content;
|
||||||
|
|
||||||
if (status.finished && status.winnerTeam == ti)
|
if (status.finished && status.winnerTeam == ti)
|
||||||
@@ -209,6 +214,8 @@ void InspectWindow::updateInfoPanel(const ArenaStatus& status)
|
|||||||
header->setText(QString::fromStdString(team.name));
|
header->setText(QString::fromStdString(team.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
threat->setText(tr("Threat: %1").arg(QString::number(team.threatLevel, 'f', 0)));
|
||||||
|
|
||||||
QString lines;
|
QString lines;
|
||||||
for (const ArenaStatus::Entry& entry : team.entries)
|
for (const ArenaStatus::Entry& entry : team.entries)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ private:
|
|||||||
std::vector<QPushButton*> m_speedButtons;
|
std::vector<QPushButton*> m_speedButtons;
|
||||||
QLabel* m_team1Header;
|
QLabel* m_team1Header;
|
||||||
QLabel* m_team2Header;
|
QLabel* m_team2Header;
|
||||||
|
QLabel* m_team1Threat;
|
||||||
|
QLabel* m_team2Threat;
|
||||||
QLabel* m_team1Content;
|
QLabel* m_team1Content;
|
||||||
QLabel* m_team2Content;
|
QLabel* m_team2Content;
|
||||||
QTimer* m_pollTimer;
|
QTimer* m_pollTimer;
|
||||||
|
|||||||
Reference in New Issue
Block a user