From 47bb92144e8d5c628d011150a18dfa4e0481c7b4 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Mon, 27 Apr 2026 08:17:31 +0200 Subject: [PATCH] =?UTF-8?q?feat(audio):=20reverb=20'stone=5Fhall'=20al=20p?= =?UTF-8?q?rimer=20LAND=20cinem=C3=A0tic=20d'INTRO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lligua el reverb al moment de la cinemàtica d'entrada de sala, no als salts en general. La sala dóna "més detall" sonor a l'INTRO; un cop el joc està en marxa, els salts (i els respawns post-mort) tornen al so sec. Així el primer aterratge es percep com a part de l'animació d'arribada, no com una propietat permanent del personatge. - RoomFsm::Config: nou camp `intro_land_reverb_preset` carregat des de `room_intro.land_reverb` a `data/scenes/game.yaml` (per defecte 'stone_hall'). Buit = sense reverb a l'INTRO. - Player: membre `next_land_reverb_preset_` consumit per `integrateJump` al primer aterratge que el trobe armat. Setter públic `armNextLandReverb(preset)` per a GameScene. Cap altre punt (respawn, exit, disable) el toca: el reverb és estrictament one-shot lligat al spawn aeri d'INTRO. - GameScene::triggerPlayerSpawn: arma el preset al jugador just després de `spawnFromAir`, així cada slot rep el seu reverb un sol cop per cinemàtica. - JumpConfig perd `land_reverb_preset` i els YAMLs de Sam/Morcus perden `land_reverb`: la decisió ja no viu al personatge. --- data/scenes/game.yaml | 5 +++++ source/game/entities/player.cpp | 10 +++++++++- source/game/entities/player.hpp | 17 +++++++++++++++++ source/game/scenes/game_scene.cpp | 5 +++++ source/game/scenes/room_fsm.cpp | 3 +++ source/game/scenes/room_fsm.hpp | 7 +++++++ 6 files changed, 46 insertions(+), 1 deletion(-) diff --git a/data/scenes/game.yaml b/data/scenes/game.yaml index f23a211..d1d0dfe 100644 --- a/data/scenes/game.yaml +++ b/data/scenes/game.yaml @@ -13,6 +13,11 @@ room_intro: line_gap_px: -4 # px extra entre les dues línies (piràmide / sala) player_spawn_delay_ms: 2400 # ms des de l'inici d'INTRO al spawn aeri. player2_spawn_delay_ms: 2700 # només mode BOTH. Trenca la simultaneïtat. + # Preset de reverb (catàleg a `data/sounds.yaml`) aplicat al primer + # LAND de cada jugador durant la cinemàtica d'entrada. La sala dona + # "més detall" sonor a l'INTRO; els salts del joc i els respawns + # post-mort cauen al so sec. Buit o absent = sense reverb. + land_reverb: stone_hall room_outro: text_enter_ms: 400 diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index bb5e14a..5efb701 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -697,7 +697,15 @@ auto Player::integrateJump(float delta_time) -> bool { state_ = State::LANDING_STUN; landing_stun_remaining_ms_ = static_cast(jump_config.landing_stun_ms); if (!jump_config.land_sound_key.empty() && Audio::get() != nullptr) { - Audio::get()->playSound(jump_config.land_sound_key, Audio::Group::GAME); + // Consumeix el preset armat per GameScene al spawn aeri + // d'INTRO (single-shot): el primer LAND el sona reverberat, + // els salts següents cauen al camí sec. + if (next_land_reverb_preset_.empty()) { + Audio::get()->playSound(jump_config.land_sound_key, Audio::Group::GAME); + } else { + Audio::get()->playSoundWithReverb(jump_config.land_sound_key, next_land_reverb_preset_, Audio::Group::GAME); + next_land_reverb_preset_.clear(); + } } if (bus_ != nullptr) { bus_->publish(Event{ diff --git a/source/game/entities/player.hpp b/source/game/entities/player.hpp index cd57274..cd3b3db 100644 --- a/source/game/entities/player.hpp +++ b/source/game/entities/player.hpp @@ -259,6 +259,15 @@ class Player : public AnimatedSprite { // al respawn post-mort: mateixa entrada cinemàtica. void spawnFromAir(float x, float y); + // Arma un preset de reverb (catàleg a `data/sounds.yaml`) per al + // pròxim aterratge d'aquest jugador. Pensat per a la cinemàtica + // d'INTRO de sala: GameScene crida aquesta funció just després de + // `spawnFromAir`, i el primer LAND consumeix el preset (queda buit + // immediatament). Els salts posteriors del joc, els respawns + // post-mort i el reset general no rearmen res. `preset` buit + // desactiva el reverb del proper LAND (no afecta cap LAND posterior). + void armNextLandReverb(std::string preset) { next_land_reverb_preset_ = std::move(preset); } + // Alçada actual del salt (píxels visuals per damunt del sòl). // 0 quan està a terra. L'usa el render i el sistema de col·lisions // per altura. @@ -390,6 +399,14 @@ class Player : public AnimatedSprite { // gestiona `intended_vx_/vy_` escalats pel resolver. bool being_pushed_{false}; + // Preset de reverb (catàleg de `data/sounds.yaml`) que el pròxim + // LAND ha de fer servir; buit = so sec. L'arma `armNextLandReverb` + // (cridada per GameScene al spawn aeri d'INTRO) i el consumeix + // `integrateJump` quan el jugador toca terra. Cap altre punt + // (respawn, exit, disable) el toca: així només sona reverberat el + // primer LAND de la cinemàtica d'entrada. + std::string next_land_reverb_preset_; + // Escala d'empenta usada per `snapToLane` quan descobreix un blocker // (mateixa del resolver). 1.0 = sense escalar (comportament si // GameScene no ha cridat setPushFactor). diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 6aab345..d316662 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -556,6 +556,11 @@ void GameScene::triggerPlayerSpawn(std::size_t slot_index) { float x = SINGLE_X; if (BOTH_ACTIVE) { x = (slot_index == 0) ? SPAWN.p1_x : SPAWN.p2_x; } players_[slot_index]->spawnFromAir(x, SPAWN.y); + // Arma el reverb del primer LAND com a part de la cinemàtica + // d'INTRO. Una sola vegada per spawn aeri: el respawn post-mort + // (que no passa per ací) cau al camí sec. Si el preset està buit + // al YAML, l'arma queda buida i el LAND és sec igualment. + players_[slot_index]->armNextLandReverb(transition_config_.intro_land_reverb_preset); std::cout << "[ROOM] spawn cinematic P" << (slot_index + 1) << ": airborne entry\n"; } diff --git a/source/game/scenes/room_fsm.cpp b/source/game/scenes/room_fsm.cpp index a510d93..0b980f8 100644 --- a/source/game/scenes/room_fsm.cpp +++ b/source/game/scenes/room_fsm.cpp @@ -47,6 +47,9 @@ namespace RoomFsm { // El YAML pot trencar la simultaneïtat fixant un valor propi. cfg.player2_spawn_delay_ms = readChildInt(n, "player2_spawn_delay_ms", cfg.player_spawn_delay_ms); cfg.intro_text_line_gap_px = readChildInt(n, "line_gap_px", cfg.intro_text_line_gap_px); + if (n.contains("land_reverb")) { + cfg.intro_land_reverb_preset = n["land_reverb"].get_value(); + } } if (yaml.contains("room_outro")) { const auto& n = yaml["room_outro"]; diff --git a/source/game/scenes/room_fsm.hpp b/source/game/scenes/room_fsm.hpp index b10836f..e57b441 100644 --- a/source/game/scenes/room_fsm.hpp +++ b/source/game/scenes/room_fsm.hpp @@ -80,6 +80,13 @@ namespace RoomFsm { UiConfig::CurveFn text_enter_curve{nullptr}; UiConfig::CurveFn text_exit_curve{nullptr}; + // Preset de reverb (catàleg a `data/sounds.yaml`) que GameScene + // arma al jugador al spawn aeri d'INTRO. Es consumeix al primer + // aterratge cinemàtic; els salts posteriors del joc i els + // respawns post-mort cauen al so sec. Buit = sense reverb (la + // cinemàtica d'entrada sona igual que un salt qualsevol). + std::string intro_land_reverb_preset; + // Panel translúcid sota el rètol de transició (INTRO i OUTRO). // Centrat verticalment al centre del playfield, ocupa tota // l'amplada del canvas. Creix d'altura 0 → height_px durant la