Render HP bar below the HQ

The HQ was drawn by drawBuildings(), which had no HP-bar code, so it
never showed an HP bar despite REQ-UI-HP-BARS requiring one. Add HQ
handling that reads HP from the HQ proxy entity and draws the bar below
the footprint, matching the defence stations.

Factor the shared bar-drawing logic into drawHpBar() and reuse it from
drawStations(), drawShips(), and the new HQ path to avoid duplication.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN
This commit is contained in:
2026-07-08 21:32:44 +02:00
parent f11db0c072
commit bf5ee8b3f1
2 changed files with 31 additions and 13 deletions

View File

@@ -39,6 +39,7 @@
#include "FactionComponent.h"
#include "GameOverEvent.h"
#include "HealthComponent.h"
#include "HqProxyComponent.h"
#include "PositionComponent.h"
#include "RepairBehavior.h"
#include "SalvageScrapBehavior.h"
@@ -883,6 +884,21 @@ void GameWorldView::drawBuildings(QPainter& painter)
painter.setBrush(Qt::NoBrush);
painter.drawRect(bboxRect.adjusted(-1, -1, 1, 1));
}
// HP bar below the HQ footprint; the HQ's HP lives on its proxy entity.
if (b.type == BuildingType::Hq)
{
m_sim->admin().forEach<HqProxyComponent, FactionComponent, HealthComponent>(
[&](entt::entity /*e*/, const HqProxyComponent& /*hq*/,
const FactionComponent& f, const HealthComponent& h)
{
if (h.maxHp > 0.0f)
{
drawHpBar(painter, bboxRect.left(), bboxRect.bottom() + 1.0,
bboxRect.width(), h.hp / h.maxHp, f.isEnemy);
}
});
}
}
painter.setOpacity(0.5);
@@ -1041,14 +1057,8 @@ void GameWorldView::drawStations(QPainter& painter)
// HP bar below footprint.
if (h.maxHp > 0.0f)
{
const float fraction = std::max(0.0f, h.hp / h.maxHp);
const qreal barH = static_cast<qreal>(tilePx()) * 0.12;
const qreal barY = bboxRect.bottom() + 1.0;
const qreal barW = bboxRect.width();
painter.fillRect(QRectF(bboxRect.left(), barY, barW, barH),
QColor(60, 60, 60));
painter.fillRect(QRectF(bboxRect.left(), barY, barW * static_cast<qreal>(fraction), barH),
f.isEnemy ? QColor(200, 60, 60) : QColor(60, 200, 60));
drawHpBar(painter, bboxRect.left(), bboxRect.bottom() + 1.0,
bboxRect.width(), h.hp / h.maxHp, f.isEnemy);
}
});
}
@@ -1094,18 +1104,24 @@ void GameWorldView::drawShips(QPainter& painter)
if (h.maxHp > 0.0f)
{
const float fraction = std::max(0.0f, h.hp / h.maxHp);
const qreal barW = static_cast<qreal>(fwd) * 2.0;
const qreal barH = static_cast<qreal>(tilePx()) * 0.12;
const qreal barX = center.x() - static_cast<qreal>(fwd);
const qreal barY = center.y() + static_cast<qreal>(fwd) + 1.0;
painter.fillRect(QRectF(barX, barY, barW, barH), QColor(60, 60, 60));
painter.fillRect(QRectF(barX, barY, barW * static_cast<qreal>(fraction), barH),
fac.isEnemy ? QColor(200, 60, 60) : QColor(60, 200, 60));
drawHpBar(painter, barX, barY, barW, h.hp / h.maxHp, fac.isEnemy);
}
});
}
void GameWorldView::drawHpBar(QPainter& painter, qreal left, qreal top, qreal width,
float fraction, bool isEnemy)
{
const qreal barH = static_cast<qreal>(tilePx()) * 0.12;
const float clamped = std::max(0.0f, fraction);
painter.fillRect(QRectF(left, top, width, barH), QColor(60, 60, 60));
painter.fillRect(QRectF(left, top, width * static_cast<qreal>(clamped), barH),
isEnemy ? QColor(200, 60, 60) : QColor(60, 200, 60));
}
void GameWorldView::drawDebugSensorRanges(QPainter& painter)
{
painter.setBrush(Qt::NoBrush);

View File

@@ -119,6 +119,8 @@ private:
void drawBeltItems(QPainter& painter);
void drawScrap(QPainter& painter);
void drawShips(QPainter& painter);
void drawHpBar(QPainter& painter, qreal left, qreal top, qreal width,
float fraction, bool isEnemy);
void drawDebugSensorRanges(QPainter& painter);
void drawDebugTargetLines(QPainter& painter);
void drawDebugOverlay(QPainter& painter);