diff --git a/data/sounds/menu_accept.wav b/data/sounds/menu_accept.wav new file mode 100644 index 0000000..3cebb3e Binary files /dev/null and b/data/sounds/menu_accept.wav differ diff --git a/data/sounds/menu_select.wav b/data/sounds/menu_select.wav new file mode 100644 index 0000000..79523b9 Binary files /dev/null and b/data/sounds/menu_select.wav differ diff --git a/data/ui.yaml b/data/ui.yaml index 0a3c5e0..a175ec2 100644 --- a/data/ui.yaml +++ b/data/ui.yaml @@ -75,3 +75,10 @@ pause: exit_ms: 250 enter_curve: outCubic exit_curve: inCubic + +# Sons del menú de servei. Els fitxers viuen a data/sounds/. SELECT +# acompanya navegació (amunt/avall) i back; ACCEPT acompanya modificació +# d'opció (esquerra/dreta/retorn) o entrada a un submenú. +sounds: + menu_select: "menu_select.wav" + menu_accept: "menu_accept.wav" diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 13b84f6..29a35c8 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -145,6 +145,14 @@ Director::Director() { Resource::Cache::get().loadMusic("playing.ogg", "data/music/playing.ogg"); Resource::Cache::get().loadMusic("room_outro.ogg", "data/music/room_outro.ogg"); + // Sons del menú de servei. Els noms venen de `data/ui.yaml` (amb + // fallback a `Defaults::UI::MENU_*_SOUND`); el ServiceMenu els + // dispara via Audio::Group::INTERFACE. + const auto& menu_select = UiConfig::get().menuSelectSound(); + const auto& menu_accept = UiConfig::get().menuAcceptSound(); + Resource::Cache::get().loadSound(menu_select, "data/sounds/" + menu_select); + Resource::Cache::get().loadSound(menu_accept, "data/sounds/" + menu_accept); + // Catàleg de presets d'efectes per als sons. Es carrega abans d'Audio // perquè els primers playSoundWith* (al començament del joc) ja // troben els presets disponibles. diff --git a/source/core/ui/ui_config.cpp b/source/core/ui/ui_config.cpp index 92e3bb6..3d6f6ab 100644 --- a/source/core/ui/ui_config.cpp +++ b/source/core/ui/ui_config.cpp @@ -96,6 +96,8 @@ UiConfig::UiConfig() pause_exit_ms_(Defaults::UI::PAUSE_EXIT_MS), pause_enter_curve_(curveByName(Defaults::UI::PAUSE_ENTER_CURVE)), pause_exit_curve_(curveByName(Defaults::UI::PAUSE_EXIT_CURVE)), + menu_select_sound_(Defaults::UI::MENU_SELECT_SOUND), + menu_accept_sound_(Defaults::UI::MENU_ACCEPT_SOUND), hud_font_{.name = Defaults::UI::Font::HUD_NAME, .gif = Defaults::UI::Font::HUD_GIF, .fnt = Defaults::UI::Font::HUD_FNT, .kerning = Defaults::UI::Font::HUD_KERNING}, placeholder_font_{.name = Defaults::UI::Font::PLACEHOLDER_NAME, .gif = Defaults::UI::Font::PLACEHOLDER_GIF, .fnt = Defaults::UI::Font::PLACEHOLDER_FNT, .kerning = Defaults::UI::Font::PLACEHOLDER_KERNING}, room_text_font_{.name = Defaults::UI::Font::ROOM_TEXT_NAME, .gif = Defaults::UI::Font::ROOM_TEXT_GIF, .fnt = Defaults::UI::Font::ROOM_TEXT_FNT, .kerning = Defaults::UI::Font::ROOM_TEXT_KERNING}, @@ -185,6 +187,12 @@ void UiConfig::load(const std::string& file_path) { readCurve(p, "exit_curve", pause_exit_curve_); } + if (yaml.contains("sounds")) { + const auto& s = yaml["sounds"]; + readField(s, "menu_select", menu_select_sound_); + readField(s, "menu_accept", menu_accept_sound_); + } + std::cout << "UiConfig: carregat des de " << file_path << '\n'; } catch (const std::exception& e) { std::cerr << "UiConfig: error parsejant " << file_path << ": " << e.what() << '\n'; diff --git a/source/core/ui/ui_config.hpp b/source/core/ui/ui_config.hpp index 1219006..8c58b74 100644 --- a/source/core/ui/ui_config.hpp +++ b/source/core/ui/ui_config.hpp @@ -76,6 +76,11 @@ class UiConfig { // retorna Easing::linear i deixa un warning al log. [[nodiscard]] static auto curveByName(const std::string& name) -> CurveFn; + // Sons del menú de servei. Claus de Resource::Cache::loadSound (els + // fitxers viuen a `data/sounds/`). + [[nodiscard]] auto menuSelectSound() const -> const std::string& { return menu_select_sound_; } + [[nodiscard]] auto menuAcceptSound() const -> const std::string& { return menu_accept_sound_; } + // Fonts de UI. [[nodiscard]] auto hudFont() const -> const Font& { return hud_font_; } [[nodiscard]] auto placeholderFont() const -> const Font& { return placeholder_font_; } @@ -121,6 +126,9 @@ class UiConfig { CurveFn pause_enter_curve_; CurveFn pause_exit_curve_; + std::string menu_select_sound_; + std::string menu_accept_sound_; + Font hud_font_; Font placeholder_font_; Font room_text_font_; diff --git a/source/game/ui/service_menu.cpp b/source/game/ui/service_menu.cpp index d872ac0..6dfefaa 100644 --- a/source/game/ui/service_menu.cpp +++ b/source/game/ui/service_menu.cpp @@ -459,6 +459,14 @@ namespace { } // namespace +void ServiceMenu::playSelectSound() { + Audio::get()->playSound(UiConfig::get().menuSelectSound(), Audio::Group::INTERFACE); +} + +void ServiceMenu::playAcceptSound() { + Audio::get()->playSound(UiConfig::get().menuAcceptSound(), Audio::Group::INTERFACE); +} + void ServiceMenu::handleGamepadButtonDown(const SDL_GamepadButtonEvent& ev) { // El botó SERVICE (per defecte BACK; remapejable per dispositiu via // `GamepadBindings`) tanca el menú sencer. Cada mando pot tenir el @@ -466,26 +474,32 @@ void ServiceMenu::handleGamepadButtonDown(const SDL_GamepadButtonEvent& ev) { const int SERVICE_BTN = serviceButtonForEvent(ev.which); if (SERVICE_BTN >= 0 && static_cast(ev.button) == SERVICE_BTN) { close(); + playSelectSound(); return; } switch (ev.button) { case SDL_GAMEPAD_BUTTON_DPAD_UP: moveCursor(-1); + playSelectSound(); break; case SDL_GAMEPAD_BUTTON_DPAD_DOWN: moveCursor(+1); + playSelectSound(); break; case SDL_GAMEPAD_BUTTON_DPAD_LEFT: activateCurrent(-1); + playAcceptSound(); break; case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: case SDL_GAMEPAD_BUTTON_EAST: case SDL_GAMEPAD_BUTTON_NORTH: activateCurrent(+1); + playAcceptSound(); break; case SDL_GAMEPAD_BUTTON_WEST: case SDL_GAMEPAD_BUTTON_SOUTH: popPage(); + playSelectSound(); break; default: break; @@ -567,21 +581,26 @@ auto ServiceMenu::handleEvent(const SDL_Event& ev) -> bool { switch (SC) { case SDL_SCANCODE_UP: moveCursor(-1); + playSelectSound(); break; case SDL_SCANCODE_DOWN: moveCursor(+1); + playSelectSound(); break; case SDL_SCANCODE_LEFT: activateCurrent(-1); + playAcceptSound(); break; case SDL_SCANCODE_RIGHT: case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: activateCurrent(+1); + playAcceptSound(); break; case SDL_SCANCODE_BACKSPACE: case SDL_SCANCODE_ESCAPE: popPage(); + playSelectSound(); break; default: break; diff --git a/source/game/ui/service_menu.hpp b/source/game/ui/service_menu.hpp index 669f7f0..8bd0d22 100644 --- a/source/game/ui/service_menu.hpp +++ b/source/game/ui/service_menu.hpp @@ -207,6 +207,12 @@ class ServiceMenu { // mètode propi per a mantenir la complexitat cognitiva d'`handleEvent` // sota el llindar de clang-tidy. void handleGamepadButtonDown(const SDL_GamepadButtonEvent& ev); + + // Sons d'UI: SELECT acompanya navegació (amunt/avall) i back; ACCEPT + // acompanya modificació d'opció (esquerra/dreta/retorn). Els noms de + // recurs venen d'`UiConfig` i el preload el fa el Director. + static void playSelectSound(); + static void playAcceptSound(); [[nodiscard]] auto top() -> Page&; [[nodiscard]] auto topConst() const -> const Page&; [[nodiscard]] static auto isItemVisible(const Item& item) -> bool; diff --git a/source/utils/defaults.hpp b/source/utils/defaults.hpp index f182fa1..411444d 100644 --- a/source/utils/defaults.hpp +++ b/source/utils/defaults.hpp @@ -216,6 +216,12 @@ namespace Defaults { constexpr int PAUSE_EXIT_MS = 250; constexpr const char* PAUSE_ENTER_CURVE = "outCubic"; constexpr const char* PAUSE_EXIT_CURVE = "inCubic"; + + // Sons del menú de servei. SELECT acompanya navegació (amunt/avall) + // i back; ACCEPT acompanya modificació d'opció (esq/dret/retorn). + // Els fitxers viuen a `data/sounds/`; només es referencia el nom. + constexpr const char* MENU_SELECT_SOUND = "menu_select.wav"; + constexpr const char* MENU_ACCEPT_SOUND = "menu_accept.wav"; } // namespace UI #ifdef _DEBUG