diff --git a/src/k_kart.c b/src/k_kart.c index fb519e521..b5ae9b39e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10663,6 +10663,12 @@ void K_KartUpdatePosition(player_t *player) player->topinfirst = 0; } + // Special stages: fade out music near the finish line + if (P_IsLocalPlayer(player)) + { + K_FadeOutSpecialMusic(player->distancetofinish); + } + player->position = position; } diff --git a/src/k_specialstage.c b/src/k_specialstage.c index efcfea382..910147a05 100644 --- a/src/k_specialstage.c +++ b/src/k_specialstage.c @@ -21,6 +21,7 @@ #include "z_zone.h" #include "k_waypoint.h" #include "k_objects.h" +#include "music.h" struct specialstageinfo specialstageinfo; @@ -127,6 +128,11 @@ void K_TickSpecialStage(void) K_MoveExitBeam(); } +/*-------------------------------------------------- + mobj_t *K_GetPossibleSpecialTarget(void) + + See header file for description. +--------------------------------------------------*/ mobj_t *K_GetPossibleSpecialTarget(void) { if (specialstageinfo.valid == false) @@ -141,3 +147,20 @@ mobj_t *K_GetPossibleSpecialTarget(void) return specialstageinfo.ufo; } + +/*-------------------------------------------------- + void K_FadeOutSpecialMusic(UINT32 distance) + + See header file for description. +--------------------------------------------------*/ +void K_FadeOutSpecialMusic(UINT32 distance) +{ + if (specialstageinfo.valid == false) + { + return; + } + + const UINT32 threshold = FixedMul(16000, mapobjectscale); + + Music_LevelVolume(min(distance, threshold) * 100 / threshold); +} diff --git a/src/k_specialstage.h b/src/k_specialstage.h index 329d7f99f..cc26ca352 100644 --- a/src/k_specialstage.h +++ b/src/k_specialstage.h @@ -68,6 +68,18 @@ void K_TickSpecialStage(void); mobj_t *K_GetPossibleSpecialTarget(void); +/*-------------------------------------------------- + void K_FadeOutSpecialMusic(UINT32 distance) + + Fade level music out at the end of a special stage. + + Input Arguments:- + distance - Distance from the finish line. + +--------------------------------------------------*/ + +void K_FadeOutSpecialMusic(UINT32 distance); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/music.cpp b/src/music.cpp index 56f048bd9..4ed22d76c 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -31,6 +31,7 @@ void Music_Init(void) tune.fade_out = 1500; tune.fade_out_inclusive = false; tune.resume_fade_in = 750; + tune.use_level_volume = true; tune.sync = true; tune.credit = true; tune.vapes = true; @@ -417,3 +418,13 @@ void Music_BatchExempt(const char* id) tune->resist_once = true; } } + +void Music_LevelVolume(int volume) +{ + g_tunes.level_volume(volume, false); +} + +void Music_ResetLevelVolume(void) +{ + g_tunes.level_volume(100, true); +} diff --git a/src/music.h b/src/music.h index 3931aedfa..2265639ea 100644 --- a/src/music.h +++ b/src/music.h @@ -103,6 +103,14 @@ void Music_Loop(const char *id, boolean loop); // as Music_StopAll. void Music_BatchExempt(const char *id); +// Set the volume for level context. TODO: this should be +// done on a more selective basis, rather than globally. +void Music_LevelVolume(int volume); + +// Reset volume back to normal. This will fade it as if the +// music is resuming after another tune ended. +void Music_ResetLevelVolume(void); + // // Query properties. diff --git a/src/music_manager.cpp b/src/music_manager.cpp index f9602166c..531a1091d 100644 --- a/src/music_manager.cpp +++ b/src/music_manager.cpp @@ -79,7 +79,13 @@ void TuneManager::tick() { if (load()) { - I_FadeInPlaySong(tune->resume ? tune->resume_fade_in : tune->fade_in, tune->loop); + I_PlaySong(tune->loop); + I_FadeSongFromVolume( + tune->use_level_volume ? level_volume_ : 100, + 0, + tune->resume ? tune->resume_fade_in : tune->fade_in, + nullptr + ); seek(tune); adjust_volume(); @@ -116,6 +122,19 @@ void TuneManager::tick() tune->ending = true; } } + + if (level_volume_ != old_level_volume_ && tune->use_level_volume && tune->can_fade_out) + { + if (volume_fade_) + { + I_FadeSong(level_volume_, tune->resume_fade_in, nullptr); + } + else + { + I_SetInternalMusicVolume(level_volume_); + } + old_level_volume_ = level_volume_; + } } void TuneManager::pause_unpause() const diff --git a/src/music_manager.hpp b/src/music_manager.hpp index c3e742e00..a5d0b31ae 100644 --- a/src/music_manager.hpp +++ b/src/music_manager.hpp @@ -88,6 +88,15 @@ public: } } + void level_volume(int vol, bool fade) + { + if (vol != old_level_volume_) + { + level_volume_ = vol; + volume_fade_ = fade; + } + } + private: std::unordered_map map_; std::string current_song_; @@ -99,6 +108,10 @@ private: bool gme_; // hack + int level_volume_ = 100; + int old_level_volume_ = 100; + bool volume_fade_ = false; + decltype(map_)::const_iterator current_iterator() const { return std::max_element( diff --git a/src/music_tune.hpp b/src/music_tune.hpp index 635dfc02b..d8b859b5d 100644 --- a/src/music_tune.hpp +++ b/src/music_tune.hpp @@ -44,6 +44,9 @@ public: // resumes. int resume_fade_in = 0; + // Adjust volume based on level context. + bool use_level_volume = false; + // Sync this tune to game logic. bool sync = false; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 5ab897cbb..414fd210b 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -8205,6 +8205,8 @@ void P_LoadLevelMusic(void) tic_t level_music_start = starttime + (TICRATE/2); Music_Seek("level", (std::max(leveltime, level_music_start) - level_music_start) * 1000UL / TICRATE); } + + Music_ResetLevelVolume(); } /** Loads a level from a lump or external wad.