diff --git a/source/fade.h b/source/fade.h index 102c3fd..a3bccbb 100644 --- a/source/fade.h +++ b/source/fade.h @@ -10,19 +10,19 @@ constexpr int FADE_RANDOM_SQUARE = 2; // Clase Fade class Fade { private: - SDL_Renderer *mRenderer; // El renderizador de la ventana - SDL_Texture *mBackbuffer; // Textura para usar como backbuffer - Uint8 mFadeType; // Tipo de fade a realizar - Uint16 mCounter; // Contador interno - bool mEnabled; // Indica si el fade está activo - bool mFinished; // Indica si ha terminado la transición - Uint8 mR, mG, mB; // Colores para el fade + SDL_Renderer *mRenderer; // El renderizador de la ventana + SDL_Texture *mBackbuffer; // Textura para usar como backbuffer + Uint8 mFadeType; // Tipo de fade a realizar + Uint16 mCounter; // Contador interno + bool mEnabled; // Indica si el fade está activo + bool mFinished; // Indica si ha terminado la transición + Uint8 mR, mG, mB; // Colores para el fade Uint8 mROriginal, mGOriginal, mBOriginal; // Colores originales para FADE_RANDOM_SQUARE - Uint32 mLastSquareTicks; // Ticks del último cuadrado dibujado (FADE_RANDOM_SQUARE) - Uint16 mSquaresDrawn; // Número de cuadrados dibujados (FADE_RANDOM_SQUARE) - bool mFullscreenDone; // Indica si el fade fullscreen ha terminado la fase de fundido - SDL_Rect mRect1; // Rectangulo usado para crear los efectos de transición - SDL_Rect mRect2; // Rectangulo usado para crear los efectos de transición + Uint32 mLastSquareTicks; // Ticks del último cuadrado dibujado (FADE_RANDOM_SQUARE) + Uint16 mSquaresDrawn; // Número de cuadrados dibujados (FADE_RANDOM_SQUARE) + bool mFullscreenDone; // Indica si el fade fullscreen ha terminado la fase de fundido + SDL_Rect mRect1; // Rectangulo usado para crear los efectos de transición + SDL_Rect mRect2; // Rectangulo usado para crear los efectos de transición public: // Constructor diff --git a/source/game.cpp b/source/game.cpp index d9597e6..28d60f2 100644 --- a/source/game.cpp +++ b/source/game.cpp @@ -305,6 +305,9 @@ void Game::init() { coffeeMachineEnabled = false; pauseCounter = 0; leavingPauseMenu = false; + pauseInitialized = false; + gameOverInitialized = false; + gameOverPostFade = 0; if (demo.enabled) { const int num = rand() % 2; @@ -2939,45 +2942,69 @@ bool Game::isDeathShaking() { return deathShake.active; } -// Bucle para el juego -void Game::run() { - while (section->name == SECTION_PROG_GAME) { - // Sección juego en pausa - if (section->subsection == SUBSECTION_GAME_PAUSE) { - runPausedGame(); +// Ejecuta un frame del juego +void Game::iterate() { + // Sección juego en pausa + if (section->subsection == SUBSECTION_GAME_PAUSE) { + if (!pauseInitialized) { + enterPausedGame(); } + updatePausedGame(); + checkEvents(); + renderPausedGame(); + } - // Sección Game Over - if (section->subsection == SUBSECTION_GAME_GAMEOVER) { - runGameOverScreen(); + // Sección Game Over + else if (section->subsection == SUBSECTION_GAME_GAMEOVER) { + if (!gameOverInitialized) { + enterGameOverScreen(); } + updateGameOverScreen(); + checkGameOverEvents(); + renderGameOverScreen(); + } - // Sección juego jugando - if ((section->subsection == SUBSECTION_GAME_PLAY_1P) || (section->subsection == SUBSECTION_GAME_PLAY_2P)) { - // Si la música no está sonando - if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { - // Reproduce la música - if (!gameCompleted) { - if (players[0]->isAlive()) { - JA_PlayMusic(gameMusic); - } + // Sección juego jugando + else if ((section->subsection == SUBSECTION_GAME_PLAY_1P) || (section->subsection == SUBSECTION_GAME_PLAY_2P)) { + // Resetea los flags de inicialización de sub-estados + pauseInitialized = false; + gameOverInitialized = false; + + // Si la música no está sonando + if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { + // Reproduce la música + if (!gameCompleted) { + if (players[0]->isAlive()) { + JA_PlayMusic(gameMusic); } } + } #ifdef PAUSE - if (!pause) - update(); -#else - // Actualiza la lógica del juego + if (!pause) update(); +#else + // Actualiza la lógica del juego + update(); #endif - // Comprueba los eventos que hay en cola - checkEvents(); + // Comprueba los eventos que hay en cola + checkEvents(); - // Dibuja los objetos - render(); - } + // Dibuja los objetos + render(); + } +} + +// Indica si el juego ha terminado +bool Game::hasFinished() const { + return section->name != SECTION_PROG_GAME; +} + +// Bucle para el juego +void Game::run() { + while (!hasFinished()) { + iterate(); } } @@ -3078,8 +3105,8 @@ void Game::renderPausedGame() { screen->blit(); } -// Bucle para el menu de pausa del juego -void Game::runPausedGame() { +// Inicializa el estado de pausa del juego +void Game::enterPausedGame() { // Pone en pausa la música if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_PauseMusic(); @@ -3091,19 +3118,11 @@ void Game::runPausedGame() { // Inicializa variables pauseCounter = 90; - - while ((section->subsection == SUBSECTION_GAME_PAUSE) && (section->name == SECTION_PROG_GAME)) { - updatePausedGame(); - checkEvents(); - renderPausedGame(); - } + pauseInitialized = true; } // Actualiza los elementos de la pantalla de game over void Game::updateGameOverScreen() { - // Variables - static int postFade = 0; - // Calcula la lógica de los objetos if (SDL_GetTicks() - ticks > ticksSpeed) { // Actualiza el contador de ticks @@ -3117,7 +3136,7 @@ void Game::updateGameOverScreen() { // Si ha terminado el fade, actua segun se haya operado if (fade->hasEnded()) { - switch (postFade) { + switch (gameOverPostFade) { case 0: // YES section->name = SECTION_PROG_GAME; deleteAllVectorObjects(); @@ -3142,12 +3161,12 @@ void Game::updateGameOverScreen() { // Comprueba si se ha seleccionado algún item del menú switch (gameOverMenu->getItemSelected()) { case 0: // YES - postFade = 0; + gameOverPostFade = 0; fade->activateFade(); break; case 1: // NO - postFade = 1; + gameOverPostFade = 1; fade->activateFade(); break; @@ -3156,8 +3175,10 @@ void Game::updateGameOverScreen() { } } } +} - // Comprueba los eventos que hay en la cola +// Comprueba los eventos de la pantalla de game over +void Game::checkGameOverEvents() { while (SDL_PollEvent(eventHandler) != 0) { // Evento de salida de la aplicación if (eventHandler->type == SDL_EVENT_QUIT) { @@ -3165,7 +3186,7 @@ void Game::updateGameOverScreen() { break; } else if (eventHandler->type == SDL_EVENT_KEY_DOWN && eventHandler->key.repeat == 0) { if (gameCompleted) { - postFade = 1; + gameOverPostFade = 1; fade->activateFade(); JA_PlaySound(itemPickUpSound); } @@ -3229,18 +3250,15 @@ void Game::renderGameOverScreen() { screen->blit(); } -// Bucle para la pantalla de game over -void Game::runGameOverScreen() { +// Inicializa el estado de game over +void Game::enterGameOverScreen() { // Guarda los puntos saveScoreFile(); // Reinicia el menu gameOverMenu->reset(); - - while ((section->subsection == SUBSECTION_GAME_GAMEOVER) && (section->name == SECTION_PROG_GAME)) { - updateGameOverScreen(); - renderGameOverScreen(); - } + gameOverPostFade = 0; + gameOverInitialized = true; } // Indica si se puede crear una powerball diff --git a/source/game.h b/source/game.h index 2e409de..df6c23a 100644 --- a/source/game.h +++ b/source/game.h @@ -90,19 +90,22 @@ class Game { // Estado para el efecto de agitación intensa (muerte del jugador) struct deathShake_t { - bool active; // Indica si el efecto está activo - Uint8 step; // Paso actual del efecto (0-7) - Uint32 lastStepTicks; // Ticks del último paso + bool active; // Indica si el efecto está activo + Uint8 step; // Paso actual del efecto (0-7) + Uint32 lastStepTicks; // Ticks del último paso }; // Fases de la secuencia de muerte del jugador - enum class DeathPhase { None, Shaking, Waiting, Done }; + enum class DeathPhase { None, + Shaking, + Waiting, + Done }; // Estado de la secuencia de muerte del jugador struct deathSequence_t { - DeathPhase phase; // Fase actual - Uint32 phaseStartTicks; // Ticks del inicio de la fase actual - Player *player; // Jugador que está muriendo + DeathPhase phase; // Fase actual + Uint32 phaseStartTicks; // Ticks del inicio de la fase actual + Player *player; // Jugador que está muriendo }; struct helper_t { @@ -252,6 +255,9 @@ class Game { int cloudsSpeed; // Velocidad a la que se desplazan las nubes int pauseCounter; // Contador para salir del menu de pausa y volver al juego bool leavingPauseMenu; // Indica si esta saliendo del menu de pausa para volver al juego + bool pauseInitialized; // Indica si la pausa ha sido inicializada + bool gameOverInitialized; // Indica si el game over ha sido inicializado + int gameOverPostFade; // Opción a realizar cuando termina el fundido del game over #ifdef PAUSE bool pause; #endif @@ -496,8 +502,8 @@ class Game { // Dibuja el menu de pausa del juego void renderPausedGame(); - // Bucle para el menu de pausa del juego - void runPausedGame(); + // Inicializa el estado de pausa del juego + void enterPausedGame(); // Actualiza los elementos de la pantalla de game over void updateGameOverScreen(); @@ -505,8 +511,11 @@ class Game { // Dibuja los elementos de la pantalla de game over void renderGameOverScreen(); - // Bucle para la pantalla de game over - void runGameOverScreen(); + // Inicializa el estado de game over + void enterGameOverScreen(); + + // Comprueba los eventos de la pantalla de game over + void checkGameOverEvents(); // Indica si se puede crear una powerball bool canPowerBallBeCreated(); @@ -547,4 +556,10 @@ class Game { // Bucle para el juego void run(); + + // Ejecuta un frame del juego + void iterate(); + + // Indica si el juego ha terminado + bool hasFinished() const; }; diff --git a/source/instructions.cpp b/source/instructions.cpp index 69463e4..f2a2a01 100644 --- a/source/instructions.cpp +++ b/source/instructions.cpp @@ -17,8 +17,6 @@ #include "texture.h" // for Texture #include "utils.h" // for color_t, section_t -const Uint8 SELF = 0; - // Constructor Instructions::Instructions(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input, Lang *lang, section_t *section) { // Copia los punteros @@ -62,12 +60,13 @@ Instructions::Instructions(SDL_Renderer *renderer, Screen *screen, Asset *asset, } // Inicializa variables - section->name = SELF; ticks = 0; ticksSpeed = 15; manualQuit = false; counter = 0; counterEnd = 600; + finished = false; + quitRequested = false; } // Destructor @@ -99,15 +98,13 @@ void Instructions::update() { counter++; if (counter == counterEnd) { - section->name = SECTION_PROG_TITLE; - section->subsection = SUBSECTION_TITLE_1; + finished = true; } } else { // Modo manual ++counter %= 60000; if (manualQuit) { - section->name = SECTION_PROG_TITLE; - section->subsection = SUBSECTION_TITLE_3; + finished = true; } } } @@ -215,7 +212,8 @@ void Instructions::checkEvents() { while (SDL_PollEvent(eventHandler) != 0) { // Evento de salida de la aplicación if (eventHandler->type == SDL_EVENT_QUIT) { - section->name = SECTION_PROG_QUIT; + quitRequested = true; + finished = true; break; } } @@ -224,7 +222,8 @@ void Instructions::checkEvents() { // Comprueba las entradas void Instructions::checkInput() { if (input->checkInput(input_exit, REPEAT_FALSE)) { - section->name = SECTION_PROG_QUIT; + quitRequested = true; + finished = true; } else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) { @@ -242,8 +241,7 @@ void Instructions::checkInput() { else if (input->checkInput(input_pause, REPEAT_FALSE) || input->checkInput(input_accept, REPEAT_FALSE) || input->checkInput(input_fire_left, REPEAT_FALSE) || input->checkInput(input_fire_center, REPEAT_FALSE) || input->checkInput(input_fire_right, REPEAT_FALSE)) { if (mode == m_auto) { JA_StopMusic(); - section->name = SECTION_PROG_TITLE; - section->subsection = SUBSECTION_TITLE_1; + finished = true; } else { if (counter > 30) { manualQuit = true; @@ -252,13 +250,41 @@ void Instructions::checkInput() { } } -// Bucle para la pantalla de instrucciones +// Bucle para la pantalla de instrucciones (compatibilidad) void Instructions::run(mode_e mode) { - this->mode = mode; + start(mode); - while (section->name == SELF) { + while (!finished) { update(); checkEvents(); render(); } + + // Aplica los cambios de sección según el resultado + if (quitRequested) { + section->name = SECTION_PROG_QUIT; + } else { + section->name = SECTION_PROG_TITLE; + section->subsection = (mode == m_auto) ? SUBSECTION_TITLE_1 : SUBSECTION_TITLE_3; + } +} + +// Inicia las instrucciones (sin bucle) +void Instructions::start(mode_e mode) { + this->mode = mode; + finished = false; + quitRequested = false; + manualQuit = false; + counter = 0; + ticks = 0; +} + +// Indica si las instrucciones han terminado +bool Instructions::hasFinished() const { + return finished; +} + +// Indica si se ha solicitado salir de la aplicación +bool Instructions::isQuitRequested() const { + return quitRequested; } diff --git a/source/instructions.h b/source/instructions.h index 7d146e5..4339a8c 100644 --- a/source/instructions.h +++ b/source/instructions.h @@ -34,21 +34,14 @@ class Instructions { section_t *section; // Estado del bucle principal para saber si continua o se sale // Variables - Uint16 counter; // Contador - Uint16 counterEnd; // Valor final para el contador - Uint32 ticks; // Contador de ticks para ajustar la velocidad del programa - Uint32 ticksSpeed; // Velocidad a la que se repiten los bucles del programa - bool manualQuit; // Indica si se quiere salir del modo manual - mode_e mode; // Modo en el que se van a ejecutar las instrucciones - - // Actualiza las variables - void update(); - - // Pinta en pantalla - void render(); - - // Comprueba los eventos - void checkEvents(); + Uint16 counter; // Contador + Uint16 counterEnd; // Valor final para el contador + Uint32 ticks; // Contador de ticks para ajustar la velocidad del programa + Uint32 ticksSpeed; // Velocidad a la que se repiten los bucles del programa + bool manualQuit; // Indica si se quiere salir del modo manual + mode_e mode; // Modo en el que se van a ejecutar las instrucciones + bool finished; // Indica si las instrucciones han terminado + bool quitRequested; // Indica si se ha solicitado salir de la aplicación // Comprueba las entradas void checkInput(); @@ -62,4 +55,22 @@ class Instructions { // Bucle principal void run(mode_e mode); + + // Inicia las instrucciones (sin bucle) + void start(mode_e mode); + + // Actualiza las variables + void update(); + + // Pinta en pantalla + void render(); + + // Comprueba los eventos + void checkEvents(); + + // Indica si las instrucciones han terminado + bool hasFinished() const; + + // Indica si se ha solicitado salir de la aplicación + bool isQuitRequested() const; }; \ No newline at end of file diff --git a/source/title.cpp b/source/title.cpp index 92d7a6b..78fd3f8 100644 --- a/source/title.cpp +++ b/source/title.cpp @@ -122,6 +122,9 @@ void Title::init() { demo = true; vibrationStep = 0; vibrationInitialized = false; + instructionsActive = false; + demoGameActive = false; + demoThenInstructions = false; // Pone valores por defecto a las opciones de control options->input.clear(); @@ -319,10 +322,8 @@ void Title::update() { counter = TITLE_COUNTER; menu.active->reset(); if (demo) { + demoThenInstructions = true; runDemoGame(); - if (section->name != SECTION_PROG_QUIT) { - runInstructions(m_auto); - } } else section->name = SECTION_PROG_LOGO; break; @@ -490,13 +491,8 @@ void Title::update() { } } else if (counter == 0) { if (demo) { + demoThenInstructions = true; runDemoGame(); - if (section->name != SECTION_PROG_QUIT) { - runInstructions(m_auto); - } - init(); - demo = false; - counter = TITLE_COUNTER; } else { section->name = SECTION_PROG_LOGO; } @@ -505,8 +501,6 @@ void Title::update() { // Sección Instrucciones if (section->subsection == SUBSECTION_TITLE_INSTRUCTIONS) { runInstructions(m_auto); - counter = TITLE_COUNTER; - demo = true; } } @@ -894,25 +888,83 @@ void Title::applyOptions() { // Bucle para el titulo del juego void Title::run() { - while (section->name == SECTION_PROG_TITLE) { + while (section->name == SECTION_PROG_TITLE || instructionsActive || demoGameActive) { + // Si las instrucciones están activas, delega el frame + if (instructionsActive) { + instructions->update(); + instructions->checkEvents(); + instructions->render(); + + if (instructions->hasFinished()) { + bool wasQuit = instructions->isQuitRequested(); + delete instructions; + instructions = nullptr; + instructionsActive = false; + + if (wasQuit) { + section->name = SECTION_PROG_QUIT; + } else if (instructionsMode == m_auto) { + // Tras instrucciones automáticas (post-demo o subsection), reinicia título + section->name = SECTION_PROG_TITLE; + init(); + demo = true; + } else { + // Tras instrucciones manuales (HOW TO PLAY), vuelve al menú + section->name = SECTION_PROG_TITLE; + section->subsection = SUBSECTION_TITLE_3; + } + } + continue; + } + + // Si el juego demo está activo, delega el frame + if (demoGameActive) { + demoGame->iterate(); + + if (demoGame->hasFinished()) { + bool wasQuit = (section->name == SECTION_PROG_QUIT); + delete demoGame; + demoGame = nullptr; + demoGameActive = false; + + if (wasQuit) { + section->name = SECTION_PROG_QUIT; + } else if (demoThenInstructions) { + // Restaura el section para Title y lanza instrucciones + section->name = SECTION_PROG_TITLE; + section->subsection = SUBSECTION_TITLE_3; + demoThenInstructions = false; + runInstructions(m_auto); + } else { + section->name = SECTION_PROG_TITLE; + section->subsection = SUBSECTION_TITLE_1; + } + } + continue; + } + + // Ejecución normal del título update(); checkEvents(); render(); } } -// Ejecuta la parte donde se muestran las instrucciones +// Inicia la parte donde se muestran las instrucciones void Title::runInstructions(mode_e mode) { instructions = new Instructions(renderer, screen, asset, input, lang, section); - instructions->run(mode); - delete instructions; + instructions->start(mode); + instructionsActive = true; + instructionsMode = mode; } -// Ejecuta el juego en modo demo +// Inicia el juego en modo demo void Title::runDemoGame() { + // Guardamos el section actual para que Game pueda funcionar + section->name = SECTION_PROG_GAME; + section->subsection = SUBSECTION_GAME_PLAY_1P; demoGame = new Game(1, 0, renderer, screen, asset, lang, input, true, options, section); - demoGame->run(); - delete demoGame; + demoGameActive = true; } // Modifica las opciones para los controles de los jugadores diff --git a/source/title.h b/source/title.h index 93574b5..21abe65 100644 --- a/source/title.h +++ b/source/title.h @@ -91,10 +91,16 @@ class Title { std::vector deviceIndex; // Indice para el jugador [i] del vector de dispositivos de entrada disponibles // Variables para la vibración del título (SUBSECTION_TITLE_2) - int vibrationStep; // Paso actual de la vibración - int vibrationCoffeeBaseX; // Posición X base del bitmap Coffee - int vibrationCrisisBaseX; // Posición X base del bitmap Crisis - bool vibrationInitialized; // Indica si se han capturado las posiciones base + int vibrationStep; // Paso actual de la vibración + int vibrationCoffeeBaseX; // Posición X base del bitmap Coffee + int vibrationCrisisBaseX; // Posición X base del bitmap Crisis + bool vibrationInitialized; // Indica si se han capturado las posiciones base + + // Variables para sub-estados delegados (instrucciones y demo) + bool instructionsActive; // Indica si las instrucciones están activas + bool demoGameActive; // Indica si el juego demo está activo + mode_e instructionsMode; // Modo de las instrucciones activas + bool demoThenInstructions; // Indica si tras la demo hay que mostrar instrucciones // Inicializa los valores void init();