From 6e46f97847cacd82a8711037b40d03920b8de876 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 10 Mar 2024 14:26:33 +0000 Subject: [PATCH] As promised, Encore (alt)music - `EncoreMusic` on mapheader - Supports up to 3 alt musics, as with every other type - Do not vape/nightcoreify if provided - To avoid complicating the Alt Music logic too much, uses "side B" of the same Prison Egg CD - That is to say, if there are 2 encore tracks and 3 normal tracks, the second Encore track will only play if you've gotten the CD associated with the second normal track I haven't actually tested it *with* any Encore music, but I've triplechecked literally everywhere to make sure it didn't break standard play --- src/deh_soc.c | 24 ++++++++++++++++++++++++ src/doomstat.h | 2 ++ src/k_podium.cpp | 9 ++++++++- src/lua_maplib.c | 24 ++++++++++++++++++++++++ src/m_cond.c | 5 +++-- src/music_tune.hpp | 7 ++++++- src/p_setup.cpp | 18 ++++++++++++++---- src/s_sound.c | 41 +++++++++++++++++++++++++++++++---------- 8 files changed, 112 insertions(+), 18 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 18ea9fe62..047a396af 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1242,6 +1242,30 @@ void readlevelheader(MYFILE *f, char * name) mapheaderinfo[num]->musname_size = j; } } + else if (fastcmp(word, "ENCOREMUSIC")) + { + if (fastcmp(word2, "NONE")) + { + mapheaderinfo[num]->encoremusname[0][0] = 0; // becomes empty string + mapheaderinfo[num]->encoremusname_size = 0; + } + else + { + UINT8 j = 0; // i was declared elsewhere + tmp = strtok(word2, ","); + do { + if (j >= MAXMUSNAMES) + break; + deh_strlcpy(mapheaderinfo[num]->encoremusname[j], tmp, + sizeof(mapheaderinfo[num]->encoremusname[j]), va("Level header %d: encore music", num)); + j++; + } while ((tmp = strtok(NULL,",")) != NULL); + + if (tmp != NULL) + deh_warning("Level header %d: additional music slots past %d discarded", num, MAXMUSNAMES); + mapheaderinfo[num]->encoremusname_size = j; + } + } else if (fastcmp(word, "ASSOCIATEDMUSIC")) { if (fastcmp(word2, "NONE")) diff --git a/src/doomstat.h b/src/doomstat.h index 761a3410b..255600a47 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -515,10 +515,12 @@ struct mapheader_t // Music information char musname[MAXMUSNAMES][7]; ///< Music tracks to play. First dimension is the track number, second is the music string. "" for no music. + char encoremusname[MAXMUSNAMES][7]; ///< Music tracks to play in Encore. First dimension is the track number, second is the music string. "" for no music. UINT16 cache_muslock[MAXMUSNAMES-1]; ///< Cached Alt Music IDs char associatedmus[MAXMUSNAMES][7]; ///< Associated music tracks for sound test unlock. char positionmus[7]; ///< Custom Position track. Doesn't play in Encore or other fun game-controlled contexts UINT8 musname_size; ///< Number of music tracks defined + UINT8 encoremusname_size; ///< Number of Encore music tracks defined UINT8 associatedmus_size; ///< Number of associated music tracks defined UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. UINT32 muspos; ///< Music position to jump to. diff --git a/src/k_podium.cpp b/src/k_podium.cpp index 272704a33..8e5ef73eb 100644 --- a/src/k_podium.cpp +++ b/src/k_podium.cpp @@ -1247,7 +1247,14 @@ void K_ResetCeremony(void) mapmusrng = 0; } - while (mapmusrng >= std::max(1, mapheaderinfo[gamemap-1]->musname_size)) + UINT8 limit = (encoremode && mapheaderinfo[gamemap-1]->encoremusname_size) + ? mapheaderinfo[gamemap-1]->encoremusname_size + : mapheaderinfo[gamemap-1]->musname_size; + + if (limit < 1) + limit = 1; + + while (mapmusrng >= limit) { mapmusrng--; } diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 9117858d1..adb65325f 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -2507,12 +2507,36 @@ static int mapheaderinfo_get(lua_State *L) lua_rawseti(L, -2, 1 + i); } } + else if (fastcmp(field,"encoremusname")) // we create a table here because it saves us from a userdata nightmare + { + UINT8 i; + lua_createtable(L, header->encoremusname_size, 0); + for (i = 0; i < header->encoremusname_size; i++) + { + lua_pushstring(L, header->encoremusname[i]); + lua_rawseti(L, -2, 1 + i); + } + } + else if (fastcmp(field,"associatedmus")) // we create a table here because it saves us from a userdata nightmare + { + UINT8 i; + lua_createtable(L, header->associatedmus_size, 0); + for (i = 0; i < header->associatedmus_size; i++) + { + lua_pushstring(L, header->associatedmus[i]); + lua_rawseti(L, -2, 1 + i); + } + } else if (fastcmp(field,"mustrack")) lua_pushinteger(L, header->mustrack); else if (fastcmp(field,"muspos")) lua_pushinteger(L, header->muspos); else if (fastcmp(field,"musname_size")) lua_pushinteger(L, header->musname_size); + else if (fastcmp(field,"encoremusname_size")) + lua_pushinteger(L, header->encoremusname_size); + else if (fastcmp(field,"associatedmus_size")) + lua_pushinteger(L, header->associatedmus_size); else if (fastcmp(field,"weather")) lua_pushinteger(L, header->weather); else if (fastcmp(field,"skytexture")) diff --git a/src/m_cond.c b/src/m_cond.c index 72a9f7792..b2d675894 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -1077,7 +1077,8 @@ static void M_PrecacheLevelLocks(void) if (map < nummapheaders && mapheaderinfo[map]) { - for (j = 1; j < mapheaderinfo[map]->musname_size; j++) + UINT8 greatersize = max(mapheaderinfo[map]->musname_size, mapheaderinfo[map]->encoremusname_size); + for (j = 1; j < greatersize; j++) { if (mapheaderinfo[map]->cache_muslock[j - 1] != MAXUNLOCKABLES) { @@ -1143,7 +1144,7 @@ static void M_PrecacheLevelLocks(void) break; } - if (j == mapheaderinfo[map]->musname_size) + if (j == greatersize) CONS_Alert(CONS_ERROR, "Unlockable %u: Too many SECRET_ALTMUSICs associated with Level %s\n", i+1, mapheaderinfo[map]->lumpname); } else diff --git a/src/music_tune.hpp b/src/music_tune.hpp index d8b859b5d..405832abe 100644 --- a/src/music_tune.hpp +++ b/src/music_tune.hpp @@ -103,7 +103,12 @@ public: return (1.f/encoremul); } - return encoremul; + if (!nightcoreable + || mapheaderinfo[gamemap-1]->encoremusname_size == 0) + { + // We only vape if the level doesn't have alternate tracks. + return encoremul; + } } return 1.f; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 3357c18f0..6820def0d 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -446,6 +446,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 num) mapheaderinfo[num]->musname[0][0] = 0; mapheaderinfo[num]->musname_size = 0; + mapheaderinfo[num]->encoremusname[0][0] = 0; + mapheaderinfo[num]->encoremusname_size = 0; for (i = 0; i < MAXMUSNAMES-1; i++) { @@ -8194,6 +8196,9 @@ void P_ResetLevelMusic(void) UINT8 idx = 0; mapheader_t* mapheader = mapheaderinfo[gamemap - 1]; + UINT8 truesize = (encoremode && mapheader->encoremusname_size) + ? mapheader->encoremusname_size + : mapheader->musname_size; // To keep RNG in sync, we will always pull from RNG, even if unused UINT32 random = P_Random(PR_MUSICSELECT); @@ -8201,20 +8206,20 @@ void P_ResetLevelMusic(void) if (demo.playback) { // mapmusrng has already been set by the demo; just make sure it's valid - if (mapmusrng >= mapheader->musname_size) + if (mapmusrng >= truesize) { mapmusrng = 0; } return; } - if (mapheader->musname_size > 1) + if (truesize > 1) { UINT8 tempmapmus[MAXMUSNAMES], tempmapmus_size = 1, i; tempmapmus[0] = 0; - for (i = 1; i < mapheader->musname_size; i++) + for (i = 1; i < truesize; i++) { if (mapheader->cache_muslock[i-1] < MAXUNLOCKABLES && !M_CheckNetUnlockByID(mapheader->cache_muslock[i-1])) @@ -8260,7 +8265,12 @@ void P_LoadLevelMusic(void) mapheader_t* mapheader = mapheaderinfo[gamemap-1]; const char *music = mapheader->musname[0]; - if (mapmusrng < mapheader->musname_size) + if (encoremode && mapheader->encoremusname_size + && mapmusrng < mapheader->encoremusname_size) + { + music = mapheader->encoremusname[mapmusrng]; + } + else if (mapmusrng < mapheader->musname_size) { music = mapheader->musname[mapmusrng]; } diff --git a/src/s_sound.c b/src/s_sound.c index 6daecbb26..7a10b4926 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1292,6 +1292,11 @@ static void S_InsertMapIntoSoundTestSequence(UINT16 map, musicdef_t ***tail) { S_InsertMusicAtSoundTestSequenceTail(mapheaderinfo[map]->associatedmus[i], map, ALTREF_REQUIRESBEATEN, tail); } + + for (i = 0; i < mapheaderinfo[map]->encoremusname_size; i++) + { + S_InsertMusicAtSoundTestSequenceTail(mapheaderinfo[map]->encoremusname[i], map, i+MAXMUSNAMES, tail); + } } void S_PopulateSoundTestSequence(void) @@ -1435,17 +1440,33 @@ static boolean S_SoundTestDefLocked(musicdef_t *def) && !(header->records.mapvisited & MV_VISITED)) return true; - // Associated music only when completed - if ((def->sequence.altref == ALTREF_REQUIRESBEATEN) - && !(header->records.mapvisited & MV_BEATEN)) - return true; - - if (def->sequence.altref != 0 && def->sequence.altref < header->musname_size) + if (def->sequence.altref != 0) { - // Alt music requires unlocking the alt - if ((header->cache_muslock[def->sequence.altref - 1] < MAXUNLOCKABLES) - && gamedata->unlocked[header->cache_muslock[def->sequence.altref - 1]] == false) - return true; + if ((def->sequence.altref == ALTREF_REQUIRESBEATEN)) + { + // Associated music only when completed + if (!(header->records.mapvisited & MV_BEATEN)) + return true; + } + else if (def->sequence.altref < MAXMUSNAMES) + { + // Alt music requires unlocking the alt + if ((header->cache_muslock[def->sequence.altref - 1] < MAXUNLOCKABLES) + && gamedata->unlocked[header->cache_muslock[def->sequence.altref - 1]] == false) + return true; + } + else if (def->sequence.altref < MAXMUSNAMES*2) + { + // Encore! + if (M_SecretUnlocked(SECRET_ENCORE, true) == false) + return true; + + // Side B of the same CD + if (def->sequence.altref > MAXMUSNAMES + && (header->cache_muslock[def->sequence.altref - (1 + MAXMUSNAMES)] < MAXUNLOCKABLES) + && gamedata->unlocked[header->cache_muslock[def->sequence.altref - (1 + MAXMUSNAMES)]] == false) + return true; + } } // Finally, do a full-fat map check.