// global_events.cpp - Implementació dels events globals // © 2026 JailDesigner #include "global_events.hpp" #include #include "core/input/define_inputs.hpp" #include "core/input/input.hpp" #include "core/input/mouse.hpp" #include "core/locale/locale.hpp" #include "core/rendering/sdl_manager.hpp" #include "core/system/notifier.hpp" #include "core/system/service_menu.hpp" #include "game/config_yaml.hpp" #include "scene_context.hpp" // Using declarations per simplificar el codi using SceneManager::SceneContext; using SceneType = SceneContext::SceneType; namespace GlobalEvents { namespace { // Reenvia el KEY_DOWN al menu de servei si esta obert i la tecla no // es F1-F12 ni ESC (que sempre passen com a globals). Retorna true si // el menu l'ha consumit. auto forwardToServiceMenu(const SDL_Event& event) -> bool { if (event.type != SDL_EVENT_KEY_DOWN) { return false; } auto* menu = System::ServiceMenu::get(); if (menu == nullptr || !menu->isOpen()) { return false; } const SDL_Scancode SC = event.key.scancode; const bool PASSTHROUGH = (SC == SDL_SCANCODE_ESCAPE) || (SC >= SDL_SCANCODE_F1 && SC <= SDL_SCANCODE_F12); if (PASSTHROUGH) { return false; } menu->handleEvent(event); return true; } // Si l'overlay de redefinicio esta actiu, engoleix tots els events. auto consumeIfDefineActive(const SDL_Event& event) -> bool { auto* di = System::DefineInputs::get(); if (di == nullptr || !di->isActive()) { return false; } di->handleEvent(event); return true; } // Botó MENU al mando d'algun jugador → alterna el menú de servei // (mateix comportament que F12 al teclat). Retorna true si l'event és // un GAMEPAD_BUTTON_DOWN consumit. auto handleGamepadMenuButton(const SDL_Event& event) -> bool { if (event.type != SDL_EVENT_GAMEPAD_BUTTON_DOWN) { return false; } auto* input = Input::get(); if (input == nullptr) { return false; } auto match_player = [&](int player_index) { auto pad = input->getPlayerGamepad(player_index); if (!pad || pad->instance_id != event.gbutton.which) { return false; } auto it = pad->bindings.find(InputAction::MENU); if (it == pad->bindings.end()) { return false; } return it->second.button == static_cast(event.gbutton.button); }; if (!match_player(0) && !match_player(1)) { return false; } if (auto* menu = System::ServiceMenu::get(); menu != nullptr) { menu->toggle(); } return true; } } // namespace auto handle(const SDL_Event& event, SDLManager& sdl, SceneContext& context) -> bool { // 1. Permitir que Input procese el evento (para hotplug de gamepads) auto event_msg = Input::get()->handleEvent(event); if (!event_msg.empty()) { std::cout << "[Input] " << event_msg << '\n'; } // 1b. Si l'overlay de redefinicio esta actiu, engoleix tots els events // (cap arriba al joc, al menu de servei ni als hotkeys F1-F12). if (consumeIfDefineActive(event)) { return true; } // 2. Procesar SDL_EVENT_QUIT directamente (no es input de juego) if (event.type == SDL_EVENT_QUIT) { context.setNextScene(SceneType::EXIT); SceneManager::actual = SceneType::EXIT; return true; } // 3. Gestió del ratolí (auto-ocultar) Mouse::handleEvent(event); // 3b. Botó MENU al mando (equivalent a F12) if (handleGamepadMenuButton(event)) { return true; } // 4. Service Menu (F12): consumeix tot KEY_DOWN excepte tecles de // funció (F1-F12) i ESC, que continuen sent globals (zoom, fullscreen, // vsync, AA, postfx, locale, exit prompt). Aixi el menu captura // ENTER/BACKSPACE/UP/DOWN/LEFT/RIGHT i lletres mentre esta obert. if (forwardToServiceMenu(event)) { return true; } // 5. Procesar acciones globales directamente desde eventos SDL // (NO usar Input::checkAction() para evitar desfase de timing) if (event.type == SDL_EVENT_KEY_DOWN) { switch (event.key.scancode) { case SDL_SCANCODE_F1: sdl.decreaseWindowSize(); return true; case SDL_SCANCODE_F2: sdl.increaseWindowSize(); return true; case SDL_SCANCODE_F3: sdl.toggleFullscreen(); return true; case SDL_SCANCODE_F4: sdl.toggleVSync(); return true; case SDL_SCANCODE_F5: sdl.toggleAntialias(); return true; case SDL_SCANCODE_F6: sdl.togglePostFx(); return true; case SDL_SCANCODE_F7: { // Toggle d'idioma en runtime entre català i anglès. Els // strings ja capturats (toast actiu, banner stage start) // sobreviuen fins al seu cicle; la resta (HUD, pantalles, // pròxims toasts) es refresquen al següent frame perquè // criden Locale::text() cada draw. const std::string NEW_LANG = (ConfigYaml::engine_config.locale == "ca") ? "en" : "ca"; if (Locale::get().switchTo(NEW_LANG)) { ConfigYaml::engine_config.locale = NEW_LANG; ConfigYaml::saveToFile(); if (auto* notifier = System::Notifier::get(); notifier != nullptr) { notifier->notifyInfo(localeSubstitute( Locale::get().text("notification.locale_switched"), "{lang}", Locale::get().text("language." + NEW_LANG))); } } return true; } case SDL_SCANCODE_F12: { // Toggle del menu de servei. Sempre passa com a global // (alterna obert/tancat des de qualsevol escena). if (auto* menu = System::ServiceMenu::get(); menu != nullptr) { menu->toggle(); } return true; } case SDL_SCANCODE_ESCAPE: { // Doble pulsació per confirmar sortida: la primera ESC // dispara un toast d'avís; només si aquest toast concret // (isExitPromptActive) segueix visible, la segona ESC // tanca el joc. Si la notificació activa és una altra // (zoom, fullscreen, vsync...), ESC obre el prompt de // sortida en lloc de tancar. auto* notifier = System::Notifier::get(); if (notifier != nullptr && !notifier->isExitPromptActive()) { notifier->notifyExit(Locale::get().text("notification.press_again_exit")); return true; } // Notifier inexistent (degradació elegant) o segona ESC // sobre el prompt de sortida: tanquem el joc. context.setNextScene(SceneType::EXIT); SceneManager::actual = SceneType::EXIT; return true; } default: // Tecla no global break; } } return false; // Event no processat } } // namespace GlobalEvents