From aa1cd4d7d024d4b908fd5502ee46e5b95a4d0be7 Mon Sep 17 00:00:00 2001 From: Cheariisan Date: Wed, 28 Aug 2024 22:04:31 +0000 Subject: [PATCH] Expose most of the music handler to Lua --- src/deh_tables.c | 11 ++ src/lua_baselib.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++ src/lua_script.h | 3 + src/music.cpp | 53 ++++++ src/music.h | 31 ++++ 5 files changed, 531 insertions(+) diff --git a/src/deh_tables.c b/src/deh_tables.c index c2fe2660c..c8da6cabd 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -25,6 +25,7 @@ #include "r_data.h" // patchalphastyle_t #include "k_boss.h" // spottype_t (for lua) #include "k_follower.h" // followermode_t (for lua) +#include "music.h" // tune flags (for lua) #include "deh_tables.h" @@ -5161,6 +5162,16 @@ struct int_const_s const INT_CONST[] = { {"FOLLOWERMODE_FLOAT",FOLLOWERMODE_FLOAT}, {"FOLLOWERMODE_GROUND",FOLLOWERMODE_GROUND}, + // tune flags + {"TN_INCLUSIVEFADE",TN_INCLUSIVEFADE}, + {"TN_USEMAPVOLUME",TN_USEMAPVOLUME}, + {"TN_SYNCMUSIC",TN_SYNCMUSIC}, + {"TN_MUSICCRED",TN_MUSICCRED}, + {"TN_VAPES",TN_VAPES}, + {"TN_NIGHTCOREABLE",TN_NIGHTCOREABLE}, + {"TN_CHANGEPITCH",TN_CHANGEPITCH}, + {"TN_LOOPING",TN_LOOPING}, + {NULL,0} }; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 13854375b..4ee70339b 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -39,6 +39,7 @@ #include "hu_stuff.h" // for the cecho #include "k_powerup.h" #include "k_hitlag.h" +#include "music.h" // music functions necessary for lua integration #include "lua_script.h" #include "lua_libs.h" @@ -339,6 +340,418 @@ static int lib_reserveLuabanks(lua_State *L) return 1; } +// MUSIC +//////////// + +static int lib_mMusicAddTune(lua_State *L) +{ + UINT32 priority, tuneflags; + + if (!lua_lumploading) + return luaL_error(L, "Tunes cannot be added from within a hook or coroutine!"); + + //NOHUD + const char *tune_id = luaL_checkstring(L, 1); + priority = (UINT32)luaL_optinteger(L, 2, 0); + tuneflags = (UINT32)luaL_optinteger(L, 3, 0); + + if (!Music_TuneExists(tune_id)) + { + Music_AddTune(tune_id, priority, tuneflags); + } + + return 0; +} + +static int lib_mMusicPlay(lua_State *L) +{ + const char *tune_id = luaL_checkstring(L, 1); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + { + player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_Play(tune_id); + + return 0; +} + +static int lib_mMusicStopAll(lua_State *L) +{ + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 1) && lua_isuserdata(L, 1)) + { + player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + Music_StopAll(); + + return 0; +} + +static int lib_mMusicPauseAll(lua_State *L) +{ + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 1) && lua_isuserdata(L, 1)) + { + player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + Music_PauseAll(); + + return 0; +} + +static int lib_mMusicUnPauseAll(lua_State *L) +{ + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 1) && lua_isuserdata(L, 1)) + { + player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + Music_UnPauseAll(); + + return 0; +} + +static int lib_mMusicRemap(lua_State *L) +{ + const char *tune_id = luaL_checkstring(L, 1); + const char *music_name = luaL_checkstring(L, 2); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) + { + player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_Remap(tune_id, music_name); + + return 0; +} + +static int lib_mMusicDim(lua_State *L) +{ + tic_t fade = (tic_t)luaL_checkinteger(L, 1); + tic_t duration = INFTICS; + + // If a dim is ongoing, do not interrupt it + if (g_musicfade.start < leveltime && g_musicfade.end < leveltime) + { + g_musicfade.start = leveltime; + } + + if (!lua_isnoneornil(L, 2)) + { + duration = (tic_t)luaL_checkinteger(L, 2); + } + + g_musicfade.end = (duration != INFTICS) ? leveltime + duration + 2*fade : INFTICS; + g_musicfade.fade = fade; + g_musicfade.ticked = false; + + return 0; +} + +static int lib_mMusicSetFadeOut(lua_State *L) +{ + UINT32 fadeoutms; + + const char *tune_id = luaL_checkstring(L, 1); + fadeoutms = (UINT32)luaL_optinteger(L, 2, 0); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) + { + player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_SetFadeOut(tune_id, fadeoutms); + + return 0; +} + +static int lib_mMusicSetFadeIn(lua_State *L) +{ + UINT32 fadeinms; + + const char *tune_id = luaL_checkstring(L, 1); + fadeinms = (UINT32)luaL_optinteger(L, 2, 0); + boolean resumefade = lua_optboolean(L, 3); + + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 4) && lua_isuserdata(L, 4)) + { + player = *((player_t **)luaL_checkudata(L, 4, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_SetFadeIn(tune_id, fadeinms, resumefade); + + return 0; +} + +static int lib_mMusicDelayEnd(lua_State *L) +{ + tic_t duration; + + const char *tune_id = luaL_checkstring(L, 1); + duration = (tic_t)luaL_optinteger(L, 2, 0); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) + { + player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_DelayEnd(tune_id, duration); + + return 0; +} + +static int lib_mMusicSeek(lua_State *L) +{ + UINT32 seekms; + + const char *tune_id = luaL_checkstring(L, 1); + seekms = (UINT32)luaL_optinteger(L, 2, 0); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) + { + player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_Seek(tune_id, seekms); + + return 0; +} + +static int lib_mMusicStop(lua_State *L) +{ + const char *tune_id = luaL_checkstring(L, 1); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + { + player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_Stop(tune_id); + + return 0; +} + +static int lib_mMusicPause(lua_State *L) +{ + const char *tune_id = luaL_checkstring(L, 1); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + { + player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_Pause(tune_id); + + return 0; +} + +static int lib_mMusicUnPause(lua_State *L) +{ + const char *tune_id = luaL_checkstring(L, 1); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + { + player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_UnPause(tune_id); + + return 0; +} + +static int lib_mMusicSuspend(lua_State *L) +{ + const char *tune_id = luaL_checkstring(L, 1); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + { + player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_Suspend(tune_id); + + return 0; +} + +static int lib_mMusicUnSuspend(lua_State *L) +{ + const char *tune_id = luaL_checkstring(L, 1); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + { + player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_UnSuspend(tune_id); + + return 0; +} + +static int lib_mMusicLoop(lua_State *L) +{ + const char *tune_id = luaL_checkstring(L, 1); + boolean loop = lua_optboolean(L, 2); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) + { + player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_Loop(tune_id, loop); + + return 0; +} + +static int lib_mMusicBatchExempt(lua_State *L) +{ + const char *tune_id = luaL_checkstring(L, 1); + player_t *player = NULL; + + //NOHUD + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + { + player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + + if (!Music_TuneExists(tune_id)) + { + return LUA_ErrNoTune(L, tune_id); + } + + Music_BatchExempt(tune_id); + + return 0; +} + // M_MENU ////////////// @@ -3933,6 +4346,26 @@ static luaL_Reg lib[] = { // hu_stuff technically? {"HU_DoTitlecardCEcho", lib_startTitlecardCecho}, + // music + {"Music_AddTune", lib_mMusicAddTune}, + {"Music_Play", lib_mMusicPlay}, + {"Music_Remap", lib_mMusicRemap}, + {"Music_SetFadeOut", lib_mMusicSetFadeOut}, + {"Music_SetFadeIn", lib_mMusicSetFadeIn}, + {"Music_DelayEnd", lib_mMusicDelayEnd}, + {"Music_Dim", lib_mMusicDim}, + {"Music_Seek", lib_mMusicSeek}, + {"Music_Stop", lib_mMusicStop}, + {"Music_Pause", lib_mMusicPause}, + {"Music_UnPause", lib_mMusicUnPause}, + {"Music_Suspend", lib_mMusicSuspend}, + {"Music_UnSuspend", lib_mMusicUnSuspend}, + {"Music_StopAll", lib_mMusicStopAll}, + {"Music_PauseAll", lib_mMusicPauseAll}, + {"Music_UnPauseAll", lib_mMusicUnPauseAll}, + {"Music_Loop", lib_mMusicLoop}, + {"Music_BatchExempt", lib_mMusicBatchExempt}, + {NULL, NULL} }; diff --git a/src/lua_script.h b/src/lua_script.h index 00ad2526d..33c9bfca5 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -114,6 +114,9 @@ void COM_Lua_f(void); #define LUA_ErrSetDirectly(L, type, field) luaL_error(L, type " field " LUA_QL(field) " cannot be set directly.") +// Music: "No tune" error. +#define LUA_ErrNoTune(L, tune) luaL_error(L, "tune \"%s\" does not exist", tune) + // Deprecation warnings // Shows once upon use. Then doesn't show again. #define LUA_Deprecated(L,this_func,use_instead)\ diff --git a/src/music.cpp b/src/music.cpp index 9d9384b5f..e9aab9b60 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -219,6 +219,8 @@ void Music_Init(void) } } + + void Music_Tick(void) { g_tunes.tick(); @@ -229,6 +231,22 @@ void Music_Flip(void) g_tunes.flip(); } +void Music_AddTune(const char* id, int priority, int tuneflags) +{ + Tune& tune = g_tunes.insert(id); + + tune.song = ""; + tune.priority = priority; + + tune.loop = (tuneflags & TN_LOOPING); + tune.fade_out_inclusive = (tuneflags & TN_INCLUSIVEFADE); + tune.use_level_volume = (tuneflags & TN_USEMAPVOLUME); + tune.sync = (tuneflags & TN_SYNCMUSIC); + tune.credit = (tuneflags & TN_MUSICCRED); + tune.vapes = (tuneflags & TN_VAPES); + tune.nightcoreable = (tuneflags & TN_NIGHTCOREABLE); +} + void Music_Play(const char* id) { Tune* tune = g_tunes.find(id); @@ -257,6 +275,30 @@ void Music_SetFadeOut(const char* id, int fade_out) } } +void Music_SetFadeIn(const char* id, int fade_in, boolean resume) +{ + Tune* tune = g_tunes.find(id); + + if (tune) + { + if (resume) + { + tune->resume_fade_in = fade_in; + } + else + { + tune->fade_in = fade_in; + } + + if (tune->elapsed() <= detail::msec_to_tics(fade_in)) + { + // If this action would cause a fade in, start + // fading immediately. + g_tunes.tick(); + } + } +} + void Music_DelayEnd(const char* id, tic_t duration) { Tune* tune = g_tunes.find(id); @@ -368,6 +410,17 @@ void Music_Remap(const char* id, const char* song) } } +boolean Music_TuneExists(const char* id) +{ + const Tune* tune = g_tunes.find(id); + + if (tune) + { + return true; + } + return false; +} + boolean Music_Playing(const char* id) { const Tune* tune = g_tunes.find(id); diff --git a/src/music.h b/src/music.h index e8ed7dbea..4b005e282 100644 --- a/src/music.h +++ b/src/music.h @@ -35,6 +35,29 @@ extern "C" { #endif +// +// Tuneflags, for Music_AddTune +// + +// inclusive fade out +#define TN_INCLUSIVEFADE 0x01 + +// should the tune use the level volume? +#define TN_USEMAPVOLUME 0x02 + +// sync tune to game logic +#define TN_SYNCMUSIC 0x04 + +// show tune credit (only on first play) +#define TN_MUSICCRED 0x08 + +// allow the game to slow down the music in encore mode +#define TN_VAPES 0x10 +#define TN_NIGHTCOREABLE 0x20 +#define TN_CHANGEPITCH (TN_VAPES | TN_NIGHTCOREABLE) + +// looping? +#define TN_LOOPING 0x40 // // Get the currently playing tune. @@ -54,6 +77,8 @@ const char *Music_CurrentId(void); // Actions that take effect immediately. // +// Add a new tune to the tunes list. +void Music_AddTune(const char* id, int priority, int tuneflags); // Begin playing a tune, duration is infinite. If the tune was // already playing, this resets its current position (seeks @@ -64,6 +89,10 @@ void Music_Play(const char *id); // with Stereo Mode. void Music_SetFadeOut(const char* id, int fade_out); +// Set fade in duration. Done for parity with the BLUA music +// functions. +void Music_SetFadeIn(const char* id, int fade_in, boolean resume); + // Postpone the end of this tune until N tics from now. The // tune should already be playing before calling this. void Music_DelayEnd(const char *id, tic_t duration); @@ -121,6 +150,8 @@ void Music_ResetLevelVolume(void); // Query properties. // +// Returns true if the tune exists. +boolean Music_TuneExists(const char* id); // Returns true if the tune is configured to loop. boolean Music_CanLoop(const char *id);