Files
dota_factory/src/ui/ShipStatsPanel.cpp

253 lines
8.1 KiB
C++

#include "ShipStatsPanel.h"
#include <QLabel>
#include <QString>
#include <QVBoxLayout>
#include "GameConfig.h"
#include "ShipStatsCalculator.h"
#include "ThreatCostCalculator.h"
namespace
{
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
ShipStatsPanel::ShipStatsPanel(const GameConfig* config, QWidget* parent)
: QWidget(parent)
, m_config(config)
{
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(4, 4, 4, 4);
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);
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);
// 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);
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);
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);
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);
// 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);
}
}
void ShipStatsPanel::setBehavior(BehaviorKind kind)
{
QString label;
switch (kind)
{
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;
}
if (label.isEmpty())
{
m_behaviorLabel->setVisible(false);
return;
}
m_behaviorLabel->setText(tr("Behavior: %1").arg(label));
m_behaviorLabel->setVisible(true);
}
void ShipStatsPanel::setThreatCost(double cost)
{
m_threatCostLabel->setText(tr("Threat Cost: %1").arg(cost, 0, 'f', 1));
m_threatCostLabel->setVisible(m_debugDraw);
}
void ShipStatsPanel::setDebugDrawEnabled(bool enabled)
{
m_debugDraw = enabled;
m_threatCostLabel->setVisible(m_debugDraw);
}