give the selection cards their own parts instead of label blobs
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
#include "ShipStatsPanel.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "BarRow.h"
|
||||
#include "GameConfig.h"
|
||||
#include "SectionBox.h"
|
||||
#include "SelectionNames.h"
|
||||
#include "ShipStatsCalculator.h"
|
||||
#include "StatRow.h"
|
||||
#include "ThreatCostCalculator.h"
|
||||
|
||||
namespace
|
||||
@@ -16,20 +19,6 @@ QString fmt(float value)
|
||||
return QString::number(static_cast<double>(value), 'f', 1);
|
||||
}
|
||||
|
||||
QLabel* makeSectionHeader(const QString& text, QWidget* parent)
|
||||
{
|
||||
QLabel* label = new QLabel(text, parent);
|
||||
QFont f = label->font();
|
||||
f.setBold(true);
|
||||
label->setFont(f);
|
||||
return label;
|
||||
}
|
||||
|
||||
QLabel* makeStatLabel(QWidget* parent)
|
||||
{
|
||||
return new QLabel(parent);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -38,215 +27,150 @@ ShipStatsPanel::ShipStatsPanel(const GameConfig* config, QWidget* parent)
|
||||
, m_config(config)
|
||||
{
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(4, 4, 4, 4);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(2);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
|
||||
// Hull stats — always visible.
|
||||
m_hpLabel = makeStatLabel(this);
|
||||
m_speedLabel = makeStatLabel(this);
|
||||
m_sensorRangeLabel = makeStatLabel(this);
|
||||
m_mainAccelLabel = makeStatLabel(this);
|
||||
m_maneuveringAccelLabel = makeStatLabel(this);
|
||||
m_angularAccelLabel = makeStatLabel(this);
|
||||
m_maxRotSpeedLabel = makeStatLabel(this);
|
||||
m_cargoCapacityLabel = makeStatLabel(this);
|
||||
m_cargoCapacityLabel->setVisible(false);
|
||||
// Hull stats -- always visible, except cargo capacity, which only a ship that can
|
||||
// carry anything shows (REQ-MOD-UI-STATS-PANEL).
|
||||
m_hpBar = new BarRow(tr("HP"), this);
|
||||
m_speedRow = new StatRow(tr("Max speed"), this);
|
||||
m_sensorRangeRow = new StatRow(tr("Sensor range"), this);
|
||||
m_mainAccelRow = new StatRow(tr("Main accel"), this);
|
||||
m_maneuveringAccelRow = new StatRow(tr("Maneuvering accel"), this);
|
||||
m_angularAccelRow = new StatRow(tr("Angular accel"), this);
|
||||
m_maxRotSpeedRow = new StatRow(tr("Max rotation"), this);
|
||||
m_cargoCapacityRow = new StatRow(tr("Cargo capacity"), this);
|
||||
m_cargoCapacityRow->hide();
|
||||
|
||||
layout->addWidget(m_hpLabel);
|
||||
layout->addWidget(m_speedLabel);
|
||||
layout->addWidget(m_sensorRangeLabel);
|
||||
layout->addWidget(m_mainAccelLabel);
|
||||
layout->addWidget(m_maneuveringAccelLabel);
|
||||
layout->addWidget(m_angularAccelLabel);
|
||||
layout->addWidget(m_maxRotSpeedLabel);
|
||||
layout->addWidget(m_cargoCapacityLabel);
|
||||
layout->addWidget(m_hpBar);
|
||||
layout->addWidget(m_speedRow);
|
||||
layout->addWidget(m_sensorRangeRow);
|
||||
layout->addWidget(m_mainAccelRow);
|
||||
layout->addWidget(m_maneuveringAccelRow);
|
||||
layout->addWidget(m_angularAccelRow);
|
||||
layout->addWidget(m_maxRotSpeedRow);
|
||||
layout->addWidget(m_cargoCapacityRow);
|
||||
|
||||
// Weapon capability section.
|
||||
m_weaponSection = new QWidget(this);
|
||||
{
|
||||
QVBoxLayout* sl = new QVBoxLayout(m_weaponSection);
|
||||
sl->setContentsMargins(0, 4, 0, 0);
|
||||
sl->setSpacing(2);
|
||||
sl->addWidget(makeSectionHeader(tr("Weapons"), m_weaponSection));
|
||||
m_weaponDpsLabel = makeStatLabel(m_weaponSection);
|
||||
m_weaponRangeLabel = makeStatLabel(m_weaponSection);
|
||||
sl->addWidget(m_weaponDpsLabel);
|
||||
sl->addWidget(m_weaponRangeLabel);
|
||||
}
|
||||
m_weaponSection->setVisible(false);
|
||||
// One section per capability module type, each shown only while at least one such
|
||||
// module is installed (REQ-MOD-UI-STATS-PANEL).
|
||||
m_weaponSection = new SectionBox(tr("Weapons"), this);
|
||||
m_weaponDpsRow = new StatRow(tr("DPS"), m_weaponSection);
|
||||
m_weaponRangeRow = new StatRow(tr("Range"), m_weaponSection);
|
||||
m_weaponSection->getContentLayout()->addWidget(m_weaponDpsRow);
|
||||
m_weaponSection->getContentLayout()->addWidget(m_weaponRangeRow);
|
||||
m_weaponSection->hide();
|
||||
layout->addWidget(m_weaponSection);
|
||||
|
||||
// Salvage capability section.
|
||||
m_salvageSection = new QWidget(this);
|
||||
{
|
||||
QVBoxLayout* sl = new QVBoxLayout(m_salvageSection);
|
||||
sl->setContentsMargins(0, 4, 0, 0);
|
||||
sl->setSpacing(2);
|
||||
sl->addWidget(makeSectionHeader(tr("Salvage"), m_salvageSection));
|
||||
m_salvageRateLabel = makeStatLabel(m_salvageSection);
|
||||
m_salvageRangeLabel = makeStatLabel(m_salvageSection);
|
||||
sl->addWidget(m_salvageRateLabel);
|
||||
sl->addWidget(m_salvageRangeLabel);
|
||||
}
|
||||
m_salvageSection->setVisible(false);
|
||||
m_salvageSection = new SectionBox(tr("Salvage"), this);
|
||||
m_salvageRateRow = new StatRow(tr("Collection rate"), m_salvageSection);
|
||||
m_salvageRangeRow = new StatRow(tr("Range"), m_salvageSection);
|
||||
m_salvageSection->getContentLayout()->addWidget(m_salvageRateRow);
|
||||
m_salvageSection->getContentLayout()->addWidget(m_salvageRangeRow);
|
||||
m_salvageSection->hide();
|
||||
layout->addWidget(m_salvageSection);
|
||||
|
||||
// Repair capability section.
|
||||
m_repairSection = new QWidget(this);
|
||||
{
|
||||
QVBoxLayout* sl = new QVBoxLayout(m_repairSection);
|
||||
sl->setContentsMargins(0, 4, 0, 0);
|
||||
sl->setSpacing(2);
|
||||
sl->addWidget(makeSectionHeader(tr("Repair"), m_repairSection));
|
||||
m_repairRateLabel = makeStatLabel(m_repairSection);
|
||||
m_repairRangeLabel = makeStatLabel(m_repairSection);
|
||||
sl->addWidget(m_repairRateLabel);
|
||||
sl->addWidget(m_repairRangeLabel);
|
||||
}
|
||||
m_repairSection->setVisible(false);
|
||||
m_repairSection = new SectionBox(tr("Repair"), this);
|
||||
m_repairRateRow = new StatRow(tr("Repair rate"), m_repairSection);
|
||||
m_repairRangeRow = new StatRow(tr("Range"), m_repairSection);
|
||||
m_repairSection->getContentLayout()->addWidget(m_repairRateRow);
|
||||
m_repairSection->getContentLayout()->addWidget(m_repairRangeRow);
|
||||
m_repairSection->hide();
|
||||
layout->addWidget(m_repairSection);
|
||||
|
||||
// Current behavior — live entities only; hidden in the static design
|
||||
// preview (REQ-UI-SHIP-BEHAVIOR).
|
||||
m_behaviorLabel = makeSectionHeader(QString(), this);
|
||||
m_behaviorLabel->setVisible(false);
|
||||
layout->addWidget(m_behaviorLabel);
|
||||
// Live entities only; the design preview has no behavior to show
|
||||
// (REQ-UI-SHIP-BEHAVIOR).
|
||||
m_behaviorRow = new StatRow(tr("Behavior"), this);
|
||||
m_behaviorRow->hide();
|
||||
layout->addWidget(m_behaviorRow);
|
||||
|
||||
// Threat cost — debug-only, initially hidden.
|
||||
m_threatCostLabel = makeStatLabel(this);
|
||||
m_threatCostLabel->setVisible(false);
|
||||
layout->addWidget(m_threatCostLabel);
|
||||
|
||||
layout->addStretch();
|
||||
}
|
||||
|
||||
void ShipStatsPanel::refresh(const std::string& shipId,
|
||||
const std::vector<PlacedModule>& modules)
|
||||
{
|
||||
const ShipStats stats = calculateShipStats(*m_config, shipId, modules);
|
||||
const QString hpText = tr("HP: %1").arg(static_cast<int>(stats.hp + 0.5f));
|
||||
applyStats(stats, hpText);
|
||||
|
||||
const double threat = calculateShipThreatCost(m_config->threatCosts, *m_config,
|
||||
shipId, modules);
|
||||
setThreatCost(threat);
|
||||
|
||||
// The static design preview has no live behavior to show.
|
||||
m_behaviorLabel->setVisible(false);
|
||||
}
|
||||
|
||||
void ShipStatsPanel::refreshFromLive(const ShipStats& stats, float currentHp)
|
||||
{
|
||||
const QString hpText = tr("HP: %1 / %2")
|
||||
.arg(static_cast<int>(currentHp + 0.5f))
|
||||
.arg(static_cast<int>(stats.hp + 0.5f));
|
||||
applyStats(stats, hpText);
|
||||
}
|
||||
|
||||
void ShipStatsPanel::applyStats(const ShipStats& stats, const QString& hpText)
|
||||
{
|
||||
m_hpLabel->setText(hpText);
|
||||
m_speedLabel->setText(
|
||||
tr("Max Speed: %1 tiles/s").arg(fmt(stats.maxSpeed_tps)));
|
||||
m_sensorRangeLabel->setText(
|
||||
tr("Sensor Range: %1 tiles").arg(fmt(stats.sensorRange_tiles)));
|
||||
m_mainAccelLabel->setText(
|
||||
tr("Main Accel: %1 tiles/s\xc2\xb2").arg(fmt(stats.mainAcceleration_tpss)));
|
||||
m_maneuveringAccelLabel->setText(
|
||||
tr("Maneuvering Accel: %1 tiles/s\xc2\xb2").arg(fmt(stats.maneuveringAcceleration_tpss)));
|
||||
m_angularAccelLabel->setText(
|
||||
tr("Angular Accel: %1 rad/s\xc2\xb2").arg(fmt(stats.angularAcceleration_radpss)));
|
||||
m_maxRotSpeedLabel->setText(
|
||||
tr("Max Rotation: %1 rad/s").arg(fmt(stats.maxRotationSpeed_radps)));
|
||||
|
||||
// Cargo capacity is shown only when the ship can actually hold cargo
|
||||
// (REQ-MOD-UI-STATS-PANEL).
|
||||
if (stats.cargoCapacity > 0)
|
||||
{
|
||||
m_cargoCapacityLabel->setText(
|
||||
tr("Cargo Capacity: %1").arg(stats.cargoCapacity));
|
||||
m_cargoCapacityLabel->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cargoCapacityLabel->setVisible(false);
|
||||
}
|
||||
|
||||
if (stats.weapons.has_value())
|
||||
{
|
||||
m_weaponDpsLabel->setText(
|
||||
tr("DPS: %1").arg(fmt(stats.weapons->combinedDps)));
|
||||
m_weaponRangeLabel->setText(
|
||||
tr("Range: %1 tiles").arg(fmt(stats.weapons->maxRange_tiles)));
|
||||
m_weaponSection->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_weaponSection->setVisible(false);
|
||||
}
|
||||
|
||||
if (stats.salvage.has_value())
|
||||
{
|
||||
m_salvageRateLabel->setText(
|
||||
tr("Collection Rate: %1/s").arg(fmt(stats.salvage->combinedCollectionRate)));
|
||||
m_salvageRangeLabel->setText(
|
||||
tr("Range: %1 tiles").arg(fmt(stats.salvage->maxRange_tiles)));
|
||||
m_salvageSection->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_salvageSection->setVisible(false);
|
||||
}
|
||||
|
||||
if (stats.repair.has_value())
|
||||
{
|
||||
m_repairRateLabel->setText(
|
||||
tr("Repair Rate: %1 HP/s").arg(fmt(stats.repair->combinedRepairRate_hps)));
|
||||
m_repairRangeLabel->setText(
|
||||
tr("Range: %1 tiles").arg(fmt(stats.repair->maxRange_tiles)));
|
||||
m_repairSection->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_repairSection->setVisible(false);
|
||||
}
|
||||
// Threat cost -- shown only while debug draw is active (REQ-UI-SHIP-STATS-PANEL).
|
||||
m_threatCostRow = new StatRow(tr("Threat cost"), this);
|
||||
m_threatCostRow->hide();
|
||||
layout->addWidget(m_threatCostRow);
|
||||
}
|
||||
|
||||
void ShipStatsPanel::setBehavior(BehaviorKind kind)
|
||||
{
|
||||
QString label;
|
||||
switch (kind)
|
||||
const QString label = getBehaviorLabel(kind);
|
||||
m_behaviorRow->setValue(label);
|
||||
m_behaviorRow->setVisible(!label.isEmpty());
|
||||
}
|
||||
|
||||
void ShipStatsPanel::refresh(const std::string& shipId,
|
||||
const std::vector<PlacedModule>& modules)
|
||||
{
|
||||
const ShipStats stats = calculateShipStats(*m_config, shipId, modules);
|
||||
applyStats(stats, 1.0, QString::number(static_cast<int>(stats.hp + 0.5f)));
|
||||
|
||||
setThreatCost(calculateShipThreatCost(m_config->threatCosts, *m_config,
|
||||
shipId, modules));
|
||||
}
|
||||
|
||||
void ShipStatsPanel::refreshFromLive(const ShipStats& stats, float currentHp)
|
||||
{
|
||||
const double fraction = (stats.hp > 0.0f)
|
||||
? static_cast<double>(currentHp) / stats.hp
|
||||
: 0.0;
|
||||
applyStats(stats, fraction, tr("%1 / %2")
|
||||
.arg(static_cast<int>(currentHp + 0.5f))
|
||||
.arg(static_cast<int>(stats.hp + 0.5f)));
|
||||
}
|
||||
|
||||
void ShipStatsPanel::applyStats(const ShipStats& stats, double hpFraction,
|
||||
const QString& hpText)
|
||||
{
|
||||
m_hpBar->setValue(hpFraction, hpText);
|
||||
m_speedRow->setValue(tr("%1 tiles/s").arg(fmt(stats.maxSpeed_tps)));
|
||||
m_sensorRangeRow->setValue(tr("%1 tiles").arg(fmt(stats.sensorRange_tiles)));
|
||||
m_mainAccelRow->setValue(
|
||||
tr("%1 tiles/s\xc2\xb2").arg(fmt(stats.mainAcceleration_tpss)));
|
||||
m_maneuveringAccelRow->setValue(
|
||||
tr("%1 tiles/s\xc2\xb2").arg(fmt(stats.maneuveringAcceleration_tpss)));
|
||||
m_angularAccelRow->setValue(
|
||||
tr("%1 rad/s\xc2\xb2").arg(fmt(stats.angularAcceleration_radpss)));
|
||||
m_maxRotSpeedRow->setValue(tr("%1 rad/s").arg(fmt(stats.maxRotationSpeed_radps)));
|
||||
|
||||
// Cargo capacity is shown only when the ship can actually hold cargo
|
||||
// (REQ-MOD-UI-STATS-PANEL).
|
||||
m_cargoCapacityRow->setVisible(stats.cargoCapacity > 0);
|
||||
if (stats.cargoCapacity > 0)
|
||||
{
|
||||
case BehaviorKind::Retreat: label = tr("Retreating"); break;
|
||||
case BehaviorKind::Attack: label = tr("Engaging"); break;
|
||||
case BehaviorKind::SalvageScrap:
|
||||
case BehaviorKind::DeliverScrap: label = tr("Salvaging"); break;
|
||||
case BehaviorKind::Repair: label = tr("Repairing"); break;
|
||||
case BehaviorKind::Rally: label = tr("Rallying"); break;
|
||||
case BehaviorKind::Standby: label = tr("Standby"); break;
|
||||
case BehaviorKind::Advance: label = tr("Advancing"); break;
|
||||
case BehaviorKind::None: break;
|
||||
m_cargoCapacityRow->setValue(QString::number(stats.cargoCapacity));
|
||||
}
|
||||
|
||||
if (label.isEmpty())
|
||||
m_weaponSection->setVisible(stats.weapons.has_value());
|
||||
if (stats.weapons.has_value())
|
||||
{
|
||||
m_behaviorLabel->setVisible(false);
|
||||
return;
|
||||
m_weaponDpsRow->setValue(fmt(stats.weapons->combinedDps));
|
||||
m_weaponRangeRow->setValue(tr("%1 tiles").arg(fmt(stats.weapons->maxRange_tiles)));
|
||||
}
|
||||
|
||||
m_behaviorLabel->setText(tr("Behavior: %1").arg(label));
|
||||
m_behaviorLabel->setVisible(true);
|
||||
m_salvageSection->setVisible(stats.salvage.has_value());
|
||||
if (stats.salvage.has_value())
|
||||
{
|
||||
m_salvageRateRow->setValue(
|
||||
tr("%1 /s").arg(fmt(stats.salvage->combinedCollectionRate)));
|
||||
m_salvageRangeRow->setValue(tr("%1 tiles").arg(fmt(stats.salvage->maxRange_tiles)));
|
||||
}
|
||||
|
||||
m_repairSection->setVisible(stats.repair.has_value());
|
||||
if (stats.repair.has_value())
|
||||
{
|
||||
m_repairRateRow->setValue(
|
||||
tr("%1 HP/s").arg(fmt(stats.repair->combinedRepairRate_hps)));
|
||||
m_repairRangeRow->setValue(tr("%1 tiles").arg(fmt(stats.repair->maxRange_tiles)));
|
||||
}
|
||||
}
|
||||
|
||||
void ShipStatsPanel::setThreatCost(double cost)
|
||||
{
|
||||
m_threatCostLabel->setText(tr("Threat Cost: %1").arg(cost, 0, 'f', 1));
|
||||
m_threatCostLabel->setVisible(m_debugDraw);
|
||||
m_threatCostRow->setValue(QString::number(cost, 'f', 1));
|
||||
m_threatCostRow->setVisible(m_debugDraw);
|
||||
}
|
||||
|
||||
void ShipStatsPanel::setDebugDrawEnabled(bool enabled)
|
||||
{
|
||||
m_debugDraw = enabled;
|
||||
m_threatCostLabel->setVisible(m_debugDraw);
|
||||
m_threatCostRow->setVisible(m_debugDraw);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user