Add building status light

This commit is contained in:
2026-07-20 22:19:58 +02:00
parent c7ecce6ac4
commit 1cbc695bc5
8 changed files with 385 additions and 57 deletions

View File

@@ -124,6 +124,20 @@ QPoint portBodyTile(QPoint portTile, Rotation direction)
return portTile;
}
// Fill color for a building's status light per its production state
// (REQ-UI-STATUS-LIGHT).
QColor statusLightFill(ProductionStatus status, const StatusLightVisuals& sl)
{
switch (status)
{
case ProductionStatus::Unconfigured: return sl.grey;
case ProductionStatus::Producing: return sl.green;
case ProductionStatus::Starved: return sl.red;
case ProductionStatus::Blocked: return sl.yellow;
}
return sl.grey;
}
} // namespace
@@ -1039,6 +1053,35 @@ void GameWorldView::drawBuildings(QPainter& painter)
port.direction, bv.outline, /*centered*/ false);
}
// Status light: a small circle in the building's upper-right corner
// (in default/East orientation), anchored to that footprint corner and
// rotating with the building (REQ-UI-STATUS-LIGHT). All status-light
// building footprints are rectangular, so a corner of the axis-aligned
// bounding box is the true corner.
if (const std::optional<ProductionStatus> status =
m_sim->getBuildings().getProductionStatus(b))
{
const float px = getTilePx();
const float r = px * 0.18f;
const float inset = r + px * 0.12f;
// Default orientation (East) puts the light at the top-right corner;
// clockwise rotation carries it around the footprint.
QPointF center(bboxRect.right() - inset, bboxRect.top() + inset);
switch (b.rotation)
{
case Rotation::East: break;
case Rotation::South: center = QPointF(bboxRect.right() - inset,
bboxRect.bottom() - inset); break;
case Rotation::West: center = QPointF(bboxRect.left() + inset,
bboxRect.bottom() - inset); break;
case Rotation::North: center = QPointF(bboxRect.left() + inset,
bboxRect.top() + inset); break;
}
painter.setBrush(statusLightFill(*status, m_visuals->statusLight));
painter.setPen(QPen(m_visuals->statusLight.outline, 1));
painter.drawEllipse(center, r, r);
}
// HP bar below the HQ footprint; the HQ's HP lives on its proxy entity.
if (b.type == BuildingType::Hq)
{

View File

@@ -60,6 +60,17 @@ struct ToastVisuals
int fontSize;
};
// Fill colors for the building status light (REQ-UI-STATUS-LIGHT), plus its
// constant outline color.
struct StatusLightVisuals
{
QColor grey; // no recipe/schematic selected
QColor green; // producing
QColor red; // idle, input missing (or Salvage Bay empty)
QColor yellow; // idle, output buffer full
QColor outline;
};
struct VisualsConfig
{
TileVisuals asteroid;
@@ -69,7 +80,8 @@ struct VisualsConfig
std::map<std::string, ItemVisuals> items;
std::map<std::string, ShipVisuals> ships;
BeamVisuals beams;
OverlayVisuals overlays;
ToastVisuals toast;
BeamVisuals beams;
OverlayVisuals overlays;
ToastVisuals toast;
StatusLightVisuals statusLight;
};

View File

@@ -237,5 +237,15 @@ VisualsConfig VisualsLoader::load(const std::string& path)
cfg.toast.fontSize = requireInt(t, "font_size", "toast");
}
// Status light (REQ-UI-STATUS-LIGHT)
{
toml::table& sl = requireSubtable(tbl, "status_light", "root");
cfg.statusLight.grey = parseColor(requireString(sl, "grey", "status_light"), "status_light.grey");
cfg.statusLight.green = parseColor(requireString(sl, "green", "status_light"), "status_light.green");
cfg.statusLight.red = parseColor(requireString(sl, "red", "status_light"), "status_light.red");
cfg.statusLight.yellow = parseColor(requireString(sl, "yellow", "status_light"), "status_light.yellow");
cfg.statusLight.outline = parseColor(requireString(sl, "outline", "status_light"), "status_light.outline");
}
return cfg;
}