#pragma once #include #include #include #include #include #include #include #include #include #include #include "BeamFiredEvent.h" #include "BuildModeController.h" #include "BuildingId.h" #include "BuildingType.h" #include "Rotation.h" #include "SelectionController.h" #include "VisualsConfig.h" #include "WorldCoordinates.h" class ItemIconCache; class Simulation; class QPainter; class QSvgRenderer; // A beam still being drawn. Lifetime is counted in game ticks so beams freeze with // the simulation when it is paused or slowed (REQ-SHP-FIRING-BEAM); the view owns // the ageing, the renderer only draws what it is given. struct ActiveBeam { BeamFiredEvent event; QVector2D targetOffset; }; // How a placement ghost is colored. Normal shows the building type's own colors // (REQ-BLD-GHOST); the other two override them (REQ-BLD-PLACE-VALID, // REQ-UI-BLUEPRINT-TRANSFER). enum class GhostTint { Normal, Invalid, Transfer }; // Everything the renderer draws that the simulation does not know about: what the // player has selected, which build mode is active, and the other transient bits of // interaction state the view owns. Assembled fresh each frame and passed by // reference, so the renderer holds no copy that could go stale. struct WorldRenderFrame { const SelectionController& selection; const BuildModeController& buildMode; const std::vector& beams; bool isBoxSelecting; QPoint boxStartTile; QPoint boxCurrentTile; bool isDebugDrawEnabled; }; // Draws the game world: terrain, buildings, items, ships, effects and the build // overlays, in back-to-front order (see the Layer Order section of // docs/architecture.md). // // Reads the simulation and never writes it, and knows nothing about input — the // view resolves clicks and owns the interaction state, and hands the parts the // renderer needs over in a WorldRenderFrame. // // Everything here is positioned in tiles. Screen-anchored chrome — the pause and // deconstruct vignettes, the replay overlay, the debug stats panel — stays with // the view, which is why nothing in this class draws translatable text. class WorldRenderer { public: // `itemIcons` is the window-wide per-item icon cache (REQ-UI-ITEM-ICON); not // owned, must outlive this renderer. `configDir` is used once, to load the // per-building world icons. WorldRenderer(Simulation& sim, const VisualsConfig& visuals, ItemIconCache* itemIcons, const std::string& configDir); ~WorldRenderer(); void render(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); private: void drawTiles(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawBuildings(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawSelectionHighlights(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawPortItems(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawStations(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawBeltItems(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawDebris(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawShips(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawDebugSensorRanges(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawDebugTargetLines(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawBeams(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawSelectedTunnelConnections(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); void drawOverlays(QPainter& painter, const WorldCoordinates& coordinates, const WorldRenderFrame& frame); // Draws a single item centered at widget-space `center`, spanning `halfPx` in // each direction (a half-tile). Uses the item's icon when one exists // (REQ-UI-ITEM-ICON), otherwise falls back to the colored square from // visuals.toml. Shared by drawBeltItems and drawPortItems. void drawWorldItem(QPainter& painter, const std::string& itemId, QPointF center, float halfPx); void drawPortGlyph(QPainter& painter, const WorldCoordinates& coordinates, QPoint tile, Rotation direction, const QColor& color, bool centered); void drawBuildingGhost(QPainter& painter, const WorldCoordinates& coordinates, BuildingType type, QPoint anchorTile, Rotation rotation, GhostTint tint, bool showPortTargetGlyphs); // Loads the per-building world icons (REQ-UI-WORLD-ICON) from // /../icons/buildings once at construction. Only the building // types with a world icon are loaded (production buildings, HQ, stations); // belts, splitters, and tunnels are deliberately excluded so their // orientation stays readable. The SVG's chip background is stripped; the // glyph is pre-rendered in both white and dark ink for auto-contrast. void loadBuildingIcons(const std::string& configDir); // Draws a building's world icon glyph centered in box, choosing the white or // dark pre-rendered variant by fill luminance so it stays legible. Returns // false if the type has no world icon (caller falls back to the text glyph). bool drawBuildingIcon(QPainter& painter, const WorldCoordinates& coordinates, BuildingType type, const QRectF& box, const QColor& fill) const; // Widget-space rectangle covering a building or construction site's footprint, // or nullopt if the id resolves to neither. Used by the selection highlight. std::optional footprintWidgetRect(const WorldCoordinates& coordinates, BuildingId id) const; std::optional entityPosition(entt::entity entity) const; // Non-const only because EntityAdmin's component accessors are; the renderer // reads the simulation and never writes it. Simulation& m_sim; const VisualsConfig& m_visuals; // Per-item icon cache (REQ-UI-ITEM-ICON), shared window-wide and owned by // MainWindow. Shared draw path for belt and port items; pixmaps are cached // per target size. ItemIconCache* m_itemIcons; // World icon glyph renderers per building type (REQ-UI-WORLD-ICON), in a // white and a dark variant so drawBuildingIcon can auto-contrast against the // building's fill. Rendered as vector at the view scale each draw so they // stay crisp. Populated once by loadBuildingIcons(). struct BuildingIconRenderers { std::unique_ptr white; std::unique_ptr dark; }; std::map m_buildingIcons; };