diff --git a/source/blood/src/blood.cpp b/source/blood/src/blood.cpp index 8649ff0fd..626ab74a8 100644 --- a/source/blood/src/blood.cpp +++ b/source/blood/src/blood.cpp @@ -2099,11 +2099,16 @@ int sndTryPlaySpecialMusic(int nMusic) { int nEpisode = nMusic/kMaxLevels; int nLevel = nMusic%kMaxLevels; - if (!sndPlaySong(gEpisodeInfo[nEpisode].at28[nLevel].atd0, true)) + if (sndPlaySong(gEpisodeInfo[nEpisode].at28[nLevel].at0, gEpisodeInfo[nEpisode].at28[nLevel].atd0, true)) { strncpy(gGameOptions.zLevelSong, gEpisodeInfo[nEpisode].at28[nLevel].atd0, BMAX_PATH); return 0; } + else + { + // Unable to stat the music. + *gGameOptions.zLevelSong = 0; + } return 1; } diff --git a/source/blood/src/levels.cpp b/source/blood/src/levels.cpp index 17db5a5bf..c1ad24fae 100644 --- a/source/blood/src/levels.cpp +++ b/source/blood/src/levels.cpp @@ -403,9 +403,10 @@ bool levelTryPlayMusic(int nEpisode, int nLevel, bool bSetLevelSong) snprintf(buffer, BMAX_PATH, "blood%02i.ogg", gEpisodeInfo[nEpisode].at28[nLevel].ate0); else strncpy(buffer, gEpisodeInfo[nEpisode].at28[nLevel].atd0, BMAX_PATH); - bool bReturn = !!sndPlaySong(buffer, true); - if (!bReturn || bSetLevelSong) + bool bReturn = !!sndPlaySong(gEpisodeInfo[nEpisode].at28[nLevel].atd0, buffer, true); + if (bReturn || bSetLevelSong) strncpy(gGameOptions.zLevelSong, buffer, BMAX_PATH); + else *gGameOptions.zLevelSong = 0; return bReturn; } diff --git a/source/blood/src/menu.cpp b/source/blood/src/menu.cpp index 85b406fd5..b8929c5ac 100644 --- a/source/blood/src/menu.cpp +++ b/source/blood/src/menu.cpp @@ -1793,7 +1793,7 @@ void UpdateMusicToggle(CGameMenuItemZBool *pItem) else { if (gGameStarted || gDemo.at1) - sndPlaySong(gGameOptions.zLevelSong, true); + sndPlaySong("*", gGameOptions.zLevelSong, true); } } @@ -1847,7 +1847,7 @@ void SetSound(CGameMenuItemChain *pItem) sfxInit(); if (mus_enabled && (gGameStarted || gDemo.at1)) - sndPlaySong(gGameOptions.zLevelSong, true); + sndPlaySong("*", gGameOptions.zLevelSong, true); } void PreDrawSound(CGameMenuItem *pItem) diff --git a/source/blood/src/osdcmd.cpp b/source/blood/src/osdcmd.cpp index bd20b478a..c7dd70d98 100644 --- a/source/blood/src/osdcmd.cpp +++ b/source/blood/src/osdcmd.cpp @@ -368,7 +368,7 @@ static int osdcmd_restartsound(osdcmdptr_t UNUSED(parm)) sfxInit(); if (MusicEnabled() && (gGameStarted || gDemo.at1)) - sndPlaySong(gGameOptions.zLevelSong, true); + sndPlaySong(nullptr, "*", true); return OSDCMD_OK; } diff --git a/source/blood/src/sound.cpp b/source/blood/src/sound.cpp index 54fe2fd5b..5dd1b1d74 100644 --- a/source/blood/src/sound.cpp +++ b/source/blood/src/sound.cpp @@ -82,7 +82,7 @@ int nSongSize; bool bWaveMusic; int nWaveMusicHandle; -int sndPlaySong(const char *songName, bool bLoop) +int sndPlaySong(const char *, const char* songName, bool bLoop) { if (!MusicEnabled()) return 0; @@ -225,13 +225,9 @@ void sndStopSong(void) nSongSize = 0; } #else -int sndPlaySong(const char* songName, bool bLoop) +int sndPlaySong(const char *mapname, const char* songName, bool bLoop) { - if (!MusicEnabled()) - return 0; - - Mus_Play(songName, bLoop); - return 0; + return Mus_Play(mapname, songName, bLoop); } bool sndIsSongPlaying(void) diff --git a/source/blood/src/sound.h b/source/blood/src/sound.h index 79aa31245..44b101e29 100644 --- a/source/blood/src/sound.h +++ b/source/blood/src/sound.h @@ -44,7 +44,7 @@ struct SFX }; int sndGetRate(int format); -int sndPlaySong(const char *songName, bool bLoop); +int sndPlaySong(const char *mapname, const char *songName, bool bLoop); bool sndIsSongPlaying(void); void sndFadeSong(int nTime); void sndSetMusicVolume(int nVolume); diff --git a/source/common/music/music.cpp b/source/common/music/music.cpp index 53ed7a93d..05b24707b 100644 --- a/source/common/music/music.cpp +++ b/source/common/music/music.cpp @@ -73,6 +73,7 @@ MusPlayingInfo mus_playing; MusicAliasMap MusicAliases; MidiDeviceMap MidiDevices; MusicVolumeMap MusicVolumes; +MusicAliasMap LevelMusicAliases; bool MusicPaused; //========================================================================== // @@ -553,9 +554,36 @@ CCMD (stopmus) mus_playing.LastSong = ""; // forget the last played song so that it won't get restarted if some volume changes occur } -void Mus_Play(const char *fn, bool loop) +static FString lastMusicLevel, lastMusic; +int Mus_Play(const char *mapname, const char *fn, bool loop) { + // Store the requested names for resuming. + lastMusicLevel = mapname; + lastMusic = fn; + + if (!MusicEnabled()) + { + return 0; + } + // A restart was requested. Ignore the music name being passed and just try tp restart what got here last. + if (*mapname == '*') + { + mapname = lastMusicLevel.GetChars(); + fn = lastMusic.GetChars(); + } + // Allow per level music substitution. + // Unlike some other engines like ZDoom or even Blood which use definition files, the music names in Duke Nukem are being defined in a CON script, making direct replacement a lot harder. + // For most cases using $musicalias would be sufficient, but that method only works if a level actually has some music defined at all. + // This way it can be done with an add-on definition lump even in cases like Redneck Rampage where no music definitions exist or where music gets reused for multiple levels but replacement is wanted individually. + if (mapname && *mapname) + { + if (*mapname == '/') mapname++; + FName *check = LevelMusicAliases.CheckKey(FName(mapname, true)); + if (check) fn = check->GetChars(); + } + S_ChangeMusic(fn, 0, loop, true); + return mus_playing.handle != nullptr; } void Mus_Stop() diff --git a/source/common/music/s_advsound.cpp b/source/common/music/s_advsound.cpp index ef18ecdb0..aeaae7d36 100644 --- a/source/common/music/s_advsound.cpp +++ b/source/common/music/s_advsound.cpp @@ -50,11 +50,13 @@ enum SICommands SI_MusicVolume, SI_MidiDevice, SI_MusicAlias, + SI_LevelMusic, }; // This specifies whether Timidity or Windows playback is preferred for a certain song (only useful for Windows.) extern MusicAliasMap MusicAliases; +extern MusicAliasMap LevelMusicAliases; extern MidiDeviceMap MidiDevices; extern MusicVolumeMap MusicVolumes; @@ -76,6 +78,7 @@ static const char *SICommandStrings[] = "$musicvolume", "$mididevice", "$musicalias", + "$levelmusic", NULL }; @@ -167,6 +170,20 @@ static void S_AddSNDINFO (int lump) } break; + case SI_LevelMusic: { + sc.MustGetString(); + FName alias = sc.String; + sc.MustGetString(); + FName mapped = sc.String; + + // only set the alias if the lump it maps to exists. + if (mapped == NAME_None || fileSystem.FindFile(sc.String) >= 0) + { + LevelMusicAliases[alias] = mapped; + } + } + break; + case SI_MidiDevice: { sc.MustGetString(); FName nm = sc.String; diff --git a/source/common/music/z_music.h b/source/common/music/z_music.h index 2036d54bd..754aca1dd 100644 --- a/source/common/music/z_music.h +++ b/source/common/music/z_music.h @@ -3,6 +3,6 @@ // Totally minimalistic interface - should be all the game modules need. void Mus_Init(); -void Mus_Play(const char *fn, bool loop); +int Mus_Play(const char *mapname, const char *fn, bool loop); void Mus_Stop(); void Mus_SetPaused(bool on); diff --git a/source/duke3d/src/sounds.cpp b/source/duke3d/src/sounds.cpp index 4ccd0f2a7..ca4b4ecea 100644 --- a/source/duke3d/src/sounds.cpp +++ b/source/duke3d/src/sounds.cpp @@ -204,8 +204,8 @@ void S_MenuSound(void) S_PlaySound(s); } -#if 0 -static int S_PlayMusic(const char *fn) +#if 0 // In case you desperately want the old system back... ;) +static int S_PlayMusic(const char *, const char *fn, int loop) { if (!MusicEnabled()) return 0; @@ -323,13 +323,9 @@ void S_PauseMusic(bool paused) } #else -static int S_PlayMusic(const char* fn, bool looping = true) +static int S_PlayMusic(const char *mapname, const char* fn, bool looping = true) { - if (!MusicEnabled()) - return 0; - - Mus_Play(fn, looping); - return 1; + return Mus_Play(mapname, fn, looping); } void S_StopMusic(void) @@ -362,15 +358,10 @@ bool S_TryPlayLevelMusic(unsigned int m) if (retval < 0) return false; - char const * musicfn = g_mapInfo[m].musicfn; - - if (musicfn != NULL) - { - if (!S_PlayMusic(musicfn)) - { - S_SetMusicIndex(m); - return false; - } + if (!S_PlayMusic(g_mapInfo[m].filename, g_mapInfo[m].musicfn)) + { + S_SetMusicIndex(m); + return false; } return true; @@ -390,7 +381,7 @@ int S_TryPlaySpecialMusic(unsigned int m) char const * musicfn = g_mapInfo[m].musicfn; if (musicfn != NULL) { - if (!S_PlayMusic(musicfn)) + if (!S_PlayMusic(nullptr, musicfn)) { S_SetMusicIndex(m); return 0; diff --git a/source/rr/src/sounds.cpp b/source/rr/src/sounds.cpp index bd27e7864..85cf2b899 100644 --- a/source/rr/src/sounds.cpp +++ b/source/rr/src/sounds.cpp @@ -166,8 +166,8 @@ void S_MenuSound(void) S_PlaySound(s); } -#if 0 -static int S_PlayMusic(const char *fn, int loop) +#if 0 // In case you desperately want the old system back... ;) +static int S_PlayMusic(const char *, const char *fn, int loop) { if (!MusicEnabled()) return 0; @@ -288,13 +288,9 @@ void S_PauseMusic(bool paused) #else -static int S_PlayMusic(const char* fn, bool looping) +static int S_PlayMusic(const char *mapname, const char* fn, bool looping = true) { - if (!MusicEnabled()) - return 0; - - Mus_Play(fn, looping); - return 1; + return Mus_Play(mapname, fn, looping); } void S_StopMusic(void) @@ -320,17 +316,11 @@ static void S_SetMusicIndex(unsigned int m) bool S_TryPlayLevelMusic(unsigned int m) { - if (RR) - return 1; - char const * musicfn = g_mapInfo[m].musicfn; - - if (musicfn != NULL) - { - if (!S_PlayMusic(musicfn, 1)) - { - S_SetMusicIndex(m); - return false; - } + // For RR only explicitly invalidate the music name, but still allow the music code to run its own music substitution logic based on map names. + if (!S_PlayMusic(g_mapInfo[m].filename,RR? nullptr : g_mapInfo[m].musicfn)) + { + S_SetMusicIndex(m); + return false; } return true; @@ -352,7 +342,7 @@ int S_TryPlaySpecialMusic(unsigned int m) char const * musicfn = g_mapInfo[m].musicfn; if (musicfn != NULL) { - if (!S_PlayMusic(musicfn, 1)) + if (!S_PlayMusic(nullptr, musicfn, 1)) { S_SetMusicIndex(m); return 0;