#include "ShipStatsPanel.h" #include #include #include "BarRow.h" #include "GameConfig.h" #include "SectionBox.h" #include "SelectionNames.h" #include "ShipStatsCalculator.h" #include "StatRow.h" #include "ThreatCostCalculator.h" namespace { QString fmt(float value) { return QString::number(static_cast(value), 'f', 1); } } // namespace ShipStatsPanel::ShipStatsPanel(const GameConfig* config, QWidget* parent) : QWidget(parent) , m_config(config) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(2); layout->setAlignment(Qt::AlignTop); // 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_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); // 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); 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); 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); // 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 -- 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) { const QString label = getBehaviorLabel(kind); m_behaviorRow->setValue(label); m_behaviorRow->setVisible(!label.isEmpty()); } void ShipStatsPanel::refresh(const std::string& shipId, const std::vector& modules) { const ShipStats stats = calculateShipStats(*m_config, shipId, modules); applyStats(stats, 1.0, QString::number(static_cast(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(currentHp) / stats.hp : 0.0; applyStats(stats, fraction, tr("%1 / %2") .arg(static_cast(currentHp + 0.5f)) .arg(static_cast(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) { m_cargoCapacityRow->setValue(QString::number(stats.cargoCapacity)); } m_weaponSection->setVisible(stats.weapons.has_value()); if (stats.weapons.has_value()) { m_weaponDpsRow->setValue(fmt(stats.weapons->combinedDps)); m_weaponRangeRow->setValue(tr("%1 tiles").arg(fmt(stats.weapons->maxRange_tiles))); } 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_threatCostRow->setValue(QString::number(cost, 'f', 1)); m_threatCostRow->setVisible(m_debugDraw); } void ShipStatsPanel::setDebugDrawEnabled(bool enabled) { m_debugDraw = enabled; m_threatCostRow->setVisible(m_debugDraw); }