Merge feat/menu-pause — pausa del món i sons al menú de servei

This commit is contained in:
JailDesigner committed 2026-04-30 20:40:04 +02:00
commit 7d11dde0b1
10 files changed
+74 -1

No files matched your search

Binary file not shown.
Binary file not shown.
+7
View File
@@ -75,3 +75,10 @@ pause:
exit_ms: 250 exit_ms: 250
enter_curve: outCubic enter_curve: outCubic
exit_curve: inCubic 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"
+8
View File
@@ -154,6 +154,14 @@ Director::Director() {
Resource::Cache::get().loadMusic("playing.ogg", "data/music/playing.ogg"); Resource::Cache::get().loadMusic("playing.ogg", "data/music/playing.ogg");
Resource::Cache::get().loadMusic("room_outro.ogg", "data/music/room_outro.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 // Catàleg de presets d'efectes per als sons. Es carrega abans d'Audio
// perquè els primers playSoundWith* (al començament del joc) ja // perquè els primers playSoundWith* (al començament del joc) ja
// troben els presets disponibles. // troben els presets disponibles.
+8
View File
@@ -96,6 +96,8 @@ UiConfig::UiConfig()
pause_exit_ms_(Defaults::UI::PAUSE_EXIT_MS), pause_exit_ms_(Defaults::UI::PAUSE_EXIT_MS),
pause_enter_curve_(curveByName(Defaults::UI::PAUSE_ENTER_CURVE)), pause_enter_curve_(curveByName(Defaults::UI::PAUSE_ENTER_CURVE)),
pause_exit_curve_(curveByName(Defaults::UI::PAUSE_EXIT_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}, 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}, 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}, 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_); 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'; std::cout << "UiConfig: carregat des de " << file_path << '\n';
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "UiConfig: error parsejant " << file_path << ": " << e.what() << '\n'; std::cerr << "UiConfig: error parsejant " << file_path << ": " << e.what() << '\n';
+8
View File
@@ -76,6 +76,11 @@ class UiConfig {
// retorna Easing::linear i deixa un warning al log. // retorna Easing::linear i deixa un warning al log.
[[nodiscard]] static auto curveByName(const std::string& name) -> CurveFn; [[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. // Fonts de UI.
[[nodiscard]] auto hudFont() const -> const Font& { return hud_font_; } [[nodiscard]] auto hudFont() const -> const Font& { return hud_font_; }
[[nodiscard]] auto placeholderFont() const -> const Font& { return placeholder_font_; } [[nodiscard]] auto placeholderFont() const -> const Font& { return placeholder_font_; }
@@ -121,6 +126,9 @@ class UiConfig {
CurveFn pause_enter_curve_; CurveFn pause_enter_curve_;
CurveFn pause_exit_curve_; CurveFn pause_exit_curve_;
std::string menu_select_sound_;
std::string menu_accept_sound_;
Font hud_font_; Font hud_font_;
Font placeholder_font_; Font placeholder_font_;
Font room_text_font_; Font room_text_font_;
+12 -1
View File
@@ -38,6 +38,7 @@
#include "game/map/map.hpp" #include "game/map/map.hpp"
#include "game/options.hpp" #include "game/options.hpp"
#include "game/ui/hud.hpp" #include "game/ui/hud.hpp"
#include "game/ui/service_menu.hpp"
#include "utils/defaults.hpp" #include "utils/defaults.hpp"
#include "utils/defines.hpp" #include "utils/defines.hpp"
#include "utils/easing.hpp" #include "utils/easing.hpp"
@@ -1120,7 +1121,17 @@ namespace {
void GameScene::update(int delta_ms) { void GameScene::update(int delta_ms) {
handlePauseInput(); handlePauseInput();
// Congelació instantània mentre el menú de servei està obert. A
// diferència de la pausa de gameplay (que és un dial amb easing i
// fade de música), aquesta és binària: el món no avança gens i la
// música no es toca. També posem en pausa el dial de la pausa
// regular si estava a mig camí, perquè no continui mentre l'usuari
// navega el menú.
const bool MENU_FROZEN = ServiceMenu::get().isOpen();
if (!MENU_FROZEN) {
updatePauseTransition(delta_ms); updatePauseTransition(delta_ms);
}
// Transició de sala: viu en temps real (no escalada per la pausa) // Transició de sala: viu en temps real (no escalada per la pausa)
// perquè és UI. Avança avans del bloc de món perquè quan PLAYING // perquè és UI. Avança avans del bloc de món perquè quan PLAYING
@@ -1172,7 +1183,7 @@ void GameScene::update(int delta_ms) {
constexpr bool EDITOR_FROZEN = false; constexpr bool EDITOR_FROZEN = false;
#endif #endif
const int SCALED_DELTA_MS = static_cast<int>(static_cast<float>(delta_ms) * pause_.scale); const int SCALED_DELTA_MS = static_cast<int>(static_cast<float>(delta_ms) * pause_.scale);
if (!EDITOR_FROZEN && SCALED_DELTA_MS > 0) { if (!EDITOR_FROZEN && !MENU_FROZEN && SCALED_DELTA_MS > 0) {
const float DT = static_cast<float>(SCALED_DELTA_MS) / 1000.0F; const float DT = static_cast<float>(SCALED_DELTA_MS) / 1000.0F;
// Pipeline de moviment en tres passes per poder intercalar la // Pipeline de moviment en tres passes per poder intercalar la
+19
View File
@@ -459,6 +459,14 @@ namespace {
} // 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) { void ServiceMenu::handleGamepadButtonDown(const SDL_GamepadButtonEvent& ev) {
// El botó SERVICE (per defecte BACK; remapejable per dispositiu via // El botó SERVICE (per defecte BACK; remapejable per dispositiu via
// `GamepadBindings`) tanca el menú sencer. Cada mando pot tenir el // `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); const int SERVICE_BTN = serviceButtonForEvent(ev.which);
if (SERVICE_BTN >= 0 && static_cast<int>(ev.button) == SERVICE_BTN) { if (SERVICE_BTN >= 0 && static_cast<int>(ev.button) == SERVICE_BTN) {
close(); close();
playSelectSound();
return; return;
} }
switch (ev.button) { switch (ev.button) {
case SDL_GAMEPAD_BUTTON_DPAD_UP: case SDL_GAMEPAD_BUTTON_DPAD_UP:
moveCursor(-1); moveCursor(-1);
playSelectSound();
break; break;
case SDL_GAMEPAD_BUTTON_DPAD_DOWN: case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
moveCursor(+1); moveCursor(+1);
playSelectSound();
break; break;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT: case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
activateCurrent(-1); activateCurrent(-1);
playAcceptSound();
break; break;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
case SDL_GAMEPAD_BUTTON_EAST: case SDL_GAMEPAD_BUTTON_EAST:
case SDL_GAMEPAD_BUTTON_NORTH: case SDL_GAMEPAD_BUTTON_NORTH:
activateCurrent(+1); activateCurrent(+1);
playAcceptSound();
break; break;
case SDL_GAMEPAD_BUTTON_WEST: case SDL_GAMEPAD_BUTTON_WEST:
case SDL_GAMEPAD_BUTTON_SOUTH: case SDL_GAMEPAD_BUTTON_SOUTH:
popPage(); popPage();
playSelectSound();
break; break;
default: default:
break; break;
@@ -567,21 +581,26 @@ auto ServiceMenu::handleEvent(const SDL_Event& ev) -> bool {
switch (SC) { switch (SC) {
case SDL_SCANCODE_UP: case SDL_SCANCODE_UP:
moveCursor(-1); moveCursor(-1);
playSelectSound();
break; break;
case SDL_SCANCODE_DOWN: case SDL_SCANCODE_DOWN:
moveCursor(+1); moveCursor(+1);
playSelectSound();
break; break;
case SDL_SCANCODE_LEFT: case SDL_SCANCODE_LEFT:
activateCurrent(-1); activateCurrent(-1);
playAcceptSound();
break; break;
case SDL_SCANCODE_RIGHT: case SDL_SCANCODE_RIGHT:
case SDL_SCANCODE_RETURN: case SDL_SCANCODE_RETURN:
case SDL_SCANCODE_KP_ENTER: case SDL_SCANCODE_KP_ENTER:
activateCurrent(+1); activateCurrent(+1);
playAcceptSound();
break; break;
case SDL_SCANCODE_BACKSPACE: case SDL_SCANCODE_BACKSPACE:
case SDL_SCANCODE_ESCAPE: case SDL_SCANCODE_ESCAPE:
popPage(); popPage();
playSelectSound();
break; break;
default: default:
break; break;
+6
View File
@@ -207,6 +207,12 @@ class ServiceMenu {
// mètode propi per a mantenir la complexitat cognitiva d'`handleEvent` // mètode propi per a mantenir la complexitat cognitiva d'`handleEvent`
// sota el llindar de clang-tidy. // sota el llindar de clang-tidy.
void handleGamepadButtonDown(const SDL_GamepadButtonEvent& ev); 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 top() -> Page&;
[[nodiscard]] auto topConst() const -> const Page&; [[nodiscard]] auto topConst() const -> const Page&;
[[nodiscard]] static auto isItemVisible(const Item& item) -> bool; [[nodiscard]] static auto isItemVisible(const Item& item) -> bool;
+6
View File
@@ -216,6 +216,12 @@ namespace Defaults {
constexpr int PAUSE_EXIT_MS = 250; constexpr int PAUSE_EXIT_MS = 250;
constexpr const char* PAUSE_ENTER_CURVE = "outCubic"; constexpr const char* PAUSE_ENTER_CURVE = "outCubic";
constexpr const char* PAUSE_EXIT_CURVE = "inCubic"; 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 } // namespace UI
#ifdef _DEBUG #ifdef _DEBUG