From 9f390db80c0053200160197a8d7542cc726bed5b Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Sun, 19 Jul 2026 22:09:50 +0200 Subject: [PATCH] Implement REQ-UI-PORT-TARGET-GLYPH builder-ghost port preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While in builder mode, draw an additional directional glyph centered in each output port's target cell (the cell just outside the footprint the port pushes into), previewing where output will flow before placement. - drawPortGlyph gains a `centered` flag so it can render either offset toward the port body's exit edge (existing on-tile glyph) or centered in a given tile (the new target cell). - drawBuildingGhost gains `showPortTargetGlyphs`, passed true only for the single-building builder ghost and false for the blueprint-placement ghost, so the preview is builder-mode only. - The Tunnel Entry is excluded — it receives items from any non-mouth edge rather than emitting into one adjacent cell. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc --- src/ui/GameWorldView.cpp | 51 +++++++++++++++++++++++++++++----------- src/ui/GameWorldView.h | 8 ++++--- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/ui/GameWorldView.cpp b/src/ui/GameWorldView.cpp index 8984339..24c6231 100644 --- a/src/ui/GameWorldView.cpp +++ b/src/ui/GameWorldView.cpp @@ -895,25 +895,31 @@ void GameWorldView::placeAtTile(QPoint tile) // Port glyph helper // --------------------------------------------------------------------------- -void GameWorldView::drawPortGlyph(QPainter& painter, QPoint bodyTile, - Rotation direction, const QColor& color) +void GameWorldView::drawPortGlyph(QPainter& painter, QPoint tile, + Rotation direction, const QColor& color, + bool centered) { const float px = getTilePx(); - const QRectF tr = tileRect(bodyTile); + const QRectF tr = tileRect(tile); const QPointF center(tr.x() + static_cast(px) * 0.5, tr.y() + static_cast(px) * 0.5); - QPointF offset; + QPointF edgeOffset; const char* ch; switch (direction) { - case Rotation::East: offset = QPointF(px * 0.25f, 0); ch = ">"; break; - case Rotation::West: offset = QPointF(-px * 0.25f, 0); ch = "<"; break; - case Rotation::North: offset = QPointF(0, -px * 0.25f); ch = "^"; break; - case Rotation::South: offset = QPointF(0, px * 0.25f); ch = "v"; break; + case Rotation::East: edgeOffset = QPointF(px * 0.25f, 0); ch = ">"; break; + case Rotation::West: edgeOffset = QPointF(-px * 0.25f, 0); ch = "<"; break; + case Rotation::North: edgeOffset = QPointF(0, -px * 0.25f); ch = "^"; break; + case Rotation::South: edgeOffset = QPointF(0, px * 0.25f); ch = "v"; break; default: return; } + // Centered glyphs sit in the middle of the tile (used for a port's target + // cell, REQ-UI-PORT-TARGET-GLYPH); otherwise offset toward the exit edge of + // the port's body tile (REQ-UI-PORT-GLYPH). + const QPointF offset = centered ? QPointF(0.0, 0.0) : edgeOffset; + const qreal half = static_cast(px) * 0.3; const QPointF pos = center + offset; const QRectF textRect(pos.x() - half, pos.y() - half, half * 2.0, half * 2.0); @@ -991,7 +997,7 @@ void GameWorldView::drawBuildings(QPainter& painter) for (const Port& port : b.outputPorts) { drawPortGlyph(painter, portBodyTile(port.tile, port.direction), - port.direction, bv.outline); + port.direction, bv.outline, /*centered*/ false); } // HP bar below the HQ footprint; the HQ's HP lives on its proxy entity. @@ -1070,7 +1076,8 @@ void GameWorldView::drawBuildings(QPainter& painter) { const QPoint absBody = s.anchor + portBodyTile(port.tile, port.direction); - drawPortGlyph(painter, absBody, port.direction, bv.outline); + drawPortGlyph(painter, absBody, port.direction, bv.outline, + /*centered*/ false); } } } @@ -1571,7 +1578,8 @@ void GameWorldView::drawOverlays(QPainter& painter) if (m_builderType.has_value()) { drawBuildingGhost(painter, *m_builderType, m_ghostTile, - m_ghostRotation, m_ghostValid); + m_ghostRotation, m_ghostValid, + /*showPortTargetGlyphs*/ true); } // Blueprint placement ghost @@ -1581,7 +1589,8 @@ void GameWorldView::drawOverlays(QPainter& painter) { const QPoint anchor = m_blueprintGhostTile + bb.offset; const bool valid = isValidPlacement(bb.type, anchor, bb.rotation); - drawBuildingGhost(painter, bb.type, anchor, bb.rotation, valid); + drawBuildingGhost(painter, bb.type, anchor, bb.rotation, valid, + /*showPortTargetGlyphs*/ false); } } @@ -1634,7 +1643,7 @@ void GameWorldView::drawOverlays(QPainter& painter) void GameWorldView::drawBuildingGhost(QPainter& painter, BuildingType type, QPoint anchorTile, Rotation rotation, - bool valid) + bool valid, bool showPortTargetGlyphs) { const BuildingDef* def = findBuildingDef(type); if (!def) { return; } @@ -1688,7 +1697,21 @@ void GameWorldView::drawBuildingGhost(QPainter& painter, BuildingType type, for (const Port& port : parsed.outputPorts) { drawPortGlyph(painter, anchorTile + portBodyTile(port.tile, port.direction), - port.direction, lineColor); + port.direction, lineColor, /*centered*/ false); + } + + // REQ-UI-PORT-TARGET-GLYPH: while in builder mode, additionally mark each + // output port's target cell (the cell just outside the footprint the port + // pushes into) with a directional glyph, previewing where output will flow. + // The Tunnel Entry is excluded — it receives items from any of its non-mouth + // edges rather than emitting into a single adjacent cell. + if (showPortTargetGlyphs && type != BuildingType::TunnelEntry) + { + for (const Port& port : parsed.outputPorts) + { + drawPortGlyph(painter, anchorTile + port.tile, port.direction, + lineColor, /*centered*/ true); + } } painter.setOpacity(1.0); diff --git a/src/ui/GameWorldView.h b/src/ui/GameWorldView.h index 835cf4a..fc2e396 100644 --- a/src/ui/GameWorldView.h +++ b/src/ui/GameWorldView.h @@ -175,11 +175,13 @@ private: std::vector buildingsInBox(QPoint cornerA, QPoint cornerB) const; QVector2D widgetToWorld(QPoint widgetPt) const; - void drawPortGlyph(QPainter& painter, QPoint bodyTile, - Rotation direction, const QColor& color); + void drawPortGlyph(QPainter& painter, QPoint tile, + Rotation direction, const QColor& color, + bool centered); void drawBuildingGhost(QPainter& painter, BuildingType type, - QPoint anchorTile, Rotation rotation, bool valid); + QPoint anchorTile, Rotation rotation, bool valid, + bool showPortTargetGlyphs); void placeBlueprintAtTile(QPoint center);