diff --git a/docs/requirements.md b/docs/requirements.md index fb9eb6d..4e582f8 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -326,6 +326,10 @@ The screen is divided into three vertical sections: - REQ-UI-DEBUG-DRAW: A debug draw mode can be toggled on and off with the **M** key (REQ-UI-HOTKEYS). It is inactive by default. While active, the sensor range of every ship — both player and enemy — is drawn as a circle centered on the ship, using that ship schematic's outline color from `visuals.toml`. +- REQ-UI-DEBUG-OVERLAY: While debug draw mode is active (REQ-UI-DEBUG-DRAW), a text overlay is drawn in the upper left corner of the game world view. The overlay has a semi-transparent black background sized to fit its content. It displays the following lines of text: + - `Accumulated Threat Level: ` — where `` is the current accumulated threat level (see REQ-WAV-THREAT-RATE). + - `Time until Wave: ` — where `` is the remaining time in seconds on the normal-wave inter-wave gap timer (see REQ-WAV-GAP). During a quiet window the gap timer is frozen; the displayed value reflects that frozen state. + ### Escape Menu - REQ-UI-GAME-MENU: Pressing Escape at any time opens the escape menu as a modal dialog and pauses the simulation (sets speed to 0×). On close, the simulation speed is restored to what it was before the menu was opened — so if the game was already paused, it remains paused. The menu contains three buttons: diff --git a/src/lib/sim/Simulation.cpp b/src/lib/sim/Simulation.cpp index e04c89a..bebf956 100644 --- a/src/lib/sim/Simulation.cpp +++ b/src/lib/sim/Simulation.cpp @@ -555,6 +555,11 @@ Tick Simulation::bossCountdownTicks() const return m_waveSystem->bossCountdownTicks(); } +Tick Simulation::normalGapRemainingTicks() const +{ + return m_waveSystem->normalGapRemainingTicks(); +} + int Simulation::schematicLevel(const std::string& shipId) const { const std::map::const_iterator it = diff --git a/src/lib/sim/Simulation.h b/src/lib/sim/Simulation.h index 5edc2dd..a5b3dac 100644 --- a/src/lib/sim/Simulation.h +++ b/src/lib/sim/Simulation.h @@ -59,7 +59,8 @@ public: bool isGameOver() const; double threatLevel() const; int bossWaveCounter() const; - Tick bossCountdownTicks() const; + Tick bossCountdownTicks() const; + Tick normalGapRemainingTicks() const; // Schematic state queries. int schematicLevel(const std::string& shipId) const; diff --git a/src/lib/sim/WaveSystem.cpp b/src/lib/sim/WaveSystem.cpp index 76a195b..ca38c35 100644 --- a/src/lib/sim/WaveSystem.cpp +++ b/src/lib/sim/WaveSystem.cpp @@ -113,6 +113,11 @@ Tick WaveSystem::bossCountdownTicks() const return m_bossCountdownTicks; } +Tick WaveSystem::normalGapRemainingTicks() const +{ + return m_normalGapRemainingTicks; +} + // --------------------------------------------------------------------------- // Private helpers // --------------------------------------------------------------------------- diff --git a/src/lib/sim/WaveSystem.h b/src/lib/sim/WaveSystem.h index 8e23b27..aceb630 100644 --- a/src/lib/sim/WaveSystem.h +++ b/src/lib/sim/WaveSystem.h @@ -46,6 +46,10 @@ public: // Ticks remaining until the next boss wave fires (REQ-WAV-BOSS-COUNTDOWN). Tick bossCountdownTicks() const; + // Ticks remaining on the current normal-wave gap timer (REQ-WAV-GAP). + // Frozen during quiet windows. + Tick normalGapRemainingTicks() const; + private: struct SpawnEntry { diff --git a/src/ui/GameWorldView.cpp b/src/ui/GameWorldView.cpp index a350256..de0b6ce 100644 --- a/src/ui/GameWorldView.cpp +++ b/src/ui/GameWorldView.cpp @@ -292,7 +292,11 @@ void GameWorldView::paintGL() drawStations(painter); drawBeltItems(painter); drawScrap(painter); - if (m_debugDraw) { drawDebugSensorRanges(painter); } + if (m_debugDraw) + { + drawDebugSensorRanges(painter); + drawDebugOverlay(painter); + } drawShips(painter); drawBeams(painter); drawOverlays(painter); @@ -937,6 +941,38 @@ void GameWorldView::drawDebugSensorRanges(QPainter& painter) }); } +void GameWorldView::drawDebugOverlay(QPainter& painter) +{ + painter.resetTransform(); + + const QString line1 = tr("Accumulated Threat Level: %1") + .arg(m_sim->threatLevel(), 0, 'f', 1); + const QString line2 = tr("Time until Wave: %1s") + .arg(ticksToSeconds(m_sim->normalGapRemainingTicks()), 0, 'f', 1); + + QFont font = painter.font(); + font.setPointSize(m_visuals->toast.fontSize); + painter.setFont(font); + + const QFontMetrics fm = painter.fontMetrics(); + const int lineH = fm.height(); + const int padding = 8; + const int spacing = 4; + const int textW = std::max(fm.horizontalAdvance(line1), + fm.horizontalAdvance(line2)); + const int bgW = textW + padding * 2; + const int bgH = lineH * 2 + spacing + padding * 2; + + const QRect bgRect(padding, padding, bgW, bgH); + painter.fillRect(bgRect, QColor(0, 0, 0, 160)); + + painter.setPen(Qt::white); + const QRect textRect1(padding * 2, padding + padding, textW, lineH); + const QRect textRect2(padding * 2, textRect1.bottom() + spacing, textW, lineH); + painter.drawText(textRect1, Qt::AlignLeft | Qt::AlignVCenter, line1); + painter.drawText(textRect2, Qt::AlignLeft | Qt::AlignVCenter, line2); +} + void GameWorldView::drawBeams(QPainter& painter) { painter.setPen(QPen(m_visuals->beams.color, m_visuals->beams.widthPx)); diff --git a/src/ui/GameWorldView.h b/src/ui/GameWorldView.h index 593bd7c..01ef36f 100644 --- a/src/ui/GameWorldView.h +++ b/src/ui/GameWorldView.h @@ -85,6 +85,7 @@ private: void drawScrap(QPainter& painter); void drawShips(QPainter& painter); void drawDebugSensorRanges(QPainter& painter); + void drawDebugOverlay(QPainter& painter); void drawBeams(QPainter& painter); void drawOverlays(QPainter& painter); void drawScreenSpace(QPainter& painter);