From 332e2efb74f4adb68f61d0b2c454e62073c5b355 Mon Sep 17 00:00:00 2001 From: Gregor Klevze Date: Sat, 29 Nov 2025 19:23:41 +0100 Subject: [PATCH] fade effect when switching states --- src/main.cpp | 66 +++++++++++++++++++++++-------- src/states/LevelSelectorState.cpp | 8 +++- src/states/MenuState.cpp | 12 +++++- src/states/OptionsState.cpp | 4 +- src/states/State.h | 2 + 5 files changed, 71 insertions(+), 21 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 020a158..b77bf4e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -913,6 +913,31 @@ int main(int, char **) running = false; }; + auto beginStateFade = [&](AppState targetState, bool armGameplayCountdown) { + if (!ctx.stateManager) { + return; + } + if (state == targetState) { + return; + } + if (menuFadePhase != MenuFadePhase::None) { + return; + } + + menuFadePhase = MenuFadePhase::FadeOut; + menuFadeClockMs = 0.0; + menuFadeAlpha = 0.0f; + menuFadeTarget = targetState; + menuPlayCountdownArmed = armGameplayCountdown; + gameplayCountdownActive = false; + gameplayCountdownIndex = 0; + gameplayCountdownElapsed = 0.0; + + if (!armGameplayCountdown) { + game.setPaused(false); + } + }; + auto startMenuPlayTransition = [&]() { if (!ctx.stateManager) { return; @@ -922,20 +947,22 @@ int main(int, char **) ctx.stateManager->setState(state); return; } - if (menuFadePhase != MenuFadePhase::None) { - return; - } - menuFadePhase = MenuFadePhase::FadeOut; - menuFadeClockMs = 0.0; - menuFadeAlpha = 0.0f; - menuFadeTarget = AppState::Playing; - menuPlayCountdownArmed = true; - gameplayCountdownActive = false; - gameplayCountdownIndex = 0; - gameplayCountdownElapsed = 0.0; + beginStateFade(AppState::Playing, true); }; ctx.startPlayTransition = startMenuPlayTransition; + auto requestStateFade = [&](AppState targetState) { + if (!ctx.stateManager) { + return; + } + if (targetState == AppState::Playing) { + startMenuPlayTransition(); + return; + } + beginStateFade(targetState, false); + }; + ctx.requestFadeTransition = requestStateFade; + // Instantiate state objects auto loadingState = std::make_unique(ctx); auto menuState = std::make_unique(ctx); @@ -1100,11 +1127,9 @@ int main(int, char **) if (pointInRect(buttonRects[0])) { startMenuPlayTransition(); } else if (pointInRect(buttonRects[1])) { - state = AppState::LevelSelector; - stateMgr.setState(state); + requestStateFade(AppState::LevelSelector); } else if (pointInRect(buttonRects[2])) { - state = AppState::Options; - stateMgr.setState(state); + requestStateFade(AppState::Options); } else if (pointInRect(buttonRects[3])) { showExitConfirmPopup = true; exitPopupSelectedButton = 1; @@ -1461,14 +1486,23 @@ int main(int, char **) menuFadeClockMs += frameMs; menuFadeAlpha = std::min(1.0f, float(menuFadeClockMs / MENU_PLAY_FADE_DURATION_MS)); if (menuFadeClockMs >= MENU_PLAY_FADE_DURATION_MS) { - if (menuFadeTarget == AppState::Playing) { + if (state != menuFadeTarget) { state = menuFadeTarget; stateMgr.setState(state); + } + + if (menuFadeTarget == AppState::Playing) { menuPlayCountdownArmed = true; gameplayCountdownActive = false; gameplayCountdownIndex = 0; gameplayCountdownElapsed = 0.0; game.setPaused(true); + } else { + menuPlayCountdownArmed = false; + gameplayCountdownActive = false; + gameplayCountdownIndex = 0; + gameplayCountdownElapsed = 0.0; + game.setPaused(false); } menuFadePhase = MenuFadePhase::FadeIn; menuFadeClockMs = MENU_PLAY_FADE_DURATION_MS; diff --git a/src/states/LevelSelectorState.cpp b/src/states/LevelSelectorState.cpp index b293204..5996c26 100644 --- a/src/states/LevelSelectorState.cpp +++ b/src/states/LevelSelectorState.cpp @@ -326,14 +326,18 @@ void LevelSelectorState::selectLevel(int level) { *ctx.startLevelSelection = level; } // Transition back to menu - if (ctx.stateManager) { + if (ctx.requestFadeTransition) { + ctx.requestFadeTransition(AppState::Menu); + } else if (ctx.stateManager) { ctx.stateManager->setState(AppState::Menu); } } void LevelSelectorState::closePopup() { // Transition back to menu without changing level - if (ctx.stateManager) { + if (ctx.requestFadeTransition) { + ctx.requestFadeTransition(AppState::Menu); + } else if (ctx.stateManager) { ctx.stateManager->setState(AppState::Menu); } } diff --git a/src/states/MenuState.cpp b/src/states/MenuState.cpp index 5b42fd1..7344229 100644 --- a/src/states/MenuState.cpp +++ b/src/states/MenuState.cpp @@ -126,10 +126,18 @@ void MenuState::handleEvent(const SDL_Event& e) { triggerPlay(); break; case 1: - ctx.stateManager->setState(AppState::LevelSelector); + if (ctx.requestFadeTransition) { + ctx.requestFadeTransition(AppState::LevelSelector); + } else if (ctx.stateManager) { + ctx.stateManager->setState(AppState::LevelSelector); + } break; case 2: - ctx.stateManager->setState(AppState::Options); + if (ctx.requestFadeTransition) { + ctx.requestFadeTransition(AppState::Options); + } else if (ctx.stateManager) { + ctx.stateManager->setState(AppState::Options); + } break; case 3: setExitPrompt(true); diff --git a/src/states/OptionsState.cpp b/src/states/OptionsState.cpp index bcd0bf4..a074894 100644 --- a/src/states/OptionsState.cpp +++ b/src/states/OptionsState.cpp @@ -267,7 +267,9 @@ void OptionsState::toggleSoundFx() { } void OptionsState::exitToMenu() { - if (ctx.stateManager) { + if (ctx.requestFadeTransition) { + ctx.requestFadeTransition(AppState::Menu); + } else if (ctx.stateManager) { ctx.stateManager->setState(AppState::Menu); } } diff --git a/src/states/State.h b/src/states/State.h index 1834d48..87601a8 100644 --- a/src/states/State.h +++ b/src/states/State.h @@ -13,6 +13,7 @@ class Starfield; class Starfield3D; class FontAtlas; class LineEffect; +enum class AppState; // Forward declare StateManager so StateContext can hold a pointer without // including the StateManager header here. @@ -58,6 +59,7 @@ struct StateContext { std::function queryFullscreen; // Optional callback if fullscreenFlag is not reliable std::function requestQuit; // Allows menu/option states to close the app gracefully std::function startPlayTransition; // Optional fade hook when transitioning from menu to gameplay + std::function requestFadeTransition; // Generic state fade requests (menu/options/level) // Pointer to the application's StateManager so states can request transitions StateManager* stateManager = nullptr; };