diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e52a5e..67fbe56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,12 +86,7 @@ set(APP_SOURCES source/game/gameplay/tilemap_renderer.cpp # Game - Scenes - source/game/scenes/credits.cpp - source/game/scenes/ending.cpp - source/game/scenes/ending2.cpp - source/game/scenes/game_over.cpp source/game/scenes/game.cpp - source/game/scenes/loading_screen.cpp source/game/scenes/logo.cpp source/game/scenes/title.cpp diff --git a/config/assets.yaml b/config/assets.yaml index 59be9ec..90622c5 100644 --- a/config/assets.yaml +++ b/config/assets.yaml @@ -229,30 +229,8 @@ assets: - ${PREFIX}/data/logo/jailgames.gif - ${PREFIX}/data/logo/since_1998.gif - # LOADING - loading: - BITMAP: - - ${PREFIX}/data/loading/loading_screen_bn.gif - - ${PREFIX}/data/loading/loading_screen_color.gif - - ${PREFIX}/data/loading/program_jaildoc.gif - # TITLE title: BITMAP: - ${PREFIX}/data/title/title_logo.gif - # ENDING - ending: - BITMAP: - - ${PREFIX}/data/ending/ending1.gif - - ${PREFIX}/data/ending/ending2.gif - - ${PREFIX}/data/ending/ending3.gif - - ${PREFIX}/data/ending/ending4.gif - - ${PREFIX}/data/ending/ending5.gif - - # CREDITS - credits: - BITMAP: - - ${PREFIX}/data/credits/shine.gif - ANIMATION: - - ${PREFIX}/data/credits/shine.yaml diff --git a/data/credits/shine.gif b/data/credits/shine.gif deleted file mode 100644 index 0463032..0000000 Binary files a/data/credits/shine.gif and /dev/null differ diff --git a/data/credits/shine.yaml b/data/credits/shine.yaml deleted file mode 100644 index c4b723f..0000000 --- a/data/credits/shine.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# shine animation -tileSetFile: shine.gif -frameWidth: 8 -frameHeight: 8 - -animations: - - name: default - speed: 0.1 - loop: -1 - frames: [0, 1, 2, 3, 4, 5, 6, 7] diff --git a/data/ending/ending1.gif b/data/ending/ending1.gif deleted file mode 100644 index 2fd78f8..0000000 Binary files a/data/ending/ending1.gif and /dev/null differ diff --git a/data/ending/ending2.gif b/data/ending/ending2.gif deleted file mode 100644 index 922f80d..0000000 Binary files a/data/ending/ending2.gif and /dev/null differ diff --git a/data/ending/ending3.gif b/data/ending/ending3.gif deleted file mode 100644 index 9b7011c..0000000 Binary files a/data/ending/ending3.gif and /dev/null differ diff --git a/data/ending/ending4.gif b/data/ending/ending4.gif deleted file mode 100644 index 0f52a6a..0000000 Binary files a/data/ending/ending4.gif and /dev/null differ diff --git a/data/ending/ending5.gif b/data/ending/ending5.gif deleted file mode 100644 index 102ece5..0000000 Binary files a/data/ending/ending5.gif and /dev/null differ diff --git a/data/loading/loading_screen_bn.gif b/data/loading/loading_screen_bn.gif deleted file mode 100644 index 330640b..0000000 Binary files a/data/loading/loading_screen_bn.gif and /dev/null differ diff --git a/data/loading/loading_screen_color.gif b/data/loading/loading_screen_color.gif deleted file mode 100644 index 2d26991..0000000 Binary files a/data/loading/loading_screen_color.gif and /dev/null differ diff --git a/data/loading/program_jaildoc.gif b/data/loading/program_jaildoc.gif deleted file mode 100644 index 5af6993..0000000 Binary files a/data/loading/program_jaildoc.gif and /dev/null differ diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index c36526c..cae33a6 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -53,12 +53,6 @@ namespace GlobalInputs { void handleSkipSection() { switch (SceneManager::current) { case SceneManager::Scene::LOGO: - case SceneManager::Scene::LOADING_SCREEN: - case SceneManager::Scene::CREDITS: - case SceneManager::Scene::DEMO: - case SceneManager::Scene::GAME_OVER: - case SceneManager::Scene::ENDING: - case SceneManager::Scene::ENDING2: SceneManager::current = SceneManager::Scene::TITLE; SceneManager::options = SceneManager::Options::NONE; break; diff --git a/source/core/system/debug.cpp b/source/core/system/debug.cpp index fbe2251..bc1985a 100644 --- a/source/core/system/debug.cpp +++ b/source/core/system/debug.cpp @@ -95,12 +95,7 @@ void Debug::setDebugFile(const std::string& path) { // Convierte string a SceneManager::Scene (para debug.yaml) static auto sceneFromString(const std::string& s) -> SceneManager::Scene { if (s == "LOGO") { return SceneManager::Scene::LOGO; } - if (s == "LOADING") { return SceneManager::Scene::LOADING_SCREEN; } if (s == "TITLE") { return SceneManager::Scene::TITLE; } - if (s == "CREDITS") { return SceneManager::Scene::CREDITS; } - if (s == "DEMO") { return SceneManager::Scene::DEMO; } - if (s == "ENDING") { return SceneManager::Scene::ENDING; } - if (s == "ENDING2") { return SceneManager::Scene::ENDING2; } return SceneManager::Scene::GAME; // Fallback seguro } @@ -109,18 +104,8 @@ static auto sceneToString(SceneManager::Scene scene) -> std::string { switch (scene) { case SceneManager::Scene::LOGO: return "LOGO"; - case SceneManager::Scene::LOADING_SCREEN: - return "LOADING"; case SceneManager::Scene::TITLE: return "TITLE"; - case SceneManager::Scene::CREDITS: - return "CREDITS"; - case SceneManager::Scene::DEMO: - return "DEMO"; - case SceneManager::Scene::ENDING: - return "ENDING"; - case SceneManager::Scene::ENDING2: - return "ENDING2"; default: return "GAME"; } diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index a189bf1..f4e8b21 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -23,12 +23,7 @@ #include "game/gameplay/cheevos.hpp" // Para Cheevos #include "game/options.hpp" // Para Options, options, OptionsVideo #include "game/scene_manager.hpp" // Para SceneManager -#include "game/scenes/credits.hpp" // Para Credits -#include "game/scenes/ending.hpp" // Para Ending -#include "game/scenes/ending2.hpp" // Para Ending2 #include "game/scenes/game.hpp" // Para Game, GameMode -#include "game/scenes/game_over.hpp" // Para GameOver -#include "game/scenes/loading_screen.hpp" // Para LoadingScreen #include "game/scenes/logo.hpp" // Para Logo #include "game/scenes/title.hpp" // Para Title #include "game/ui/console.hpp" // Para Console @@ -330,48 +325,12 @@ void Director::runLogo() { logo->run(); } -// Ejecuta la seccion de juego de la pantalla de carga -void Director::runLoadingScreen() { - auto loading_screen = std::make_unique(); - loading_screen->run(); -} - // Ejecuta la seccion de juego con el titulo y los menus void Director::runTitle() { auto title = std::make_unique(); title->run(); } -// Ejecuta la seccion de los creditos del juego -void Director::runCredits() { - auto credits = std::make_unique<Credits>(); - credits->run(); -} - -// Ejecuta la seccion de la demo, donde se ven pantallas del juego -void Director::runDemo() { - auto game = std::make_unique<Game>(Game::Mode::DEMO); - game->run(); -} - -// Ejecuta la seccion del final del juego -void Director::runEnding() { - auto ending = std::make_unique<Ending>(); - ending->run(); -} - -// Ejecuta la seccion del final del juego -void Director::runEnding2() { - auto ending2 = std::make_unique<Ending2>(); - ending2->run(); -} - -// Ejecuta la seccion del final de la partida -void Director::runGameOver() { - auto game_over = std::make_unique<GameOver>(); - game_over->run(); -} - // Ejecuta la seccion de juego donde se juega void Director::runGame() { Audio::get()->stopMusic(); @@ -389,38 +348,14 @@ auto Director::run() -> int { runLogo(); break; - case SceneManager::Scene::LOADING_SCREEN: - runLoadingScreen(); - break; - case SceneManager::Scene::TITLE: runTitle(); break; - case SceneManager::Scene::CREDITS: - runCredits(); - break; - - case SceneManager::Scene::DEMO: - runDemo(); - break; - case SceneManager::Scene::GAME: runGame(); break; - case SceneManager::Scene::GAME_OVER: - runGameOver(); - break; - - case SceneManager::Scene::ENDING: - runEnding(); - break; - - case SceneManager::Scene::ENDING2: - runEnding2(); - break; - case SceneManager::Scene::RESTART_CURRENT: // La escena salió por RESTART_CURRENT → relanzar la escena guardada SceneManager::current = SceneManager::scene_before_restart; diff --git a/source/core/system/director.hpp b/source/core/system/director.hpp index 33c8c5e..2d24e4f 100644 --- a/source/core/system/director.hpp +++ b/source/core/system/director.hpp @@ -19,12 +19,6 @@ class Director { void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema donde guardar datos void setFileList(); // Carga la configuración de assets desde assets.yaml static void runLogo(); // Ejecuta la seccion de juego con el logo - static void runLoadingScreen(); // Ejecuta la seccion de juego de la pantalla de carga static void runTitle(); // Ejecuta la seccion de juego con el titulo y los menus - static void runCredits(); // Ejecuta la seccion de los creditos del juego - static void runDemo(); // Ejecuta la seccion de la demo, donde se ven pantallas del juego - static void runEnding(); // Ejecuta la seccion del final del juego - static void runEnding2(); // Ejecuta la seccion del final del juego - static void runGameOver(); // Ejecuta la seccion del final de la partida static void runGame(); // Ejecuta la seccion de juego donde se juega }; \ No newline at end of file diff --git a/source/game/scene_manager.hpp b/source/game/scene_manager.hpp index 89a2f35..11926cd 100644 --- a/source/game/scene_manager.hpp +++ b/source/game/scene_manager.hpp @@ -12,30 +12,21 @@ namespace SceneManager { // --- Escenas del programa --- enum class Scene { LOGO, // Pantalla del logo - LOADING_SCREEN, // Pantalla de carga TITLE, // Pantalla de título/menú principal - CREDITS, // Créditos del juego GAME, // Juego principal - DEMO, // Modo demostración - GAME_OVER, // Pantalla de game over - ENDING, // Final del juego (ending 1) - ENDING2, // Final del juego (ending 2) RESTART_CURRENT, // Especial: reinicia la escena que estaba corriendo QUIT // Salir del programa }; // --- Opciones para transiciones entre escenas --- enum class Options { - NONE, // Sin opciones especiales - LOGO_TO_LOADING_SCREEN, // Del logo a la intro - LOGO_TO_TITLE, // Del logo al título - TITLE_WITH_LOADING_SCREEN, // Al título mostrando pantalla de carga - TITLE_WITHOUT_LOADING_SCREEN // Al título sin pantalla de carga + NONE, // Sin opciones especiales + LOGO_TO_TITLE, // Del logo al título }; // --- Variables de estado globales --- - inline Scene current = Scene::LOGO; // Escena actual (en _DEBUG sobrescrito por Director tras cargar debug.yaml) - inline Options options = Options::LOGO_TO_LOADING_SCREEN; // Opciones de la escena actual - inline Scene scene_before_restart = Scene::LOGO; // escena a relanzar tras RESTART_CURRENT + inline Scene current = Scene::LOGO; // Escena actual (en _DEBUG sobrescrito por Director tras cargar debug.yaml) + inline Options options = Options::LOGO_TO_TITLE; // Opciones de la escena actual + inline Scene scene_before_restart = Scene::LOGO; // escena a relanzar tras RESTART_CURRENT } // namespace SceneManager diff --git a/source/game/scenes/credits.cpp b/source/game/scenes/credits.cpp deleted file mode 100644 index 3f628f8..0000000 --- a/source/game/scenes/credits.cpp +++ /dev/null @@ -1,247 +0,0 @@ -#include "game/scenes/credits.hpp" - -#include <SDL3/SDL.h> - -#include "core/audio/audio.hpp" // Para Audio -#include "core/input/global_inputs.hpp" // Para check -#include "core/input/input.hpp" // Para Input -#include "core/locale/locale.hpp" // Para Locale -#include "core/rendering/pixel_reveal.hpp" // Para PixelReveal -#include "core/rendering/screen.hpp" // Para Screen -#include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite -#include "core/rendering/surface.hpp" // Para Surface -#include "core/rendering/text.hpp" // Para Text, Text::CENTER_FLAG, Text::COLOR_FLAG -#include "core/resources/resource_cache.hpp" // Para Resource -#include "core/system/global_events.hpp" // Para check -#include "game/options.hpp" // Para Options, options, OptionsGame, Sectio... -#include "game/scene_manager.hpp" // Para SceneManager -#include "utils/defines.hpp" // Para GAME_SPEED, PlayArea::CENTER_X, PLAY_... -#include "utils/delta_timer.hpp" // Para DeltaTimer -#include "utils/utils.hpp" // Para PaletteColor - -// Destructor -Credits::~Credits() = default; - -// Constructor -Credits::Credits() - : text_surface_(std::make_shared<Surface>(Options::game.width, Options::game.height)), - shining_sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData("shine.yaml"))), - delta_timer_(std::make_unique<DeltaTimer>()) { - // Configura la escena - SceneManager::current = SceneManager::Scene::CREDITS; - SceneManager::options = SceneManager::Options::NONE; - shining_sprite_->setPos({.x = 194, .y = 174, .w = 8, .h = 8}); - - Screen::get()->setBorderColor(0); // Cambia el color del borde - fillTexture(); // Escribe el texto en la textura - Audio::get()->playMusic("574071_EA_DTV.ogg"); // Inicia la musica -} - -// Comprueba el manejador de eventos -void Credits::handleEvents() { - SDL_Event event; - while (SDL_PollEvent(&event)) { - GlobalEvents::handle(event); - } -} - -// Comprueba las entradas -void Credits::handleInput() { - Input::get()->update(); - GlobalInputs::handle(); -} - -// Inicializa los textos -void Credits::iniTexts() { // NOLINT(readability-convert-member-functions-to-static) - auto* loc = Locale::get(); - - texts_.clear(); - texts_.push_back({.label = "", .color = 14}); - texts_.push_back({.label = loc->get("credits.instructions"), .color = 12}); - texts_.push_back({.label = "", .color = 14}); - texts_.push_back({.label = loc->get("credits.l0"), .color = 14}); - texts_.push_back({.label = loc->get("credits.l1"), .color = 14}); - texts_.push_back({.label = loc->get("credits.l2"), .color = 14}); - texts_.push_back({.label = "", .color = 14}); - texts_.push_back({.label = "", .color = 14}); - - texts_.push_back({.label = loc->get("credits.keys"), .color = 12}); - texts_.push_back({.label = "", .color = 14}); - texts_.push_back({.label = loc->get("credits.keys_move"), .color = 14}); - texts_.push_back({.label = loc->get("credits.f8"), .color = 14}); - texts_.push_back({.label = loc->get("credits.f11"), .color = 14}); - texts_.push_back({.label = loc->get("credits.f1f2"), .color = 14}); - texts_.push_back({.label = loc->get("credits.f3"), .color = 14}); - texts_.push_back({.label = loc->get("credits.f9"), .color = 14}); - texts_.push_back({.label = "", .color = 14}); - texts_.push_back({.label = "", .color = 14}); - - texts_.push_back({.label = loc->get("credits.author"), .color = 12}); - texts_.push_back({.label = loc->get("credits.date"), .color = 12}); - texts_.push_back({.label = "", .color = 14}); - texts_.push_back({.label = "", .color = 14}); - - texts_.push_back({.label = loc->get("credits.love"), .color = 14}); - texts_.push_back({.label = "", .color = 14}); -} - -// Escribe el texto en la textura -void Credits::fillTexture() { - // Inicializa los textos - iniTexts(); - - // Rellena la textura de texto - auto previuos_renderer = Screen::get()->getRendererSurface(); - Screen::get()->setRendererSurface(text_surface_); - text_surface_->clear(0); - - auto text = Resource::Cache::get()->getText("smb2"); - - // Escribe el texto en la textura - const int SIZE = text->getCharacterSize(); - int pos_y = 0; - - for (const auto& t : texts_) { - text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, pos_y * SIZE, t.label, 1, t.color); - pos_y++; - } - - // Escribe el corazón - const int TEXT_LENGHT = text->length(texts_[22].label, 1) - text->length(" ", 1); // Se resta el ultimo caracter que es un espacio - const int POS_X = ((PlayArea::WIDTH - TEXT_LENGHT) / 2) + TEXT_LENGHT; - text->writeColored(POS_X, 176, "ä", 5); - Screen::get()->setRendererSurface(previuos_renderer); - - // Recoloca el sprite del brillo - shining_sprite_->setPosX(POS_X + 2); - - // Crea el efecto de revelado pixel a pixel - pixel_reveal_ = std::make_unique<PixelReveal>(Options::game.width, Options::game.height, PIXELS_PER_SECOND, STEP_DURATION, REVEAL_STEPS); -} - -// Actualiza las variables -void Credits::update() { - const float DELTA_TIME = delta_timer_->tick(); - total_time_ += DELTA_TIME; // Actualiza el tiempo total - - handleEvents(); // Comprueba los eventos - handleInput(); // Comprueba las entradas - - updateState(DELTA_TIME); // Actualiza la máquina de estados - - pixel_reveal_->update(reveal_time_); // Actualiza el efecto de revelado - - // Actualiza el sprite con el brillo si está después del tiempo de inicio - if (reveal_time_ > SHINE_START_TIME) { - shining_sprite_->update(DELTA_TIME); - } - - Audio::update(); // Actualiza el objeto Audio - Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen -} - -// Transición entre estados -void Credits::transitionToState(State new_state) { - state_ = new_state; - state_time_ = 0.0F; -} - -// Actualiza la máquina de estados -void Credits::updateState(float delta_time) { - state_time_ += delta_time; - - switch (state_) { - case State::REVEALING_TEXT: - reveal_time_ += delta_time; // Incrementa reveal_time durante revelación - if (state_time_ >= REVEAL_PHASE_1_DURATION) { - transitionToState(State::PAUSE_1); - } - break; - - case State::PAUSE_1: - // reveal_time_ NO incrementa durante pausa (se congela) - if (state_time_ >= PAUSE_DURATION) { - transitionToState(State::REVEALING_TEXT_2); - } - break; - - case State::REVEALING_TEXT_2: - reveal_time_ += delta_time; // Incrementa reveal_time durante revelación - if (state_time_ >= REVEAL_PHASE_2_DURATION) { - transitionToState(State::PAUSE_2); - } - break; - - case State::PAUSE_2: - // reveal_time_ NO incrementa durante pausa (se congela) - if (state_time_ >= PAUSE_DURATION) { - transitionToState(State::REVEALING_TEXT_3); - } - break; - - case State::REVEALING_TEXT_3: - reveal_time_ += delta_time; // Incrementa reveal_time durante revelación - if (state_time_ >= REVEAL_PHASE_3_DURATION) { - transitionToState(State::PAUSE_3); - } - break; - - case State::PAUSE_3: - // reveal_time_ NO incrementa durante pausa (se congela) - if (state_time_ >= PAUSE_DURATION) { - transitionToState(State::DISPLAYING_WITH_SHINE); - } - break; - - case State::DISPLAYING_WITH_SHINE: - reveal_time_ += delta_time; // Incrementa reveal_time durante revelación - if (state_time_ >= DISPLAY_WITH_SHINE_DURATION) { - transitionToState(State::FADING_OUT); - } - break; - - case State::FADING_OUT: - reveal_time_ += delta_time; // Incrementa reveal_time durante fade - if (state_time_ >= FADE_OUT_DURATION) { - transitionToState(State::EXITING); - } - break; - - case State::EXITING: - SceneManager::current = SceneManager::Scene::DEMO; - break; - } -} - -// Dibuja en pantalla -void Credits::render() { - // Prepara para empezar a dibujar en la textura de juego - Screen::get()->start(); - - // Limpia la pantalla - Screen::get()->clearSurface(0); - - if (state_ != State::EXITING) { - // Dibuja la textura con el texto en pantalla - text_surface_->render(0, 0); - - // Dibuja la máscara de revelado pixel a pixel - pixel_reveal_->render(0, 0); - - // Dibuja el sprite con el brillo - if (reveal_time_ > SHINE_START_TIME) { - shining_sprite_->render(1, 15); - } - } - - // Vuelca el contenido del renderizador en pantalla - Screen::get()->render(); -} - -// Bucle para el logo del juego -void Credits::run() { - while (SceneManager::current == SceneManager::Scene::CREDITS) { - update(); - render(); - } -} \ No newline at end of file diff --git a/source/game/scenes/credits.hpp b/source/game/scenes/credits.hpp deleted file mode 100644 index ade576f..0000000 --- a/source/game/scenes/credits.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include <SDL3/SDL.h> - -#include <memory> // Para shared_ptr -#include <string> // Para string -#include <vector> // Para vector -class AnimatedSprite; // lines 11-11 -class Surface; -class PixelReveal; -class DeltaTimer; - -class Credits { - public: - // --- Constructor y Destructor --- - Credits(); - ~Credits(); // NOLINT(modernize-use-equals-default, performance-trivially-destructible) -- defined in .cpp for unique_ptr with forward declarations - - // --- Bucle principal --- - void run(); - - private: - // --- Tipos anidados --- - enum class State { - REVEALING_TEXT, - PAUSE_1, - REVEALING_TEXT_2, - PAUSE_2, - REVEALING_TEXT_3, - PAUSE_3, - DISPLAYING_WITH_SHINE, - FADING_OUT, - EXITING - }; - - struct Captions { - std::string label; // Texto a escribir - Uint8 color{0}; // Color del texto - }; - - // --- Constantes de tiempo (basado en 60 FPS) --- - static constexpr float REVEAL_PHASE_1_DURATION = 3.733F; // 224 frames @ 60fps - static constexpr float PAUSE_DURATION = 1.667F; // 100 frames @ 60fps - static constexpr float REVEAL_PHASE_2_DURATION = 5.333F; // 320 frames (544-224) @ 60fps - static constexpr float REVEAL_PHASE_3_DURATION = 2.133F; // 128 frames (672-544) @ 60fps - static constexpr float DISPLAY_WITH_SHINE_DURATION = 7.967F; // 478 frames (1150-672) @ 60fps - static constexpr float FADE_OUT_DURATION = 0.833F; // 50 frames (1200-1150) @ 60fps - static constexpr float TOTAL_DURATION = 20.0F; // 1200 frames @ 60fps - static constexpr float SHINE_START_TIME = 12.833F; // 770 frames @ 60fps - static constexpr float FADE_OUT_START = 19.167F; // 1150 frames @ 60fps - static constexpr float PIXELS_PER_SECOND = 15.0F; // Filas reveladas por segundo (REVEAL_SPEED/8*2 = 60/8*2 = 15) - static constexpr float STEP_DURATION = 2.0F / 60.0F; // Segundos por paso de revelado (2 frames @ 60fps) - static constexpr int REVEAL_STEPS = 16; // Pasos de revelado por fila (más pasos = efecto más visible) - - // --- Métodos privados --- - void update(); // Actualiza las variables - void render(); // Dibuja en pantalla - static void handleEvents(); // Comprueba el manejador de eventos - static void handleInput(); // Comprueba las entradas - void updateState(float delta_time); // Actualiza la máquina de estados - void transitionToState(State new_state); // Transición entre estados - void iniTexts(); // Inicializa los textos - void fillTexture(); // Escribe el texto en la textura - - // --- Variables miembro --- - // Recursos gráficos - std::shared_ptr<Surface> text_surface_; // Textura para dibujar el texto - std::unique_ptr<PixelReveal> pixel_reveal_; // Efecto de revelado pixel a pixel - std::shared_ptr<AnimatedSprite> shining_sprite_; // Sprite para el brillo del corazón - - // Temporizadores y estado - std::unique_ptr<DeltaTimer> delta_timer_; // Temporizador delta para time-based update - State state_{State::REVEALING_TEXT}; // Estado actual - float state_time_{0.0F}; // Tiempo acumulado en el estado actual - float total_time_{0.0F}; // Tiempo total acumulado - float reveal_time_{0.0F}; // Tiempo acumulado solo durante revelación (se congela en pausas) - - // Textos - std::vector<Captions> texts_; // Vector con los textos -}; diff --git a/source/game/scenes/ending.cpp b/source/game/scenes/ending.cpp deleted file mode 100644 index aac5e2a..0000000 --- a/source/game/scenes/ending.cpp +++ /dev/null @@ -1,434 +0,0 @@ -#include "game/scenes/ending.hpp" - -#include <SDL3/SDL.h> - -#include "core/audio/audio.hpp" // Para Audio -#include "core/input/global_inputs.hpp" // Para check -#include "core/input/input.hpp" // Para Input -#include "core/locale/locale.hpp" // Para Locale -#include "core/rendering/pixel_reveal.hpp" // Para PixelReveal -#include "core/rendering/screen.hpp" // Para Screen -#include "core/rendering/sprite/sprite.hpp" // Para SSprite -#include "core/rendering/surface.hpp" // Para Surface -#include "core/rendering/text.hpp" // Para Text, TEXT_STROKE -#include "core/resources/resource_cache.hpp" // Para Resource -#include "core/system/global_events.hpp" // Para check -#include "game/options.hpp" // Para Options, options, OptionsGame, SectionS... -#include "game/scene_manager.hpp" // Para SceneManager -#include "utils/delta_timer.hpp" // Para DeltaTimer -#include "utils/utils.hpp" // Para PaletteColor - -// Destructor -Ending::~Ending() = default; - -// Constructor -Ending::Ending() - : delta_timer_(std::make_unique<DeltaTimer>()) { - SceneManager::current = SceneManager::Scene::ENDING; - SceneManager::options = SceneManager::Options::NONE; - - iniTexts(); // Inicializa los textos - iniPics(); // Inicializa las imagenes - iniScenes(); // Inicializa las escenas - - Screen::get()->setBorderColor(0); // Cambia el color del borde -} - -// Actualiza el objeto -void Ending::update() { - const float DELTA_TIME = delta_timer_->tick(); - total_time_ += DELTA_TIME; // Actualiza el tiempo total - - handleEvents(); // Comprueba los eventos - handleInput(); // Comprueba las entradas - - updateState(DELTA_TIME); // Actualiza la máquina de estados - updateSpriteCovers(); // Actualiza las cortinillas de los elementos - - Audio::update(); // Actualiza el objeto Audio - Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen -} - -// Dibuja el final en pantalla -void Ending::render() { - // Prepara para empezar a dibujar en la textura de juego - Screen::get()->start(); - - // Limpia la pantalla - Screen::get()->clearSurface(0); - - // Skip rendering durante WARMING_UP - if (state_ != State::WARMING_UP) { - // Dibuja las imagenes de la escena - const auto& pic = sprite_pics_.at(current_scene_); - pic.image_sprite->render(); - pic.pixel_reveal->render(pic.pos_x, pic.pos_y); - - // Dibuja los textos de la escena - for (const auto& ti : scenes_.at(current_scene_).text_index) { - // Convertir trigger de frames a segundos @ 60fps - const float TRIGGER_TIME = static_cast<float>(ti.trigger) / 60.0F; - - if (state_time_ > TRIGGER_TIME) { - const auto& txt = sprite_texts_.at(ti.index); - txt.image_sprite->render(); - txt.pixel_reveal->render(txt.pos_x, txt.pos_y); - } - } - - // Dibuja la cortinilla de cambio de escena - if (scene_cover_) { - scene_cover_->render(0, 0); - } - } - - // Vuelca el contenido del renderizador en pantalla - Screen::get()->render(); -} - -// Comprueba el manejador de eventos -void Ending::handleEvents() { - SDL_Event event; - while (SDL_PollEvent(&event)) { - GlobalEvents::handle(event); - } -} - -// Comprueba las entradas -void Ending::handleInput() { - Input::get()->update(); - GlobalInputs::handle(); -} - -// Transición entre estados -void Ending::transitionToState(State new_state) { - state_ = new_state; - state_time_ = 0.0F; - - // Al cambiar a una escena, resetear la cortinilla de salida y el contador de fade - if (new_state != State::WARMING_UP && new_state != State::ENDING) { - fadeout_time_ = 0.0F; - scene_cover_.reset(); - } -} - -// Lógica de fade común a los estados SCENE_N -void Ending::handleSceneFadeout(float scene_duration, float delta_time) { - if (state_time_ >= scene_duration - FADEOUT_START_OFFSET) { - fadeout_time_ += delta_time; - if (!scene_cover_) { - scene_cover_ = std::make_unique<PixelReveal>(Options::game.width, Options::game.height, COVER_PIXELS_PER_SECOND, STEP_DURATION, COVER_STEPS, true); - } - scene_cover_->update(fadeout_time_); - } -} - -// Actualiza la máquina de estados -void Ending::updateState(float delta_time) { - state_time_ += delta_time; - - switch (state_) { - case State::WARMING_UP: - if (state_time_ >= WARMUP_DURATION) { - transitionToState(State::SCENE_0); - current_scene_ = 0; - } - break; - - case State::SCENE_0: - checkChangeScene(); - handleSceneFadeout(SCENE_0_DURATION, delta_time); - break; - - case State::SCENE_1: - checkChangeScene(); - handleSceneFadeout(SCENE_1_DURATION, delta_time); - break; - - case State::SCENE_2: - checkChangeScene(); - handleSceneFadeout(SCENE_2_DURATION, delta_time); - break; - - case State::SCENE_3: - checkChangeScene(); - handleSceneFadeout(SCENE_3_DURATION, delta_time); - break; - - case State::SCENE_4: - checkChangeScene(); - handleSceneFadeout(SCENE_4_DURATION, delta_time); - break; - - case State::ENDING: - // Esperar ENDING_DURATION y luego transicionar a ENDING2 - if (state_time_ >= ENDING_DURATION) { - SceneManager::current = SceneManager::Scene::ENDING2; - } - break; - } -} - -// Inicializa los textos -void Ending::iniTexts() { // NOLINT(readability-convert-member-functions-to-static) - // Vector con los textos (traducidos según el idioma activo) - std::vector<TextAndPosition> texts; - auto* loc = Locale::get(); - - // Escena #0 - texts.push_back({.caption = loc->get("ending.t0"), .pos = 32}); - texts.push_back({.caption = loc->get("ending.t1"), .pos = 42}); - texts.push_back({.caption = loc->get("ending.t2"), .pos = 142}); - texts.push_back({.caption = loc->get("ending.t3"), .pos = 152}); - - // Escena #1 - texts.push_back({.caption = loc->get("ending.t4"), .pos = 1}); - texts.push_back({.caption = loc->get("ending.t5"), .pos = 11}); - texts.push_back({.caption = loc->get("ending.t6"), .pos = 21}); - - texts.push_back({.caption = loc->get("ending.t7"), .pos = 161}); - texts.push_back({.caption = loc->get("ending.t8"), .pos = 171}); - - texts.push_back({.caption = loc->get("ending.t9"), .pos = 181}); - - // Escena #2 - texts.push_back({.caption = loc->get("ending.t10"), .pos = 19}); - texts.push_back({.caption = loc->get("ending.t11"), .pos = 29}); - - // Escena #3 - texts.push_back({.caption = loc->get("ending.t12"), .pos = 36}); - texts.push_back({.caption = loc->get("ending.t13"), .pos = 46}); - - // Escena #4 - texts.push_back({.caption = loc->get("ending.t14"), .pos = 36}); - texts.push_back({.caption = loc->get("ending.t15"), .pos = 46}); - texts.push_back({.caption = loc->get("ending.t16"), .pos = 158}); - - // Crea los sprites - sprite_texts_.clear(); - - for (const auto& txt : texts) { - auto text = Resource::Cache::get()->getText("smb2"); - - const float WIDTH = text->length(txt.caption, 1) + 2 + 2; - const float HEIGHT = text->getCharacterSize() + 2 + 2; - auto text_color = 14; - auto shadow_color = 0; - - EndingSurface st; - - // Crea la textura - st.image_surface = std::make_shared<Surface>(WIDTH, HEIGHT); - auto previuos_renderer = Screen::get()->getRendererSurface(); - Screen::get()->setRendererSurface(st.image_surface); - text->writeDX(Text::STROKE_FLAG, 2, 2, txt.caption, 1, text_color, 2, shadow_color); - - // Crea el sprite - st.image_sprite = std::make_shared<Sprite>(st.image_surface, 0, 0, st.image_surface->getWidth(), st.image_surface->getHeight()); - st.pos_x = static_cast<int>((Options::game.width - st.image_surface->getWidth()) / 2); - st.pos_y = txt.pos; - st.image_sprite->setPosition(st.pos_x, st.pos_y); - - // Crea el efecto de revelado pixel a pixel - st.pixel_reveal = std::make_unique<PixelReveal>(static_cast<int>(WIDTH), static_cast<int>(HEIGHT), TEXT_PIXELS_PER_SECOND, STEP_DURATION, REVEAL_STEPS); - - sprite_texts_.push_back(std::move(st)); - Screen::get()->setRendererSurface(previuos_renderer); - } -} - -// Inicializa las imagenes -void Ending::iniPics() { - // Vector con las rutas y la posición - std::vector<TextAndPosition> pics; - - pics.push_back({.caption = "ending1.gif", .pos = 48}); - pics.push_back({.caption = "ending2.gif", .pos = 26}); - pics.push_back({.caption = "ending3.gif", .pos = 29}); - pics.push_back({.caption = "ending4.gif", .pos = 63}); - pics.push_back({.caption = "ending5.gif", .pos = 53}); - - // Crea los sprites - sprite_pics_.clear(); - - for (const auto& pic : pics) { - EndingSurface sp; - - // Crea la texture - sp.image_surface = Resource::Cache::get()->getSurface(pic.caption); - sp.image_surface->setTransparentColor(); - const float WIDTH = sp.image_surface->getWidth(); - const float HEIGHT = sp.image_surface->getHeight(); - - // Crea el sprite - sp.pos_x = static_cast<int>((Options::game.width - WIDTH) / 2); - sp.pos_y = pic.pos; - sp.image_sprite = std::make_shared<Sprite>(sp.image_surface, 0, 0, WIDTH, HEIGHT); - sp.image_sprite->setPosition(sp.pos_x, sp.pos_y); - - // Crea el efecto de revelado pixel a pixel - sp.pixel_reveal = std::make_unique<PixelReveal>(static_cast<int>(WIDTH), static_cast<int>(HEIGHT), IMAGE_PIXELS_PER_SECOND, STEP_DURATION, REVEAL_STEPS); - - sprite_pics_.push_back(std::move(sp)); - } -} - -// Inicializa las escenas -void Ending::iniScenes() { // NOLINT(readability-convert-member-functions-to-static) - // Variable para los tiempos - int trigger; - constexpr int LAPSE = 80; - - // Crea el contenedor - SceneData sc; - - // Inicializa el vector - scenes_.clear(); - - // Crea la escena #0 - sc.counter_end = 1000; - sc.picture_index = 0; - sc.text_index.clear(); - trigger = 85 * 2; - trigger += LAPSE; - sc.text_index.push_back({.index = 0, .trigger = trigger}); - trigger += LAPSE; - sc.text_index.push_back({.index = 1, .trigger = trigger}); - trigger += LAPSE * 3; - sc.text_index.push_back({.index = 2, .trigger = trigger}); - trigger += LAPSE; - sc.text_index.push_back({.index = 3, .trigger = trigger}); - scenes_.push_back(sc); - - // Crea la escena #1 - sc.counter_end = 1400; - sc.picture_index = 1; - sc.text_index.clear(); - trigger = 140 * 2; - trigger += LAPSE; - sc.text_index.push_back({.index = 4, .trigger = trigger}); - trigger += LAPSE; - sc.text_index.push_back({.index = 5, .trigger = trigger}); - trigger += LAPSE; - sc.text_index.push_back({.index = 6, .trigger = trigger}); - trigger += LAPSE * 3; - sc.text_index.push_back({.index = 7, .trigger = trigger}); - trigger += LAPSE; - sc.text_index.push_back({.index = 8, .trigger = trigger}); - trigger += LAPSE * 3; - sc.text_index.push_back({.index = 9, .trigger = trigger}); - scenes_.push_back(sc); - - // Crea la escena #2 - sc.counter_end = 1000; - sc.picture_index = 2; - sc.text_index.clear(); - trigger = 148 / 2; - trigger += LAPSE; - sc.text_index.push_back({.index = 10, .trigger = trigger}); - trigger += LAPSE; - sc.text_index.push_back({.index = 11, .trigger = trigger}); - scenes_.push_back(sc); - - // Crea la escena #3 - sc.counter_end = 800; - sc.picture_index = 3; - sc.text_index.clear(); - trigger = 87 / 2; - trigger += LAPSE; - sc.text_index.push_back({.index = 12, .trigger = trigger}); - trigger += LAPSE / 2; - sc.text_index.push_back({.index = 13, .trigger = trigger}); - scenes_.push_back(sc); - - // Crea la escena #4 - sc.counter_end = 1000; - sc.picture_index = 4; - sc.text_index.clear(); - trigger = 91 * 2; - trigger += LAPSE; - sc.text_index.push_back({.index = 14, .trigger = trigger}); - trigger += LAPSE * 2; - sc.text_index.push_back({.index = 15, .trigger = trigger}); - trigger += LAPSE * 3; - sc.text_index.push_back({.index = 16, .trigger = trigger}); - scenes_.push_back(sc); -} - -// Bucle principal -void Ending::run() { - Audio::get()->playMusic("574070_KUVO_Farewell_to_school.ogg"); - - while (SceneManager::current == SceneManager::Scene::ENDING) { - update(); - render(); - } - - Audio::get()->stopMusic(); -} - -// Actualiza las cortinillas de los elementos -void Ending::updateSpriteCovers() { - // Skip durante WARMING_UP - if (state_ == State::WARMING_UP) { - return; - } - - // Actualiza el revelado de los textos - for (const auto& ti : scenes_.at(current_scene_).text_index) { - const float TRIGGER_TIME = static_cast<float>(ti.trigger) / 60.0F; - - if (state_time_ > TRIGGER_TIME) { - const float TIME_SINCE_TRIGGER = state_time_ - TRIGGER_TIME; - sprite_texts_.at(ti.index).pixel_reveal->update(TIME_SINCE_TRIGGER); - } - } - - // Actualiza el revelado de la imagen (desde el inicio de la escena) - sprite_pics_.at(current_scene_).pixel_reveal->update(state_time_); -} - -// Comprueba si se ha de cambiar de escena -void Ending::checkChangeScene() { - // Obtener duración de la escena actual - float current_duration = 0.0F; - State next_state = State::ENDING; - - switch (state_) { - case State::SCENE_0: - current_duration = SCENE_0_DURATION; - next_state = State::SCENE_1; - break; - case State::SCENE_1: - current_duration = SCENE_1_DURATION; - next_state = State::SCENE_2; - break; - case State::SCENE_2: - current_duration = SCENE_2_DURATION; - next_state = State::SCENE_3; - break; - case State::SCENE_3: - current_duration = SCENE_3_DURATION; - next_state = State::SCENE_4; - break; - case State::SCENE_4: - current_duration = SCENE_4_DURATION; - next_state = State::ENDING; - break; - default: - return; - } - - // Comprobar si ha pasado la duración de la escena - if (state_time_ >= current_duration) { - if (next_state == State::ENDING) { - // Transición al estado ENDING con fade de audio - transitionToState(State::ENDING); - Audio::get()->fadeOutMusic(MUSIC_FADE_DURATION); - } else { - // Transición a la siguiente escena - current_scene_++; - transitionToState(next_state); - } - } -} diff --git a/source/game/scenes/ending.hpp b/source/game/scenes/ending.hpp deleted file mode 100644 index b071084..0000000 --- a/source/game/scenes/ending.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include <SDL3/SDL.h> - -#include <memory> // Para shared_ptr -#include <string> // Para string -#include <vector> // Para vector -class Sprite; // lines 8-8 -class Surface; // lines 9-9 -class PixelReveal; -class DeltaTimer; - -class Ending { - public: - // --- Constructor y Destructor --- - Ending(); - ~Ending(); // NOLINT(modernize-use-equals-default, performance-trivially-destructible) -- defined in .cpp for unique_ptr with forward declarations - - // --- Bucle principal --- - void run(); - - private: - // --- Enumeraciones --- - enum class State { - WARMING_UP, - SCENE_0, - SCENE_1, - SCENE_2, - SCENE_3, - SCENE_4, - ENDING - }; - - // --- Estructuras --- - struct EndingSurface { - std::shared_ptr<Surface> image_surface; // Surface a mostrar - std::shared_ptr<Sprite> image_sprite; // SSprite para mostrar la textura - std::unique_ptr<PixelReveal> pixel_reveal; // Efecto de revelado pixel a pixel - int pos_x{0}; // Posición X de renderizado - int pos_y{0}; // Posición Y de renderizado - }; - - struct TextAndPosition { - std::string caption; // Texto - int pos{0}; // Posición - }; - - struct TextIndex { - int index{0}; // Índice del texto - int trigger{0}; // Disparador temporal - }; - - struct SceneData { - std::vector<TextIndex> text_index; // Índices del vector de textos a mostrar y su disparador - int picture_index{0}; // Índice del vector de imágenes a mostrar - int counter_end{0}; // Valor del contador en el que finaliza la escena - }; - - // --- Constantes de tiempo (basado en 60 FPS) --- - static constexpr float WARMUP_DURATION = 3.333F; // 200 frames @ 60fps - static constexpr float SCENE_0_DURATION = 16.667F; // 1000 frames @ 60fps - static constexpr float SCENE_1_DURATION = 23.333F; // 1400 frames @ 60fps - static constexpr float SCENE_2_DURATION = 16.667F; // 1000 frames @ 60fps - static constexpr float SCENE_3_DURATION = 13.333F; // 800 frames @ 60fps - static constexpr float SCENE_4_DURATION = 16.667F; // 1000 frames @ 60fps - static constexpr float TEXT_PIXELS_PER_SECOND = 30.0F; // Filas de texto reveladas por segundo - static constexpr float IMAGE_PIXELS_PER_SECOND = 60.0F; // Filas de imagen reveladas por segundo - static constexpr float STEP_DURATION = 2.0F / 60.0F; // Segundos por paso de revelado (2 frames @ 60fps) - static constexpr int REVEAL_STEPS = 4; // Pasos de revelado por fila - static constexpr float TEXT_LAPSE = 1.333F; // 80 frames @ 60fps - static constexpr float FADEOUT_START_OFFSET = 1.667F; // Inicio cortinilla 100 frames antes del fin - static constexpr float COVER_PIXELS_PER_SECOND = 120.0F; // Filas cubiertas por segundo - static constexpr int COVER_STEPS = 4; // Pasos por fila - static constexpr float ENDING_DURATION = 2.0F; // Duración del estado ENDING (2 segundos) - static constexpr int MUSIC_FADE_DURATION = 1800; // Fade de audio en milisegundos (1.8 segundos) - - // --- Métodos --- - void update(); // Actualiza el objeto - void render(); // Dibuja el final en pantalla - static void handleEvents(); // Comprueba el manejador de eventos - static void handleInput(); // Comprueba las entradas - void iniTexts(); // Inicializa los textos - void iniPics(); // Inicializa las imágenes - void iniScenes(); // Inicializa las escenas - void updateState(float delta_time); // Actualiza la máquina de estados - void handleSceneFadeout(float scene_duration, float delta_time); // Lógica de fade común a los estados SCENE_N - void transitionToState(State new_state); // Transición entre estados - void updateSpriteCovers(); // Actualiza las cortinillas de los elementos - void checkChangeScene(); // Comprueba si se ha de cambiar de escena - void updateMusicVolume() const; // Actualiza el volumen de la música - - // --- Variables miembro --- - // Objetos y punteros a recursos - std::unique_ptr<PixelReveal> scene_cover_; // Cortinilla de salida (negro sobre la escena) - std::unique_ptr<DeltaTimer> delta_timer_; // Timer para time-based update - std::vector<EndingSurface> sprite_texts_; // Vector con los sprites de texto con su cortinilla - std::vector<EndingSurface> sprite_pics_; // Vector con los sprites de imágenes con su cortinilla - std::vector<SceneData> scenes_; // Vector con los textos e imágenes de cada escena - - // Variables de estado - State state_{State::WARMING_UP}; // Estado actual - float state_time_{0.0F}; // Tiempo acumulado en el estado actual - float total_time_{0.0F}; // Tiempo total acumulado desde el inicio - float fadeout_time_{0.0F}; // Tiempo acumulado para la cortinilla de salida - int current_scene_{0}; // Escena actual (0-4) -}; \ No newline at end of file diff --git a/source/game/scenes/ending2.cpp b/source/game/scenes/ending2.cpp deleted file mode 100644 index 1327668..0000000 --- a/source/game/scenes/ending2.cpp +++ /dev/null @@ -1,502 +0,0 @@ -#include "game/scenes/ending2.hpp" - -#include <SDL3/SDL.h> - -#include <algorithm> // Para max, replace - -#include "core/audio/audio.hpp" // Para Audio -#include "core/input/global_inputs.hpp" // Para check -#include "core/input/input.hpp" // Para Input -#include "core/locale/locale.hpp" // Para Locale -#include "core/rendering/screen.hpp" // Para Screen -#include "core/rendering/sprite/dissolve_sprite.hpp" // Para SurfaceDissolveSprite -#include "core/rendering/sprite/moving_sprite.hpp" // Para SMovingSprite -#include "core/rendering/surface.hpp" // Para Surface -#include "core/rendering/text.hpp" // Para Text -#include "core/resources/resource_cache.hpp" // Para Resource -#include "core/system/global_events.hpp" // Para check -#include "game/options.hpp" // Para Options, options, OptionsGame, Sectio... -#include "game/scene_manager.hpp" // Para SceneManager -#include "utils/defines.hpp" // Para GameCanvas::CENTER_X, GameCanvas::CENTER_Y -#include "utils/delta_timer.hpp" // Para DeltaTimer -#include "utils/utils.hpp" - -// Constructor -Ending2::Ending2() - : delta_timer_(std::make_unique<DeltaTimer>()), - state_{.state = EndingState::PRE_CREDITS, .duration = STATE_PRE_CREDITS_DURATION} { - // Establece la escena - SceneManager::current = SceneManager::Scene::ENDING2; - SceneManager::options = SceneManager::Options::NONE; - - // Inicializa el vector de colores - colors_ = {14, 12, 10, 8, 6, 4, 2, 0}; - - Screen::get()->setBorderColor(0); // Cambia el color del borde - iniSpriteList(); // Inicializa la lista de sprites - loadSprites(); // Carga todos los sprites desde una lista - placeSprites(); // Coloca los sprites en su sito - createSpriteTexts(); // Crea los sprites con las texturas con los textos - createTexts(); // Crea los sprites con las texturas con los textos del final -} - -// Actualiza el objeto -void Ending2::update() { - const float DELTA_TIME = delta_timer_->tick(); - - handleEvents(); // Comprueba los eventos - handleInput(); // Comprueba las entradas - - updateState(DELTA_TIME); // Actualiza el estado - - switch (state_.state) { - case EndingState::CREDITS: - // Actualiza los sprites, los textos y los textos del final - updateSprites(DELTA_TIME); - updateTextSprites(DELTA_TIME); - updateTexts(DELTA_TIME); - break; - - case EndingState::FADING: - // Actualiza el fade final - updateFinalFade(); - break; - - default: - // No hacer nada si el estado no corresponde a un caso manejado - break; - } - - Audio::update(); // Actualiza el objeto Audio - Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen -} - -// Dibuja el final en pantalla -void Ending2::render() { - // Prepara para empezar a dibujar en la surface de juego - Screen::get()->start(); - - // Limpia la pantalla - Screen::get()->clearSurface(0); - - // Dibuja los sprites - renderSprites(); - - // Dibuja los sprites con el texto - renderSpriteTexts(); - - // Dibuja los sprites con el texto del final - renderTexts(); - - // Vuelca el contenido del renderizador en pantalla - Screen::get()->render(); -} - -// Comprueba el manejador de eventos -void Ending2::handleEvents() { - SDL_Event event; - while (SDL_PollEvent(&event)) { - GlobalEvents::handle(event); - } -} - -// Comprueba las entradas -void Ending2::handleInput() { - Input::get()->update(); - GlobalInputs::handle(); -} - -// Bucle principal -void Ending2::run() { - Audio::get()->playMusic("574071_EA_DTV.ogg"); - - while (SceneManager::current == SceneManager::Scene::ENDING2) { - update(); - render(); - } - - Audio::get()->stopMusic(); -} - -// Actualiza el estado -void Ending2::updateState(float delta_time) { - state_time_ += delta_time; - - switch (state_.state) { - case EndingState::PRE_CREDITS: - if (state_time_ >= STATE_PRE_CREDITS_DURATION) { - transitionToState(EndingState::CREDITS); - } - break; - - case EndingState::CREDITS: - if (texts_.back()->getPosY() <= GameCanvas::CENTER_Y) { - transitionToState(EndingState::POST_CREDITS); - } - break; - - case EndingState::POST_CREDITS: - if (state_time_ >= STATE_POST_CREDITS_DURATION) { - transitionToState(EndingState::FADING); - } - break; - - case EndingState::FADING: - if (state_time_ >= STATE_FADE_DURATION) { - SceneManager::current = SceneManager::Scene::LOGO; - SceneManager::options = SceneManager::Options::LOGO_TO_TITLE; - } - break; - - default: - break; - } -} - -// Transición entre estados -void Ending2::transitionToState(EndingState new_state) { - state_.state = new_state; - state_time_ = 0.0F; - - // Actualizar duración según el nuevo estado - switch (new_state) { - case EndingState::PRE_CREDITS: - state_.duration = STATE_PRE_CREDITS_DURATION; - break; - case EndingState::POST_CREDITS: - state_.duration = STATE_POST_CREDITS_DURATION; - break; - case EndingState::FADING: - state_.duration = STATE_FADE_DURATION; - // Al entrar en FADING, iniciar fade de audio - Audio::get()->fadeOutMusic(MUSIC_FADE_DURATION); - break; - case EndingState::CREDITS: - state_.duration = 0.0F; // CREDITS no tiene duración fija, termina cuando el último texto llega al centro - break; - default: - break; - } -} - -// Inicializa la lista de sprites -void Ending2::iniSpriteList() { - // Reinicia el vector - sprite_list_.clear(); - - // Añade los valores - sprite_list_.emplace_back("bin"); - sprite_list_.emplace_back("floppy"); - sprite_list_.emplace_back("bird"); - sprite_list_.emplace_back("chip"); - sprite_list_.emplace_back("jeannine"); - sprite_list_.emplace_back("spark"); - sprite_list_.emplace_back("code"); - sprite_list_.emplace_back("paco"); - sprite_list_.emplace_back("elsa"); - sprite_list_.emplace_back("z80"); - - sprite_list_.emplace_back("bell"); - sprite_list_.emplace_back("dong"); - - sprite_list_.emplace_back("amstrad_cs"); - sprite_list_.emplace_back("breakout"); - - sprite_list_.emplace_back("flying_arounder"); - sprite_list_.emplace_back("stopped_arounder"); - sprite_list_.emplace_back("walking_arounder"); - sprite_list_.emplace_back("arounders_door"); - sprite_list_.emplace_back("arounders_machine"); - - sprite_list_.emplace_back("abad"); - sprite_list_.emplace_back("abad_bell"); - sprite_list_.emplace_back("lord_abad"); - - sprite_list_.emplace_back("bat"); - sprite_list_.emplace_back("batman_bell"); - sprite_list_.emplace_back("batman_fire"); - sprite_list_.emplace_back("batman"); - - sprite_list_.emplace_back("demon"); - sprite_list_.emplace_back("heavy"); - sprite_list_.emplace_back("dimallas"); - sprite_list_.emplace_back("guitar"); - - sprite_list_.emplace_back("jailbattle_alien"); - sprite_list_.emplace_back("jailbattle_human"); - - sprite_list_.emplace_back("jailer1"); - sprite_list_.emplace_back("jailer2"); - sprite_list_.emplace_back("jailer3"); - sprite_list_.emplace_back("bry"); - sprite_list_.emplace_back("upv_student"); - - sprite_list_.emplace_back("lamp"); - sprite_list_.emplace_back("robot"); - sprite_list_.emplace_back("congo"); - sprite_list_.emplace_back("crosshair"); - sprite_list_.emplace_back("tree_thing"); - - sprite_list_.emplace_back("matatunos"); - sprite_list_.emplace_back("tuno"); - - sprite_list_.emplace_back("mummy"); - sprite_list_.emplace_back("sam"); - - sprite_list_.emplace_back("qvoid"); - sprite_list_.emplace_back("sigmasua"); - - sprite_list_.emplace_back("tv_panel"); - sprite_list_.emplace_back("tv"); - - sprite_list_.emplace_back("spider"); - sprite_list_.emplace_back("shock"); - sprite_list_.emplace_back("wave"); - - sprite_list_.emplace_back("player"); -} - -// Carga todos los sprites desde una lista -void Ending2::loadSprites() { - // Inicializa variables - sprite_max_width_ = 0; - sprite_max_height_ = 0; - - // Carga los sprites - for (const auto& file : sprite_list_) { - const auto& animation_data = Resource::Cache::get()->getAnimationData(file + ".yaml"); - sprites_.emplace_back(std::make_shared<DissolveSprite>(animation_data)); - sprites_.back()->setColorReplace(1, 4); - sprites_.back()->setProgress(1.0F); // comença invisible - sprite_max_width_ = std::max(sprites_.back()->getWidth(), sprite_max_width_); - sprite_max_height_ = std::max(sprites_.back()->getHeight(), sprite_max_height_); - } - - // El último sprite (player) va en blanco, no en rojo - sprites_.back()->setColorReplace(1, 14); -} - -// Actualiza los sprites -void Ending2::updateSprites(float delta) { - for (const auto& sprite : sprites_) { - sprite->update(delta); - - const float Y = sprite->getPosY(); - const float H = sprite->getHeight(); - const auto CANVAS_H = Options::game.height; - - // Checkpoint inferior: sprite entra per baix → generar de dalt a baix - if (Y > static_cast<float>(ENTRY_EXIT_PADDING) && Y <= CANVAS_H - H - ENTRY_EXIT_PADDING && sprite->getProgress() >= 1.0F && sprite->isTransitionDone()) { - sprite->startGenerate(TRANSITION_DURATION_MS, DissolveDirection::UP); - } - - // Checkpoint superior: sprite surt per dalt → dissoldre de dalt a baix - if (Y <= static_cast<float>(ENTRY_EXIT_PADDING) && sprite->getProgress() <= 0.0F && sprite->isTransitionDone()) { - sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN); - } - } -} - -// Actualiza los sprites de texto -void Ending2::updateTextSprites(float delta) { - for (const auto& sprite : sprite_texts_) { - sprite->update(delta); - - const float Y = sprite->getPosY(); - const float H = sprite->getHeight(); - const auto CANVAS_H = Options::game.height; - - if (Y > static_cast<float>(ENTRY_EXIT_PADDING) && Y <= CANVAS_H - H - ENTRY_EXIT_PADDING && sprite->getProgress() >= 1.0F && sprite->isTransitionDone()) { - sprite->startGenerate(TRANSITION_DURATION_MS, DissolveDirection::UP); - } - - if (Y <= static_cast<float>(ENTRY_EXIT_PADDING) && sprite->getProgress() <= 0.0F && sprite->isTransitionDone()) { - sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN); - } - } -} - -// Actualiza los sprites de texto del final -void Ending2::updateTexts(float delta) { - for (const auto& sprite : texts_) { - sprite->update(delta); - - const float Y = sprite->getPosY(); - const float H = sprite->getHeight(); - const auto CANVAS_H = Options::game.height; - - // Checkpoint inferior: text entra per baix → generar de dalt a baix - if (Y > static_cast<float>(ENTRY_EXIT_PADDING) && Y <= CANVAS_H - H - ENTRY_EXIT_PADDING && sprite->getProgress() >= 1.0F && sprite->isTransitionDone()) { - sprite->startGenerate(TRANSITION_DURATION_MS, DissolveDirection::UP); - } - - // Checkpoint superior: text surt per dalt → dissoldre de dalt a baix - if (Y <= static_cast<float>(ENTRY_EXIT_PADDING) && sprite->getProgress() <= 0.0F && sprite->isTransitionDone()) { - sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN); - } - } -} - -// Dibuja los sprites -void Ending2::renderSprites() { - for (const auto& sprite : sprites_) { - const bool A = sprite->getRect().y + sprite->getRect().h > 0; - const bool B = sprite->getRect().y < Options::game.height; - if (A && B) { - sprite->render(); - } - } -} - -// Dibuja los sprites con el texto -void Ending2::renderSpriteTexts() { - for (const auto& sprite : sprite_texts_) { - const bool A = sprite->getRect().y + sprite->getRect().h > 0; - const bool B = sprite->getRect().y < Options::game.height; - if (A && B) { - sprite->render(); - } - } -} - -// Dibuja los sprites con el texto del final -void Ending2::renderTexts() { - for (const auto& sprite : texts_) { - const bool A = sprite->getRect().y + sprite->getRect().h > 0; - const bool B = sprite->getRect().y < Options::game.height; - if (A && B) { - sprite->render(); - } - } -} - -// Coloca los sprites en su sito -void Ending2::placeSprites() const { - for (int i = 0; i < static_cast<int>(sprites_.size()); ++i) { - const float X = i % 2 == 0 ? FIRST_COL : SECOND_COL; - const float Y = ((i / 1) * (sprite_max_height_ + DIST_SPRITE_TEXT + Resource::Cache::get()->getText("smb2")->getCharacterSize() + DIST_SPRITE_SPRITE)) + Options::game.height + INITIAL_Y_OFFSET; - const float W = sprites_.at(i)->getWidth(); - const float H = sprites_.at(i)->getHeight(); - const float DX = -(W / 2); - const float DY = sprite_max_height_ - H; - - sprites_.at(i)->setPos({.x = X + DX, .y = Y + DY, .w = W, .h = H}); - sprites_.at(i)->setVelY(SPRITE_DESP_SPEED); - } - - // Recoloca el sprite del jugador, que es el último de la lista - const float X = (Options::game.width - sprites_.back()->getWidth()) / 2; - const float Y = sprites_.back()->getPosY() + (sprite_max_height_ * 2); - sprites_.back()->setPos(X, Y); - sprites_.back()->setCurrentAnimation("default"); -} - -// Crea los sprites con las texturas con los textos -void Ending2::createSpriteTexts() { // NOLINT(readability-convert-member-functions-to-static) - // Crea los sprites de texto a partir de la lista - for (size_t i = 0; i < sprite_list_.size(); ++i) { - auto text = Resource::Cache::get()->getText("smb2"); - - // Procesa y ajusta el texto del sprite actual - std::string txt = sprite_list_[i]; - std::ranges::replace(txt, '_', ' '); // Reemplaza '_' por ' ' - if (txt == "player") { - txt = Locale::get()->get("ending2.jaildoctor"); // NOLINT(readability-static-accessed-through-instance) Reemplaza "player" por nombre localizado - } - - // Calcula las dimensiones del texto - const float W = text->length(txt, 1); - const float H = text->getCharacterSize(); - - // Determina la columna y la posición X del texto - const float X = (i == sprite_list_.size() - 1) - ? (GameCanvas::CENTER_X - (W / 2)) - : ((i % 2 == 0 ? FIRST_COL : SECOND_COL) - (W / 2)); - - // Calcula la posición Y del texto en base a la posición y altura del sprite - const float Y = sprites_.at(i)->getPosY() + sprites_.at(i)->getHeight() + DIST_SPRITE_TEXT; - - // Crea la surface - auto surface = std::make_shared<Surface>(W, H); - auto previuos_renderer = Screen::get()->getRendererSurface(); - Screen::get()->setRendererSurface(surface); - text->write(0, 0, txt); - - // Crea el sprite - SDL_FRect pos = {.x = X, .y = Y, .w = W, .h = H}; - sprite_texts_.emplace_back(std::make_shared<DissolveSprite>(surface, pos)); - sprite_texts_.back()->setColorReplace(1, 14); - sprite_texts_.back()->setProgress(1.0F); // comença invisible - sprite_texts_.back()->setVelY(SPRITE_DESP_SPEED); - Screen::get()->setRendererSurface(previuos_renderer); - } -} - -// Crea los sprites con las texturas con los textos del final -void Ending2::createTexts() { // NOLINT(readability-convert-member-functions-to-static) - // Crea los primeros textos - std::vector<std::string> list; - list.emplace_back(Locale::get()->get("ending2.starring")); - - auto text = Resource::Cache::get()->getText("smb2"); - - // Crea los sprites de texto a partir de la lista - for (size_t i = 0; i < list.size(); ++i) { - // Calcula constantes - const float W = text->length(list[i], 1); - const float H = text->getCharacterSize(); - const float X = GameCanvas::CENTER_X; - const float DX = -(W / 2); - const float Y = Options::game.height + (text->getCharacterSize() * (i * TEXT_SPACING_MULTIPLIER)); - - // Crea la surface - auto surface = std::make_shared<Surface>(W, H); - auto previuos_renderer = Screen::get()->getRendererSurface(); - Screen::get()->setRendererSurface(surface); - text->write(0, 0, list[i]); - - // Crea el sprite - SDL_FRect pos = {.x = X + DX, .y = Y, .w = W, .h = H}; - texts_.emplace_back(std::make_shared<DissolveSprite>(surface, pos)); - texts_.back()->setProgress(1.0F); // comença invisible - texts_.back()->setVelY(SPRITE_DESP_SPEED); - Screen::get()->setRendererSurface(previuos_renderer); - } - - // Crea los últimos textos - // El primer texto va a continuación del ultimo spriteText - const int START = sprite_texts_.back()->getPosY() + (text->getCharacterSize() * 15); - list.clear(); - list.emplace_back(Locale::get()->get("ending2.thank_you")); - list.emplace_back(Locale::get()->get("ending2.for_playing")); - - // Crea los sprites de texto a partir de la lista - for (size_t i = 0; i < list.size(); ++i) { - // Calcula constantes - const float W = text->length(list[i], 1); - const float H = text->getCharacterSize(); - const float X = GameCanvas::CENTER_X; - const float DX = -(W / 2); - const float Y = START + (text->getCharacterSize() * (i * TEXT_SPACING_MULTIPLIER)); - - // Crea la surface - auto surface = std::make_shared<Surface>(W, H); - auto previuos_renderer = Screen::get()->getRendererSurface(); - Screen::get()->setRendererSurface(surface); - text->write(0, 0, list[i]); - - // Crea el sprite - SDL_FRect pos = {.x = X + DX, .y = Y, .w = W, .h = H}; - texts_.emplace_back(std::make_shared<DissolveSprite>(surface, pos)); - texts_.back()->setProgress(1.0F); // comença invisible - texts_.back()->setVelY(SPRITE_DESP_SPEED); - Screen::get()->setRendererSurface(previuos_renderer); - } -} - -// Actualiza el fade final -void Ending2::updateFinalFade() { - for (const auto& sprite : texts_) { - sprite->getSurface()->fadeSubPalette(0); - } -} diff --git a/source/game/scenes/ending2.hpp b/source/game/scenes/ending2.hpp deleted file mode 100644 index ff8da99..0000000 --- a/source/game/scenes/ending2.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include <SDL3/SDL.h> - -#include <memory> // Para shared_ptr -#include <string> // Para string -#include <vector> // Para vector - -#include "core/rendering/sprite/dissolve_sprite.hpp" // Para SurfaceDissolveSprite -#include "utils/defines.hpp" // Para GameCanvas::WIDTH, GameCanvas::FIRST_QUAR... - -class MovingSprite; -class DeltaTimer; - -class Ending2 { - public: - // --- Constructor y Destructor --- - Ending2(); - ~Ending2() = default; - - // --- Bucle principal --- - void run(); - - private: - // --- Enumeraciones --- - enum class EndingState : int { - PRE_CREDITS, // Estado previo a los créditos - CREDITS, // Estado de los créditos - POST_CREDITS, // Estado posterior a los créditos - FADING, // Estado de fundido de los textos a negro - }; - - // --- Estructuras --- - struct State { - EndingState state{EndingState::PRE_CREDITS}; // Estado actual - float duration{0.0F}; // Duración en segundos para el estado actual - }; - - // --- Constantes --- - static constexpr int FIRST_COL = GameCanvas::FIRST_QUARTER_X + (GameCanvas::WIDTH / 16); // Primera columna por donde desfilan los sprites - static constexpr int SECOND_COL = GameCanvas::THIRD_QUARTER_X - (GameCanvas::WIDTH / 16); // Segunda columna por donde desfilan los sprites - static constexpr int DIST_SPRITE_TEXT = 8; // Distancia entre el sprite y el texto que lo acompaña - static constexpr int DIST_SPRITE_SPRITE = 0; // Distancia entre dos sprites de la misma columna - static constexpr int INITIAL_Y_OFFSET = 40; // Offset inicial en Y para posicionar sprites - static constexpr int SCREEN_MESH_HEIGHT = 8; // Altura de la malla superior/inferior de la pantalla - static constexpr int FADE_H = 24; // Alçada de la zona de dissolució als cantons (files) - static constexpr float TRANSITION_DURATION_MS = 500.0F; // ms per canviar d'estat (generar o dissoldre) - static constexpr int ENTRY_EXIT_PADDING = 2; // px de padding als bordes per activar dissolució/generació - static constexpr int TEXT_SPACING_MULTIPLIER = 2; // Multiplicador para espaciado entre líneas de texto - - // Constantes de tiempo (basadas en tiempo real, no en frames) - static constexpr float SPRITE_DESP_SPEED = -12.0F; // Velocidad de desplazamiento en pixels/segundo (era -0.2 px/frame @ 60fps) - static constexpr float STATE_PRE_CREDITS_DURATION = 3.0F; // Duración del estado previo a créditos en segundos - static constexpr float STATE_POST_CREDITS_DURATION = 5.0F; // Duración del estado posterior a créditos en segundos - static constexpr float STATE_FADE_DURATION = 5.0F; // Duración del fade final en segundos - static constexpr int MUSIC_FADE_DURATION = 3000; // Duración del fade de música en milisegundos (para Audio API) - - // --- Métodos --- - void update(); // Actualiza el objeto - void render(); // Dibuja el final en pantalla - static void handleEvents(); // Comprueba el manejador de eventos - static void handleInput(); // Comprueba las entradas - void updateState(float delta_time); // Actualiza el estado - void transitionToState(EndingState new_state); // Transición entre estados - void iniSpriteList(); // Inicializa la lista de sprites - void loadSprites(); // Carga todos los sprites desde una lista - void updateSprites(float delta); // Actualiza los sprites - void updateTextSprites(float delta); // Actualiza los sprites de texto - void updateTexts(float delta); // Actualiza los sprites de texto del final - void renderSprites(); // Dibuja los sprites - void renderSpriteTexts(); // Dibuja los sprites con el texto - void renderTexts(); // Dibuja los sprites con el texto del final - void placeSprites() const; // Coloca los sprites en su sitio - void createSpriteTexts(); // Crea los sprites con las texturas con los textos - void createTexts(); // Crea los sprites con las texturas con los textos del final - void updateFinalFade(); // Actualiza el fade final - - // --- Variables miembro --- - // Objetos y punteros a recursos - std::vector<std::shared_ptr<DissolveSprite>> sprites_; // Vector con todos los sprites a dibujar - std::vector<std::shared_ptr<DissolveSprite>> sprite_texts_; // Vector con los sprites de texto de los sprites - std::vector<std::shared_ptr<DissolveSprite>> texts_; // Vector con los sprites de texto - std::unique_ptr<DeltaTimer> delta_timer_; // Timer para time-based update - - // Variables de estado - State state_; // Controla el estado de la clase - float state_time_{0.0F}; // Tiempo acumulado en el estado actual - - // Variables auxiliares - std::vector<std::string> sprite_list_; // Lista con todos los sprites a dibujar - std::vector<Uint8> colors_; // Vector con los colores para el fade - float sprite_max_width_{0.0F}; // El valor de ancho del sprite más ancho - float sprite_max_height_{0.0F}; // El valor de alto del sprite más alto -}; diff --git a/source/game/scenes/game.cpp b/source/game/scenes/game.cpp index 8221967..c9b6e39 100644 --- a/source/game/scenes/game.cpp +++ b/source/game/scenes/game.cpp @@ -157,7 +157,7 @@ Game::Game(Mode mode) }; #endif - SceneManager::current = (mode_ == Mode::GAME) ? SceneManager::Scene::GAME : SceneManager::Scene::DEMO; + SceneManager::current = SceneManager::Scene::GAME; SceneManager::options = SceneManager::Options::NONE; } @@ -265,7 +265,7 @@ void Game::run() { Audio::get()->pauseMusic(); } - while (SceneManager::current == SceneManager::Scene::GAME || SceneManager::current == SceneManager::Scene::DEMO) { + while (SceneManager::current == SceneManager::Scene::GAME) { update(); render(); } @@ -375,7 +375,7 @@ void Game::updateGameOver(float delta_time) { // Pequeño delay antes de cambiar escena state_time_ += delta_time; if (state_time_ > 0.1F) { // 100ms de delay mínimo - SceneManager::current = SceneManager::Scene::GAME_OVER; + SceneManager::current = SceneManager::Scene::TITLE; } } @@ -409,7 +409,7 @@ void Game::updatePostFadeEnding(float delta_time) { // Después del delay, cambiar a la escena de ending if (state_time_ >= POST_FADE_DELAY) { - SceneManager::current = SceneManager::Scene::ENDING; + SceneManager::current = SceneManager::Scene::TITLE; } } diff --git a/source/game/scenes/game_over.cpp b/source/game/scenes/game_over.cpp deleted file mode 100644 index 18e72c1..0000000 --- a/source/game/scenes/game_over.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include "game/scenes/game_over.hpp" - -#include <SDL3/SDL.h> - -#include <algorithm> // Para min, max -#include <string> // Para basic_string, operator+, to_string - -#include "core/audio/audio.hpp" // Para Audio -#include "core/input/global_inputs.hpp" // Para check -#include "core/input/input.hpp" // Para Input -#include "core/locale/locale.hpp" // Para Locale -#include "core/rendering/screen.hpp" // Para Screen -#include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite -#include "core/rendering/text.hpp" // Para Text::CENTER_FLAG, Text::COLOR_FLAG, Text -#include "core/resources/resource_cache.hpp" // Para Resource -#include "core/system/global_events.hpp" // Para check -#include "game/options.hpp" // Para Options, options, OptionsStats, Secti... -#include "game/scene_manager.hpp" // Para SceneManager -#include "utils/defines.hpp" // Para GameCanvas::CENTER_X -#include "utils/delta_timer.hpp" // Para DeltaTimer -#include "utils/utils.hpp" - -// Constructor -GameOver::GameOver() - : player_sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData("player_game_over.yaml"))), - tv_sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData("tv.yaml"))), - delta_timer_(std::make_shared<DeltaTimer>()) { - SceneManager::current = SceneManager::Scene::GAME_OVER; - SceneManager::options = SceneManager::Options::NONE; - - // Inicializa las posiciones de los sprites usando las constantes - player_sprite_->setPosX(GameCanvas::CENTER_X + PLAYER_X_OFFSET); - player_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET); - tv_sprite_->setPosX(GameCanvas::CENTER_X - tv_sprite_->getWidth() - TV_X_OFFSET); - tv_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET); - - Screen::get()->setBorderColor(0); - - // Inicializa el vector de colores (de brillante a oscuro para fade) - colors_ = {14, 12, 10, 8, 6, 4, 2, 0}; - color_ = colors_.back(); // Empieza en black -} - -// Actualiza el objeto -void GameOver::update() { - const float DELTA_TIME = delta_timer_->tick(); - elapsed_time_ += DELTA_TIME; - - handleEvents(); // Comprueba los eventos - handleInput(); // Comprueba las entradas - - updateState(); // Actualiza el estado de la escena - updateColor(); // Actualiza el color usado para renderizar los textos e imagenes - player_sprite_->update(DELTA_TIME); // Actualiza el sprite - tv_sprite_->update(DELTA_TIME); // Actualiza el sprite - - Audio::update(); // Actualiza el objeto Audio - Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen -} - -// Dibuja el final en pantalla -void GameOver::render() { - Screen::get()->start(); - Screen::get()->clearSurface(0); - - auto text = Resource::Cache::get()->getText("smb2"); - - // Escribe el texto de GAME OVER - auto* loc = Locale::get(); - text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y, loc->get("game_over.title"), 1, color_); // NOLINT(readability-static-accessed-through-instance) - - // Dibuja los sprites (ya posicionados en el constructor, solo ajustamos Y) - player_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET); - tv_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET); - renderSprites(); - - // Escribe el texto con las habitaciones y los items - const std::string ITEMS_TEXT = std::to_string(Options::stats.items / 100) + std::to_string((Options::stats.items % 100) / 10) + std::to_string(Options::stats.items % 10); - const std::string ROOMS_TEXT = std::to_string(Options::stats.rooms / 100) + std::to_string((Options::stats.rooms % 100) / 10) + std::to_string(Options::stats.rooms % 10); - text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ITEMS_Y_OFFSET, loc->get("game_over.items") + ITEMS_TEXT, 1, color_); // NOLINT(readability-static-accessed-through-instance) - text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ROOMS_Y_OFFSET, loc->get("game_over.rooms") + ROOMS_TEXT, 1, color_); // NOLINT(readability-static-accessed-through-instance) - - // Vuelca el contenido del renderizador en pantalla - Screen::get()->render(); -} - -// Comprueba el manejador de eventos -void GameOver::handleEvents() { - SDL_Event event; - while (SDL_PollEvent(&event)) { - GlobalEvents::handle(event); - } -} - -// Comprueba las entradas -void GameOver::handleInput() { - Input::get()->update(); - GlobalInputs::handle(); -} - -// Bucle principal -void GameOver::run() { - while (SceneManager::current == SceneManager::Scene::GAME_OVER) { - update(); - render(); - } -} - -// Actualiza el color usado para renderizar los textos e imagenes -void GameOver::updateColor() { - // Calcula el color basado en el estado actual - switch (state_) { - case State::WAITING: - // Durante la espera, mantener en black - color_ = colors_.back(); // black - break; - - case State::FADE_IN: { - // Fade in: de black (último color) a white (primer color) - // Progreso: 0.0 (black) -> 1.0 (white) - const float PROGRESS = std::min(elapsed_time_ / FADE_IN_DURATION, 1.0F); - const int INDEX = (colors_.size() - 1) - static_cast<int>((colors_.size() - 1) * PROGRESS); - color_ = colors_[std::clamp(INDEX, 0, static_cast<int>(colors_.size() - 1))]; - break; - } - - case State::DISPLAY: - // Durante display, mantener el color más brillante - color_ = colors_[0]; // white - break; - - case State::FADE_OUT: { - // Fade out: de white (primer color) a black (último color) - // Progreso: 0.0 (white) -> 1.0 (black) - const float PROGRESS = std::min(elapsed_time_ / FADE_OUT_DURATION, 1.0F); - const int INDEX = static_cast<int>((colors_.size() - 1) * PROGRESS); - color_ = colors_[std::clamp(INDEX, 0, static_cast<int>(colors_.size() - 1))]; - break; - } - - case State::ENDING: - case State::TRANSITION: - // Al final, mantener en black - color_ = colors_.back(); // black - break; - } -} - -// Dibuja los sprites -void GameOver::renderSprites() { - player_sprite_->render(1, color_); - tv_sprite_->render(1, color_); -} - -// Actualiza el estado de la escena y gestiona transiciones -void GameOver::updateState() { - // Máquina de estados basada en tiempo transcurrido - switch (state_) { - case State::WAITING: - // Espera inicial antes de empezar - if (elapsed_time_ >= WAITING_DURATION) { - state_ = State::FADE_IN; - elapsed_time_ = 0.0F; - // Hace sonar la música cuando termina la espera - Audio::get()->playMusic("574070_KUVO_Farewell_to_school.ogg", 0); - } - break; - - case State::FADE_IN: - // Fade in de colores desde black - if (elapsed_time_ >= FADE_IN_DURATION) { - state_ = State::DISPLAY; - elapsed_time_ = 0.0F; - } - break; - - case State::DISPLAY: - // Mostrando contenido con color brillante - if (elapsed_time_ >= DISPLAY_DURATION) { - state_ = State::FADE_OUT; - elapsed_time_ = 0.0F; - } - break; - - case State::FADE_OUT: - // Fade out hacia black - if (elapsed_time_ >= FADE_OUT_DURATION) { - state_ = State::ENDING; - elapsed_time_ = 0.0F; - } - break; - - case State::ENDING: - // Pantalla en negro antes de salir - if (elapsed_time_ >= ENDING_DURATION) { - state_ = State::TRANSITION; - elapsed_time_ = 0.0F; - } - break; - - case State::TRANSITION: - // Transición a la escena de logo - SceneManager::current = SceneManager::Scene::LOGO; - SceneManager::options = SceneManager::Options::LOGO_TO_TITLE; - break; - } -} \ No newline at end of file diff --git a/source/game/scenes/game_over.hpp b/source/game/scenes/game_over.hpp deleted file mode 100644 index 5e2191e..0000000 --- a/source/game/scenes/game_over.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include <SDL3/SDL.h> - -#include <memory> // Para shared_ptr -#include <vector> // Para vector -class AnimatedSprite; // lines 7-7 -class DeltaTimer; // Forward declaration - -class GameOver { - public: - // Constructor y Destructor - GameOver(); - ~GameOver() = default; - - // Bucle principal - void run(); - - private: - // --- Enumeraciones --- - enum class State { - WAITING, // Espera inicial antes de empezar - FADE_IN, // Fade in de colores desde black - DISPLAY, // Mostrando contenido con color brillante - FADE_OUT, // Fade out hacia black - ENDING, // Pantalla en negro antes de salir - TRANSITION // Cambio a logo - }; - - // --- Constantes de duración (segundos) --- - static constexpr float WAITING_DURATION = 0.8F; // Espera inicial - static constexpr float FADE_IN_DURATION = 0.32F; // Duración del fade in - static constexpr float DISPLAY_DURATION = 4.64F; // Duración mostrando contenido - static constexpr float FADE_OUT_DURATION = 0.32F; // Duración del fade out - static constexpr float ENDING_DURATION = 1.12F; // Espera en negro antes de salir - - // --- Constantes de posición --- - static constexpr int TEXT_Y = 32; // Posición Y del texto principal - static constexpr int SPRITE_Y_OFFSET = 30; // Offset Y para sprites desde TEXT_Y - static constexpr int PLAYER_X_OFFSET = 10; // Offset X del jugador desde el centro - static constexpr int TV_X_OFFSET = 10; // Offset X del TV desde el centro - static constexpr int ITEMS_Y_OFFSET = 80; // Offset Y del texto de items desde TEXT_Y - static constexpr int ROOMS_Y_OFFSET = 90; // Offset Y del texto de rooms desde TEXT_Y - - // --- Métodos --- - void update(); // Actualiza el objeto - void render(); // Dibuja el final en pantalla - static void handleEvents(); // Comprueba el manejador de eventos - static void handleInput(); // Comprueba las entradas - void updateState(); // Actualiza el estado y transiciones - void updateColor(); // Actualiza el color usado para renderizar - void renderSprites(); // Dibuja los sprites - - // --- Variables miembro --- - // Objetos y punteros a recursos - std::shared_ptr<AnimatedSprite> player_sprite_; // Sprite con el jugador - std::shared_ptr<AnimatedSprite> tv_sprite_; // Sprite con el televisor - std::shared_ptr<DeltaTimer> delta_timer_; // Timer para time-based logic - - // Variables de estado de la escena - State state_{State::WAITING}; // Estado actual de la escena - float elapsed_time_{0.0F}; // Tiempo transcurrido en el estado actual - - // Variables de efectos visuales - std::vector<Uint8> colors_; // Vector con los colores para el fade - Uint8 color_{0}; // Color actual para texto y sprites -}; \ No newline at end of file diff --git a/source/game/scenes/loading_screen.cpp b/source/game/scenes/loading_screen.cpp deleted file mode 100644 index b5b754a..0000000 --- a/source/game/scenes/loading_screen.cpp +++ /dev/null @@ -1,498 +0,0 @@ -#include "game/scenes/loading_screen.hpp" - -#include <SDL3/SDL.h> - -#include <cmath> // Para std::sin -#include <cstdlib> // Para rand - -#include "core/audio/audio.hpp" // Para Audio -#include "core/input/global_inputs.hpp" // Para check -#include "core/input/input.hpp" // Para Input -#include "core/rendering/screen.hpp" // Para Screen -#include "core/rendering/sprite/sprite.hpp" // Para SSprite -#include "core/rendering/surface.hpp" // Para Surface -#include "core/resources/resource_cache.hpp" // Para Resource -#include "core/system/global_events.hpp" // Para check -#include "game/options.hpp" // Para Options, options, SectionState, Options... -#include "game/scene_manager.hpp" // Para SceneManager -#include "utils/defines.hpp" // Para GAME_SPEED -#include "utils/utils.hpp" - -// Constructor -LoadingScreen::LoadingScreen() - : mono_loading_screen_surface_(Resource::Cache::get()->getSurface("loading_screen_bn.gif")), - color_loading_screen_surface_(Resource::Cache::get()->getSurface("loading_screen_color.gif")), - mono_loading_screen_sprite_(std::make_unique<Sprite>(mono_loading_screen_surface_, 0, 0, mono_loading_screen_surface_->getWidth(), mono_loading_screen_surface_->getHeight())), - color_loading_screen_sprite_(std::make_unique<Sprite>(color_loading_screen_surface_, 0, 0, color_loading_screen_surface_->getWidth(), color_loading_screen_surface_->getHeight())), - program_sprite_(std::make_unique<Sprite>(Resource::Cache::get()->getSurface("program_jaildoc.gif"))), - screen_surface_(std::make_shared<Surface>(Options::game.width, Options::game.height)), - delta_timer_(std::make_unique<DeltaTimer>()) { - // Configura la superficie donde se van a pintar los sprites - screen_surface_->clear(14); - - // Inicializa variables - SceneManager::current = SceneManager::Scene::LOADING_SCREEN; - SceneManager::options = SceneManager::Options::NONE; - program_sprite_->setPosition(0.0F, 8.0F); - - // Inicializa el array de índices de líneas - initLineIndexArray(); - - // Cambia el color del borde - Screen::get()->setBorderColor(14); - transitionToState(State::SILENT1); -} - -// Destructor -LoadingScreen::~LoadingScreen() { - Audio::get()->stopMusic(); -} - -// Comprueba el manejador de eventos -void LoadingScreen::handleEvents() { - SDL_Event event; - while (SDL_PollEvent(&event)) { - GlobalEvents::handle(event); - } -} - -// Comprueba las entradas -void LoadingScreen::handleInput() { - Input::get()->update(); - GlobalInputs::handle(); -} - -// Inicializa el array de índices de líneas (imita el direccionamiento de memoria del Spectrum) -void LoadingScreen::initLineIndexArray() { // NOLINT(readability-convert-member-functions-to-static) - for (int i = 0; i < MONO_TOTAL_LINES; ++i) { - if (i < 64) { // Primer bloque de 2K - line_index_[i] = ((i % 8) * 8) + (i / 8); - } else if (i < 128) { // Segundo bloque de 2K - line_index_[i] = 64 + ((i % 8) * 8) + ((i - 64) / 8); - } else { // Tercer bloque de 2K - line_index_[i] = 128 + ((i % 8) * 8) + ((i - 128) / 8); - } - } -} - -// Transiciona a un nuevo estado -void LoadingScreen::transitionToState(State new_state) { - state_ = new_state; - state_time_ = 0.0F; - - // Acciones específicas al entrar en cada estado - switch (new_state) { - case State::SILENT1: - case State::SILENT2: - current_border_type_ = Border::RED; - Audio::get()->stopMusic(); - break; - - case State::HEADER1: - case State::HEADER2: - current_border_type_ = Border::RED_AND_CYAN; - Audio::get()->playMusic("574071_EA_DTV.ogg", 0); - break; - - case State::DATA1: - printProgramName(); - current_border_type_ = Border::YELLOW_AND_BLUE; - Audio::get()->playMusic("574071_EA_DTV.ogg", 0); - break; - case State::DATA2: - current_border_type_ = Border::YELLOW_AND_BLUE; - Audio::get()->playMusic("574070_KUVO_Farewell_to_school.ogg", 0); - break; - case State::LOADING_MONO: - current_border_type_ = Border::YELLOW_AND_BLUE; - Audio::get()->playMusic("574071_EA_DTV.ogg", 0); - last_mono_step_ = -1; // Resetear contador de pasos mono - break; - - case State::LOADING_COLOR: - current_border_type_ = Border::YELLOW_AND_BLUE; - Audio::get()->playMusic("574070_KUVO_Farewell_to_school.ogg", 0); - last_color_block_ = -1; // Resetear contador de bloques color - break; - - case State::COMPLETE: - current_border_type_ = Border::BLACK; - // Transicionar a la pantalla de título - SceneManager::current = SceneManager::Scene::TITLE; - SceneManager::options = SceneManager::Options::TITLE_WITH_LOADING_SCREEN; - Audio::get()->stopMusic(); - break; - } -} - -// Actualiza el estado actual -void LoadingScreen::updateState(float delta_time) { - state_time_ += delta_time; - - // Transiciones automáticas por tiempo para los estados iniciales - // LOADING_MONO y LOADING_COLOR transicionan en sus propias funciones - switch (state_) { - case State::SILENT1: - if (state_time_ >= SILENT1_DURATION) { - transitionToState(State::HEADER1); - } - break; - - case State::HEADER1: - if (state_time_ >= HEADER1_DURATION) { - transitionToState(State::DATA1); - } - break; - - case State::DATA1: - if (state_time_ >= DATA1_DURATION) { - transitionToState(State::SILENT2); - } - break; - - case State::SILENT2: - if (state_time_ >= SILENT2_DURATION) { - transitionToState(State::HEADER2); - } - break; - - case State::HEADER2: - if (state_time_ >= HEADER2_DURATION) { - transitionToState(State::LOADING_MONO); - } - break; - - case State::DATA2: - if (state_time_ >= DATA2_DURATION) { - transitionToState(State::COMPLETE); - } - break; - - case State::LOADING_MONO: - case State::LOADING_COLOR: - case State::COMPLETE: - // Estos estados se gestionan en updateMonoLoad/updateColorLoad - break; - } -} - -// Gestiona la carga monocromática (time-based simplificado) -void LoadingScreen::updateMonoLoad(float delta_time) { - // Calcular progreso lineal (0.0 - 1.0) - float progress = state_time_ / LOADING_MONO_DURATION; - progress = std::min(progress, 1.0F); - - // Calcular paso total actual (0-959) - const int TOTAL_STEPS = MONO_TOTAL_LINES * MONO_STEPS_PER_LINE; // 192 * 5 = 960 - const int CURRENT_STEP = static_cast<int>(progress * TOTAL_STEPS); - - // Verificar si ha completado todas las líneas - if (CURRENT_STEP >= TOTAL_STEPS) { - transitionToState(State::LOADING_COLOR); - return; - } - - // Dibujar todos los pasos intermedios desde el último dibujado - const float TEXTURE_WIDTH = mono_loading_screen_surface_->getWidth(); - const float CLIP_WIDTH = TEXTURE_WIDTH / MONO_STEPS_PER_LINE; - - auto previous_renderer = Screen::get()->getRendererSurface(); - Screen::get()->setRendererSurface(screen_surface_); - - for (int step = last_mono_step_ + 1; step <= CURRENT_STEP; ++step) { - // Calcular línea y sub-paso para este paso - const int CURRENT_LINE = step / MONO_STEPS_PER_LINE; // 0-191 - const int CURRENT_SUBSTEP = step % MONO_STEPS_PER_LINE; // 0-4 - - // Saltar si excede el total de líneas - if (CURRENT_LINE >= MONO_TOTAL_LINES) { - break; - } - - // Calcular rectángulo de clip para este paso - const float CLIP_X = CURRENT_SUBSTEP * CLIP_WIDTH; - - load_rect_.x = CLIP_X; - load_rect_.y = static_cast<float>(line_index_[CURRENT_LINE]); - load_rect_.w = CLIP_WIDTH; - load_rect_.h = 1.0F; - - // Configurar y dibujar sobre screen_surface_ - mono_loading_screen_sprite_->setClip(load_rect_); - mono_loading_screen_sprite_->setPosition(load_rect_); - mono_loading_screen_sprite_->render(); - } - - Screen::get()->setRendererSurface(previous_renderer); - - // Actualizar el último paso dibujado - last_mono_step_ = CURRENT_STEP; -} - -// Gestiona la carga en color -void LoadingScreen::updateColorLoad(float delta_time) { - // Calcular progreso lineal (0.0 - 1.0) - float progress = state_time_ / LOADING_COLOR_DURATION; - progress = std::min(progress, 1.0F); - - // Calcular bloque actual (0-767) - ahora pinta de 1 en 1 en lugar de 2 en 2 - const int CURRENT_BLOCK = static_cast<int>(progress * COLOR_TOTAL_BLOCKS); - - // Verificar si ha completado todos los bloques - if (CURRENT_BLOCK >= COLOR_TOTAL_BLOCKS) { - transitionToState(State::DATA2); - return; - } - - // Dibujar todos los bloques intermedios desde el último dibujado - auto previous_renderer = Screen::get()->getRendererSurface(); - Screen::get()->setRendererSurface(screen_surface_); - - // Iterar desde el último bloque + 1 hasta el bloque actual (de 1 en 1) - for (int block = last_color_block_ + 1; block <= CURRENT_BLOCK; ++block) { - // Saltar si excede el total de bloques - if (block >= COLOR_TOTAL_BLOCKS) { - break; - } - - // Calcular posición del bloque - load_rect_.x = static_cast<float>((block * COLOR_BLOCK_SPACING) % 256); - load_rect_.y = static_cast<float>((block / COLOR_BLOCKS_PER_ROW) * COLOR_BLOCK_SPACING); - load_rect_.w = static_cast<float>(COLOR_BLOCK_WIDTH); - load_rect_.h = static_cast<float>(COLOR_BLOCK_HEIGHT); - - // Configurar y dibujar sobre screen_surface_ - color_loading_screen_sprite_->setClip(load_rect_); - color_loading_screen_sprite_->setPosition(load_rect_); - color_loading_screen_sprite_->render(); - } - - Screen::get()->setRendererSurface(previous_renderer); - - // Actualizar el último bloque dibujado - last_color_block_ = CURRENT_BLOCK; -} - -// Dibuja el efecto de carga amarillo y azul en el borde -void LoadingScreen::renderDataBorder() { - // Obtiene la Surface del borde - auto border = Screen::get()->getBorderSurface(); - - // Pinta el borde de color azul - border->clear(2); - - // Añade lineas amarillas - const auto COLOR = 12; - const int WIDTH = Options::game.width + (Options::video.border.width * 2); - const int HEIGHT = Options::game.height + (Options::video.border.height * 2); - bool draw_enabled = rand() % 2 == 0; - - int row = 0; - while (row < HEIGHT) { - const int ROW_HEIGHT = (rand() % 4) + 3; - if (draw_enabled) { - for (int i = row; i < row + ROW_HEIGHT; ++i) { - border->drawLine(0, i, WIDTH, i, COLOR); - } - } - row += ROW_HEIGHT; - draw_enabled = !draw_enabled; - } -} - -// Dibuja el efecto de carga rojo y azul en el borde -void LoadingScreen::renderHeaderBorder() const { - // Obtiene la Surface del borde - auto border = Screen::get()->getBorderSurface(); - - // Pinta el borde de color azul o rojo - border->clear(carrier_.toggle ? 10 : 4); - - // Añade lineas rojas o azules - const auto COLOR = carrier_.toggle ? 4 : 10; - const int WIDTH = Options::game.width + (Options::video.border.width * 2); - const int HEIGHT = Options::game.height + (Options::video.border.height * 2); - - // Primera linea (con el color y tamaño de la portadora) - int row = 0; - const int FIRST_ROW_HEIGHT = static_cast<int>(carrier_.offset); - for (int i = row; i < row + FIRST_ROW_HEIGHT; ++i) { - border->drawLine(0, i, WIDTH, i, COLOR); - } - row += FIRST_ROW_HEIGHT; - - // Resto de lineas (siguen a la portadora) - bool draw_enabled = false; - while (row < HEIGHT) { - if (draw_enabled) { - for (int i = row; i < row + HEADER_DATAROW_HEIGHT; ++i) { - border->drawLine(0, i, WIDTH, i, COLOR); - } - } - row += HEADER_DATAROW_HEIGHT; - draw_enabled = !draw_enabled; - } -} - -// Dibuja el borde de color -void LoadingScreen::renderColoredBorder(Uint8 color) { - // Obtiene la Surface del borde - auto border = Screen::get()->getBorderSurface(); - - // Pinta el borde del color indicado - border->clear(color); -} - -// Actualiza las variables -void LoadingScreen::update() { - const float DELTA_TIME = delta_timer_->tick(); - - handleEvents(); // Comprueba los eventos - handleInput(); // Comprueba las entradas - - updateState(DELTA_TIME); // Actualiza el estado y gestiona transiciones - - // Actualizar la carga según el estado actual - switch (state_) { - case State::DATA1: - case State::DATA2: - // Por ahora no hacen nada específico - break; - case State::SILENT1: - case State::SILENT2: - updateSilent(DELTA_TIME); - break; - case State::HEADER1: - case State::HEADER2: - updateCarrier(DELTA_TIME); - break; - - case State::LOADING_MONO: - updateMonoLoad(DELTA_TIME); - break; - - case State::LOADING_COLOR: - updateColorLoad(DELTA_TIME); - break; - - case State::COMPLETE: - // No hay más actualizaciones - break; - } - - Audio::update(); // Actualiza el objeto Audio - Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen -} - -// Dibuja en pantalla -void LoadingScreen::render() { - // Pinta el borde - renderBorder(); - - // Prepara para empezar a dibujar en la textura de juego - Screen::get()->start(); - Screen::get()->clearSurface(14); - - // Copia la surface a la surface de Screen - screen_surface_->render(0, 0); - - // Vuelca el contenido del renderizador en pantalla - Screen::get()->render(); -} - -// Bucle para el logo del juego -void LoadingScreen::run() { - // Ajusta el volumen - Audio::get()->setMusicVolume(50); - - // Limpia la pantalla - Screen::get()->start(); - Screen::get()->clearRenderer(); - Screen::get()->render(); - - while (SceneManager::current == SceneManager::Scene::LOADING_SCREEN) { - update(); - render(); - } - - Audio::get()->setMusicVolume(100); -} - -// Pinta el borde -void LoadingScreen::renderBorder() { - if (Options::video.border.enabled) { - // Dibuja el efecto de carga en el borde según el tipo actual - switch (current_border_type_) { - case Border::YELLOW_AND_BLUE: - renderDataBorder(); - break; - case Border::RED_AND_CYAN: - renderHeaderBorder(); - break; - case Border::WHITE: - renderColoredBorder(14); - break; - case Border::BLACK: - renderColoredBorder(0); - break; - case Border::RED: - renderColoredBorder(4); - break; - case Border::CYAN: - renderColoredBorder(10); - break; - case Border::NONE: - // No renderizar borde - break; - } - } -} - -// Escribe el nombre del programa -void LoadingScreen::printProgramName() { // NOLINT(readability-convert-member-functions-to-static) - auto previous_renderer = Screen::get()->getRendererSurface(); - Screen::get()->setRendererSurface(screen_surface_); - program_sprite_->render(); - Screen::get()->setRendererSurface(previous_renderer); -} - -// Actualiza la portadora -void LoadingScreen::updateCarrier(float delta_time) { - constexpr float CARRIER_BASE_SPEED = -250.0F; - constexpr float CARRIER_HEIGHT = HEADER_DATAROW_HEIGHT; - - // Oscilación compuesta: mezcla de dos frecuencias para evitar patrón predecible - const float MODULATION = std::sin(carrier_.total_time * 1.2F) * std::sin((carrier_.total_time * 0.35F) + 1.0F); - const float SPEED = CARRIER_BASE_SPEED * (0.5F + (0.5F * MODULATION)); // rango [-200, 0] - - carrier_.offset += SPEED * delta_time; - - if (carrier_.offset < 0.0F) { - carrier_.offset += CARRIER_HEIGHT; // reinicia al rango [0,HEADER_DATAROW_HEIGHT] - carrier_.toggle = !carrier_.toggle; - } - - carrier_.total_time += delta_time; -} - -// Actualiza el ruido durante el tiempo de silencio -void LoadingScreen::updateSilent(float delta_time) { - constexpr float NOISE_THRESHOLD = 0.35F; - - // Oscilación compuesta para simular picos de ruido - const float MODULATION = std::sin(noise_.total_time * 4.2F) * std::sin((noise_.total_time * 1.7F) + 0.5F); - noise_.value = std::fabs(MODULATION); // rango [0.0, 1.0] - - // Detecta cruce de umbral solo si venía de abajo - if (noise_.value > NOISE_THRESHOLD && !noise_.crossed) { - noise_.crossed = true; - current_border_type_ = (current_border_type_ == Border::RED) ? Border::CYAN : Border::RED; - } - - // Restablece el flag cuando baja del umbral - if (noise_.value < NOISE_THRESHOLD) { - noise_.crossed = false; - } - - noise_.total_time += delta_time; -} \ No newline at end of file diff --git a/source/game/scenes/loading_screen.hpp b/source/game/scenes/loading_screen.hpp deleted file mode 100644 index 71daf8a..0000000 --- a/source/game/scenes/loading_screen.hpp +++ /dev/null @@ -1,122 +0,0 @@ -#pragma once - -#include <SDL3/SDL.h> - -#include <array> // Para std::array -#include <memory> // Para shared_ptr - -#include "utils/delta_timer.hpp" // Para DeltaTimer -class Sprite; // Forward declaration -class Surface; // Forward declaration - -class LoadingScreen { - public: - // --- Constructor y Destructor --- - LoadingScreen(); - ~LoadingScreen(); - - // --- Bucle principal --- - void run(); - - private: - // --- Enumeraciones --- - // Estados de la secuencia de carga - enum class State { - SILENT1, // Pausa inicial antes de empezar - HEADER1, // Cabecera - DATA1, // Datos - SILENT2, // Segunda pausa - HEADER2, // Cabecera pantalla - LOADING_MONO, // Carga de pantalla monocromática (escaneo de líneas) - LOADING_COLOR, // Carga de pantalla en color (bloques) - DATA2, // Datos - COMPLETE // Carga completa - }; - - // Tipos de borde para la pantalla de carga - enum class Border { - NONE, - YELLOW_AND_BLUE, - RED_AND_CYAN, - WHITE, - BLACK, - RED, - CYAN - }; - - // --- Estructuras --- - struct Carrier { - float offset{0.0F}; // Offset para la carga de cabeceras - bool toggle{false}; // Para cambiar el color inicial - float total_time{0.0F}; // Tiempo acumulado para modulación de velocidad - }; - - struct Noise { - float value{0.0F}; // Nivel actual de ruido (0.0 a 1.0) - float total_time{0.0F}; // Tiempo acumulado para modulación - bool crossed{false}; // Flag para detectar cruce de umbral - }; - - // --- Constantes de tiempo (en segundos) --- - static constexpr float SILENT1_DURATION = 2.0F; // Pausa inicial - static constexpr float HEADER1_DURATION = 4.0F; // Cabecera - static constexpr float DATA1_DURATION = 0.18F; // Datos - static constexpr float SILENT2_DURATION = 1.6F; // Segunda pausa - static constexpr float HEADER2_DURATION = 2.0F; // Cabecera pantalla - static constexpr float LOADING_MONO_DURATION = 16.0F; // Duración total de la carga monocromática - static constexpr float LOADING_COLOR_DURATION = 4.0F; // Duración total de la carga en color - static constexpr float DATA2_DURATION = 5.0F; // Datos - - // --- Constantes de geometría --- - static constexpr int MONO_TOTAL_LINES = 192; // Total de líneas en carga monocromática - static constexpr int MONO_STEPS_PER_LINE = 5; // Pasos de animación por línea - static constexpr int COLOR_TOTAL_BLOCKS = 768; // Total de bloques en carga color - static constexpr int COLOR_BLOCK_WIDTH = 16; // Ancho del bloque de color - static constexpr int COLOR_BLOCK_HEIGHT = 8; // Alto del bloque de color - static constexpr int COLOR_BLOCKS_PER_ROW = 32; // Bloques por fila (256 / 8) - static constexpr int COLOR_BLOCK_SPACING = 8; // Espaciado entre bloques - static constexpr int HEADER_DATAROW_HEIGHT = 9.0F; // Alto de las barras del borde de la carga de las cabeceras - - // --- Métodos --- - void update(); // Actualiza las variables - void render(); // Dibuja en pantalla - static void handleEvents(); // Comprueba el manejador de eventos - static void handleInput(); // Comprueba las entradas - void updateState(float delta_time); // Actualiza el estado actual - void transitionToState(State new_state); // Transiciona a un nuevo estado - void updateMonoLoad(float delta_time); // Gestiona la carga monocromática (time-based) - void updateColorLoad(float delta_time); // Gestiona la carga en color (time-based) - void renderBorder(); // Pinta el borde - static void renderDataBorder(); // Dibuja el efecto de carga amarillo y azul en el borde - void renderHeaderBorder() const; // Dibuja el efecto de carga rojo y azul en el borde - static void renderColoredBorder(Uint8 color); // Dibuja el borde de color - void initLineIndexArray(); // Inicializa el array de índices de líneas - void printProgramName(); // Escribe el nombre del programa - void updateCarrier(float delta_time); // Actualiza la portadora - void updateSilent(float delta_time); // Actualiza el ruido durante el tiempo de silencio - - // --- Variables miembro --- - // Objetos y punteros a recursos - std::shared_ptr<Surface> mono_loading_screen_surface_; // Surface con la pantalla de carga en blanco y negro - std::shared_ptr<Surface> color_loading_screen_surface_; // Surface con la pantalla de carga en color - std::unique_ptr<Sprite> mono_loading_screen_sprite_; // SurfaceSprite para manejar la textura mono_loading_screen_surface_ - std::unique_ptr<Sprite> color_loading_screen_sprite_; // SurfaceSprite para manejar la textura color_loading_screen_surface_ - std::unique_ptr<Sprite> program_sprite_; // SurfaceSprite para manejar la textura con el nombre del programa - std::shared_ptr<Surface> screen_surface_; // Surface para dibujar la pantalla de carga - std::unique_ptr<DeltaTimer> delta_timer_; // Timer para delta time - - // Variables de estado de la secuencia - State state_{State::SILENT1}; // Estado actual de la secuencia - float state_time_{0.0F}; // Tiempo acumulado en el estado actual - Border current_border_type_{Border::NONE}; // Tipo de borde actual - - // Arrays y estructuras auxiliares - std::array<int, MONO_TOTAL_LINES> line_index_; // El orden en el que se procesan las 192 líneas de la pantalla de carga - SDL_FRect load_rect_{.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 1.0F}; // Rectángulo para dibujar la pantalla de carga - Carrier carrier_; // Estructura para los efectos de la carga de cabeceras - Noise noise_; // Variaciones de ruido durante los silencios - - // Variables de seguimiento para evitar saltos de pasos/bloques - int last_mono_step_{-1}; // Último paso mono dibujado - int last_color_block_{-1}; // Último bloque color dibujado -}; \ No newline at end of file diff --git a/source/game/scenes/logo.cpp b/source/game/scenes/logo.cpp index 795d3ff..60b2e7a 100644 --- a/source/game/scenes/logo.cpp +++ b/source/game/scenes/logo.cpp @@ -238,19 +238,7 @@ void Logo::run() { // Termina la sección void Logo::endSection() { - switch (SceneManager::options) { - case SceneManager::Options::LOGO_TO_TITLE: - SceneManager::current = SceneManager::Scene::TITLE; - break; - - case SceneManager::Options::LOGO_TO_LOADING_SCREEN: - SceneManager::current = SceneManager::Scene::LOADING_SCREEN; - break; - - default: - SceneManager::current = SceneManager::Scene::LOADING_SCREEN; - break; - } + SceneManager::current = SceneManager::Scene::TITLE; } // Inicializa el vector de colores diff --git a/source/game/scenes/title.cpp b/source/game/scenes/title.cpp index 444ed34..9bd70e1 100644 --- a/source/game/scenes/title.cpp +++ b/source/game/scenes/title.cpp @@ -26,25 +26,21 @@ Title::Title() : game_logo_surface_(Resource::Cache::get()->getSurface("title_logo.gif")), game_logo_sprite_(std::make_unique<Sprite>(game_logo_surface_, 29, 9, game_logo_surface_->getWidth(), game_logo_surface_->getHeight())), - loading_screen_surface_(Resource::Cache::get()->getSurface("loading_screen_color.gif")), - loading_screen_sprite_(std::make_unique<Sprite>(loading_screen_surface_, 0, 0, loading_screen_surface_->getWidth(), loading_screen_surface_->getHeight())), title_surface_(std::make_shared<Surface>(Options::game.width, Options::game.height)), delta_timer_(std::make_unique<DeltaTimer>()), - marquee_text_(Resource::Cache::get()->getText("gauntlet")), menu_text_(Resource::Cache::get()->getText("gauntlet")) { // Inicializa arrays con valores por defecto temp_keys_.fill(SDL_SCANCODE_UNKNOWN); temp_buttons_.fill(-1); - // Determina el estado inicial basado en opciones - state_ = SceneManager::options == SceneManager::Options::TITLE_WITH_LOADING_SCREEN ? State::SHOW_LOADING_SCREEN : State::MAIN_MENU; + // Estado inicial: menú principal + state_ = State::MAIN_MENU; // Establece SceneManager SceneManager::current = SceneManager::Scene::TITLE; SceneManager::options = SceneManager::Options::NONE; // Acciones iniciales - initMarquee(); // Inicializa la marquesina createCheevosTexture(); // Crea y rellena la textura para mostrar los logros Screen::get()->setBorderColor(0); // Cambia el color del borde Audio::get()->playMusic("574071_EA_DTV.ogg"); // Inicia la musica @@ -52,35 +48,9 @@ Title::Title() // Destructor Title::~Title() { // NOLINT(modernize-use-equals-default) - loading_screen_surface_->resetSubPalette(); title_surface_->resetSubPalette(); } -// Inicializa la marquesina -void Title::initMarquee() { - letters_.clear(); - long_text_ = Locale::get()->get("title.marquee"); - - // Pre-calcular anchos de caracteres para eficiencia (iteración por codepoints UTF-8) - size_t pos = 0; - while (pos < long_text_.size()) { - uint32_t cp = Text::nextCodepoint(long_text_, pos); - Glyph l; - l.codepoint = cp; - l.clip = marquee_text_->getGlyphClip(cp); // Pre-calcular clip rect (evita búsqueda por frame) - l.x = MARQUEE_START_X; - l.width = static_cast<float>(marquee_text_->glyphWidth(cp, 0)); // Pre-calcular ancho visual del glifo - l.enabled = false; - letters_.push_back(l); - } - - if (!letters_.empty()) { - letters_[0].enabled = true; - } - first_active_letter_ = 0; - last_active_letter_ = 0; -} - // Comprueba el manejador de eventos void Title::handleEvents() { SDL_Event event; @@ -170,12 +140,6 @@ void Title::handleInput(float delta_time) { } switch (state_) { - case State::SHOW_LOADING_SCREEN: - if (Input::get()->checkAction(InputAction::ACCEPT, Input::DO_NOT_ALLOW_REPEAT)) { - transitionToState(State::FADE_LOADING_SCREEN); - } - break; - case State::CHEEVOS_MENU: if (Input::get()->checkAction(InputAction::ACCEPT, Input::DO_NOT_ALLOW_REPEAT) || Input::get()->checkAction(InputAction::CANCEL, Input::DO_NOT_ALLOW_REPEAT)) { @@ -191,53 +155,6 @@ void Title::handleInput(float delta_time) { GlobalInputs::handle(); } -// Actualiza la marquesina -void Title::updateMarquee(float delta_time) { - const float DISPLACEMENT = MARQUEE_SPEED * delta_time; - - // Solo procesar letras en rango activo + 1 para poder activar la siguiente - for (int i = first_active_letter_; i <= last_active_letter_ + 1 && i < (int)letters_.size(); ++i) { - auto& letter = letters_[i]; - - if (letter.enabled) { - letter.x -= DISPLACEMENT; - - // Desactivar si sale de pantalla - if (letter.x < MARQUEE_EXIT_X) { - letter.enabled = false; - if (i == first_active_letter_) { - first_active_letter_++; // Avanzar inicio del rango - } - } - } else if (i > 0 && letters_[i - 1].x < MARQUEE_START_X && letters_[i - 1].enabled) { - // Activar siguiente letra usando ancho pre-calculado - letter.enabled = true; - letter.x = letters_[i - 1].x + letters_[i - 1].width + MARQUEE_LETTER_SPACING; - last_active_letter_ = i; // Expandir fin del rango - } - } - - // Comprueba si ha terminado la marquesina y la reinicia - if (letters_[letters_.size() - 1].x < MARQUEE_EXIT_X) { - initMarquee(); - } -} - -// Dibuja la marquesina -void Title::renderMarquee() const { - auto* sprite = marquee_text_->getSprite(); - sprite->setY(MARQUEE_Y); - // Solo renderizar letras activas (optimización: usa cache y rangos) - for (int i = first_active_letter_; i <= last_active_letter_ + 1 && i < (int)letters_.size(); ++i) { - const auto& letter = letters_[i]; - if (letter.enabled && letter.clip.w > 0.0F) { - sprite->setClip(letter.clip); - sprite->setX(letter.x); - sprite->render(1, 6); - } - } -} - // Actualiza las variables void Title::update() { const float DELTA_TIME = delta_timer_->tick(); @@ -254,14 +171,6 @@ void Title::update() { // Actualiza el estado actual void Title::updateState(float delta_time) { switch (state_) { - case State::SHOW_LOADING_SCREEN: - updateShowLoadingScreen(delta_time); - break; - - case State::FADE_LOADING_SCREEN: - updateFadeLoadingScreen(delta_time); - break; - case State::MAIN_MENU: updateMainMenu(delta_time); break; @@ -290,30 +199,8 @@ void Title::transitionToState(State new_state) { fade_accumulator_ = 0.0F; } -// Actualiza el estado SHOW_LOADING_SCREEN -void Title::updateShowLoadingScreen(float delta_time) { - state_time_ += delta_time; - if (state_time_ >= SHOW_LOADING_DURATION) { - transitionToState(State::FADE_LOADING_SCREEN); - } -} - -// Actualiza el estado FADE_LOADING_SCREEN -void Title::updateFadeLoadingScreen(float delta_time) { - fade_accumulator_ += delta_time; - if (fade_accumulator_ >= FADE_STEP_INTERVAL) { - fade_accumulator_ = 0.0F; - if (loading_screen_surface_->fadeSubPalette()) { - transitionToState(State::MAIN_MENU); - } - } -} - // Actualiza el estado MAIN_MENU void Title::updateMainMenu(float delta_time) { - // Actualiza la marquesina - updateMarquee(delta_time); - // Si estamos en modo remap, manejar la lógica específica if (is_remapping_keyboard_ || is_remapping_joystick_) { // Decrementar cooldown de ejes si estamos capturando botones de joystick @@ -341,19 +228,11 @@ void Title::updateMainMenu(float delta_time) { } else { // Incrementa el temporizador solo en el menú principal normal state_time_ += delta_time; - - // Si el tiempo alcanza el timeout, va a créditos con fade - if (state_time_ >= MAIN_MENU_IDLE_TIMEOUT) { - exit_scene_ = SceneManager::Scene::CREDITS; - transitionToState(State::FADE_MENU); - } } } // Actualiza el estado CHEEVOS_MENU void Title::updateCheevosMenu(float delta_time) { - // Actualiza la marquesina (sigue visible en fondo) - updateMarquee(delta_time); // Determina la velocidad objetivo basada en el input float target_velocity = 0.0F; @@ -405,8 +284,6 @@ void Title::updateFadeMenu(float delta_time) { transitionToState(State::POST_FADE_MENU); } } - // Actualiza la marquesina (sigue visible en fondo) - updateMarquee(delta_time); } // Actualiza el estado POST_FADE_MENU @@ -561,19 +438,13 @@ void Title::fillTitleSurface() { case State::FADE_MENU: renderGameLogo(); renderMainMenu(); - renderMarquee(); + break; case State::CHEEVOS_MENU: renderGameLogo(); renderCheevosMenu(); - renderMarquee(); - break; - case State::SHOW_LOADING_SCREEN: - case State::FADE_LOADING_SCREEN: - loading_screen_sprite_->render(); - renderGameLogo(); break; default: diff --git a/source/game/scenes/title.hpp b/source/game/scenes/title.hpp index d05b1d0..f915f09 100644 --- a/source/game/scenes/title.hpp +++ b/source/game/scenes/title.hpp @@ -5,8 +5,6 @@ #include <array> // Para std::array #include <memory> // Para shared_ptr #include <string> // Para string -#include <vector> // Para vector - #include "game/scene_manager.hpp" // Para SceneManager::Scene #include "utils/delta_timer.hpp" // Para DeltaTimer class Sprite; // Forward declaration @@ -24,17 +22,7 @@ class Title { private: // --- Estructuras y enumeraciones --- - struct Glyph { - uint32_t codepoint{0}; // Codepoint Unicode del carácter - SDL_FRect clip{}; // Clip rect pre-calculado en el bitmap de fuente - float x{0.0F}; // Posición en el eje x (float para precisión con delta time) - float width{0.0F}; // Ancho pre-calculado del carácter - bool enabled{false}; // Solo se escriben y mueven si estan habilitadas - }; - enum class State { - SHOW_LOADING_SCREEN, - FADE_LOADING_SCREEN, MAIN_MENU, CHEEVOS_MENU, FADE_MENU, @@ -42,22 +30,13 @@ class Title { }; // --- Constantes de tiempo (en segundos) --- - static constexpr float SHOW_LOADING_DURATION = 5.0F; // Tiempo mostrando loading screen (antes 500 frames) static constexpr float FADE_STEP_INTERVAL = 0.05F; // Intervalo entre pasos de fade (antes cada 4 frames) static constexpr float POST_FADE_DELAY = 1.0F; // Delay después del fade (pantalla en negro) - static constexpr float MAIN_MENU_IDLE_TIMEOUT = 20.0F; // Timeout para ir a créditos (antes 2200 frames) static constexpr float KEYBOARD_REMAP_DISPLAY_DELAY = 2.0F; // Tiempo mostrando teclas definidas antes de guardar - static constexpr float MARQUEE_SPEED = 100.0F; // Velocidad de marquesina (pixels/segundo) static constexpr float CHEEVOS_SCROLL_MAX_SPEED = 180.0F; // Velocidad máxima de scroll de logros (pixels/segundo) static constexpr float CHEEVOS_SCROLL_ACCELERATION = 600.0F; // Aceleración del scroll (pixels/segundo²) static constexpr float CHEEVOS_SCROLL_DECELERATION = 800.0F; // Desaceleración del scroll (pixels/segundo²) - // --- Constantes de marquesina --- - static constexpr float MARQUEE_START_X = 256.0F; // Posición inicial (ancho pantalla) - static constexpr float MARQUEE_EXIT_X = -10.0F; // Cuando desaparece de pantalla - static constexpr float MARQUEE_Y = 184.0F; // Posición Y - static constexpr float MARQUEE_LETTER_SPACING = 1.0F; // Espaciado entre letras - // --- Métodos --- void update(); // Actualiza las variables void render(); // Dibuja en pantalla @@ -66,15 +45,10 @@ class Title { void handleInput(float delta_time); // Comprueba las entradas void updateState(float delta_time); // Actualiza el estado actual void transitionToState(State new_state); // Transiciona a un nuevo estado - void updateShowLoadingScreen(float delta_time); // Actualiza SHOW_LOADING_SCREEN - void updateFadeLoadingScreen(float delta_time); // Actualiza FADE_LOADING_SCREEN void updateMainMenu(float delta_time); // Actualiza MAIN_MENU void updateCheevosMenu(float delta_time); // Actualiza CHEEVOS_MENU void updateFadeMenu(float delta_time); // Actualiza FADE_MENU void updatePostFadeMenu(float delta_time); // Actualiza POST_FADE_MENU - void initMarquee(); // Inicializa la marquesina - void updateMarquee(float delta_time); // Actualiza la marquesina (time-based) - void renderMarquee() const; // Dibuja la marquesina void renderGameLogo(); // Dibuja el logo con el titulo del juego void renderMainMenu(); // Dibuja el menu principal void renderCheevosMenu(); // Dibuja el menu de logros @@ -97,21 +71,12 @@ class Title { // Objetos y punteros std::shared_ptr<Surface> game_logo_surface_; // Textura con los graficos std::unique_ptr<Sprite> game_logo_sprite_; // SSprite para manejar la surface - std::shared_ptr<Surface> loading_screen_surface_; // Surface con los gráficos de la pantalla de carga - std::unique_ptr<Sprite> loading_screen_sprite_; // SSprite con los gráficos de la pantalla de carga std::shared_ptr<Surface> cheevos_surface_; // Textura con la lista de logros std::unique_ptr<Sprite> cheevos_sprite_; // SSprite para manejar la surface con la lista de logros std::shared_ptr<Surface> title_surface_; // Surface donde se dibuja toda la clase std::unique_ptr<DeltaTimer> delta_timer_; // Timer para delta time - std::shared_ptr<Text> marquee_text_; // Texto para marquesina std::shared_ptr<Text> menu_text_; // Texto para los menus - // Variables de estado de marquesina - std::string long_text_; // Texto que aparece en la parte inferior del titulo - std::vector<Glyph> letters_; // Vector con las letras de la marquesina - int first_active_letter_{0}; // Primera letra activa (optimización) - int last_active_letter_{0}; // Última letra activa (optimización) - // Variables de estado del menú de logros SDL_FRect cheevos_surface_view_; // Zona visible de la surface con el listado de logros float cheevos_scroll_velocity_{0.0F}; // Velocidad actual del scroll de logros (pixels/segundo) diff --git a/source/game/ui/console_commands.cpp b/source/game/ui/console_commands.cpp index 38feff2..4ee4efb 100644 --- a/source/game/ui/console_commands.cpp +++ b/source/game/ui/console_commands.cpp @@ -494,21 +494,9 @@ static auto cmdDebug(const std::vector<std::string>& args) -> std::string { // } else if (args[2] == "LOGO") { target = SceneManager::Scene::LOGO; name = "logo"; - } else if (args[2] == "LOADING") { - target = SceneManager::Scene::LOADING_SCREEN; - name = "loading"; } else if (args[2] == "TITLE") { target = SceneManager::Scene::TITLE; name = "title"; - } else if (args[2] == "CREDITS") { - target = SceneManager::Scene::CREDITS; - name = "credits"; - } else if (args[2] == "ENDING") { - target = SceneManager::Scene::ENDING; - name = "ending"; - } else if (args[2] == "ENDING2") { - target = SceneManager::Scene::ENDING2; - name = "ending2"; } else { std::string scene_lower = args[2]; std::ranges::transform(scene_lower, scene_lower.begin(), ::tolower); @@ -651,7 +639,7 @@ static auto cmdItems(const std::vector<std::string>& args) -> std::string { return "Items: " + std::to_string(count); } -// SCENE [LOGO|LOADING|TITLE|CREDITS|GAME|ENDING|ENDING2|RESTART] +// SCENE [LOGO|TITLE|GAME|RESTART] static auto cmdScene(const std::vector<std::string>& args) -> std::string { if (Options::kiosk.enabled) { return "Not allowed in kiosk mode"; } if (args.empty()) { return "usage: scene [logo|loading|title|credits|game|ending|ending2|restart]"; } @@ -673,12 +661,8 @@ static auto cmdScene(const std::vector<std::string>& args) -> std::string { }; if (args[0] == "LOGO") { return GO_TO(SceneManager::Scene::LOGO, "Logo"); } - if (args[0] == "LOADING") { return GO_TO(SceneManager::Scene::LOADING_SCREEN, "Loading"); } if (args[0] == "TITLE") { return GO_TO(SceneManager::Scene::TITLE, "Title"); } - if (args[0] == "CREDITS") { return GO_TO(SceneManager::Scene::CREDITS, "Credits"); } if (args[0] == "GAME") { return GO_TO(SceneManager::Scene::GAME, "Game"); } - if (args[0] == "ENDING") { return GO_TO(SceneManager::Scene::ENDING, "Ending"); } - if (args[0] == "ENDING2") { return GO_TO(SceneManager::Scene::ENDING2, "Ending 2"); } return "Unknown scene: " + args[0]; }