diff --git a/src/b_bot.c b/src/b_bot.c index 7281013be..2bdbbbee9 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -36,7 +36,7 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm return; // Lua can handle it! - if (LUAh_BotAI(sonic, tails, cmd)) + if (LUA_HookBotAI(sonic, tails, cmd)) return; if (tails->player->pflags & (PF_MACESPIN|PF_ITEMHANG)) @@ -119,7 +119,7 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd) } // Let Lua scripts build ticcmds - if (LUAh_BotTiccmd(player, cmd)) + if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd))) return; // We don't have any main character AI, sorry. D: diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5feef98b2..89461616e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3118,7 +3118,7 @@ void CL_RemovePlayer(INT32 playernum, INT32 reason) } } - LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting + LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting if (playernum == displayplayers[0] && !demo.playback) displayplayers[0] = consoleplayer; // don't look through someone's view who isn't there @@ -4287,10 +4287,10 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) D_AddPostloadFiles(); - LUAh_PlayerJoin(newplayernum); + LUA_HookInt(newplayernum, HOOK(PlayerJoin)); if (newplayernum == consoleplayer) - LUAh_ServerJoin(); + LUA_HookVoid(HOOK(ServerJoin)); #ifdef HAVE_DISCORDRPC DRPC_UpdatePresence(); diff --git a/src/doomtype.h b/src/doomtype.h index d7ad320c8..429eba77e 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -365,6 +365,30 @@ typedef UINT32 tic_t; #define UINT2RGBA(a) (UINT32)((a&0xff)<<24)|((a&0xff00)<<8)|((a&0xff0000)>>8)|(((UINT32)a&0xff000000)>>24) #endif +#define TOSTR(x) #x + +typedef UINT8 bitarray_t; + +#define BIT_ARRAY_SIZE(n) (((n) + 7) >> 3) + +static inline int +in_bit_array (const bitarray_t * const array, const int value) +{ + return (array[value >> 3] & (1<<(value & 7))); +} + +static inline void +set_bit_array (bitarray_t * const array, const int value) +{ + array[value >> 3] |= (1<<(value & 7)); +} + +static inline void +unset_bit_array (bitarray_t * const array, const int value) +{ + array[value >> 3] &= ~(1<<(value & 7)); +} + typedef UINT64 precise_t; #endif //__DOOMTYPE__ diff --git a/src/g_game.c b/src/g_game.c index 6bb369c44..279cbdb00 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1201,7 +1201,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) -Making a Mario Kart 8 Deluxe tier baby mode that steers you away from walls and whatnot. You know what, do what you want! */ if (gamestate == GS_LEVEL) - LUAh_PlayerCmd(player, cmd); + LUA_HookTiccmd(player, cmd, HOOK(PlayerCmd)); //Reset away view if a command is given. if ((cmd->forwardmove || cmd->sidemove || cmd->buttons) @@ -2397,7 +2397,7 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost) if (starpost) //Don't even bother with looking for a place to spawn. { P_MovePlayerToStarpost(playernum); - LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) + LUA_HookPlayer(&players[playernum], HOOK(PlayerSpawn)); // Lua hook for player spawning :) return; } @@ -2443,7 +2443,7 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost) } P_MovePlayerToSpawn(playernum, spawnpoint); - LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) + LUA_HookPlayer(&players[playernum], HOOK(PlayerSpawn)); // Lua hook for player spawning :) } mapthing_t *G_FindCTFStart(INT32 playernum) @@ -3955,7 +3955,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer); else { - LUAh_MapChange(gamemap); + LUA_HookInt(gamemap, HOOK(MapChange)); S_CheckMap(); G_DoLoadLevel(resetplayer); } @@ -7076,7 +7076,7 @@ void G_DoPlayDemo(char *defdemoname) // didn't start recording right away. demo.deferstart = false; - //LUAh_MapChange(gamemap); + //LUA_HookInt(gamemap, HOOK(MapChange)); displayplayers[0] = consoleplayer = 0; memset(playeringame,0,sizeof(playeringame)); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 9da5f4752..73f2c23b9 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1000,7 +1000,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first. - if (LUAh_PlayerMsg(playernum, target, flags, msg, spam_eatmsg)) + if (LUA_HookPlayerMsg(playernum, target, flags, msg, spam_eatmsg)) return; if (spam_eatmsg) @@ -2271,7 +2271,7 @@ void HU_Drawer(void) if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_scores); - LUAh_ScoresHUD(luahuddrawlist_scores); + LUA_HUDHOOK(scores, luahuddrawlist_scores); } LUA_HUD_DrawList(luahuddrawlist_scores); diff --git a/src/k_kart.c b/src/k_kart.c index e9becb30e..3414b11e8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2333,7 +2333,7 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto UINT8 scoremultiply = 1; // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. boolean force = false; // Used to check if Lua ShouldSpin should get us damaged reguardless of flashtics or heck knows what. - UINT8 shouldForce = LUAh_ShouldSpin(player, inflictor, source); + UINT8 shouldForce = LUA_HookShouldSpin(player, inflictor, source); if (P_MobjWasRemoved(player->mo)) return; // mobj was removed (in theory that shouldn't happen) @@ -2366,7 +2366,7 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto } } - if (LUAh_PlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. + if (LUA_HookPlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. return; if (source && source != player->mo && source->player) @@ -2474,7 +2474,7 @@ void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) UINT8 scoremultiply = 1; // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. boolean force = false; // Used to check if Lua ShouldSquish should get us damaged reguardless of flashtics or heck knows what. - UINT8 shouldForce = LUAh_ShouldSquish(player, inflictor, source); + UINT8 shouldForce = LUA_HookShouldSquish(player, inflictor, source); if (P_MobjWasRemoved(player->mo)) return; // mobj was removed (in theory that shouldn't happen) if (shouldForce == 1) @@ -2504,7 +2504,7 @@ void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) } } - if (LUAh_PlayerSquish(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. + if (LUA_HookPlayerSquish(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. return; player->kartstuff[k_sneakertimer] = 0; @@ -2585,7 +2585,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b fixed_t upgoer; UINT8 scoremultiply = 1; boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what. - UINT8 shouldForce = LUAh_ShouldExplode(player, inflictor, source); + UINT8 shouldForce = LUA_HookShouldExplode(player, inflictor, source); if (P_MobjWasRemoved(player->mo)) return; // mobj was removed (in theory that shouldn't happen) @@ -2618,7 +2618,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b } } - if (LUAh_PlayerExplode(player, inflictor, source)) // Same thing. Also make sure to let Instashield happen blah blah + if (LUA_HookPlayerExplode(player, inflictor, source)) // Same thing. Also make sure to let Instashield happen blah blah return; if (source && source != player->mo && source->player) diff --git a/src/lua_hook.h b/src/lua_hook.h index 921336cd2..45237aa80 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -12,105 +12,129 @@ #include "r_defs.h" #include "d_player.h" +#include "s_sound.h" +#include "lua_hudlib_drawlist.h" -enum hook { - hook_NetVars=0, - hook_MapChange, - hook_MapLoad, - hook_PlayerJoin, - hook_PreThinkFrame, - hook_ThinkFrame, - hook_PostThinkFrame, - hook_MobjSpawn, - hook_MobjCollide, - hook_MobjMoveCollide, - hook_TouchSpecial, - hook_MobjFuse, - hook_MobjThinker, - hook_BossThinker, - hook_ShouldDamage, - hook_MobjDamage, - hook_MobjDeath, - hook_BossDeath, - hook_MobjRemoved, - hook_JumpSpecial, - hook_AbilitySpecial, - hook_SpinSpecial, - hook_JumpSpinSpecial, - hook_BotTiccmd, - hook_BotAI, - hook_LinedefExecute, - hook_PlayerMsg, - hook_HurtMsg, - hook_PlayerSpawn, - hook_PlayerQuit, - hook_PlayerThink, - hook_MusicChange, - hook_ShouldSpin, //SRB2KART - hook_ShouldExplode, //SRB2KART - hook_ShouldSquish, //SRB2KART - hook_PlayerSpin, //SRB2KART - hook_PlayerExplode, //SRB2KART - hook_PlayerSquish, //SRB2KART - hook_PlayerCmd, //SRB2KART - hook_IntermissionThinker, //SRB2KART - hook_VoteThinker, //SRB2KART - hook_ServerJoin, // SRB2KART - Saturn 32p +/* + * Do you know what an 'X Macro' is? Such a macro is called over each element of + * a list and expands the input. I use it for the hook lists because both an enum + * and array of hook names need to be kept in order. The X Macro handles this + * automatically. + */ - hook_MAX // last hook -}; -extern const char *const hookNames[]; +#define MOBJ_HOOK_LIST(X) \ + X (MobjSpawn),/* P_SpawnMobj */\ + X (MobjCollide),/* PIT_CheckThing */\ + X (MobjMoveCollide),/* tritto */\ + X (TouchSpecial),/* P_TouchSpecialThing */\ + X (MobjFuse),/* when mobj->fuse runs out */\ + X (MobjThinker),/* P_MobjThinker, P_SceneryThinker */\ + X (BossThinker),/* P_GenericBossThinker */\ + X (ShouldDamage),/* P_DamageMobj (Should mobj take damage?) */\ + X (MobjDamage),/* P_DamageMobj (Mobj actually takes damage!) */\ + X (MobjDeath),/* P_KillMobj */\ + X (BossDeath),/* A_BossDeath */\ + X (MobjRemoved),/* P_RemoveMobj */\ + X (HurtMsg),/* imhurttin */\ + +#define HOOK_LIST(X) \ + X (NetVars),/* add to archive table (netsave) */\ + X (MapChange),/* (before map load) */\ + X (MapLoad),\ + X (PlayerJoin),/* Got_AddPlayer */\ + X (PreThinkFrame)/* frame (before mobj and player thinkers) */,\ + X (ThinkFrame),/* frame (after mobj and player thinkers) */\ + X (PostThinkFrame),/* frame (at end of tick, ie after overlays, precipitation, specials) */\ + X (JumpSpecial),/* P_DoJumpStuff (Any-jumping) */\ + X (AbilitySpecial),/* P_DoJumpStuff (Double-jumping) */\ + X (SpinSpecial),/* P_DoSpinAbility (Spin button effect) */\ + X (JumpSpinSpecial),/* P_DoJumpStuff (Spin button effect (mid-air)) */\ + X (BotTiccmd),/* B_BuildTiccmd */\ + X (PlayerMsg),/* chat messages */\ + X (PlayerSpawn),/* G_SpawnPlayer */\ + X (PlayerQuit),\ + X (PlayerThink),/* P_PlayerThink */\ + X (MusicChange),\ + X (ShouldSpin),/*SRB2KART*/\ + X (ShouldExplode),/*SRB2KART*/\ + X (ShouldSquish),/*SRB2KART*/\ + X (PlayerSpin),/*SRB2KART*/\ + X (PlayerExplode),/*SRB2KART*/\ + X (PlayerSquish),/*SRB2KART*/\ + X (PlayerCmd),/* building the player's ticcmd struct (Ported from SRB2Kart) */\ + X (IntermissionThinker),/* Y_Ticker */\ + X (VoteThinker),/*SRB2KART*/\ + X (ServerJoin),/* SRB2KART - Saturn 32p*/\ + +#define STRING_HOOK_LIST(X) \ + X (BotAI),/* B_BuildTailsTiccmd by skin name */\ + X (LinedefExecute),\ + +#define HUD_HOOK_LIST(X) \ + X (game),\ + X (scores),/* emblems/multiplayer list */\ + X (intermission),\ + X (vote),\ + +/* + * I chose to access the hook enums through a macro as well. This could provide + * a hint to lookup the macro's definition instead of the enum's definition. + * (Since each enumeration is not defined in the source code, but by the list + * macros above, it is not greppable.) The name passed to the macro can also be + * grepped and found in the lists above. + */ + +#define MOBJ_HOOK(name) mobjhook_ ## name +#define HOOK(name) hook_ ## name +#define HUD_HOOK(name) hudhook_ ## name +#define STRING_HOOK(name) stringhook_ ## name + +#define ENUM(X) enum { X ## _LIST (X) X(MAX) } + +ENUM (MOBJ_HOOK); +ENUM (HOOK); +ENUM (HUD_HOOK); +ENUM (STRING_HOOK); + +#undef ENUM + +/* dead simple, LUA_HOOK(GameQuit) */ +#define LUA_HOOK(type) LUA_HookVoid(HOOK(type)) +#define LUA_HUDHOOK(type,drawlist) LUA_HookHUD(HUD_HOOK(type),(drawlist)) extern boolean hook_cmd_running; // This is used by PlayerCmd and lua_playerlib to prevent anything from being wirtten to player while we run PlayerCmd. extern int hook_defrosting; -void LUAh_MapChange(INT16 mapnumber); // Hook for map change (before load) -void LUAh_MapLoad(void); // Hook for map load -void LUAh_PlayerJoin(int playernum); // Hook for Got_AddPlayer -void LUAh_PreThinkFrame(void); // Hook for frame (before mobj and player thinkers) -void LUAh_ThinkFrame(void); // Hook for frame (after mobj and player thinkers) -void LUAh_PostThinkFrame(void); // Hook for frame (at end of tick, ie after overlays, precipitation, specials) -boolean LUAh_MobjHook(mobj_t *mo, enum hook which); -boolean LUAh_PlayerHook(player_t *plr, enum hook which); -#define LUAh_MobjSpawn(mo) LUAh_MobjHook(mo, hook_MobjSpawn) // Hook for P_SpawnMobj by mobj type -UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which); -#define LUAh_MobjCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjCollide) // Hook for PIT_CheckThing by (thing) mobj type -#define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type -boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type -#define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type -boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type -#define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type -UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Should mobj take damage?) -boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) -boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source); // Hook for P_KillMobj by mobj type -#define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type -#define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type -#define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping) -#define LUAh_AbilitySpecial(player) LUAh_PlayerHook(player, hook_AbilitySpecial) // Hook for P_DoJumpStuff (Double-jumping) -#define LUAh_SpinSpecial(player) LUAh_PlayerHook(player, hook_SpinSpecial) // Hook for P_DoSpinDash (Spin button effect) -#define LUAh_JumpSpinSpecial(player) LUAh_PlayerHook(player, hook_JumpSpinSpecial) // Hook for P_DoJumpStuff (Spin button effect (mid-air)) -boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd); // Hook for B_BuildTiccmd -boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name -boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors -boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg, int mute); // Hook for chat messages -boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source); // Hook for hurt messages -#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer -void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting -boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, - UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms); // Hook for music changes +void LUA_HookVoid(int hook); +void LUA_HookHUD(int hook, huddrawlist_h drawlist); -UINT8 LUAh_ShouldSpin(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Should player be spun out? -UINT8 LUAh_ShouldExplode(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Should player be exploded? -UINT8 LUAh_ShouldSquish(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Should player be squished? +int LUA_HookMobj(mobj_t *, int hook); +int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook); +void LUA_HookInt(INT32 integer, int hook); +//void LUA_HookBool(boolean value, int hook); +int LUA_HookPlayer(player_t *, int hook); +int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook); -boolean LUAh_PlayerSpin(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Hook for K_SpinPlayer. Allows Lua to execute code and/or overwrite its behavior. -boolean LUAh_PlayerExplode(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Hook for K_ExplodePlayer. Allows Lua to execute code and/or overwrite its behavior. -boolean LUAh_PlayerSquish(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Hook for K_SquishPlayer. Allows Lua to execute code and/or overwrite its behavior. +void LUA_HookPreThinkFrame(void); +void LUA_HookThinkFrame(void); +void LUA_HookPostThinkFrame(void); +int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher); +int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); +int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); +int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source); +int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); +void LUA_HookLinedefExecute(line_t *, mobj_t *, sector_t *); +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg, int mute); +int LUA_HookHurtMsg(player_t *, mobj_t *inflictor, mobj_t *source); +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 *); -void LUAh_ServerJoin(void); // SRB2KART - Saturn 32p: called by client when joining a server, allows lua to load any sort of config file +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? +int LUA_HookShouldSquish(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Should player be squished? -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Allows to write to player cmd before the game does anything with them. - -void LUAh_IntermissionThinker(void); // Hook for Y_Ticker -void LUAh_VoteThinker(void); // Hook for Y_VoteTicker -#define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink +boolean LUA_HookPlayerSpin(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Hook for K_SpinPlayer. Allows Lua to execute code and/or overwrite its behavior. +boolean LUA_HookPlayerExplode(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Hook for K_ExplodePlayer. Allows Lua to execute code and/or overwrite its behavior. +boolean LUA_HookPlayerSquish(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Hook for K_SquishPlayer. Allows Lua to execute code and/or overwrite its behavior. diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 1aadea9a3..9276e61aa 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -24,1950 +24,1097 @@ #include "lua_hook.h" #include "lua_hud.h" // hud_running errors -static UINT8 hooksAvailable[(hook_MAX/8)+1]; +/* ========================================================================= + ABSTRACTION + ========================================================================= */ -const char *const hookNames[hook_MAX+1] = { - "NetVars", - "MapChange", - "MapLoad", - "PlayerJoin", - "PreThinkFrame", - "ThinkFrame", - "PostThinkFrame", - "MobjSpawn", - "MobjCollide", - "MobjMoveCollide", - "TouchSpecial", - "MobjFuse", - "MobjThinker", - "BossThinker", - "ShouldDamage", - "MobjDamage", - "MobjDeath", - "BossDeath", - "MobjRemoved", - "JumpSpecial", - "AbilitySpecial", - "SpinSpecial", - "JumpSpinSpecial", - "BotTiccmd", - "BotAI", - "LinedefExecute", - "PlayerMsg", - "HurtMsg", - "PlayerSpawn", - "PlayerQuit", - "PlayerThink", - "MusicChange", - "ShouldSpin", - "ShouldExplode", - "ShouldSquish", - "PlayerSpin", - "PlayerExplode", - "PlayerSquish", - "PlayerCmd", - "IntermissionThinker", - "VoteThinker", - "ServerJoin", - NULL -}; +#define LIST(id, M) \ + static const char * const id [] = { M (TOSTR) NULL } -// Hook metadata -struct hook_s +LIST (mobjHookNames, MOBJ_HOOK_LIST); +LIST (hookNames, HOOK_LIST); +LIST (hudHookNames, HUD_HOOK_LIST); +LIST (stringHookNames, STRING_HOOK_LIST); + +#undef LIST + +typedef struct { + int numHooks; + int *ids; +} hook_t; + +typedef struct { + int numGeneric; + int ref; +} stringhook_t; + +static hook_t hookIds[HOOK(MAX)]; +static hook_t hudHookIds[HUD_HOOK(MAX)]; +static hook_t mobjHookIds[NUMMOBJTYPES][MOBJ_HOOK(MAX)]; + +// Lua tables are used to lookup string hook ids. +static stringhook_t stringHooks[STRING_HOOK(MAX)]; + +// This will be indexed by hook id, the value of which fetches the registry. +static int * hookRefs; +static int nextid; + +// After a hook errors once, don't print the error again. +static UINT8 * hooksErrored; + +static int errorRef; + +static boolean mobj_hook_available(int hook_type, mobjtype_t mobj_type) { - struct hook_s *next; - enum hook type; - UINT16 id; - union { - mobjtype_t mt; - char *skinname; - char *funcname; - } s; - boolean error; -}; -typedef struct hook_s* hook_p; + return + ( + mobjHookIds [MT_NULL] [hook_type].numHooks > 0 || + (mobj_type < NUMMOBJTYPES && mobjHookIds[mobj_type][hook_type].numHooks > 0) + ); +} -// For each mobj type, a linked list to its thinker and collision hooks. -// That way, we don't have to iterate through all the hooks. -// We could do that with all other mobj hooks, but it would probably just be -// a waste of memory since they are only called occasionally. Probably... -static hook_p mobjthinkerhooks[NUMMOBJTYPES]; -static hook_p mobjcollidehooks[NUMMOBJTYPES]; +static unsigned hook_in_list +( + const char * const name, + const char * const * const list +){ + unsigned type; -// For each mobj type, a linked list for other mobj hooks -static hook_p mobjhooks[NUMMOBJTYPES]; + for (type = 0; list[type] != NULL; ++type) + { + if (strcmp(name, list[type]) == 0) + break; + } -// A linked list for player hooks -static hook_p playerhooks; + return type; +} -// A linked list for linedef executor hooks -static hook_p linedefexecutorhooks; +static void get_table(lua_State *L) +{ + lua_pushvalue(L, -1); + lua_rawget(L, -3); -// For other hooks, a unique linked list -hook_p roothook; + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_createtable(L, 1, 0); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, -5); + } + + lua_remove(L, -2); +} + +static void add_hook_to_table(lua_State *L, int n) +{ + lua_pushnumber(L, nextid); + lua_rawseti(L, -2, n); +} + +static void add_string_hook(lua_State *L, int type) +{ + stringhook_t * hook = &stringHooks[type]; + + char * string = NULL; + + switch (type) + { + case STRING_HOOK(BotAI): + if (lua_isstring(L, 3)) + { // lowercase copy + string = Z_StrDup(lua_tostring(L, 3)); + strlwr(string); + } + + /*hook.s.skinname = NULL; + if (lua_isstring(L, 2)) + { // lowercase copy + const char *s = lua_tostring(L, 2); + char *p = hook.s.skinname = ZZ_Alloc(strlen(s)+1); + do { + *p = tolower(*s); + ++p; + } while(*(++s)); + *p = 0; + }*/ + break; + + case STRING_HOOK(LinedefExecute): + string = Z_StrDup(luaL_checkstring(L, 3)); + strupr(string); + + /*{ // uppercase copy + const char *s = luaL_checkstring(L, 2); + char *p = hook.s.funcname = ZZ_Alloc(strlen(s)+1); + do { + *p = toupper(*s); + ++p; + } while(*(++s)); + *p = 0; + }*/ + break; + } + + if (hook->ref > 0) + lua_getref(L, hook->ref); + else + { + lua_newtable(L); + lua_pushvalue(L, -1); + hook->ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + if (string) + { + lua_pushstring(L, string); + get_table(L); + add_hook_to_table(L, 1 + lua_objlen(L, -1)); + } + else + add_hook_to_table(L, ++hook->numGeneric); +} + +static void add_hook(hook_t *map) +{ + Z_Realloc(map->ids, (map->numHooks + 1) * sizeof *map->ids, + PU_STATIC, &map->ids); + map->ids[map->numHooks++] = nextid; +} + +static void add_mobj_hook(lua_State *L, int hook_type) +{ + mobjtype_t mobj_type = luaL_optnumber(L, 3, MT_NULL); + + luaL_argcheck(L, mobj_type < NUMMOBJTYPES, 3, "invalid mobjtype_t"); + + add_hook(&mobjHookIds[mobj_type][hook_type]); +} + +static void add_hud_hook(lua_State *L, int idx) +{ + add_hook(&hudHookIds[luaL_checkoption(L, + idx, "game", hudHookNames)]); +} + +static void add_hook_ref(lua_State *L, int idx) +{ + if (!(nextid & 7)) + { + Z_Realloc(hooksErrored, + BIT_ARRAY_SIZE (nextid + 1) * sizeof *hooksErrored, + PU_STATIC, &hooksErrored); + hooksErrored[nextid >> 3] = 0; + } + + Z_Realloc(hookRefs, (nextid + 1) * sizeof *hookRefs, PU_STATIC, &hookRefs); + + // set the hook function in the registry. + lua_pushvalue(L, idx); + hookRefs[nextid++] = luaL_ref(L, LUA_REGISTRYINDEX); +} // Takes hook, function, and additional arguments (mobj type to act on, etc.) static int lib_addHook(lua_State *L) { - static struct hook_s hook = {NULL, 0, 0, {0}, false}; - static UINT32 nextid; - hook_p hookp, *lastp; - - hook.type = luaL_checkoption(L, 1, NULL, hookNames); - lua_remove(L, 1); - - luaL_checktype(L, 1, LUA_TFUNCTION); + const char * name; + unsigned type; if (hud_running) return luaL_error(L, "HUD rendering code should not call this function!"); - switch(hook.type) + name = luaL_checkstring(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + + /* this is a very special case */ + if (( type = hook_in_list(name, stringHookNames) ) < STRING_HOOK(MAX)) { - // Take a mobjtype enum which this hook is specifically for. - case hook_MobjSpawn: - case hook_MobjCollide: - case hook_MobjMoveCollide: - case hook_TouchSpecial: - case hook_MobjFuse: - case hook_MobjThinker: - case hook_BossThinker: - case hook_ShouldDamage: - case hook_MobjDamage: - case hook_MobjDeath: - case hook_BossDeath: - case hook_MobjRemoved: - case hook_HurtMsg: - hook.s.mt = MT_NULL; - if (lua_isnumber(L, 2)) - hook.s.mt = lua_tonumber(L, 2); - luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t"); - break; - case hook_BotAI: - hook.s.skinname = NULL; - if (lua_isstring(L, 2)) - { // lowercase copy - const char *s = lua_tostring(L, 2); - char *p = hook.s.skinname = ZZ_Alloc(strlen(s)+1); - do { - *p = tolower(*s); - ++p; - } while(*(++s)); - *p = 0; - } - break; - case hook_LinedefExecute: // Linedef executor functions - { // uppercase copy - const char *s = luaL_checkstring(L, 2); - char *p = hook.s.funcname = ZZ_Alloc(strlen(s)+1); - do { - *p = toupper(*s); - ++p; - } while(*(++s)); - *p = 0; - } - break; - case hook_ShouldSpin: - case hook_ShouldExplode: - case hook_ShouldSquish: - case hook_PlayerSpin: - case hook_PlayerExplode: - case hook_PlayerSquish: - default: - break; + add_string_hook(L, type); } - lua_settop(L, 1); // lua stack contains only the function now. - - hooksAvailable[hook.type/8] |= 1<<(hook.type%8); - - // set hook.id to the highest id + 1 - hook.id = nextid++; - - // Special cases for some hook types (see the comments above mobjthinkerhooks declaration) - switch(hook.type) + else if (( type = hook_in_list(name, mobjHookNames) ) < MOBJ_HOOK(MAX)) { - case hook_MobjThinker: - lastp = &mobjthinkerhooks[hook.s.mt]; - break; - case hook_MobjCollide: - case hook_MobjMoveCollide: - lastp = &mobjcollidehooks[hook.s.mt]; - break; - case hook_MobjSpawn: - case hook_TouchSpecial: - case hook_MobjFuse: - case hook_BossThinker: - case hook_ShouldDamage: - case hook_MobjDamage: - case hook_MobjDeath: - case hook_BossDeath: - case hook_MobjRemoved: - lastp = &mobjhooks[hook.s.mt]; - break; - case hook_JumpSpecial: - case hook_AbilitySpecial: - case hook_SpinSpecial: - case hook_JumpSpinSpecial: - case hook_PlayerSpawn: - case hook_PlayerThink: - lastp = &playerhooks; - break; - case hook_LinedefExecute: - lastp = &linedefexecutorhooks; - break; - case hook_ShouldSpin: - case hook_ShouldExplode: - case hook_ShouldSquish: - case hook_PlayerSpin: - case hook_PlayerExplode: - case hook_PlayerSquish: - case hook_ServerJoin: - default: - lastp = &roothook; - break; + add_mobj_hook(L, type); + } + else if (( type = hook_in_list(name, hookNames) ) < HOOK(MAX)) + { + add_hook(&hookIds[type]); + } + /*else if (strcmp(name, "HUD") == 0) // ehh this is cool and all, but i dont want modders potentially breaking vanilla clients lol + { + add_hud_hook(L, 3); + }*/ + else + { + return luaL_argerror(L, 1, lua_pushfstring(L, "invalid hook " LUA_QS, name)); } - // iterate the hook metadata structs - // set lastp to the last hook struct's "next" pointer. - for (hookp = *lastp; hookp; hookp = hookp->next) - lastp = &hookp->next; - // allocate a permanent memory struct to stuff hook. - hookp = ZZ_Alloc(sizeof(struct hook_s)); - memcpy(hookp, &hook, sizeof(struct hook_s)); - // tack it onto the end of the linked list. - *lastp = hookp; + add_hook_ref(L, 2);/* the function */ - // set the hook function in the registry. - lua_getfield(L, LUA_REGISTRYINDEX, "hooks"); - lua_pushvalue(L, 1); - lua_rawseti(L, -2, hook.id); return 0; } int LUA_HookLib(lua_State *L) { - memset(hooksAvailable,0,sizeof(UINT8[(hook_MAX/8)+1])); - roothook = NULL; - lua_register(L, "addHook", lib_addHook); + lua_pushcfunction(L, LUA_GetErrorMessage); + errorRef = luaL_ref(L, LUA_REGISTRYINDEX); - lua_newtable(L); - lua_setfield(L, LUA_REGISTRYINDEX, "hooks"); + lua_register(L, "addHook", lib_addHook); return 0; } -boolean LUAh_MobjHook(mobj_t *mo, enum hook which) + +int lib_hudadd(lua_State *L);/* yeah compiler */ +int lib_hudadd(lua_State *L) { - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) + luaL_checktype(L, 1, LUA_TFUNCTION); + + add_hud_hook(L, 2); + add_hook_ref(L, 1); + + return 0; +} + +typedef struct Hook_State Hook_State; +typedef void (*Hook_Callback)(Hook_State *); + +struct Hook_State { + INT32 status;/* return status to calling function */ + void * userdata; + int hook_type; + mobjtype_t mobj_type;/* >0 if mobj hook */ + boolean force_mobj;// allows mobj_type equal 0 + const char * string;/* used to fetch table, ran first if set */ + int top;/* index of last argument passed to hook */ + int id;/* id to fetch ref */ + int values;/* num arguments passed to hook */ + int results;/* num values returned by hook */ + Hook_Callback results_handler;/* callback when hook successfully returns */ +}; + +enum { + EINDEX = 1,/* error handler */ + SINDEX = 2,/* string itself is pushed in case of string hook */ +}; + +static void push_error_handler(void) +{ + lua_getref(gL, errorRef); +} + +/* repush hook string */ +static void push_string(void) +{ + lua_pushvalue(gL, SINDEX); +} + +static boolean begin_hook_values(Hook_State *hook) +{ + hook->top = lua_gettop(gL); + return true; +} + +static void start_hook_stack(void) +{ + lua_settop(gL, 0); + push_error_handler(); +} + +static boolean init_hook_type +( + Hook_State * hook, + int status, + int hook_type, + mobjtype_t mobj_type, + const char * string, + int nonzero +){ + hook->status = status; + + if (nonzero) + { + start_hook_stack(); + hook->hook_type = hook_type; + hook->mobj_type = mobj_type; + hook->string = string; + return begin_hook_values(hook); + } + else return false; +} - I_Assert(mo->type < NUMMOBJTYPES); +static boolean prepare_hook +( + Hook_State * hook, + int default_status, + int hook_type +){ + hook->force_mobj = false; - if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) + return init_hook_type(hook, default_status, + hook_type, 0, NULL, + hookIds[hook_type].numHooks); +} + +static boolean prepare_mobj_hook +( + Hook_State * hook, + int default_status, + int hook_type, + mobj_t * primary_mobj +){ + const mobjtype_t mobj_type = + primary_mobj ? primary_mobj->type : NUMMOBJTYPES; + + hook->force_mobj = true; + + return init_hook_type(hook, default_status, + hook_type, mobj_type, NULL, + mobj_hook_available(hook_type, mobj_type)); +} + +static boolean prepare_string_hook +( + Hook_State * hook, + int default_status, + int hook_type, + const char * string +){ + hook->force_mobj = false; + + if (init_hook_type(hook, default_status, + hook_type, 0, string, + stringHooks[hook_type].ref)) + { + lua_pushstring(gL, string); + return begin_hook_values(hook); + } + else return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // Look for all generic mobj hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - LUA_PushUserdata(gL, mo, META_MOBJ); - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - LUA_PushUserdata(gL, mo, META_MOBJ); - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; } -boolean LUAh_PlayerHook(player_t *plr, enum hook which) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = playerhooks; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - LUA_PushUserdata(gL, plr, META_PLAYER); - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; +static void init_hook_call +( + Hook_State * hook, + int results, + Hook_Callback results_handler +){ + const int top = lua_gettop(gL); + hook->values = (top - hook->top); + hook->top = top; + hook->results = results; + hook->results_handler = results_handler; } -// Hook for map change (before load) -void LUAh_MapChange(INT16 mapnumber) +static void get_hook(Hook_State *hook, const int *ids, int n) { - hook_p hookp; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - lua_pushinteger(gL, mapnumber); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MapChange) - continue; - - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - } - - lua_settop(gL, 0); + hook->id = ids[n]; + lua_getref(gL, hookRefs[hook->id]); } -// Hook for map load -void LUAh_MapLoad(void) +static void get_hook_from_table(Hook_State *hook, int n) { - hook_p hookp; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_MapLoad/8] & (1<<(hook_MapLoad%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - lua_pushinteger(gL, gamemap); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MapLoad) - continue; - - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - } - - lua_settop(gL, 0); + lua_rawgeti(gL, -1, n); + hook->id = lua_tonumber(gL, -1); + lua_pop(gL, 1); + lua_getref(gL, hookRefs[hook->id]); } -// Hook for Got_AddPlayer -void LUAh_PlayerJoin(int playernum) +static int call_single_hook_no_copy(Hook_State *hook) { - hook_p hookp; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_PlayerJoin/8] & (1<<(hook_PlayerJoin%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - lua_pushinteger(gL, playernum); - - for (hookp = roothook; hookp; hookp = hookp->next) + if (lua_pcall(gL, hook->values, hook->results, EINDEX) == 0) { - if (hookp->type != hook_PlayerJoin) - continue; - - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - } - - lua_settop(gL, 0); -} - -// Hook for frame (before mobj and player thinkers) -void LUAh_PreThinkFrame(void) -{ - hook_p hookp; - int HOOKSINDEX; - // variables used by perf stats - int hook_index = 0; - precise_t time_taken = 0; - if (!gL || !(hooksAvailable[hook_PreThinkFrame/8] & (1<<(hook_PreThinkFrame%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PreThinkFrame) - continue; - - if (cv_perfstats.value == 4) - time_taken = I_GetPreciseTime(); - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - if (cv_perfstats.value == 4) + if (hook->results > 0) { - lua_Debug ar; - time_taken = I_GetPreciseTime() - time_taken; - // we need the function, let's just retrieve it again - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_getinfo(gL, ">S", &ar); - PS_SetPreThinkFrameHookInfo(hook_index, time_taken, ar.short_src); - hook_index++; + (*hook->results_handler)(hook); + lua_pop(gL, hook->results); } } + else + { + /* print the error message once */ + if (cv_debug & DBG_LUA || !in_bit_array(hooksErrored, hook->id)) + { + CONS_Alert(CONS_WARNING, "%s\n", lua_tostring(gL, -1)); + set_bit_array(hooksErrored, hook->id); + } + lua_pop(gL, 1); + } - lua_pop(gL, 2); // Pop error handler and hooks table + return 1; } -// Hook for frame (after mobj and player thinkers) -void LUAh_ThinkFrame(void) +static int call_single_hook(Hook_State *hook) +{ + int i; + + for (i = -(hook->values) + 1; i <= 0; ++i) + lua_pushvalue(gL, hook->top + i); + + return call_single_hook_no_copy(hook); +} + +static int call_hook_table_for(Hook_State *hook, int n) +{ + int k; + + for (k = 1; k <= n; ++k) + { + get_hook_from_table(hook, k); + call_single_hook(hook); + } + + return n; +} + +static int call_hook_table(Hook_State *hook) +{ + return call_hook_table_for(hook, lua_objlen(gL, -1)); +} + +static int call_mapped(Hook_State *hook, const hook_t *map) +{ + int k; + + for (k = 0; k < map->numHooks; ++k) + { + get_hook(hook, map->ids, k); + call_single_hook(hook); + } + + return map->numHooks; +} + +static int call_string_hooks(Hook_State *hook) +{ + const stringhook_t *map = &stringHooks[hook->hook_type]; + + int calls = 0; + + lua_getref(gL, map->ref); + + /* call generic string hooks first */ + calls += call_hook_table_for(hook, map->numGeneric); + + push_string(); + lua_rawget(gL, -2); + calls += call_hook_table(hook); + + return calls; +} + +static int call_mobj_type_hooks(Hook_State *hook, mobjtype_t mobj_type) +{ + return call_mapped(hook, &mobjHookIds[mobj_type][hook->hook_type]); +} + +static int call_hooks +( + Hook_State * hook, + int results, + Hook_Callback results_handler +){ + int calls = 0; + + init_hook_call(hook, results, results_handler); + + if (hook->string) + { + calls += call_string_hooks(hook); + } + else if (hook->force_mobj) + { + /* call generic mobj hooks first */ + calls += call_mobj_type_hooks(hook, MT_NULL); + + if (hook->mobj_type < NUMMOBJTYPES) + calls += call_mobj_type_hooks(hook, hook->mobj_type); + + ps_lua_mobjhooks.value.i += calls; + } + else + calls += call_mapped(hook, &hookIds[hook->hook_type]); + + lua_settop(gL, 0); + + return calls; +} + +/* ========================================================================= + COMMON RESULT HANDLERS + ========================================================================= */ + +#define res_none NULL + +static void res_true(Hook_State *hook) +{ + if (lua_toboolean(gL, -1)) + hook->status = true; +} + +/*static void res_false(Hook_State *hook) +{ + if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1)) + hook->status = false; +}*/ + +static void res_force(Hook_State *hook) +{ + if (!lua_isnil(gL, -1)) + { + if (lua_toboolean(gL, -1)) + hook->status = 1; // Force yes + else + hook->status = 2; // Force no + } +} + +static void res_hud(Hook_State *hook) +{ + (void)hook; + + hud_interpolate = hud_interpstring = hud_interplatch = false; + hud_interpcounter++; +} + +/* ========================================================================= + GENERALISED HOOKS + ========================================================================= */ + +int LUA_HookMobj(mobj_t *mobj, int hook_type) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, hook_type, mobj)) + { + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +int LUA_Hook2Mobj(mobj_t *t1, mobj_t *t2, int hook_type) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, hook_type, t1)) + { + LUA_PushUserdata(gL, t1, META_MOBJ); + LUA_PushUserdata(gL, t2, META_MOBJ); + call_hooks(&hook, 1, res_force); + } + return hook.status; +} + +void LUA_HookVoid(int type) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, type)) + call_hooks(&hook, 0, res_none); +} + +void LUA_HookInt(INT32 number, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) + { + lua_pushinteger(gL, number); + call_hooks(&hook, 0, res_none); + } +} + +/*void LUA_HookBool(boolean value, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) + { + //lua_pushboolean(gL, value); + call_hooks(&hook, 0, res_none); + } +}*/ + +int LUA_HookPlayer(player_t *player, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, cmd, META_TICCMD); + + if (hook_type == HOOK(PlayerCmd)) + hook_cmd_running = true; + + call_hooks(&hook, 1, res_true); + + if (hook_type == HOOK(PlayerCmd)) + hook_cmd_running = false; + } + return hook.status; +} + +void LUA_HookHUD(int hook_type, huddrawlist_h list) +{ + const hook_t * map = &hudHookIds[hook_type]; + Hook_State hook; + if (map->numHooks > 0) + { + start_hook_stack(); + begin_hook_values(&hook); + + LUA_SetHudHook(hook_type, list); + + hud_running = true; // local hook + hud_interpcounter = 0; + hud_interpolate = hud_interpstring = hud_interplatch = false; + init_hook_call(&hook, 1, res_hud); + call_mapped(&hook, map); + lua_settop(gL, 0); // destroy le stack!! >:3 + hud_running = false; + + lua_pushnil(gL); + lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); + } +} + +/* ========================================================================= + SPECIALIZED HOOKS + ========================================================================= */ + +static void hook_think_frame(int type) { - hook_p hookp; - int HOOKSINDEX; // variables used by perf stats int hook_index = 0; precise_t time_taken = 0; - if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8)))) - return; + Hook_State hook; - lua_pushcfunction(gL, LUA_GetErrorMessage); + const hook_t * map = &hookIds[type]; + int k; - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) + if (prepare_hook(&hook, 0, type)) { - if (hookp->type != hook_ThinkFrame) - continue; + init_hook_call(&hook, 0, res_none); - if (cv_perfstats.value == 3) - time_taken = I_GetPreciseTime(); - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - if (cv_perfstats.value == 3) + for (k = 0; k < map->numHooks; ++k) { - lua_Debug ar; - time_taken = I_GetPreciseTime() - time_taken; - // we need the function, let's just retrieve it again - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_getinfo(gL, ">S", &ar); - PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); - hook_index++; - } - } + get_hook(&hook, map->ids, k); - lua_pop(gL, 2); // Pop error handler and hooks table -} - -// Hook for frame (at end of tick, ie after overlays, precipitation, specials) -void LUAh_PostThinkFrame(void) -{ - hook_p hookp; - int HOOKSINDEX; - // variables used by perf stats - int hook_index = 0; - precise_t time_taken = 0; - if (!gL || !(hooksAvailable[hook_PostThinkFrame/8] & (1<<(hook_PostThinkFrame%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PostThinkFrame) - continue; - - if (cv_perfstats.value == 5) - time_taken = I_GetPreciseTime(); - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - if (cv_perfstats.value == 5) - { - lua_Debug ar; - time_taken = I_GetPreciseTime() - time_taken; - // we need the function, let's just retrieve it again - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_getinfo(gL, ">S", &ar); - PS_SetPostThinkFrameHookInfo(hook_index, time_taken, ar.short_src); - hook_index++; - } - } - - lua_pop(gL, 2); // Pop error handler and hooks table -} - -// Hook for Y_Ticker -void LUAh_IntermissionThinker(void) -{ - hook_p hookp; - int HOOKSINDEX; - - if (!gL || !(hooksAvailable[hook_IntermissionThinker/8] & (1<<(hook_IntermissionThinker%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage);; - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_IntermissionThinker) - continue; - - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - } - - lua_pop(gL, 2); // Pop error handler and hooks table -} - -// Hook for Y_VoteTicker -void LUAh_VoteThinker(void) -{ - hook_p hookp; - int HOOKSINDEX; - - if (!gL || !(hooksAvailable[hook_VoteThinker/8] & (1<<(hook_VoteThinker%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_VoteThinker) - continue; - - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - } - - lua_pop(gL, 2); // Pop error handler and hooks table -} - - -// Hook for mobj collisions -UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) -{ - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - int HOOKSINDEX; - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return 0; - - I_Assert(thing1->type < NUMMOBJTYPES); - - if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing1->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // Look for all generic mobj collision hooks - for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, thing1, META_MOBJ); - LUA_PushUserdata(gL, thing2, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, thing1, META_MOBJ); - LUA_PushUserdata(gL, thing2, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldCollide; -} - -// Hook for mobj thinkers -boolean LUAh_MobjThinker(mobj_t *mo) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8)))) - return false; - - I_Assert(mo->type < NUMMOBJTYPES); - - if (!(mobjthinkerhooks[MT_NULL] || mobjthinkerhooks[mo->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // Look for all generic mobj thinker hooks - for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next) - { - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - LUA_PushUserdata(gL, mo, META_MOBJ); - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next) - { - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - LUA_PushUserdata(gL, mo, META_MOBJ); - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_TouchSpecialThing by mobj type -boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_TouchSpecial/8] & (1<<(hook_TouchSpecial%8)))) - return false; - - I_Assert(special->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[special->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // Look for all generic touch special hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TouchSpecial) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, special, META_MOBJ); - LUA_PushUserdata(gL, toucher, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TouchSpecial) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, special, META_MOBJ); - LUA_PushUserdata(gL, toucher, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_DamageMobj by mobj type (Should mobj take damage?) -UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) -{ - hook_p hookp; - UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no. - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_ShouldDamage/8] & (1<<(hook_ShouldDamage%8)))) - return 0; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // Look for all generic should damage hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldDamage) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldDamage) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldDamage; -} - -// Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) -boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8)))) - return false; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // Look for all generic mobj damage hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDamage) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDamage) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_KillMobj by mobj type -boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_MobjDeath/8] & (1<<(hook_MobjDeath%8)))) - return false; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // Look for all generic mobj death hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDeath) - continue; - - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDeath) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for B_BuildTiccmd -boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_BotTiccmd/8] & (1<<(hook_BotTiccmd%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotTiccmd) - continue; - - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, bot, META_PLAYER); - LUA_PushUserdata(gL, cmd, META_TICCMD); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for G_BuildTicCmd -boolean hook_cmd_running = false; -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_PlayerCmd/8] & (1<<(hook_PlayerCmd%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - hook_cmd_running = true; - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerCmd) - continue; - - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, cmd, META_TICCMD); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - hook_cmd_running = false; - lua_settop(gL, 0); - return hooked; -} - -// Hook for B_BuildTailsTiccmd by skin name -boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotAI - || (hookp->s.skinname && strcmp(hookp->s.skinname, ((skin_t*)tails->skin)->name))) - continue; - - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, sonic, META_MOBJ); - LUA_PushUserdata(gL, tails, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 8, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - - // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. - if (lua_istable(gL, -8)) { - boolean forward=false, backward=false, left=false, right=false, strafeleft=false, straferight=false, jump=false, spin=false; -#define CHECKFIELD(field) \ - lua_getfield(gL, -8, #field);\ - if (lua_toboolean(gL, -1))\ - field = true;\ - lua_pop(gL, 1); - - CHECKFIELD(forward) - CHECKFIELD(backward) - CHECKFIELD(left) - CHECKFIELD(right) - CHECKFIELD(strafeleft) - CHECKFIELD(straferight) - CHECKFIELD(jump) - CHECKFIELD(spin) -#undef CHECKFIELD - B_KeysToTiccmd(tails, cmd, forward, backward, left, right, strafeleft, straferight, jump, spin); - } else - B_KeysToTiccmd(tails, cmd, lua_toboolean(gL, -8), lua_toboolean(gL, -7), lua_toboolean(gL, -6), lua_toboolean(gL, -5), lua_toboolean(gL, -4), lua_toboolean(gL, -3), lua_toboolean(gL, -2), lua_toboolean(gL, -1)); - - lua_pop(gL, 8); - hooked = true; - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for linedef executors -boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_LinedefExecute/8] & (1<<(hook_LinedefExecute%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next) - { - if (strcmp(hookp->s.funcname, line->text)) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, line, META_LINE); - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, sector, META_SECTOR); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - hooked = true; - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for player chat -// Added the "mute" field. It's set to true if the message was supposed to be eaten by spam protection. -// But for netgame consistency purposes, this hook is ran first reguardless, so this boolean allows for modders to adapt if they so desire. -boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg, int mute) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_PlayerMsg/8] & (1<<(hook_PlayerMsg%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerMsg) - continue; - - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player - if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c - lua_pushinteger(gL, 3); // type - lua_pushnil(gL); // target - } else if (target == -1) { // sayteam - lua_pushinteger(gL, 1); // type - lua_pushnil(gL); // target - } else if (target == 0) { // say - lua_pushinteger(gL, 0); // type - lua_pushnil(gL); // target - } else { // sayto - lua_pushinteger(gL, 2); // type - LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target + if (cv_perfstats.value >= 3) + { + lua_pushvalue(gL, -1);/* need the function again */ + time_taken = I_GetPreciseTime(); } - lua_pushstring(gL, msg); // msg - if (mute) - lua_pushboolean(gL, true); // the message was supposed to be eaten by spamprotecc. - else - lua_pushboolean(gL, false); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - lua_settop(gL, 0); - return hooked; + call_single_hook(&hook); + + if (cv_perfstats.value >= 3) + { + lua_Debug ar; + time_taken = I_GetPreciseTime() - time_taken; + lua_getinfo(gL, ">S", &ar); + if (type == 4) // sorry for magic numbers + PS_SetPreThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + else if (type == 5) + PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + else if (type == 6) + PS_SetPostThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + + hook_index++; + } + } + + lua_settop(gL, 0); + } } -// Hook for hurt messages -boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source) +void LUA_HookPreThinkFrame(void) { - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_HurtMsg/8] & (1<<(hook_HurtMsg%8)))) - return false; + hook_think_frame(HOOK(PreThinkFrame)); +} - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); +void LUA_HookThinkFrame(void) +{ + hook_think_frame(HOOK(ThinkFrame)); +} - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); +void LUA_HookPostThinkFrame(void) +{ + hook_think_frame(HOOK(PostThinkFrame)); +} - for (hookp = roothook; hookp; hookp = hookp->next) +int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(TouchSpecial), special)) { - if (hookp->type != hook_HurtMsg - || (hookp->s.mt && !(inflictor && hookp->s.mt == inflictor->type))) - continue; + LUA_PushUserdata(gL, special, META_MOBJ); + LUA_PushUserdata(gL, toucher, META_MOBJ); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} - if (lua_gettop(gL) == 2) +static int damage_hook +( + mobj_t *target, + mobj_t *inflictor, + mobj_t *source, + INT32 damage, + int hook_type, + Hook_Callback results_handler +){ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, hook_type, target)) + { + LUA_PushUserdata(gL, target, META_MOBJ); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + if (hook_type != MOBJ_HOOK(MobjDeath)) + lua_pushinteger(gL, damage); + call_hooks(&hook, 1, results_handler); + } + return hook.status; +} + +int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) +{ + return damage_hook(target, inflictor, source, damage, + MOBJ_HOOK(ShouldDamage), res_force); +} + +int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) +{ + return damage_hook(target, inflictor, source, damage, + MOBJ_HOOK(MobjDamage), res_true); +} + +int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source) +{ + return damage_hook(target, inflictor, source, 0, + MOBJ_HOOK(MobjDeath), res_true); +} + +typedef struct { + mobj_t * tails; + ticcmd_t * cmd; +} BotAI_State; + +static boolean checkbotkey(const char *field) +{ + return lua_toboolean(gL, -1) && strcmp(lua_tostring(gL, -2), field) == 0; +} + +static void res_botai(Hook_State *hook) +{ + BotAI_State *botai = hook->userdata; + + int k[8]; + + int fields = 0; + + // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. + if (lua_istable(gL, -8)) { + lua_pushnil(gL); // key + while (lua_next(gL, -9)) { +#define CHECK(n, f) (checkbotkey(f) ? (k[(n)-1] = 1) : 0) + if ( + CHECK(1, "forward") || CHECK(2, "backward") || + CHECK(3, "left") || CHECK(4, "right") || + CHECK(5, "strafeleft") || CHECK(6, "straferight") || + CHECK(7, "jump") || CHECK(8, "spin") + ){ + if (8 <= ++fields) + { + lua_pop(gL, 2); // pop key and value + break; + } + } + + lua_pop(gL, 1); // pop value +#undef CHECK + } + } else { + while (fields < 8) { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -void LUAh_NetArchiveHook(lua_CFunction archFunc) -{ - hook_p hookp; - int errorhandlerindex; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_NetVars/8] & (1<<(hook_NetVars%8)))) - return; - - // stack: tables - I_Assert(lua_gettop(gL) > 0); - I_Assert(lua_istable(gL, -1)); - - lua_pushcfunction(gL, LUA_GetErrorMessage); - errorhandlerindex = lua_gettop(gL); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // tables becomes an upvalue of archFunc - lua_pushvalue(gL, -3); - lua_pushcclosure(gL, archFunc, 1); - // stack: tables, archFunc - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_NetVars) - continue; - - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -2); // archFunc - if (lua_pcall(gL, 1, 0, errorhandlerindex)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); + k[fields] = lua_toboolean(gL, -8 + fields); + fields++; } } - lua_pop(gL, 3); // Pop archFunc, error handler and hooks table - // stack: tables + B_KeysToTiccmd(botai->tails, botai->cmd, + k[0],k[1],k[2],k[3],k[4],k[5],k[6],k[7]); + + hook->status = true; } -void LUAh_PlayerQuit(player_t *plr, int reason) +int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) { - hook_p hookp; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8)))) - return; + const char *skin = ((skin_t *)tails->skin)->name; - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); + Hook_State hook; + BotAI_State botai; - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) + if (prepare_string_hook(&hook, false, STRING_HOOK(BotAI), skin)) { - if (hookp->type != hook_PlayerQuit) - continue; + LUA_PushUserdata(gL, sonic, META_MOBJ); + LUA_PushUserdata(gL, tails, META_MOBJ); - if (lua_gettop(gL) == 2) + botai.tails = tails; + botai.cmd = cmd; + + hook.userdata = &botai; + + call_hooks(&hook, 8, res_botai); + } + + return hook.status; +} + +void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) +{ + Hook_State hook; + if (prepare_string_hook + (&hook, 0, STRING_HOOK(LinedefExecute), line->text)) + { + LUA_PushUserdata(gL, line, META_LINE); + LUA_PushUserdata(gL, mo, META_MOBJ); + LUA_PushUserdata(gL, sector, META_SECTOR); + ps_lua_mobjhooks.value.i += call_hooks(&hook, 0, res_none); + } +} + +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg, int mute) +{ + Hook_State hook; + if (prepare_hook(&hook, false, HOOK(PlayerMsg))) + { + LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player + if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c + lua_pushinteger(gL, 3); // type + lua_pushnil(gL); // target + } else if (target == -1) { // sayteam + lua_pushinteger(gL, 1); // type + lua_pushnil(gL); // target + } else if (target == 0) { // say + lua_pushinteger(gL, 0); // type + lua_pushnil(gL); // target + } else { // sayto + lua_pushinteger(gL, 2); // type + LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target + } + lua_pushstring(gL, msg); // msg + if (mute) + lua_pushboolean(gL, true); // the message was supposed to be eaten by spamprotecc. + else + lua_pushboolean(gL, false); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +int LUA_HookHurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(HurtMsg), inflictor)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +void LUA_HookNetArchive(lua_CFunction archFunc) +{ + const hook_t * map = &hookIds[HOOK(NetVars)]; + Hook_State hook; + hook.force_mobj = false; + /* this is a remarkable case where the stack isn't reset */ + if (map->numHooks > 0) + { + // stack: tables + I_Assert(lua_gettop(gL) > 0); + I_Assert(lua_istable(gL, -1)); + + push_error_handler(); + lua_insert(gL, EINDEX); + + begin_hook_values(&hook); + + // tables becomes an upvalue of archFunc + lua_pushvalue(gL, -1); + lua_pushcclosure(gL, archFunc, 1); + // stack: tables, archFunc + + init_hook_call(&hook, 0, res_none); + call_mapped(&hook, map); + + lua_pop(gL, 1); // pop archFunc + lua_remove(gL, EINDEX); // pop error handler + // stack: tables + } +} + +void LUA_HookPlayerQuit(player_t *plr, int reason) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerQuit))) + { + LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit + lua_pushinteger(gL, reason); // Reason for quitting + call_hooks(&hook, 0, res_none); + } +} + +boolean hook_cmd_running = false; + +static void update_music_name(struct MusicChange *musicchange) +{ + size_t length; + const char * new = lua_tolstring(gL, -6, &length); + + if (length < 7) + { + strcpy(musicchange->newname, new); + lua_pushvalue(gL, -6);/* may as well keep it for next call */ + } + else + { + memcpy(musicchange->newname, new, 6); + musicchange->newname[6] = '\0'; + lua_pushlstring(gL, new, 6); + } + + lua_replace(gL, -7); +} + +static void res_musicchange(Hook_State *hook) +{ + struct MusicChange *musicchange = hook->userdata; + + // output 1: true, false, or string musicname override + if (lua_isstring(gL, -6)) + update_music_name(musicchange); + else if (lua_isboolean(gL, -6) && lua_toboolean(gL, -6)) + hook->status = true; + + // output 2: mflags override + if (lua_isnumber(gL, -5)) + *musicchange->mflags = lua_tonumber(gL, -5); + // output 3: looping override + if (lua_isboolean(gL, -4)) + *musicchange->looping = lua_toboolean(gL, -4); + // output 4: position override + if (lua_isnumber(gL, -3)) + *musicchange->position = lua_tonumber(gL, -3); + // output 5: prefadems override + if (lua_isnumber(gL, -2)) + *musicchange->prefadems = lua_tonumber(gL, -2); + // output 6: fadeinms override + if (lua_isnumber(gL, -1)) + *musicchange->fadeinms = lua_tonumber(gL, -1); +} + +int LUA_HookMusicChange(const char *oldname, struct MusicChange *param) +{ + const int type = HOOK(MusicChange); + const hook_t * map = &hookIds[type]; + + Hook_State hook; + + int k; + + if (prepare_hook(&hook, false, type)) + { + init_hook_call(&hook, 6, res_musicchange); + hook.values = 7;/* values pushed later */ + hook.userdata = param; + + lua_pushstring(gL, oldname);/* the only constant value */ + lua_pushstring(gL, param->newname);/* semi constant */ + + for (k = 0; k < map->numHooks; ++k) { - LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit - lua_pushinteger(gL, reason); // Reason for quitting - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); + get_hook(&hook, map->ids, k); + + lua_pushvalue(gL, -3); + lua_pushvalue(gL, -3); + lua_pushinteger(gL, *param->mflags); + lua_pushboolean(gL, *param->looping); + lua_pushinteger(gL, *param->position); + lua_pushinteger(gL, *param->prefadems); + lua_pushinteger(gL, *param->fadeinms); + + call_single_hook_no_copy(&hook); } + + lua_settop(gL, 0); } - lua_settop(gL, 0); + return hook.status; } -// Hook for music changes -boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, - UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms) -{ - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - - if (!gL || !(hooksAvailable[hook_MusicChange/8] & (1<<(hook_MusicChange%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - for (hookp = roothook; hookp; hookp = hookp->next) +static int kartdamage_hook +( + player_t *player, + mobj_t *inflictor, + mobj_t *source, + int hook_type, + Hook_Callback results_handler +){ + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) { - if (hookp->type != hook_MusicChange) - continue; - - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushstring(gL, oldname); - lua_pushstring(gL, newname); - lua_pushinteger(gL, *mflags); - lua_pushboolean(gL, *looping); - lua_pushinteger(gL, *position); - lua_pushinteger(gL, *prefadems); - lua_pushinteger(gL, *fadeinms); - if (lua_pcall(gL, 7, 6, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1)); - lua_pop(gL, 1); - continue; - } - - // output 1: true, false, or string musicname override - if (lua_isboolean(gL, -6) && lua_toboolean(gL, -6)) - hooked = true; - else if (lua_isstring(gL, -6)) - strncpy(newname, lua_tostring(gL, -6), 7); - // output 2: mflags override - if (lua_isnumber(gL, -5)) - *mflags = lua_tonumber(gL, -5); - // output 3: looping override - if (lua_isboolean(gL, -4)) - *looping = lua_toboolean(gL, -4); - // output 4: position override - if (lua_isnumber(gL, -3)) - *position = lua_tonumber(gL, -3); - // output 5: prefadems override - if (lua_isnumber(gL, -2)) - *prefadems = lua_tonumber(gL, -2); - // output 6: fadeinms override - if (lua_isnumber(gL, -1)) - *fadeinms = lua_tonumber(gL, -1); - - lua_pop(gL, 6); + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + call_hooks(&hook, 1, results_handler); } - - lua_settop(gL, 0); - newname[6] = 0; - return hooked; + return hook.status; } // Hook for K_SpinPlayer. Determines if yes or no we should get damaged reguardless of circumstances. -UINT8 LUAh_ShouldSpin(player_t *player, mobj_t *inflictor, mobj_t *source) +int LUA_HookShouldSpin(player_t *player, mobj_t *inflictor, mobj_t *source) { - hook_p hookp; - UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no. - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_ShouldSpin/8] & (1<<(hook_ShouldSpin%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // We can afford not to check for mobj type because it will always be MT_PLAYER in this case. - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldSpin) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldDamage; + return kartdamage_hook(player, inflictor, source, HOOK(ShouldSpin), res_force); } // Hook for K_ExplodePlayer. Determines if yes or no we should get damaged reguardless of circumstances. -UINT8 LUAh_ShouldExplode(player_t *player, mobj_t *inflictor, mobj_t *source) +int LUA_HookShouldExplode(player_t *player, mobj_t *inflictor, mobj_t *source) { - hook_p hookp; - UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no. - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_ShouldExplode/8] & (1<<(hook_ShouldExplode%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // We can afford not to check for mobj type because it will always be MT_PLAYER in this case. - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldExplode) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldDamage; + return kartdamage_hook(player, inflictor, source, HOOK(ShouldExplode), res_force); } // Hook for K_SquishPlayer. Determines if yes or no we should get damaged reguardless of circumstances. -UINT8 LUAh_ShouldSquish(player_t *player, mobj_t *inflictor, mobj_t *source) +int LUA_HookShouldSquish(player_t *player, mobj_t *inflictor, mobj_t *source) { - hook_p hookp; - UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no. - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_ShouldSquish/8] & (1<<(hook_ShouldSquish%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // We can afford not to check for mobj type because it will always be MT_PLAYER in this case. - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldSquish) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldDamage; + return kartdamage_hook(player, inflictor, source, HOOK(ShouldSquish), res_force); } // Hook for K_SpinPlayer. This is used when the player has actually been spun out, but before anything has actually been done. This allows Lua to overwrite the behavior or to just perform actions. -boolean LUAh_PlayerSpin(player_t *player, mobj_t *inflictor, mobj_t *source) +boolean LUA_HookPlayerSpin(player_t *player, mobj_t *inflictor, mobj_t *source) { - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_PlayerSpin/8] & (1<<(hook_PlayerSpin%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // We can afford not to look for target->type because it will always be MT_PLAYER. - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerSpin) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; + return kartdamage_hook(player, inflictor, source, HOOK(PlayerSpin), res_true); } // Hook for K_SquishPlayer. This is used when the player has actually been spun out, but before anything has actually been done. This allows Lua to overwrite the behavior or to just perform actions. -boolean LUAh_PlayerSquish(player_t *player, mobj_t *inflictor, mobj_t *source) +boolean LUA_HookPlayerSquish(player_t *player, mobj_t *inflictor, mobj_t *source) { - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_PlayerSquish/8] & (1<<(hook_PlayerSquish%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // We can afford not to look for target->type because it will always be MT_PLAYER. - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerSquish) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; + return kartdamage_hook(player, inflictor, source, HOOK(PlayerSquish), res_true); } // Hook for K_ExplodePlayer. This is used when the player has actually been spun out, but before anything has actually been done. This allows Lua to overwrite the behavior or to just perform actions. -boolean LUAh_PlayerExplode(player_t *player, mobj_t *inflictor, mobj_t *source) +boolean LUA_HookPlayerExplode(player_t *player, mobj_t *inflictor, mobj_t *source) { - hook_p hookp; - boolean hooked = false; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_PlayerExplode/8] & (1<<(hook_PlayerExplode%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // We can afford not to look for target->type because it will always be MT_PLAYER. - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerExplode) - continue; - - ps_lua_mobjhooks.value.i++; - if (lua_gettop(gL) == 2) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - } - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -void LUAh_ServerJoin(void) -{ - hook_p hookp; - int HOOKSINDEX; - if (!gL || !(hooksAvailable[hook_ServerJoin/8] & (1<<(hook_ServerJoin%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - lua_getfield(gL, LUA_REGISTRYINDEX, "hooks"); - HOOKSINDEX = lua_gettop(gL); - I_Assert(lua_istable(L, HOOKSINDEX)); - - // We can afford not to look for target->type because it will always be MT_PLAYER. - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ServerJoin) - continue; - - lua_rawgeti(gL, HOOKSINDEX, hookp->id); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - } - - lua_settop(gL, 0); + return kartdamage_hook(player, inflictor, source, HOOK(PlayerExplode), res_true); } diff --git a/src/lua_hud.h b/src/lua_hud.h index 5d1f41c83..76ce9eb48 100644 --- a/src/lua_hud.h +++ b/src/lua_hud.h @@ -50,9 +50,6 @@ extern boolean hud_interplatch; // string mode was toggled boolean LUA_HudEnabled(enum hud option); -void LUAh_GameHUD(huddrawlist_h list); -void LUAh_ScoresHUD(huddrawlist_h list); -void LUAh_IntermissionHUD(huddrawlist_h list); -void LUAh_VoteHUD(huddrawlist_h list); +void LUA_SetHudHook(int hook, huddrawlist_h list); #endif // __LUA_HUD_H__ diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 5618727af..f3c01f4eb 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -28,6 +28,7 @@ #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" +#include "lua_hook.h" #define HUDONLY if (!hud_running) return luaL_error(L, "HUD rendering code should not be called outside of rendering hooks!"); @@ -40,8 +41,6 @@ boolean hud_interpstring = false; boolean hud_interplatch = false; static UINT8 hud_enabled[(hud_MAX/8)+1]; -static UINT8 hudAvailable; // hud hooks field - static UINT8 camnum = 1; // must match enum hud in lua_hud.h @@ -93,19 +92,6 @@ static const char *const patch_opt[] = { "topoffset", NULL}; -enum hudhook { - hudhook_game = 0, - hudhook_scores = 1, - hudhook_intermission = 2, - hudhook_vote = 3, -}; -static const char *const hudhook_opt[] = { - "game", - "scores", - "intermission", - "vote", - NULL}; - static int patch_fields_ref = LUA_NOREF; // alignment types for v.drawString @@ -1215,6 +1201,8 @@ static luaL_Reg lib_draw[] = { {NULL, NULL} }; +static int lib_draw_ref; + // // lib_hud // @@ -1248,25 +1236,7 @@ static int lib_hudenabled(lua_State *L) } // add a HUD element for rendering -static int lib_hudadd(lua_State *L) -{ - enum hudhook field; - - luaL_checktype(L, 1, LUA_TFUNCTION); - field = luaL_checkoption(L, 2, "game", hudhook_opt); - - lua_getfield(L, LUA_REGISTRYINDEX, "HUD"); - I_Assert(lua_istable(L, -1)); - lua_rawgeti(L, -1, field+2); // HUD[2+] - I_Assert(lua_istable(L, -1)); - lua_remove(L, -2); - - lua_pushvalue(L, 1); - lua_rawseti(L, -2, (int)(lua_objlen(L, -2) + 1)); - - hudAvailable |= 1<flags & MF_BOSS && special->flags2 & MF2_FRET) return; - if (LUAh_TouchSpecial(special, toucher) || P_MobjWasRemoved(special)) + if (LUA_HookTouchSpecial(special, toucher) || P_MobjWasRemoved(special)) return; if (special->flags & MF_BOSS) @@ -1615,7 +1615,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) target->flags2 &= ~(MF2_SKULLFLY|MF2_NIGHTSPULL); target->health = 0; // This makes it easy to check if something's dead elsewhere. - if (LUAh_MobjDeath(target, inflictor, source) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDeath(target, inflictor, source) || P_MobjWasRemoved(target)) return; // SRB2kart @@ -2274,7 +2274,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da // Everything above here can't be forced. if (!metalrecording) { - UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage); + UINT8 shouldForce = LUA_HookShouldDamage(target, inflictor, source, damage); if (P_MobjWasRemoved(target)) return (shouldForce == 1); // mobj was removed if (shouldForce == 1) @@ -2315,7 +2315,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!force && target->fuse) // Invincible return false; - if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target)) return true; if (target->health > 1) @@ -2341,7 +2341,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!force && target->flags2 & MF2_FRET) // Currently flashing from being hit return false; - if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target)) return true; if (target->health > 1) @@ -2349,7 +2349,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } else if (target->flags & MF_ENEMY) { - if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target)) return true; } @@ -2371,14 +2371,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; // Don't hit yourself with your own paraloop, baka } - if (LUAh_MobjDamage(target, inflictor, source, damage)) + if (LUA_HookMobjDamage(target, inflictor, source, damage)) return true; P_NiGHTSDamage(target, source); // -5s :( return true; } - if (LUAh_MobjDamage(target, inflictor, source, damage)) + if (LUA_HookMobjDamage(target, inflictor, source, damage)) return true; if (!force && inflictor && (inflictor->flags & MF_FIRE)) diff --git a/src/p_map.c b/src/p_map.c index 3034f2a06..6c141eb16 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -444,7 +444,7 @@ static boolean PIT_CheckThing(mobj_t *thing) } { - UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type + UINT8 shouldCollide = LUA_Hook2Mobj(thing, tmthing, MOBJ_HOOK(MobjCollide)); // checks hook for thing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) return true; // one of them was removed??? if (shouldCollide == 1) @@ -453,7 +453,7 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; // force no collide } { - UINT8 shouldCollide = LUAh_MobjMoveCollide(tmthing, thing); // checks hook for tmthing's type + UINT8 shouldCollide = LUA_Hook2Mobj(tmthing, thing, MOBJ_HOOK(MobjMoveCollide)); // checks hook for tmthing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) return true; // one of them was removed??? if (shouldCollide == 1) diff --git a/src/p_mobj.c b/src/p_mobj.c index 2420d2a50..158038824 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6152,7 +6152,7 @@ void P_MobjThinker(mobj_t *mobj) // Special thinker for scenery objects if (mobj->flags & MF_SCENERY) { - if (LUAh_MobjThinker(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker))) return; if (P_MobjWasRemoved(mobj)) @@ -6850,7 +6850,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->fuse--; if (!mobj->fuse) { - if (!LUAh_MobjFuse(mobj)) + if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse))) P_RemoveMobj(mobj); return; } @@ -6865,13 +6865,13 @@ void P_MobjThinker(mobj_t *mobj) // Check for a Lua thinker first if (!mobj->player) { - if (LUAh_MobjThinker(mobj) || P_MobjWasRemoved(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)) || P_MobjWasRemoved(mobj)) return; } else if (!mobj->player->spectator) { // You cannot short-circuit the player thinker like you can other thinkers. - LUAh_MobjThinker(mobj); + LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)); if (P_MobjWasRemoved(mobj)) return; } @@ -6896,7 +6896,7 @@ void P_MobjThinker(mobj_t *mobj) } else if (mobj->flags & MF_BOSS) { - if (LUAh_BossThinker(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(BossThinker))) { if (P_MobjWasRemoved(mobj)) return; @@ -8863,7 +8863,7 @@ void P_MobjThinker(mobj_t *mobj) fixed_t x, y, z; mobj_t *flagmo, *newmobj; - if (!LUAh_MobjFuse(mobj) && !P_MobjWasRemoved(mobj)) + if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)) && !P_MobjWasRemoved(mobj)) { switch (mobj->type) { @@ -9453,7 +9453,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // DANGER! This can cause P_SpawnMobj to return NULL! // Avoid using P_RemoveMobj on the newly created mobj in "MobjSpawn" Lua hooks! - if (LUAh_MobjSpawn(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjSpawn))) { if (P_MobjWasRemoved(mobj)) return NULL; @@ -9988,7 +9988,7 @@ void P_RemoveMobj(mobj_t *mobj) return; // something already removing this mobj. mobj->thinker.function.acp1 = (actionf_p1)P_RemoveThinkerDelayed; // shh. no recursing. - LUAh_MobjRemoved(mobj); + LUA_HookMobj(mobj, MOBJ_HOOK(MobjRemoved)); mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; // needed for P_UnsetThingPosition, etc. to work. // Rings only, please! diff --git a/src/p_setup.c b/src/p_setup.c index 27bd62eb7..7e28cec7a 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -63,7 +63,7 @@ #include "md5.h" // map MD5 -// for LUAh_MapLoad +// for MapLoad hook #include "lua_script.h" #include "lua_hook.h" @@ -2874,7 +2874,7 @@ boolean P_SetupLevel(boolean skipprecip, boolean reloadinggamestate) } P_PreTicker(2); if (!reloadinggamestate) - LUAh_MapLoad(); + LUA_HookInt(gamemap, HOOK(MapLoad)); } if (rendermode != render_none && !reloadinggamestate) diff --git a/src/p_spec.c b/src/p_spec.c index 2ff1b78b4..2ab2c20df 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -35,7 +35,7 @@ #include "hu_stuff.h" #include "m_misc.h" #include "m_cond.h" //unlock triggers -#include "lua_hook.h" // LUAh_LinedefExecute +#include "lua_hook.h" // LUA_HookLinedefExecute #include "k_kart.h" // SRB2kart #include "console.h" // CON_LogMessage @@ -2850,7 +2850,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 443: // Calls a named Lua function if (line->text) - LUAh_LinedefExecute(line, mo, callsec); + LUA_HookLinedefExecute(line, mo, callsec); else CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in the front texture fields)\n", sizeu1(line-lines)); break; diff --git a/src/p_tick.c b/src/p_tick.c index f4d75683b..7ec8670ec 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -528,7 +528,7 @@ void P_Ticker(boolean run) ps_checkposition_calls.value.i = 0; PS_START_TIMING(ps_lua_prethinkframe_time); - LUAh_PreThinkFrame(); + LUA_HookPreThinkFrame(); PS_STOP_TIMING(ps_lua_prethinkframe_time); PS_START_TIMING(ps_playerthink_time); @@ -564,7 +564,7 @@ void P_Ticker(boolean run) } PS_START_TIMING(ps_lua_thinkframe_time); - LUAh_ThinkFrame(); + LUA_HookThinkFrame(); PS_STOP_TIMING(ps_lua_thinkframe_time); } @@ -668,7 +668,7 @@ void P_Ticker(boolean run) D_MapChange(gamemap, gametype, encoremode, true, 0, false, false); PS_START_TIMING(ps_lua_postthinkframe_time); - LUAh_PostThinkFrame(); + LUA_HookPostThinkFrame(); PS_STOP_TIMING(ps_lua_postthinkframe_time); } @@ -725,7 +725,7 @@ void P_PreTicker(INT32 frames) R_UpdateMobjInterpolators(); - LUAh_PreThinkFrame(); + LUA_HOOK(PreThinkFrame); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) @@ -754,7 +754,7 @@ void P_PreTicker(INT32 frames) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); - LUAh_ThinkFrame(); + LUA_HookThinkFrame(); P_RunOverlays(); P_RunShadows(); @@ -762,7 +762,7 @@ void P_PreTicker(INT32 frames) P_UpdateSpecials(); P_RespawnSpecials(); - LUAh_PostThinkFrame(); + LUA_HOOK(PostThinkFrame); R_UpdateLevelInterpolators(); R_UpdateViewInterpolation(); diff --git a/src/p_user.c b/src/p_user.c index 49916ce77..e582bfcc8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4137,7 +4137,7 @@ void P_PlayerThink(player_t *player) } if (player->playerstate == PST_REBORN) { - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } } @@ -4243,7 +4243,7 @@ void P_PlayerThink(player_t *player) if (player->playerstate == PST_DEAD) { - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } } @@ -4309,7 +4309,7 @@ void P_PlayerThink(player_t *player) else player->mo->flags2 &= ~MF2_SHADOW; P_DeathThink(player); - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } @@ -4448,7 +4448,7 @@ void P_PlayerThink(player_t *player) if (!player->mo) { - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; // P_MovePlayer removed player->mo. } @@ -4625,7 +4625,7 @@ void P_PlayerThink(player_t *player) if (rendermode != render_none) DoABarrelRoll(player); - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); } // diff --git a/src/s_sound.c b/src/s_sound.c index 065d56082..5ed05e166 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1763,6 +1763,15 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 { char newmusic[7] = {0}; + struct MusicChange hook_param = { + newmusic, + &mflags, + &looping, + &position, + &prefadems, + &fadeinms + }; + if (S_MusicDisabled() || demo.rewinding // Don't mess with music while rewinding! || demo.title) // SRB2Kart: Demos don't interrupt title screen music @@ -1772,7 +1781,7 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 S_CheckEventMus(newmusic); - if (LUAh_MusicChange(music_name, newmusic, &mflags, &looping, &position, &prefadems, &fadeinms)) + if (LUA_HookMusicChange(music_name, &hook_param)) return; // No Music (empty string) diff --git a/src/s_sound.h b/src/s_sound.h index 6008cf0b4..9ee63a2b8 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -229,6 +229,16 @@ UINT32 S_GetMusicPosition(void); // Music Playback // +/* this is for the sake of the hook */ +struct MusicChange { + char * newname; + UINT16 * mflags; + boolean * looping; + UINT32 * position; + UINT32 * prefadems; + UINT32 * fadeinms; +}; + // Start music track, arbitrary, given its name, and set whether looping // note: music flags 12 bits for tracknum (gme, other formats with more than one track) // 13-15 aren't used yet diff --git a/src/st_stuff.c b/src/st_stuff.c index 6967b2aaf..05d5bcfc3 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -725,7 +725,7 @@ static void ST_overlayDrawer(void) { if (renderisnewtic) { - LUAh_GameHUD(luahuddrawlist_game[stplyrnum]); + LUA_HUDHOOK(game, luahuddrawlist_game[stplyrnum]); } } diff --git a/src/y_inter.c b/src/y_inter.c index e3abb111b..68398e4df 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -592,7 +592,7 @@ void Y_IntermissionDrawer(void) if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_intermission); - LUAh_IntermissionHUD(luahuddrawlist_intermission); + LUA_HUDHOOK(intermission, luahuddrawlist_intermission); } LUA_HUD_DrawList(luahuddrawlist_intermission); } @@ -622,7 +622,7 @@ void Y_Ticker(void) if (paused || P_AutoPause()) return; - LUAh_IntermissionThinker(); + LUA_HookVoid(HOOK(IntermissionThinker)); intertic++; @@ -1211,7 +1211,7 @@ void Y_VoteDrawer(void) if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_vote); - LUAh_VoteHUD(luahuddrawlist_vote); + LUA_HUDHOOK(vote, luahuddrawlist_vote); } LUA_HUD_DrawList(luahuddrawlist_vote); } @@ -1258,7 +1258,7 @@ void Y_VoteTicker(void) if (paused || P_AutoPause() || !voteclient.loaded) return; - LUAh_VoteThinker(); + LUA_HOOK(VoteThinker); votetic++;