From 06d47124939290acf0929346d0f25c3010b7701e Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sun, 12 Apr 2026 19:32:31 +0200 Subject: [PATCH] migrat a SDL3 Callback API (SDL_AppInit/Iterate/Event/Quit) (milestone 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - main.cpp reescrit amb SDL_MAIN_USE_CALLBACKS - Director convertit a màquina d'estats amb iterate() i handleEvent() - Seccions (Logo, Intro, Title, Game) amb iterate() i handleEvent() - Events SDL enrutats via SDL_AppEvent → Director → secció activa - Eliminat SDL_PollEvent de iterate(), events via handleEvent() - Transicions entre seccions gestionades per handleSectionTransition() - Instructions i Game (demo) delegats frame a frame des de Title Co-Authored-By: Claude Opus 4.6 (1M context) --- source/director.cpp | 149 +++++++++++++++++++++++++++++++------------- source/director.h | 31 +++++---- source/game.cpp | 33 ++++++++-- source/game.h | 3 + source/intro.cpp | 17 ++++- source/intro.h | 6 ++ source/logo.cpp | 17 ++++- source/logo.h | 6 ++ source/main.cpp | 25 +++++--- source/title.cpp | 145 ++++++++++++++++++++++++------------------ source/title.h | 6 ++ 11 files changed, 306 insertions(+), 132 deletions(-) diff --git a/source/director.cpp b/source/director.cpp index d7144dd..94e7dbb 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -81,6 +81,8 @@ Director::Director(int argc, const char *argv[]) { initInput(); screen = new Screen(window, renderer, asset, options); + + activeSection = ActiveSection::None; } Director::~Director() { @@ -568,50 +570,113 @@ bool Director::saveConfigFile() { return success; } -void Director::runLogo() { - auto logo = std::make_unique(renderer, screen, asset, input, section); - logo->run(); -} - -void Director::runIntro() { - auto intro = std::make_unique(renderer, screen, asset, input, lang, section); - intro->run(); -} - -void Director::runTitle() { - auto title = std::make_unique(renderer, screen, input, asset, options, lang, section); - title->run(); -} - -void Director::runGame() { - const int numPlayers = section->subsection == SUBSECTION_GAME_PLAY_1P ? 1 : 2; - auto game = std::make_unique<Game>(numPlayers, 0, renderer, screen, asset, lang, input, false, options, section); - game->run(); -} - -int Director::run() { - // Bucle principal - while (section->name != SECTION_PROG_QUIT) { - switch (section->name) { - case SECTION_PROG_LOGO: - runLogo(); - break; - - case SECTION_PROG_INTRO: - runIntro(); - break; - - case SECTION_PROG_TITLE: - runTitle(); - break; - - case SECTION_PROG_GAME: - runGame(); - break; - } +// Gestiona las transiciones entre secciones +void Director::handleSectionTransition() { + // Determina qué sección debería estar activa + ActiveSection targetSection = ActiveSection::None; + switch (section->name) { + case SECTION_PROG_LOGO: + targetSection = ActiveSection::Logo; + break; + case SECTION_PROG_INTRO: + targetSection = ActiveSection::Intro; + break; + case SECTION_PROG_TITLE: + targetSection = ActiveSection::Title; + break; + case SECTION_PROG_GAME: + targetSection = ActiveSection::Game; + break; } - return 0; + // Si no ha cambiado, no hay nada que hacer + if (targetSection == activeSection) return; + + // Destruye la sección anterior + logo.reset(); + intro.reset(); + title.reset(); + game.reset(); + + // Crea la nueva sección + activeSection = targetSection; + switch (activeSection) { + case ActiveSection::Logo: + logo = std::make_unique<Logo>(renderer, screen, asset, input, section); + break; + case ActiveSection::Intro: + intro = std::make_unique<Intro>(renderer, screen, asset, input, lang, section); + break; + case ActiveSection::Title: + title = std::make_unique<Title>(renderer, screen, input, asset, options, lang, section); + break; + case ActiveSection::Game: { + const int numPlayers = section->subsection == SUBSECTION_GAME_PLAY_1P ? 1 : 2; + game = std::make_unique<Game>(numPlayers, 0, renderer, screen, asset, lang, input, false, options, section); + break; + } + case ActiveSection::None: + break; + } +} + +// Ejecuta un frame del juego +SDL_AppResult Director::iterate() { + if (section->name == SECTION_PROG_QUIT) { + return SDL_APP_SUCCESS; + } + + // Gestiona las transiciones entre secciones + handleSectionTransition(); + + // Ejecuta un frame de la sección activa + switch (activeSection) { + case ActiveSection::Logo: + logo->iterate(); + break; + case ActiveSection::Intro: + intro->iterate(); + break; + case ActiveSection::Title: + title->iterate(); + break; + case ActiveSection::Game: + game->iterate(); + break; + case ActiveSection::None: + break; + } + + return SDL_APP_CONTINUE; +} + +// Procesa un evento +SDL_AppResult Director::handleEvent(SDL_Event *event) { + // Evento de salida de la aplicación + if (event->type == SDL_EVENT_QUIT) { + section->name = SECTION_PROG_QUIT; + return SDL_APP_SUCCESS; + } + + // Reenvía el evento a la sección activa + switch (activeSection) { + case ActiveSection::Logo: + logo->handleEvent(event); + break; + case ActiveSection::Intro: + intro->handleEvent(event); + break; + case ActiveSection::Title: + title->handleEvent(event); + break; + case ActiveSection::Game: + game->handleEvent(event); + break; + case ActiveSection::None: + break; + } + + return SDL_APP_CONTINUE; } // Asigna variables a partir de dos cadenas diff --git a/source/director.h b/source/director.h index d770e29..8d6885f 100644 --- a/source/director.h +++ b/source/director.h @@ -2,6 +2,7 @@ #include <SDL3/SDL.h> +#include <memory> #include <string> // for string, basic_string class Asset; class Game; @@ -17,6 +18,9 @@ struct section_t; // Textos constexpr const char *WINDOW_CAPTION = "© 2020 Coffee Crisis — JailDesigner"; +// Secciones activas del Director +enum class ActiveSection { None, Logo, Intro, Title, Game }; + class Director { private: // Objetos y punteros @@ -28,6 +32,13 @@ class Director { Asset *asset; // Objeto que gestiona todos los ficheros de recursos section_t *section; // Sección y subsección actual del programa; + // Secciones del juego + ActiveSection activeSection; + std::unique_ptr<Logo> logo; + std::unique_ptr<Intro> intro; + std::unique_ptr<Title> title; + std::unique_ptr<Game> game; + // Variables struct options_t *options; // Variable con todas las opciones del programa std::string executablePath; // Path del ejecutable @@ -63,17 +74,8 @@ class Director { // Crea la carpeta del sistema donde guardar datos void createSystemFolder(const std::string &folder); - // Ejecuta la seccion de juego con el logo - void runLogo(); - - // Ejecuta la seccion de juego de la introducción - void runIntro(); - - // Ejecuta la seccion de juego con el titulo y los menus - void runTitle(); - - // Ejecuta la seccion de juego donde se juega - void runGame(); + // Gestiona las transiciones entre secciones + void handleSectionTransition(); public: // Constructor @@ -82,6 +84,9 @@ class Director { // Destructor ~Director(); - // Bucle principal - int run(); + // Ejecuta un frame del juego + SDL_AppResult iterate(); + + // Procesa un evento + SDL_AppResult handleEvent(SDL_Event *event); }; diff --git a/source/game.cpp b/source/game.cpp index 28d60f2..e4d061d 100644 --- a/source/game.cpp +++ b/source/game.cpp @@ -2950,7 +2950,6 @@ void Game::iterate() { enterPausedGame(); } updatePausedGame(); - checkEvents(); renderPausedGame(); } @@ -2960,7 +2959,6 @@ void Game::iterate() { enterGameOverScreen(); } updateGameOverScreen(); - checkGameOverEvents(); renderGameOverScreen(); } @@ -2988,9 +2986,6 @@ void Game::iterate() { update(); #endif - // Comprueba los eventos que hay en cola - checkEvents(); - // Dibuja los objetos render(); } @@ -3001,6 +2996,34 @@ bool Game::hasFinished() const { return section->name != SECTION_PROG_GAME; } +// Procesa un evento individual +void Game::handleEvent(SDL_Event *event) { + // SDL_EVENT_QUIT ya lo maneja Director + + if (event->type == SDL_EVENT_WINDOW_FOCUS_LOST) { + section->subsection = SUBSECTION_GAME_PAUSE; + } + +#ifdef PAUSE + if (event->type == SDL_EVENT_KEY_DOWN) { + if (event->key.scancode == SDL_SCANCODE_P) { + pause = !pause; + } + } +#endif + + // Eventos específicos de la pantalla de game over + if (section->subsection == SUBSECTION_GAME_GAMEOVER) { + if (event->type == SDL_EVENT_KEY_DOWN && event->key.repeat == 0) { + if (gameCompleted) { + gameOverPostFade = 1; + fade->activateFade(); + JA_PlaySound(itemPickUpSound); + } + } + } +} + // Bucle para el juego void Game::run() { while (!hasFinished()) { diff --git a/source/game.h b/source/game.h index df6c23a..6bb1ec6 100644 --- a/source/game.h +++ b/source/game.h @@ -562,4 +562,7 @@ class Game { // Indica si el juego ha terminado bool hasFinished() const; + + // Procesa un evento + void handleEvent(SDL_Event *event); }; diff --git a/source/intro.cpp b/source/intro.cpp index 0e0e1a7..a432b4b 100644 --- a/source/intro.cpp +++ b/source/intro.cpp @@ -153,6 +153,8 @@ Intro::Intro(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input, for (auto text : texts) { text->center(GAMECANVAS_CENTER_X); } + + JA_PlayMusic(music, 0); } // Destructor @@ -403,8 +405,17 @@ void Intro::run() { JA_PlayMusic(music, 0); while (section->name == SECTION_PROG_INTRO) { - update(); - checkEvents(); - render(); + iterate(); } } + +// Ejecuta un frame +void Intro::iterate() { + update(); + render(); +} + +// Procesa un evento individual +void Intro::handleEvent(SDL_Event *event) { + // SDL_EVENT_QUIT ya lo maneja Director +} diff --git a/source/intro.h b/source/intro.h index 44eeef6..17dda15 100644 --- a/source/intro.h +++ b/source/intro.h @@ -63,4 +63,10 @@ class Intro { // Bucle principal void run(); + + // Ejecuta un frame + void iterate(); + + // Procesa un evento + void handleEvent(SDL_Event *event); }; diff --git a/source/logo.cpp b/source/logo.cpp index 7edd2d2..f31124b 100644 --- a/source/logo.cpp +++ b/source/logo.cpp @@ -38,6 +38,8 @@ Logo::Logo(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input, s section->subsection = 0; ticks = 0; ticksSpeed = 15; + + JA_StopMusic(); } // Destructor @@ -144,8 +146,17 @@ void Logo::run() { JA_StopMusic(); while (section->name == SECTION_PROG_LOGO) { - update(); - checkEvents(); - render(); + iterate(); } } + +// Ejecuta un frame +void Logo::iterate() { + update(); + render(); +} + +// Procesa un evento individual +void Logo::handleEvent(SDL_Event *event) { + // SDL_EVENT_QUIT ya lo maneja Director +} diff --git a/source/logo.h b/source/logo.h index bd17edc..28b820c 100644 --- a/source/logo.h +++ b/source/logo.h @@ -53,4 +53,10 @@ class Logo { // Bucle principal void run(); + + // Ejecuta un frame + void iterate(); + + // Procesa un evento + void handleEvent(SDL_Event *event); }; diff --git a/source/main.cpp b/source/main.cpp index 35b083e..7258c2e 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -39,15 +39,26 @@ Reescribiendo el código el 27/09/2022 */ -#include <memory> +#define SDL_MAIN_USE_CALLBACKS 1 +#include <SDL3/SDL_main.h> #include "director.h" #include "stb_vorbis.c" -int main(int argc, char *argv[]) { - // Crea el objeto Director - auto director = std::make_unique<Director>(argc, const_cast<const char **>(argv)); +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) { + auto *director = new Director(argc, const_cast<const char **>(argv)); + *appstate = director; + return SDL_APP_CONTINUE; +} - // Bucle principal - return director->run(); -} \ No newline at end of file +SDL_AppResult SDL_AppIterate(void *appstate) { + return static_cast<Director *>(appstate)->iterate(); +} + +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { + return static_cast<Director *>(appstate)->handleEvent(event); +} + +void SDL_AppQuit(void *appstate, SDL_AppResult result) { + delete static_cast<Director *>(appstate); +} diff --git a/source/title.cpp b/source/title.cpp index 78fd3f8..0b44e0c 100644 --- a/source/title.cpp +++ b/source/title.cpp @@ -886,67 +886,94 @@ void Title::applyOptions() { createTiledBackground(); } -// Bucle para el titulo del juego +// Ejecuta un frame +void Title::iterate() { + // Si las instrucciones están activas, delega el frame + if (instructionsActive) { + instructions->update(); + 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) { + section->name = SECTION_PROG_TITLE; + init(); + demo = true; + } else { + section->name = SECTION_PROG_TITLE; + section->subsection = SUBSECTION_TITLE_3; + } + } + return; + } + + // 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) { + 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; + } + } + return; + } + + // Ejecución normal del título + update(); + render(); +} + +// Procesa un evento individual +void Title::handleEvent(SDL_Event *event) { + // Si hay un sub-estado activo, delega el evento + if (instructionsActive && instructions) { + // SDL_EVENT_QUIT ya lo maneja Director + return; + } + + if (demoGameActive && demoGame) { + demoGame->handleEvent(event); + return; + } + + // SDL_EVENT_QUIT ya lo maneja Director + + if (event->type == SDL_EVENT_RENDER_DEVICE_RESET || event->type == SDL_EVENT_RENDER_TARGETS_RESET) { + reLoadTextures(); + } + + if (section->subsection == SUBSECTION_TITLE_3) { + if ((event->type == SDL_EVENT_KEY_UP) || (event->type == SDL_EVENT_JOYSTICK_BUTTON_UP)) { + menuVisible = true; + counter = TITLE_COUNTER; + } + } +} + +// Bucle para el titulo del juego (compatibilidad) void Title::run() { 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(); + iterate(); } } diff --git a/source/title.h b/source/title.h index 21abe65..f89c731 100644 --- a/source/title.h +++ b/source/title.h @@ -156,4 +156,10 @@ class Title { // Bucle para el titulo del juego void run(); + + // Ejecuta un frame + void iterate(); + + // Procesa un evento + void handleEvent(SDL_Event *event); };