Add exit-confirm modal (fullscreen dim, centered P2 text) and keyboard shortcuts
Add an in-game exit confirmation modal for Playing state: ESC opens modal and pauses the game; YES resets and returns to Menu; NO hides modal and resumes. Draw a full-window translucent dim background (reset viewport) so overlay covers any window size / fullscreen. Use PressStart2P (pixel P2) font for all modal text and center title/body/button labels using measured text widths. Add FontAtlas::measure(...) to accurately measure text sizes (used for proper centering). Ensure popup rendering and mouse hit-testing use the same logical/content-local coordinate math so visuals and clicks align. Add keyboard shortcuts for modal (Enter = confirm, Esc = cancel) and suppress other gameplay input while modal is active. Add helper scripts for debug build+run: build-debug-and-run.ps1 and build-debug-and-run.bat. Minor fixes to related rendering & state wiring; verified Debug build completes and modal behavior in runtime.
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
#include "PlayingState.h"
|
||||
#include "core/StateManager.h"
|
||||
#include "gameplay/Game.h"
|
||||
#include "gameplay/LineEffect.h"
|
||||
#include "persistence/Scores.h"
|
||||
@ -14,14 +15,45 @@ void PlayingState::onExit() {
|
||||
}
|
||||
|
||||
void PlayingState::handleEvent(const SDL_Event& e) {
|
||||
// We keep short-circuited input here; main still handles mouse UI
|
||||
// We keep short-circuited input here; main still owns mouse UI
|
||||
if (e.type == SDL_EVENT_KEY_DOWN && !e.key.repeat) {
|
||||
if (!ctx.game) return;
|
||||
// Pause toggle (P)
|
||||
if (e.key.scancode == SDL_SCANCODE_P) {
|
||||
bool paused = ctx.game->isPaused();
|
||||
ctx.game->setPaused(!paused);
|
||||
return;
|
||||
}
|
||||
|
||||
// If exit-confirm popup is visible, handle shortcuts here
|
||||
if (ctx.showExitConfirmPopup && *ctx.showExitConfirmPopup) {
|
||||
// Confirm with Enter (main or keypad)
|
||||
if (e.key.scancode == SDL_SCANCODE_RETURN || e.key.scancode == SDL_SCANCODE_KP_ENTER) {
|
||||
*ctx.showExitConfirmPopup = false;
|
||||
// Reset game and return to menu
|
||||
ctx.game->reset(false);
|
||||
if (ctx.stateManager) ctx.stateManager->setState(AppState::Menu);
|
||||
return;
|
||||
}
|
||||
// Cancel with Esc
|
||||
if (e.key.scancode == SDL_SCANCODE_ESCAPE) {
|
||||
*ctx.showExitConfirmPopup = false;
|
||||
ctx.game->setPaused(false);
|
||||
return;
|
||||
}
|
||||
// While modal is open, suppress other gameplay keys
|
||||
return;
|
||||
}
|
||||
|
||||
// ESC key - open confirmation popup
|
||||
if (e.key.scancode == SDL_SCANCODE_ESCAPE) {
|
||||
if (ctx.showExitConfirmPopup) {
|
||||
if (ctx.game) ctx.game->setPaused(true);
|
||||
*ctx.showExitConfirmPopup = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Other gameplay keys already registered by main's Playing handler for now
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,10 @@ class Starfield3D;
|
||||
class FontAtlas;
|
||||
class LineEffect;
|
||||
|
||||
// Forward declare StateManager so StateContext can hold a pointer without
|
||||
// including the StateManager header here.
|
||||
class StateManager;
|
||||
|
||||
// Shared context passed to states so they can access common resources
|
||||
struct StateContext {
|
||||
// Core subsystems (may be null if not available)
|
||||
@ -41,6 +45,9 @@ struct StateContext {
|
||||
// Menu popups (exposed from main)
|
||||
bool* showLevelPopup = nullptr;
|
||||
bool* showSettingsPopup = nullptr;
|
||||
bool* showExitConfirmPopup = nullptr; // If true, show "Exit game?" confirmation while playing
|
||||
// Pointer to the application's StateManager so states can request transitions
|
||||
StateManager* stateManager = nullptr;
|
||||
};
|
||||
|
||||
class State {
|
||||
|
||||
Reference in New Issue
Block a user