diff --git a/LUASTUFF.md b/LUASTUFF.md index 8960fad48..83ec76545 100644 --- a/LUASTUFF.md +++ b/LUASTUFF.md @@ -161,6 +161,37 @@ Duration is in milliseconds and is optional to set, default value is 84ms. Exactly same as P_CheckSight but uses cheaper algorithm, useful for things like nametags. Doesn't work exactly like P_CheckSight so don't use it for anything gameplay-related. +## musicdef_t + +Userdata structure representing a musicdef. Fields: + +`musicdef.name` - song identifier ("kmap01" for example). + +`musicdef.usage`, `musicdef.source` - fields from vanilla MUSICDEFS lump. + +`musicdef.filename` - unused for now. + +`musicdef.title`, `musicdef.alttitle`, `musicdef.authors` - fields from MUSCINFO lump. + +`#musicdef` - returns integer id for musicdef (which can be used as index in `musicdefs`). + +All fields are read-only. + +## S_FindMusicCredit(name) + +Returns musicdef corresponding to music with given identifier. For example, `S_FindMusicCredit("kmap01")` will return +musicdef for green hills music. + +## musicdefs + +Global table for all musicdefs, similar to mobjinfo, states, etc. Can take either integer indices, +from `0` to `#musicdefs-1`, or string indices (which is equal to calling `S_FindMusicCredit`). + +## addHook("MusicCredit", function(musicdef)) + +Hook is called whenever `S_ShowMusicCredit` (either from game or mod) is called. Takes musicdef as only argument, returning true +will overwrite vanilla behavior (not show music credit), can be used to implement custom music credit pop-ups. + # Other changes ## P_PlayRinglossSound(source, damager) diff --git a/src/lua_hook.h b/src/lua_hook.h index 45237aa80..c31a58a67 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -55,6 +55,7 @@ X (PlayerQuit),\ X (PlayerThink),/* P_PlayerThink */\ X (MusicChange),\ + X (MusicCredit),\ X (ShouldSpin),/*SRB2KART*/\ X (ShouldExplode),/*SRB2KART*/\ X (ShouldSquish),/*SRB2KART*/\ @@ -130,6 +131,7 @@ int LUA_HookPlayerCanDamage(player_t *, mobj_t *); void LUA_HookPlayerQuit(player_t *, int); int LUA_HookPlayerCmd(player_t *, ticcmd_t *); int LUA_HookMusicChange(const char *oldname, struct MusicChange *); +int LUA_HookMusicCredit(musicdef_t *musicdef); int LUA_HookShouldSpin(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Should player be spun out? int LUA_HookShouldExplode(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Should player be exploded? diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index a0f795985..1ef02b886 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -1065,6 +1065,18 @@ int LUA_HookMusicChange(const char *oldname, struct MusicChange *param) return hook.status; } +int LUA_HookMusicCredit(musicdef_t *musicdef) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(MusicCredit))) + { + LUA_PushUserdata(gL, musicdef, META_MUSICDEF); + call_hooks(&hook, 1, res_true); + } + + return hook.status; +} + static int kartdamage_hook ( player_t *player, diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 3ff2d97ae..0ac308bcb 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -16,6 +16,7 @@ #include "dehacked.h" #include "p_mobj.h" #include "p_local.h" +#include "s_sound.h" #include "z_zone.h" #include "lua_script.h" @@ -1072,6 +1073,109 @@ static int sfxinfo_num(lua_State *L) return 1; } +enum musicdef_e { + musicdef_name = 0, + musicdef_usage, + musicdef_source, + musicdef_filename, + musicdef_title, + musicdef_alttitle, + musicdef_authors, +}; + +const char *const musicdef_opt[] = { + "name", + "usage", + "source", + "filename", + "title", + "alttitle", + "authors", + NULL +}; + +static int musicdef_fields_ref = LUA_NOREF; + +static int lib_sFindMusicCredit(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + LUA_PushUserdata(L, S_FindMusicCredit(name), META_MUSICDEF); + return 1; +} + +static int lib_getMusicDef(lua_State *L) +{ + musicdef_t *def = NULL; + + if (lua_isnumber(L, 2)) + def = S_GetMusicCredit(lua_tonumber(L, 2)); + else if (lua_isstring(L, 2)) + def = S_FindMusicCredit(lua_tostring(L, 2)); + else + return luaL_error(L, "musicdefs index must be number or string (got %s)", luaL_typename(L, 2)); + + LUA_PushUserdata(L, def, META_MUSICDEF); + + return 1; +} + +static int lib_setMusicDef(lua_State *L) +{ + return luaL_error(L, "musicdefs is read only"); +} + +static int lib_musicdefslen(lua_State *L) +{ + lua_pushinteger(L, nummusicdefs); + return 1; +} + +static int musicdef_get(lua_State *L) +{ + musicdef_t *musicdef = *((musicdef_t **)luaL_checkudata(L, 1, META_MUSICDEF)); + enum musicdef_e field = Lua_optoption(L, 2, -1, musicdef_fields_ref); + + I_Assert(musicdef != NULL); + + switch (field) + { + case musicdef_name: + lua_pushstring(L, musicdef->name); + return 1; + case musicdef_usage: + lua_pushstring(L, musicdef->usage); + return 1; + case musicdef_source: + lua_pushstring(L, musicdef->source); + return 1; + case musicdef_filename: + lua_pushstring(L, musicdef->filename); + return 1; + case musicdef_title: + lua_pushstring(L, musicdef->title); + return 1; + case musicdef_alttitle: + lua_pushstring(L, musicdef->alttitle); + return 1; + case musicdef_authors: + lua_pushstring(L, musicdef->authors); + return 1; + default: + return luaL_error(L, LUA_QL("musicdef_t") " has no field named " LUA_QS, lua_tostring(L, 2)); + } + return 0; +} + +static int musicdef_num(lua_State *L) +{ + musicdef_t *musicdef = *((musicdef_t **)luaL_checkudata(L, 1, META_MUSICDEF)); + + I_Assert(musicdef != NULL); + + lua_pushinteger(L, musicdef->num); + return 1; +} + ////////////////////////////// // // Now push all these functions into the Lua state! @@ -1126,6 +1230,18 @@ int LUA_InfoLib(lua_State *L) sfxinfo_fields_ref = Lua_CreateFieldTable(L, sfxinfo_opt); + luaL_newmetatable(L, META_MUSICDEF); + lua_pushcfunction(L, musicdef_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, musicdef_num); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + + musicdef_fields_ref = Lua_CreateFieldTable(L, musicdef_opt); + + lua_register(L, "S_FindMusicCredit", lib_sFindMusicCredit); + lua_newuserdata(L, 0); lua_createtable(L, 0, 2); lua_pushcfunction(L, lib_getSprname); @@ -1176,5 +1292,19 @@ int LUA_InfoLib(lua_State *L) lua_pushvalue(L, -1); lua_setglobal(L, "S_sfx"); lua_setglobal(L, "sfxinfo"); + + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getMusicDef); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_setMusicDef); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, lib_musicdefslen); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "musicdefs"); + return 0; } diff --git a/src/lua_libs.h b/src/lua_libs.h index c226c4714..729cea68d 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -10,6 +10,9 @@ /// \file lua_libs.h /// \brief libraries for Lua scripting +#include "doomtype.h" +#include "blua/lua.h" + extern lua_State *gL; #define LREG_VALID "VALID_USERDATA" @@ -20,6 +23,7 @@ extern lua_State *gL; #define META_STATE "STATE_T*" #define META_MOBJINFO "MOBJINFO_T*" #define META_SFXINFO "SFXINFO_T*" +#define META_MUSICDEF "MUSICDEF_T*" #define META_MOBJ "MOBJ_T*" #define META_MAPTHING "MAPTHING_T*" @@ -68,4 +72,4 @@ int LUA_SkinLib(lua_State *L); int LUA_ThinkerLib(lua_State *L); int LUA_MapLib(lua_State *L); int LUA_BlockmapLib(lua_State *L); -int LUA_HudLib(lua_State *L); \ No newline at end of file +int LUA_HudLib(lua_State *L); diff --git a/src/m_menu.c b/src/m_menu.c index 2fb38a4d5..9e9fbe1c9 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8458,7 +8458,7 @@ static void M_MusicTest(INT32 choice) { (void)choice; - if (!S_PrepareSoundTest()) + if (!nummusicdefs) { M_StartMessage(M_GetText("No selectable tracks found.\n"),NULL,MM_NOTHING); return; @@ -8537,33 +8537,33 @@ static void M_DrawMusicTest(void) { INT32 t, b, q, m = 128; - if (numsoundtestdefs <= 8) + if (nummusicdefs <= 8) { t = 0; - b = numsoundtestdefs - 1; + b = nummusicdefs - 1; i = 0; } else { q = m; - m = (5*m)/numsoundtestdefs; + m = (5*m)/nummusicdefs; if (st_sel < 3) { t = 0; b = 7; i = 0; } - else if (st_sel >= numsoundtestdefs-4) + else if (st_sel >= nummusicdefs-4) { - t = numsoundtestdefs - 8; - b = numsoundtestdefs - 1; + t = nummusicdefs - 8; + b = nummusicdefs - 1; i = q-m; } else { t = st_sel - 3; b = st_sel + 4; - i = (t * (q-m))/(numsoundtestdefs - 8); + i = (t * (q-m))/(nummusicdefs - 8); } } @@ -8572,7 +8572,7 @@ static void M_DrawMusicTest(void) if (t != 0) V_DrawString(20+280+4, 60+4 - (skullAnimCounter/5), V_YELLOWMAP, "\x1A"); - if (b != numsoundtestdefs - 1) + if (b != nummusicdefs - 1) V_DrawString(20+280+4, 60+128-12 + (skullAnimCounter/5), V_YELLOWMAP, "\x1B"); x = 24; @@ -8586,8 +8586,9 @@ static void M_DrawMusicTest(void) V_DrawFill(20, y-4, 280-1, 16, 237); { + const musicdef_t *def = S_GetMusicCredit(t); const size_t MAXLENGTH = 34; - const char *songname = soundtestdefs[t]->title[0] ? soundtestdefs[t]->title : soundtestdefs[t]->source; + const char *songname = def->title[0] ? def->title : def->source; size_t namelength = strlen(songname); @@ -8600,7 +8601,7 @@ static void M_DrawMusicTest(void) buf[MAXLENGTH] = 0; V_DrawString(x, y, (t == st_sel ? V_YELLOWMAP : 0)|V_ALLOWLOWERCASE|V_MONOSPACE, buf); - if (curplaying == soundtestdefs[t]) + if (curplaying == def) { V_DrawFill(20+280-9, y-4, 8, 16, 230); } @@ -8618,7 +8619,7 @@ static void M_HandleMusicTest(INT32 choice) switch (choice) { case KEY_DOWNARROW: - if (st_sel++ >= numsoundtestdefs-1) + if (st_sel++ >= nummusicdefs-1) st_sel = 0; { S_StartSound(NULL, sfx_menu1); @@ -8627,18 +8628,18 @@ static void M_HandleMusicTest(INT32 choice) break; case KEY_UPARROW: if (!st_sel--) - st_sel = numsoundtestdefs-1; + st_sel = nummusicdefs-1; { S_StartSound(NULL, sfx_menu1); } st_musictime = 0; break; case KEY_PGDN: - if (st_sel < numsoundtestdefs-1) + if (st_sel < nummusicdefs-1) { st_sel += 3; - if (st_sel >= numsoundtestdefs-1) - st_sel = numsoundtestdefs-1; + if (st_sel >= nummusicdefs-1) + st_sel = nummusicdefs-1; S_StartSound(NULL, sfx_menu1); } st_musictime = 0; @@ -8672,7 +8673,7 @@ static void M_HandleMusicTest(INT32 choice) case KEY_ENTER: S_StopSounds(); S_StopMusic(); - curplaying = soundtestdefs[st_sel]; + curplaying = S_GetMusicCredit(st_sel); S_ChangeMusicInternal(curplaying->name, true); break; @@ -8681,9 +8682,6 @@ static void M_HandleMusicTest(INT32 choice) } if (exitmenu) { - Z_Free(soundtestdefs); - soundtestdefs = NULL; - if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else diff --git a/src/s_sound.c b/src/s_sound.c index 922d864d5..e4f15333f 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -32,6 +32,7 @@ #include "m_menu.h" // bird music stuff #include "lua_hook.h" // MusicChange hook +#include "lua_hud.h" // LUA_HudEnabled(hud_musiccredit) static boolean S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *vol, INT32 *sep, INT32 *pitch, sfxinfo_t *sfxinfo); static void SetChannelsNum(void); @@ -1202,8 +1203,58 @@ static consvar_t *music_refade_cv; /// Music Definitions /// ------------------------ -musicdef_t *musicdefstart = NULL; // First music definition -struct cursongcredit cursongcredit; // Currently displayed song credit info +// Similar system to vissprite allocation. Exists so external pointers to musicdefs are not invalidated +// when allocating more space for musicdefs +#define MUSICDEFCHUNKBITS 6 +#define MUSICDEFSPERCHUNK (1 << MUSICDEFCHUNKBITS) +#define MUSICDEFINDEXMASK (MUSICDEFSPERCHUNK - 1) +static musicdef_t **musicdefs = NULL; +static INT32 numchunks = 0; +INT32 nummusicdefs = 0; + +// +// S_GetMusicCredit +// +// Return music credit with given index. +// +musicdef_t *S_GetMusicCredit(INT32 i) +{ + if (i < 0 || i >= nummusicdefs) + return NULL; + + INT32 chunk = i >> MUSICDEFCHUNKBITS; + i &= MUSICDEFINDEXMASK; + + return &musicdefs[chunk][i]; +} + +// +// S_AddMusicCredit +// +// Return new music credit, allocates memory for it if needed. +// +static musicdef_t *S_AddMusicCredit(void) +{ + INT32 chunk = nummusicdefs >> MUSICDEFCHUNKBITS; + INT32 i = nummusicdefs & MUSICDEFINDEXMASK; + + // Allocate new chunk if needed. Other chunks stay valid + if (chunk == numchunks) + { + ++numchunks; + musicdefs = (musicdef_t**)Z_Realloc(musicdefs, sizeof(musicdef_t*)*numchunks, PU_STATIC, NULL); + musicdefs[chunk] = Z_Calloc(sizeof(musicdef_t)*MUSICDEFSPERCHUNK, PU_STATIC, NULL); + } + + // Store "id" of new musicdef (mostly exists only for lua) + musicdefs[chunk][i].num = nummusicdefs; + + ++nummusicdefs; + + return &musicdefs[chunk][i]; +} + +struct cursongcredit cursongcredit = {0}; // Currently displayed song credit info static boolean ReadMusicDefFields (UINT16 wadnum, int line, char *stoken, musicdef_t **defp) @@ -1231,14 +1282,11 @@ ReadMusicDefFields (UINT16 wadnum, int line, char *stoken, musicdef_t **defp) // Nothing found, add to the end. if (!def) { - def = Z_Calloc(sizeof (musicdef_t), PU_STATIC, NULL); + def = S_AddMusicCredit(); STRBUFCPY(def->name, value); strlwr(def->name); def->hash = quickncasehash (def->name, 6); - - def->next = musicdefstart; - musicdefstart = def; } (*defp) = def; @@ -1423,8 +1471,10 @@ musicdef_t *S_FindMusicCredit(const char *musname) UINT32 hash = quickncasehash (musname, 6); musicdef_t *def; - for (def = musicdefstart; def; def = def->next) + for (INT32 i = 0; i < nummusicdefs; ++i) { + def = S_GetMusicCredit(i); + if (hash != def->hash) continue; if (stricmp(def->name, musname)) @@ -1452,7 +1502,7 @@ void S_ShowSpecifiedMusicCredit(const char *musname) def = S_FindMusicCredit(musname); - if (def) + if (def && !LUA_HookMusicCredit(def)) { cursongcredit.def = def; cursongcredit.anim = 5*TICRATE; @@ -1471,41 +1521,6 @@ void S_ShowMusicCredit(void) S_ShowSpecifiedMusicCredit(music.name); } -musicdef_t **soundtestdefs = NULL; -INT32 numsoundtestdefs = 0; - -// -// S_PrepareSoundTest -// -// Prepare sound test. What am I, your butler? -// -boolean S_PrepareSoundTest(void) -{ - musicdef_t *def; - INT32 pos = numsoundtestdefs = 0; - - for (def = musicdefstart; def; def = def->next) - { - numsoundtestdefs++; - } - - if (!numsoundtestdefs) - return false; - - if (soundtestdefs) - Z_Free(soundtestdefs); - - if (!(soundtestdefs = Z_Malloc(numsoundtestdefs*sizeof(musicdef_t *), PU_STATIC, NULL))) - I_Error("S_PrepareSoundTest(): could not allocate soundtestdefs."); - - for (def = musicdefstart; def /*&& i < numsoundtestdefs*/; def = def->next) - { - soundtestdefs[pos++] = def; - } - - return true; -} - /// ------------------------ /// Music Status /// ------------------------ diff --git a/src/s_sound.h b/src/s_sound.h index 92e206e1b..d909bf63d 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -182,14 +182,9 @@ typedef struct musicdef_s char alttitle[256]; char authors[256]; boolean use_info; - struct musicdef_s *next; + size_t num; } musicdef_t; -extern musicdef_t *musicdefstart; -extern musicdef_t **soundtestdefs; -extern INT32 numsoundtestdefs; -extern UINT8 soundtestpage; - extern struct cursongcredit { musicdef_t *def; @@ -198,17 +193,17 @@ extern struct cursongcredit UINT8 trans; } cursongcredit; +extern INT32 nummusicdefs; void S_LoadMusicDefs(UINT16 wadnum); void S_InitMusicDefs(void); void S_LoadMTDefs(UINT16 wadnum); void S_InitMTDefs(void); +musicdef_t *S_GetMusicCredit(INT32 i); musicdef_t *S_FindMusicCredit(const char *musname); void S_ShowSpecifiedMusicCredit(const char *musname); void S_ShowMusicCredit(void); -boolean S_PrepareSoundTest(void); - // // Music Seeking //