diff --git a/data/lang/ba_BA.txt b/data/lang/ba_BA.txt index b71b671..5a1d826 100644 --- a/data/lang/ba_BA.txt +++ b/data/lang/ba_BA.txt @@ -140,7 +140,7 @@ CONTINUAR? CONTINUAR ## 47 - MENU DE PAUSA -EIXIR DEL JOC +TORNAR AL TITOL ## 48 - MENU GAME OVER SI @@ -299,4 +299,7 @@ Finestra Shader ## 100 - NOTIFICACIO HOTKEY -Preset \ No newline at end of file +Preset + +## 101 - NOTIFICACIO HOTKEY +Torna a premer ESC per a eixir \ No newline at end of file diff --git a/data/lang/en_UK.txt b/data/lang/en_UK.txt index 079b427..de41965 100644 --- a/data/lang/en_UK.txt +++ b/data/lang/en_UK.txt @@ -140,7 +140,7 @@ CONTINUE? CONTINUE ## 47 - MENU DE PAUSA -LEAVE GAME +BACK TO TITLE ## 48 - MENU GAME OVER YES @@ -299,4 +299,7 @@ Window Shader ## 100 - HOTKEY NOTIFICATION -Preset \ No newline at end of file +Preset + +## 101 - HOTKEY NOTIFICATION +Press ESC again to quit \ No newline at end of file diff --git a/data/lang/es_ES.txt b/data/lang/es_ES.txt index ba8335e..bb546ce 100644 --- a/data/lang/es_ES.txt +++ b/data/lang/es_ES.txt @@ -140,7 +140,7 @@ CONTINUAR? CONTINUAR ## 47 - MENU DE PAUSA -SALIR DEL JUEGO +VOLVER AL TITULO ## 48 - MENU GAME OVER SI @@ -299,4 +299,7 @@ Ventana Shader ## 100 - NOTIFICACION HOTKEY -Preset \ No newline at end of file +Preset + +## 101 - NOTIFICACION HOTKEY +Vuelve a pulsar ESC para salir \ No newline at end of file diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index de6c1ba..d4ebc6c 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -17,13 +17,24 @@ namespace GlobalInputs { constexpr int LANG_WINDOW = 98; constexpr int LANG_SHADER = 99; constexpr int LANG_PRESET = 100; + constexpr int LANG_EXIT_CONFIRM = 101; constexpr Uint32 NOTIFY_MS = 1500; + constexpr Uint32 EXIT_CONFIRM_MS = 2000; const Color BLACK = {0x00, 0x00, 0x00}; const Color CYAN = {0x00, 0xFF, 0xFF}; const Color YELLOW = {0xFF, 0xE0, 0x40}; const Color MAGENTA = {0xFF, 0x00, 0xFF}; const Color GREEN = {0x00, 0xFF, 0x80}; + const Color RED = {0xFF, 0x40, 0x40}; + + // Patró de doble pulsació: la primera pulsació d'EXIT mostra una + // notificació en vermell i obre una finestra de confirmació; una + // segona pulsació dins la finestra activa `quit_requested`. La + // finestra coincideix amb la durada del missatge perquè usuari i + // sistema sempre estiguin sincronitzats. + Uint32 exit_window_until_ticks = 0; + bool quit_requested = false; void notifyZoom() { const std::string MSG = Lang::get()->getText(LANG_ZOOM) + " " + std::to_string(Options::window.zoom) + "x"; @@ -51,11 +62,25 @@ namespace GlobalInputs { const std::string MSG = Lang::get()->getText(LANG_PRESET) + " " + Screen::get()->getCurrentPresetName(); Screen::get()->notify(MSG, GREEN, BLACK, NOTIFY_MS); } + + void onExit() { + const Uint32 NOW = SDL_GetTicks(); + if (NOW < exit_window_until_ticks) { + quit_requested = true; + return; + } + exit_window_until_ticks = NOW + EXIT_CONFIRM_MS; + Screen::get()->notify(Lang::get()->getText(LANG_EXIT_CONFIRM), RED, BLACK, EXIT_CONFIRM_MS); + } } // namespace auto handle() -> bool { if (Screen::get() == nullptr || Input::get() == nullptr) { return false; } + if (Input::get()->checkInput(Input::Action::EXIT, Input::Repeat::OFF)) { + onExit(); + return true; + } if (Input::get()->checkInput(Input::Action::WINDOW_FULLSCREEN, Input::Repeat::OFF)) { Screen::get()->toggleVideoMode(); notifyFullscreen(); @@ -95,4 +120,8 @@ namespace GlobalInputs { return false; } + auto wantsQuit() -> bool { + return quit_requested; + } + } // namespace GlobalInputs diff --git a/source/core/input/global_inputs.hpp b/source/core/input/global_inputs.hpp index c0800a9..b8b4f1d 100644 --- a/source/core/input/global_inputs.hpp +++ b/source/core/input/global_inputs.hpp @@ -3,9 +3,14 @@ namespace GlobalInputs { // Gestiona els atalls globals disponibles en qualsevol escena: zoom de // finestra (F1/F2), fullscreen (F3), toggle shader (F4), tipus de shader - // POSTFX↔CRTPI (F5) i següent preset (F6). Cada hotkey emet una + // POSTFX↔CRTPI (F5), següent preset (F6) i la confirmació d'eixida amb + // ESC (Action::EXIT) en dues pulsacions. Cada hotkey emet una // notificació localitzada. Retorna true si ha consumit alguna tecla (per // si la capa cridant vol suprimir-la del processament específic de // l'escena). auto handle() -> bool; + + // True si la doble pulsació d'ESC s'ha confirmat. Director consulta açò + // a iterate() per a posar `section_->name = SECTION_PROG_QUIT`. + [[nodiscard]] auto wantsQuit() -> bool; } // namespace GlobalInputs diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 59064ed..bb0abef 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -16,13 +16,14 @@ #include #include // for basic_string, operator+, char_t... -#include "core/audio/audio.hpp" // for Audio::init, Audio::destroy -#include "core/input/input.h" // for Input, InputAction -#include "core/input/mouse.hpp" // for Mouse::handleEvent, Mouse::upda... -#include "core/locale/lang.h" // for Lang, Lang::Code -#include "core/rendering/screen.h" // for Screen -#include "core/rendering/texture.h" // for Texture -#include "core/resources/asset.h" // for Asset, Asset::Type +#include "core/audio/audio.hpp" // for Audio::init, Audio::destroy +#include "core/input/global_inputs.hpp" // for GlobalInputs::wantsQuit +#include "core/input/input.h" // for Input, InputAction +#include "core/input/mouse.hpp" // for Mouse::handleEvent, Mouse::upda... +#include "core/locale/lang.h" // for Lang, Lang::Code +#include "core/rendering/screen.h" // for Screen +#include "core/rendering/texture.h" // for Texture +#include "core/resources/asset.h" // for Asset, Asset::Type #include "core/resources/resource.h" #include "core/resources/resource_helper.h" #include "game/defaults.hpp" // for SECTION_PROG_LOGO, GAMECANVAS_H... @@ -218,9 +219,12 @@ void Director::initInput() { // Teclado - Otros Input::get()->bindKey(Input::Action::ACCEPT, SDL_SCANCODE_RETURN); - Input::get()->bindKey(Input::Action::CANCEL, SDL_SCANCODE_ESCAPE); - Input::get()->bindKey(Input::Action::PAUSE, SDL_SCANCODE_ESCAPE); + // ESC només dispara EXIT (gestionat globalment per GlobalInputs com a + // confirmació de doble pulsació). PAUSE i CANCEL tenen tecles dedicades + // perquè cap escena ha de tractar ESC localment. Input::get()->bindKey(Input::Action::EXIT, SDL_SCANCODE_ESCAPE); + Input::get()->bindKey(Input::Action::CANCEL, SDL_SCANCODE_BACKSPACE); + Input::get()->bindKey(Input::Action::PAUSE, SDL_SCANCODE_F12); Input::get()->bindKey(Input::Action::WINDOW_DEC_ZOOM, SDL_SCANCODE_F1); Input::get()->bindKey(Input::Action::WINDOW_INC_ZOOM, SDL_SCANCODE_F2); Input::get()->bindKey(Input::Action::WINDOW_FULLSCREEN, SDL_SCANCODE_F3); @@ -579,6 +583,13 @@ void Director::handleSectionTransition() { // Ejecuta un frame del juego auto Director::iterate() -> SDL_AppResult { +#ifndef __EMSCRIPTEN__ + // Doble pulsació d'ESC confirmada des de qualsevol escena. + if (GlobalInputs::wantsQuit()) { + section_->name = SECTION_PROG_QUIT; + } +#endif + #ifdef __EMSCRIPTEN__ // En WASM no se puede salir: reinicia al logo if (section->name == SECTION_PROG_QUIT) { diff --git a/source/game/game.cpp b/source/game/game.cpp index b024d22..10d2904 100644 --- a/source/game/game.cpp +++ b/source/game/game.cpp @@ -2809,6 +2809,16 @@ void Game::updatePauseMenuUI() { pause_menu_->update(); pause_menu_->checkInput(); + // F12 (Action::PAUSE) també tanca el menú de pausa — mateix comportament + // que seleccionar "Continue" / cancel·lar amb BACKSPACE. + if (Input::get()->checkInput(Input::Action::PAUSE, Input::Repeat::OFF)) { + leaving_pause_menu_ = true; + if (!Options::gameplay.pause_countdown) { + pause_counter_ = 0; + } + return; + } + switch (pause_menu_->getItemSelected()) { case 1: leaving_pause_menu_ = true; @@ -2884,15 +2894,6 @@ void Game::enterPausedGame() { Audio::get()->pauseMusic(); } - // ESC esta vinculada a PAUSE, CANCEL y EXIT a la vez (director.cpp), y cada - // Action tiene su propio flag de edge-trigger. La pulsacion que nos ha - // traido aqui solo ha actualizado el flag de PAUSE; CANCEL y EXIT siguen - // a false y dispararian un falso flanco la primera vez que el menu de - // pausa los lea. Hacemos una lectura sincronizadora descartada para - // ponerlos al dia con el estado real de la tecla. - Input::get()->checkInput(Input::Action::CANCEL, Input::Repeat::OFF); - Input::get()->checkInput(Input::Action::EXIT, Input::Repeat::OFF); - // Reinicia el menu pause_menu_->reset(); leaving_pause_menu_ = false; diff --git a/source/game/scenes/instructions.cpp b/source/game/scenes/instructions.cpp index 88c2843..1cb8a73 100644 --- a/source/game/scenes/instructions.cpp +++ b/source/game/scenes/instructions.cpp @@ -210,13 +210,8 @@ void Instructions::checkEvents() { // Comprueba las entradas void Instructions::checkInput() { -#ifndef __EMSCRIPTEN__ - if (Input::get()->checkInput(Input::Action::EXIT, Input::Repeat::OFF)) { - quit_requested_ = true; - finished_ = true; - return; - } -#endif + // ESC (Action::EXIT) ja el gestiona GlobalInputs::handle() amb doble + // pulsació; el quit es propaga via Director::iterate. if (GlobalInputs::handle()) { return; } if (Input::get()->checkInput(Input::Action::PAUSE, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::ACCEPT, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_LEFT, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_CENTER, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_RIGHT, Input::Repeat::OFF)) { diff --git a/source/game/scenes/intro.cpp b/source/game/scenes/intro.cpp index 663e362..4955887 100644 --- a/source/game/scenes/intro.cpp +++ b/source/game/scenes/intro.cpp @@ -166,12 +166,8 @@ Intro::~Intro() { // Comprueba las entradas void Intro::checkInput() { -#ifndef __EMSCRIPTEN__ - if (Input::get()->checkInput(Input::Action::EXIT, Input::Repeat::OFF)) { - section_->name = SECTION_PROG_QUIT; - return; - } -#endif + // ESC (Action::EXIT) ja el gestiona GlobalInputs::handle() amb doble + // pulsació; el quit es propaga via Director::iterate. if (GlobalInputs::handle()) { return; } if (Input::get()->checkInput(Input::Action::PAUSE, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::ACCEPT, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_LEFT, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_CENTER, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_RIGHT, Input::Repeat::OFF)) { diff --git a/source/game/scenes/logo.cpp b/source/game/scenes/logo.cpp index b9791fe..ea30fa4 100644 --- a/source/game/scenes/logo.cpp +++ b/source/game/scenes/logo.cpp @@ -56,12 +56,8 @@ void Logo::checkLogoEnd() { // Comprueba las entradas void Logo::checkInput() { -#ifndef __EMSCRIPTEN__ - if (Input::get()->checkInput(Input::Action::EXIT, Input::Repeat::OFF)) { - section_->name = SECTION_PROG_QUIT; - return; - } -#endif + // ESC (Action::EXIT) ja el gestiona GlobalInputs::handle() amb doble + // pulsació; el quit es propaga via Director::iterate. if (GlobalInputs::handle()) { return; } if (Input::get()->checkInput(Input::Action::PAUSE, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::ACCEPT, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_LEFT, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_CENTER, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_RIGHT, Input::Repeat::OFF)) { diff --git a/source/game/scenes/title.cpp b/source/game/scenes/title.cpp index cbdc0c5..b1f157a 100644 --- a/source/game/scenes/title.cpp +++ b/source/game/scenes/title.cpp @@ -619,14 +619,10 @@ void Title::render() { } } -// Comprueba las entradas +// Comprueba las entradas. ESC (Action::EXIT) ja el gestiona +// GlobalInputs::handle() amb doble pulsació; el quit es propaga via +// Director::iterate. void Title::checkInput() { -#ifndef __EMSCRIPTEN__ - if (Input::get()->checkInput(Input::Action::EXIT, Input::Repeat::OFF)) { - section_->name = SECTION_PROG_QUIT; - return; - } -#endif GlobalInputs::handle(); } diff --git a/source/game/scenes/title.h b/source/game/scenes/title.h index 09b6c15..b8d5a21 100644 --- a/source/game/scenes/title.h +++ b/source/game/scenes/title.h @@ -74,17 +74,17 @@ class Title { // Variables Ja::Music *title_music_; // Musica para el titulo Ja::Sound *crash_sound_; // Sonido con el impacto del título - int background_counter_; // Temporizador para el fondo de tiles de la pantalla de titulo - int counter_; // Temporizador para la pantalla de titulo - Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa - Uint8 background_mode_; // Variable para almacenar el tipo de efecto que hará el fondo de la pantalla de titulo - float sin_[360]; // Vector con los valores del seno precalculados - bool menu_visible_; // Indicador para saber si se muestra el menu del titulo o la frase intermitente - bool demo_; // Indica si el modo demo estará activo - Section next_section_; // Indica cual es la siguiente sección a cargar cuando termine el contador del titulo - Uint32 ticks_speed_; // Velocidad a la que se repiten los bucles del programa - Uint8 post_fade_; // Opción a realizar cuando termina el fundido - MenuData menu_; // Variable con todos los objetos menus y sus variables + int background_counter_; // Temporizador para el fondo de tiles de la pantalla de titulo + int counter_; // Temporizador para la pantalla de titulo + Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa + Uint8 background_mode_; // Variable para almacenar el tipo de efecto que hará el fondo de la pantalla de titulo + float sin_[360]; // Vector con los valores del seno precalculados + bool menu_visible_; // Indicador para saber si se muestra el menu del titulo o la frase intermitente + bool demo_; // Indica si el modo demo estará activo + Section next_section_; // Indica cual es la siguiente sección a cargar cuando termine el contador del titulo + Uint32 ticks_speed_; // Velocidad a la que se repiten los bucles del programa + Uint8 post_fade_; // Opción a realizar cuando termina el fundido + MenuData menu_; // Variable con todos los objetos menus y sus variables // Snapshot per a permetre CANCEL al menú d'opcions. Options::Video prev_video_; Options::Window prev_window_; @@ -105,10 +105,10 @@ class Title { Instructions::Mode instructions_mode_{Instructions::Mode::AUTO}; // Modo de las instrucciones activas bool demo_then_instructions_; // Indica si tras la demo hay que mostrar instrucciones - void init(); // Inicializa los valores - void update(); // Actualiza las variables del objeto - void render(); // Dibuja el objeto en pantalla - void checkInput(); // Comprueba las entradas + void init(); // Inicializa los valores + void update(); // Actualiza las variables del objeto + void render(); // Dibuja el objeto en pantalla + static void checkInput(); // Comprueba las entradas (només delega a GlobalInputs) // Helpers de update, uno por cada subsección y por cada switch dentro del título 3 void updateTitle1();