diff --git a/Makefile b/Makefile index 0997b24..e589e99 100644 --- a/Makefile +++ b/Makefile @@ -302,6 +302,22 @@ wasm: cp build/wasm/$(TARGET_NAME).data $(DIST_DIR)/wasm/ @echo "Output: $(DIST_DIR)/wasm/" +# Versió Debug del build wasm: arrenca directament a la GAME (sense logo/loading/title) +# i activa l'editor i la consola. Sortida a dist/wasm_debug/. +wasm_debug: + @echo "Compilando WebAssembly Debug - Version: $(VERSION)" + docker run --rm \ + -v $(DIR_ROOT):/src \ + -w /src \ + emscripten/emsdk:latest \ + bash -c "emcmake cmake -S . -B build/wasm_debug -DCMAKE_BUILD_TYPE=Debug && cmake --build build/wasm_debug" + $(MKDIR) "$(DIST_DIR)/wasm_debug" + cp build/wasm_debug/$(TARGET_NAME).html $(DIST_DIR)/wasm_debug/ + cp build/wasm_debug/$(TARGET_NAME).js $(DIST_DIR)/wasm_debug/ + cp build/wasm_debug/$(TARGET_NAME).wasm $(DIST_DIR)/wasm_debug/ + cp build/wasm_debug/$(TARGET_NAME).data $(DIST_DIR)/wasm_debug/ + @echo "Output: $(DIST_DIR)/wasm_debug/" + # ============================================================================== # REGLAS ESPECIALES # ============================================================================== @@ -324,6 +340,7 @@ help: @echo " make linux_release - Crear release para Linux" @echo " make macos_release - Crear release para macOS" @echo " make wasm - Crear release per a WebAssembly (requereix Docker)" + @echo " make wasm_debug - Crear build Debug per a WebAssembly (entra directe a la GAME)" @echo "" @echo " Herramientas:" @echo " make compile_shaders - Compilar shaders SPIR-V" @@ -334,4 +351,4 @@ help: @echo " make show_version - Mostrar version actual ($(VERSION))" @echo " make help - Mostrar esta ayuda" -.PHONY: all debug release windows_release macos_release linux_release wasm compile_shaders pack_tool resources.pack show_version help +.PHONY: all debug release windows_release macos_release linux_release wasm wasm_debug compile_shaders pack_tool resources.pack show_version help diff --git a/data/locale/ca.yaml b/data/locale/ca.yaml index 47a059d..7a1814e 100644 --- a/data/locale/ca.yaml +++ b/data/locale/ca.yaml @@ -8,6 +8,7 @@ title: keyboard: "2. REDEFINIR TECLES" joystick: "3. REDEFINIR MANDO" projects: "4. PROJECTES" + press_to_play: "PREM PER JUGAR" keys: prompt0: "PREM UNA TECLA PER A ESQUERRA" prompt1: "PREM UNA TECLA PER A DRETA" @@ -103,6 +104,8 @@ achievements: ui: press_again_menu: "PREM DE NOU PER TORNAR AL MENÚ" press_again_exit: "PREM DE NOU PER EIXIR" + gamepad_connected: "CONNECTAT" + gamepad_disconnected: "DESCONNECTAT" border_enabled: "VORA ACTIVADA" border_disabled: "VORA DESACTIVADA" fullscreen_enabled: "PANTALLA COMPLETA ACTIVADA" diff --git a/data/locale/en.yaml b/data/locale/en.yaml index 3d87fac..e77cb63 100644 --- a/data/locale/en.yaml +++ b/data/locale/en.yaml @@ -8,6 +8,7 @@ title: keyboard: "2. REDEFINE KEYBOARD" joystick: "3. REDEFINE JOYSTICK" projects: "4. PROJECTS" + press_to_play: "PRESS TO PLAY" keys: prompt0: "PRESS KEY FOR LEFT" prompt1: "PRESS KEY FOR RIGHT" @@ -103,6 +104,8 @@ achievements: ui: press_again_menu: "PRESS AGAIN TO RETURN TO MENU" press_again_exit: "PRESS AGAIN TO EXIT" + gamepad_connected: "CONNECTED" + gamepad_disconnected: "DISCONNECTED" border_enabled: "BORDER ENABLED" border_disabled: "BORDER DISABLED" fullscreen_enabled: "FULLSCREEN ENABLED" diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index 879d515..7c284d6 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -9,6 +9,7 @@ #include "core/locale/locale.hpp" // Para Locale #include "core/rendering/render_info.hpp" // Para RenderInfo #include "core/rendering/screen.hpp" // Para Screen +#include "core/system/global_events.hpp" // Para GlobalEvents::consumeGamepadButtonPressed #include "game/options.hpp" // Para Options, options, OptionsVideo, Section #include "game/scene_manager.hpp" // Para SceneManager #include "game/ui/console.hpp" // Para Console @@ -152,6 +153,12 @@ namespace GlobalInputs { // Detecta qué acción global ha sido presionada (si alguna) auto getPressedAction() -> InputAction { // NOLINT(readability-function-cognitive-complexity) + // Qualsevol botó del comandament actua com a ACCEPT (saltar escenes + // d'attract mode: logo, loading, credits, demo, ending...). Es prioritza + // sobre EXIT perquè s'envia com a flag d'event, no com a check d'acció. + if (GlobalEvents::consumeGamepadButtonPressed()) { + return InputAction::ACCEPT; + } if (Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) { return InputAction::EXIT; } diff --git a/source/core/input/input.cpp b/source/core/input/input.cpp index 7c2e949..adb3475 100644 --- a/source/core/input/input.cpp +++ b/source/core/input/input.cpp @@ -421,7 +421,13 @@ auto Input::addGamepad(int device_index) -> std::string { // NOLINT(readability auto name = gamepad->name; std::cout << "Gamepad connected (" << name << ")" << '\n'; gamepads_.push_back(std::move(gamepad)); - return name + " CONNECTED"; + + // Aplica els bindings d'Options al nou gamepad (en hot-plug/wasm el ctor + // ja ha cridat applyGamepadBindingsFromOptions però llavors gamepads_ + // estava buit i no s'ha fet res). + applyGamepadBindingsFromOptions(); + + return name; } auto Input::removeGamepad(SDL_JoystickID id) -> std::string { // NOLINT(readability-convert-member-functions-to-static) @@ -433,7 +439,7 @@ auto Input::removeGamepad(SDL_JoystickID id) -> std::string { // NOLINT(readabi std::string name = (*it)->name; std::cout << "Gamepad disconnected (" << name << ")" << '\n'; gamepads_.erase(it); - return name + " DISCONNECTED"; + return name; } std::cerr << "No se encontró el gamepad con ID " << id << '\n'; return {}; diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index c0b8a74..d06deb5 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -11,19 +11,19 @@ #include // Para istreambuf_iterator, operator== #include // Para char_traits, string, operator+, operator== -#include "core/input/mouse.hpp" // Para updateCursorVisibility -#include "core/rendering/render_info.hpp" // Para RenderInfo +#include "core/input/mouse.hpp" // Para updateCursorVisibility +#include "core/rendering/render_info.hpp" // Para RenderInfo #ifndef __EMSCRIPTEN__ #include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader (no suportat a WebGL2) #endif -#include "core/rendering/surface.hpp" // Para Surface, readPalFile -#include "core/rendering/text.hpp" // Para Text -#include "core/resources/resource_cache.hpp" // Para Resource -#include "core/resources/resource_helper.hpp" // Para ResourceHelper -#include "core/resources/resource_list.hpp" // Para Asset, AssetType -#include "game/options.hpp" // Para Options, options, OptionsVideo, Border -#include "game/ui/console.hpp" // Para Console -#include "game/ui/notifier.hpp" // Para Notifier +#include "core/rendering/surface.hpp" // Para Surface, readPalFile +#include "core/rendering/text.hpp" // Para Text +#include "core/resources/resource_cache.hpp" // Para Resource +#include "core/resources/resource_helper.hpp" // Para ResourceHelper +#include "core/resources/resource_list.hpp" // Para Asset, AssetType +#include "game/options.hpp" // Para Options, options, OptionsVideo, Border +#include "game/ui/console.hpp" // Para Console +#include "game/ui/notifier.hpp" // Para Notifier // [SINGLETON] Screen* Screen::screen = nullptr; diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 163106a..2c12eb4 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -152,7 +152,9 @@ Director::Director() { // i desactivem el borde per aprofitar al màxim l'espai del canvas. Options::video.fullscreen = false; Options::window.zoom = 4; - Options::video.border.enabled = false; + Options::video.border.enabled = true; + Options::video.border.height = 8; + Options::video.border.width = 8; #endif // Configura la ruta y carga los presets de PostFX @@ -197,9 +199,15 @@ Director::Director() { #ifdef _DEBUG Debug::init(); +#ifdef __EMSCRIPTEN__ + // A wasm el debug.yaml viu a SYSTEM_FOLDER (MEMFS no persistent) i no està + // disponible. Saltem el loadFromFile i entrem directament a la GAME. + SceneManager::current = SceneManager::Scene::GAME; +#else Debug::get()->setDebugFile(Resource::List::get()->get("debug.yaml")); Debug::get()->loadFromFile(); SceneManager::current = Debug::get()->getInitialScene(); +#endif MapEditor::init(); #endif diff --git a/source/core/system/director.hpp b/source/core/system/director.hpp index 92b47a4..efb939c 100644 --- a/source/core/system/director.hpp +++ b/source/core/system/director.hpp @@ -22,8 +22,8 @@ class Director { std::string executable_path_; // Path del ejecutable std::string system_folder_; // Carpeta del sistema donde guardar datos - std::unique_ptr active_scene_; // Escena activa - SceneManager::Scene current_scene_{SceneManager::Scene::LOGO}; // Tipus d'escena activa + std::unique_ptr active_scene_; // Escena activa + SceneManager::Scene current_scene_{SceneManager::Scene::LOGO}; // Tipus d'escena activa // --- Funciones --- void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema donde guardar datos diff --git a/source/core/system/global_events.cpp b/source/core/system/global_events.cpp index b9b7a74..e4e3c2d 100644 --- a/source/core/system/global_events.cpp +++ b/source/core/system/global_events.cpp @@ -1,10 +1,20 @@ #include "core/system/global_events.hpp" +#include "core/input/input.hpp" // Para Input (gamepad add/remove) #include "core/input/mouse.hpp" -#include "game/options.hpp" // Para Options, options, OptionsGame, OptionsAudio -#include "game/ui/console.hpp" // Para Console +#include "core/locale/locale.hpp" // Para Locale +#include "game/options.hpp" // Para Options, options, OptionsGame, OptionsAudio +#include "game/ui/console.hpp" // Para Console +#include "game/ui/notifier.hpp" // Para Notifier namespace GlobalEvents { + + namespace { + // Flag per saber si en aquest frame s'ha rebut un button down del gamepad. + // El consumeix GlobalInputs perquè un botó del comandament salti escenes. + bool gamepad_button_pressed_ = false; + } // namespace + // Comprueba los eventos que se pueden producir en cualquier sección del juego. // Nota: SDL_EVENT_QUIT el gestiona Director::handleEvent() directament. void handle(const SDL_Event& event) { @@ -12,6 +22,28 @@ namespace GlobalEvents { // reLoadTextures(); } + // Connexió/desconnexió de gamepads: cal enrutar-los a Input perquè + // afegisca el dispositiu a gamepads_. Sense això, en wasm els gamepads + // mai es detecten (la Gamepad API del navegador només els exposa + // després que l'usuari els active, més tard que el discoverGamepads + // inicial). En desktop també arregla la connexió en calent. + if (event.type == SDL_EVENT_GAMEPAD_ADDED || event.type == SDL_EVENT_GAMEPAD_REMOVED) { + if (Input::get() != nullptr) { + std::string name = Input::get()->handleEvent(event); + if (!name.empty() && Notifier::get() != nullptr && Locale::get() != nullptr) { + const std::string KEY = (event.type == SDL_EVENT_GAMEPAD_ADDED) + ? "ui.gamepad_connected" + : "ui.gamepad_disconnected"; + Notifier::get()->show({name + " " + Locale::get()->get(KEY)}); + } + } + } + + // Marcar polsació de qualsevol botó del comandament (els consumirà GlobalInputs). + if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { + gamepad_button_pressed_ = true; + } + // Enrutar eventos de texto a la consola cuando está activa if (Console::get() != nullptr && Console::get()->isActive()) { if (event.type == SDL_EVENT_TEXT_INPUT || event.type == SDL_EVENT_KEY_DOWN) { @@ -22,4 +54,10 @@ namespace GlobalEvents { Mouse::handleEvent(event); } + + auto consumeGamepadButtonPressed() -> bool { + const bool RESULT = gamepad_button_pressed_; + gamepad_button_pressed_ = false; + return RESULT; + } } // namespace GlobalEvents \ No newline at end of file diff --git a/source/core/system/global_events.hpp b/source/core/system/global_events.hpp index ebc5cf3..4b0c2d3 100644 --- a/source/core/system/global_events.hpp +++ b/source/core/system/global_events.hpp @@ -5,4 +5,9 @@ namespace GlobalEvents { // Comprueba los eventos que se pueden producir en cualquier sección del juego void handle(const SDL_Event& event); + + // True si en aquest frame s'ha rebut un SDL_EVENT_GAMEPAD_BUTTON_DOWN. + // Es consumeix (i es reseteja) per GlobalInputs::getPressedAction perquè + // qualsevol botó del comandament actuï com a "ACCEPT" (saltar escena). + auto consumeGamepadButtonPressed() -> bool; } // namespace GlobalEvents \ No newline at end of file diff --git a/source/game/scenes/credits.hpp b/source/game/scenes/credits.hpp index 482a91e..968307f 100644 --- a/source/game/scenes/credits.hpp +++ b/source/game/scenes/credits.hpp @@ -2,12 +2,12 @@ #include -#include // Para shared_ptr -#include // Para string -#include // Para vector +#include // Para shared_ptr +#include // Para string +#include // Para vector #include "game/scenes/scene.hpp" // Para Scene -class AnimatedSprite; // lines 11-11 +class AnimatedSprite; // lines 11-11 class Surface; class PixelReveal; class DeltaTimer; diff --git a/source/game/scenes/ending.hpp b/source/game/scenes/ending.hpp index ee097e0..b8e81f5 100644 --- a/source/game/scenes/ending.hpp +++ b/source/game/scenes/ending.hpp @@ -7,8 +7,8 @@ #include // Para vector #include "game/scenes/scene.hpp" // Para Scene -class Sprite; // lines 8-8 -class Surface; // lines 9-9 +class Sprite; // lines 8-8 +class Surface; // lines 9-9 class PixelReveal; class DeltaTimer; diff --git a/source/game/scenes/game_over.hpp b/source/game/scenes/game_over.hpp index b96337b..4ad9e7e 100644 --- a/source/game/scenes/game_over.hpp +++ b/source/game/scenes/game_over.hpp @@ -2,12 +2,12 @@ #include -#include // Para shared_ptr -#include // Para vector +#include // Para shared_ptr +#include // Para vector #include "game/scenes/scene.hpp" // Para Scene -class AnimatedSprite; // lines 7-7 -class DeltaTimer; // Forward declaration +class AnimatedSprite; // lines 7-7 +class DeltaTimer; // Forward declaration class GameOver : public Scene { public: @@ -51,9 +51,9 @@ class GameOver : public Scene { void update(); // Actualiza el objeto void render(); // Dibuja el final en pantalla 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 + 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 diff --git a/source/game/scenes/title.cpp b/source/game/scenes/title.cpp index 59e0ba7..3baf982 100644 --- a/source/game/scenes/title.cpp +++ b/source/game/scenes/title.cpp @@ -92,6 +92,15 @@ void Title::handleEvent(const SDL_Event& event) { return; // No procesar más este evento } + // Qualsevol botó del comandament al menú principal inicia partida directament + // (els bindings ja estan definits, no cal "pulsar 1" amb el teclat). + if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN && + state_ == State::MAIN_MENU && + !is_remapping_keyboard_ && !is_remapping_joystick_) { + handleMainMenuKeyPress(SDLK_1); // PLAY + return; + } + if (event.type == SDL_EVENT_KEY_DOWN && !Console::get()->isActive()) { // Si estamos en modo remap de teclado, capturar tecla if (is_remapping_keyboard_ && !remap_completed_) { diff --git a/source/utils/defines.hpp b/source/utils/defines.hpp index 1173ffb..0c3b79f 100644 --- a/source/utils/defines.hpp +++ b/source/utils/defines.hpp @@ -6,7 +6,7 @@ namespace Texts { constexpr const char* WINDOW_CAPTION = "© 2022 JailDoctor's Dilemma — JailDesigner"; constexpr const char* COPYRIGHT = "@2022 JailDesigner"; - constexpr const char* VERSION = "1.13"; // Versión por defecto + constexpr const char* VERSION = "1.14"; // Versión por defecto } // namespace Texts // Tamaño de bloque