feat(audio): reverb 'stone_hall' al primer LAND cinemàtic d'INTRO

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.
This commit is contained in:
JailDesigner committed 2026-04-27 08:31:51 +02:00
1 parent d3698a1504
commit 47bb92144e
6 files changed
+45

No files matched your search

+5
View File
@@ -13,6 +13,11 @@ room_intro:
line_gap_px: -4 # px extra entre les dues línies (piràmide / sala) 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. 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. 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: room_outro:
text_enter_ms: 400 text_enter_ms: 400
+8
View File
@@ -697,7 +697,15 @@ auto Player::integrateJump(float delta_time) -> bool {
state_ = State::LANDING_STUN; state_ = State::LANDING_STUN;
landing_stun_remaining_ms_ = static_cast<float>(jump_config.landing_stun_ms); landing_stun_remaining_ms_ = static_cast<float>(jump_config.landing_stun_ms);
if (!jump_config.land_sound_key.empty() && Audio::get() != nullptr) { if (!jump_config.land_sound_key.empty() && Audio::get() != nullptr) {
// 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); 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) { if (bus_ != nullptr) {
bus_->publish(Event{ bus_->publish(Event{
+17
View File
@@ -259,6 +259,15 @@ class Player : public AnimatedSprite {
// al respawn post-mort: mateixa entrada cinemàtica. // al respawn post-mort: mateixa entrada cinemàtica.
void spawnFromAir(float x, float y); 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). // 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 // 0 quan està a terra. L'usa el render i el sistema de col·lisions
// per altura. // per altura.
@@ -390,6 +399,14 @@ class Player : public AnimatedSprite {
// gestiona `intended_vx_/vy_` escalats pel resolver. // gestiona `intended_vx_/vy_` escalats pel resolver.
bool being_pushed_{false}; 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 // Escala d'empenta usada per `snapToLane` quan descobreix un blocker
// (mateixa del resolver). 1.0 = sense escalar (comportament si // (mateixa del resolver). 1.0 = sense escalar (comportament si
// GameScene no ha cridat setPushFactor). // GameScene no ha cridat setPushFactor).
+5
View File
@@ -556,6 +556,11 @@ void GameScene::triggerPlayerSpawn(std::size_t slot_index) {
float x = SINGLE_X; float x = SINGLE_X;
if (BOTH_ACTIVE) { x = (slot_index == 0) ? SPAWN.p1_x : SPAWN.p2_x; } if (BOTH_ACTIVE) { x = (slot_index == 0) ? SPAWN.p1_x : SPAWN.p2_x; }
players_[slot_index]->spawnFromAir(x, SPAWN.y); 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"; std::cout << "[ROOM] spawn cinematic P" << (slot_index + 1) << ": airborne entry\n";
} }
+3
View File
@@ -47,6 +47,9 @@ namespace RoomFsm {
// El YAML pot trencar la simultaneïtat fixant un valor propi. // 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.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); 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<std::string>();
}
} }
if (yaml.contains("room_outro")) { if (yaml.contains("room_outro")) {
const auto& n = yaml["room_outro"]; const auto& n = yaml["room_outro"];
+7
View File
@@ -80,6 +80,13 @@ namespace RoomFsm {
UiConfig::CurveFn text_enter_curve{nullptr}; UiConfig::CurveFn text_enter_curve{nullptr};
UiConfig::CurveFn text_exit_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). // Panel translúcid sota el rètol de transició (INTRO i OUTRO).
// Centrat verticalment al centre del playfield, ocupa tota // Centrat verticalment al centre del playfield, ocupa tota
// l'amplada del canvas. Creix d'altura 0 → height_px durant la // l'amplada del canvas. Creix d'altura 0 → height_px durant la