#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "Blueprint.h" #include "BlueprintModeExitedEvent.h" #include "BlueprintPlacementRequestedEvent.h" #include "BuilderModeExitedEvent.h" #include "BuildingType.h" #include "BuildingTypeSelectedEvent.h" #include "BuildingId.h" #include "DemolishModeChangedEvent.h" #include "DemolishModeToggleRequestedEvent.h" #include "EventHandler.h" #include "ExitBlueprintModeRequestedEvent.h" #include "ExitBuilderModeRequestedEvent.h" #include "DebugDrawToggledEvent.h" #include "BeamFiredEvent.h" #include "CommandRequestedEvent.h" #include "SchematicChoiceOption.h" #include "SpeedChangeRequestedEvent.h" #include "entt/entity/entity.hpp" #include "CommandManager.h" #include "EntitySelectedEvent.h" #include "GameConfig.h" #include "Rotation.h" #include "Tick.h" #include "TickDriver.h" #include "VisualsConfig.h" struct Command; struct ParsedReplay; class ReplayPlayer; class Simulation; class QPainter; struct QPointCompare { bool operator()(const QPoint& a, const QPoint& b) const { if (a.x() != b.x()) { return a.x() < b.x(); } return a.y() < b.y(); } }; class GameWorldView : public QOpenGLWidget, public CombinedEventHandler { Q_OBJECT public: GameWorldView(Simulation* sim, const GameConfig* config, const VisualsConfig* visuals, const std::string& configDir, const ParsedReplay* replay, QWidget* parent = nullptr); ~GameWorldView() override; double gameSpeed() const; bool isDebugDrawEnabled() const; void resetFrameTimer(); void setGameSpeed(double multiplier); void resetForNewGame(); protected: void initializeGL() override; void paintGL() override; void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; private slots: void onFrame(); private: void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; // Enqueue a sim command onto the CommandManager (the single mutation path). void enqueueCommand(std::shared_ptr command); // Enqueue a plain (unconfigured) building placement. void enqueuePlaceBuilding(BuildingType type, QPoint anchor, Rotation rotation); // True if the player can currently afford to place one building of `type`. // Used to pre-validate placements whose UI follow-up depends on success. bool canAfford(BuildingType type) const; void drawTiles(QPainter& painter); void drawBuildings(QPainter& painter); void drawStations(QPainter& painter); void drawBeltItems(QPainter& painter); void drawScrap(QPainter& painter); void drawShips(QPainter& painter); void drawDebugSensorRanges(QPainter& painter); void drawDebugTargetLines(QPainter& painter); void drawDebugOverlay(QPainter& painter); void drawBeams(QPainter& painter); void drawOverlays(QPainter& painter); void drawScreenSpace(QPainter& painter); void drawReplayOverlay(QPainter& painter); float tilePx() const; float viewportWidthTiles() const; QPointF worldToWidget(QVector2D worldPos) const; QPointF tileToWidget(QPoint tile) const; QPoint widgetToTile(QPoint widgetPt) const; QRectF tileRect(QPoint tile) const; QRect viewportRect() const; float asteroidLeftEdge() const; float enemyStationRightEdge() const; void clampScroll(); bool isValidPlacement(BuildingType type, QPoint anchor, Rotation rot) const; const BuildingDef* findBuildingDef(BuildingType type) const; BuildingId buildingAtTile(QPoint tile) const; BuildingId siteAtTile(QPoint tile) const; QVector2D widgetToWorld(QPoint widgetPt) const; void drawPortGlyph(QPainter& painter, QPoint bodyTile, Rotation direction, const QColor& color); void drawBuildingGhost(QPainter& painter, BuildingType type, QPoint anchorTile, Rotation rotation, bool valid); void placeBlueprintAtTile(QPoint center); std::optional entityPosition(entt::entity entity) const; void stepSpeed(int delta); void placeAtTile(QPoint tile); void enterBuilderMode(BuildingType type); void exitBuilderMode(); void enterBlueprintMode(Blueprint blueprint); void exitBlueprintMode(); void toggleDemolishMode(); void rotateGhost(bool clockwise); struct ActiveBeam { BeamFiredEvent event; QVector2D targetOffset; }; // Beam lifetime in game ticks so beams freeze with the simulation when // paused or slowed, instead of fading on wall-clock time (REQ-SHP-FIRING-BEAM). static constexpr Tick kBeamLifetimeTicks = secondsToTicks(0.3); static constexpr float kScrollSpeedTilesPerSec = 10.0f; Simulation* m_sim; const GameConfig* m_config; const VisualsConfig* m_visuals; // Funnels all player input into the single Simulation::apply chokepoint. CommandManager m_commandManager; // A Reset command was enqueued; reset the view after the next drain applies it. bool m_viewResetPending = false; // Non-null => view-only playback: ticks are driven by the recorded stream and // live input is ignored (the CommandManager is in replay mode). std::unique_ptr m_replayPlayer; TickDriver m_tickDriver; QElapsedTimer m_frameTimer; std::mt19937 m_rng; double m_gameSpeedMultiplier; double m_prevNonZeroSpeed; float m_scrollXTiles; QTimer* m_renderTimer; std::vector m_activeBeams; std::optional m_builderType; Rotation m_ghostRotation; QPoint m_ghostTile; bool m_ghostValid; std::set m_beltDragTiles; bool m_dragging; std::optional m_blueprintMode; QPoint m_blueprintGhostTile; bool m_demolishMode; BuildingId m_demolishHoverBuildingId; bool m_debugDraw; std::vector m_selectedBuildingIds; std::optional m_selectedEntity; bool m_boxSelecting; QPoint m_boxStartTile; QPoint m_boxCurrentTile; bool m_scrollLeft; bool m_scrollRight; bool m_gameOverShown; bool m_schematicChoiceShown; Tick m_lastTick = Tick(-1); int m_lastBlocks = -1; int m_lastBossCounter = -1; Tick m_lastBossCountdown = Tick(-1); };