mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-24 05:11:08 +00:00
2063 lines
49 KiB
C
2063 lines
49 KiB
C
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2012-2016 by John "JTE" Muniz.
|
|
// Copyright (C) 2012-2020 by Sonic Team Junior.
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file lua_hooklib.c
|
|
/// \brief hooks for Lua scripting
|
|
|
|
#include "doomdef.h"
|
|
#include "doomstat.h"
|
|
#include "p_mobj.h"
|
|
#include "g_game.h"
|
|
#include "r_skins.h"
|
|
#include "b_bot.h"
|
|
#include "z_zone.h"
|
|
|
|
#include "lua_script.h"
|
|
#include "lua_libs.h"
|
|
#include "lua_hook.h"
|
|
#include "lua_hud.h" // hud_running errors
|
|
|
|
#include "m_perfstats.h"
|
|
#include "d_netcmd.h" // for cv_perfstats
|
|
#include "i_system.h" // I_GetPreciseTime
|
|
|
|
static UINT8 hooksAvailable[(hook_MAX/8)+1];
|
|
|
|
const char *const hookNames[hook_MAX+1] = {
|
|
"NetVars",
|
|
"MapChange",
|
|
"MapLoad",
|
|
"PlayerJoin",
|
|
"PreThinkFrame",
|
|
"ThinkFrame",
|
|
"PostThinkFrame",
|
|
"MobjSpawn",
|
|
"MobjCollide",
|
|
"MobjLineCollide",
|
|
"MobjMoveCollide",
|
|
"TouchSpecial",
|
|
"MobjFuse",
|
|
"MobjThinker",
|
|
"BossThinker",
|
|
"ShouldDamage",
|
|
"MobjDamage",
|
|
"MobjDeath",
|
|
"BossDeath",
|
|
"MobjRemoved",
|
|
"JumpSpecial",
|
|
"AbilitySpecial",
|
|
"SpinSpecial",
|
|
"JumpSpinSpecial",
|
|
"BotTiccmd",
|
|
"BotAI",
|
|
"BotRespawn",
|
|
"LinedefExecute",
|
|
"PlayerMsg",
|
|
"HurtMsg",
|
|
"PlayerSpawn",
|
|
"ShieldSpawn",
|
|
"ShieldSpecial",
|
|
"MobjMoveBlocked",
|
|
"MapThingSpawn",
|
|
"FollowMobj",
|
|
"PlayerCanDamage",
|
|
"PlayerQuit",
|
|
"IntermissionThinker",
|
|
"TeamSwitch",
|
|
"ViewpointSwitch",
|
|
"SeenPlayer",
|
|
"PlayerThink",
|
|
"ShouldJingleContinue",
|
|
"GameQuit",
|
|
"PlayerCmd",
|
|
"MusicChange",
|
|
"PlayerHeight",
|
|
"PlayerCanEnterSpinGaps",
|
|
NULL
|
|
};
|
|
|
|
// Hook metadata
|
|
struct hook_s
|
|
{
|
|
struct hook_s *next;
|
|
enum hook type;
|
|
UINT16 id;
|
|
union {
|
|
mobjtype_t mt;
|
|
char *str;
|
|
} s;
|
|
boolean error;
|
|
};
|
|
typedef struct hook_s* hook_p;
|
|
|
|
#define FMT_HOOKID "hook_%d"
|
|
|
|
// 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];
|
|
|
|
// For each mobj type, a linked list for other mobj hooks
|
|
static hook_p mobjhooks[NUMMOBJTYPES];
|
|
|
|
// A linked list for player hooks
|
|
static hook_p playerhooks;
|
|
|
|
// A linked list for linedef executor hooks
|
|
static hook_p linedefexecutorhooks;
|
|
|
|
// For other hooks, a unique linked list
|
|
hook_p roothook;
|
|
|
|
static void PushHook(lua_State *L, hook_p hookp)
|
|
{
|
|
lua_pushfstring(L, FMT_HOOKID, hookp->id);
|
|
lua_gettable(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);
|
|
|
|
if (!lua_lumploading)
|
|
return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
|
|
|
|
switch(hook.type)
|
|
{
|
|
// Take a mobjtype enum which this hook is specifically for.
|
|
case hook_MobjSpawn:
|
|
case hook_MobjCollide:
|
|
case hook_MobjLineCollide:
|
|
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:
|
|
case hook_MobjMoveBlocked:
|
|
case hook_MapThingSpawn:
|
|
case hook_FollowMobj:
|
|
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:
|
|
case hook_ShouldJingleContinue:
|
|
hook.s.str = NULL;
|
|
if (lua_isstring(L, 2))
|
|
{ // lowercase copy
|
|
hook.s.str = Z_StrDup(lua_tostring(L, 2));
|
|
strlwr(hook.s.str);
|
|
}
|
|
break;
|
|
case hook_LinedefExecute: // Linedef executor functions
|
|
hook.s.str = Z_StrDup(luaL_checkstring(L, 2));
|
|
strupr(hook.s.str);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
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)
|
|
{
|
|
case hook_MobjThinker:
|
|
lastp = &mobjthinkerhooks[hook.s.mt];
|
|
break;
|
|
case hook_MobjCollide:
|
|
case hook_MobjLineCollide:
|
|
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:
|
|
case hook_MobjMoveBlocked:
|
|
case hook_MapThingSpawn:
|
|
case hook_FollowMobj:
|
|
lastp = &mobjhooks[hook.s.mt];
|
|
break;
|
|
case hook_JumpSpecial:
|
|
case hook_AbilitySpecial:
|
|
case hook_SpinSpecial:
|
|
case hook_JumpSpinSpecial:
|
|
case hook_PlayerSpawn:
|
|
case hook_PlayerCanDamage:
|
|
case hook_TeamSwitch:
|
|
case hook_ViewpointSwitch:
|
|
case hook_SeenPlayer:
|
|
case hook_ShieldSpawn:
|
|
case hook_ShieldSpecial:
|
|
case hook_PlayerThink:
|
|
case hook_PlayerHeight:
|
|
case hook_PlayerCanEnterSpinGaps:
|
|
lastp = &playerhooks;
|
|
break;
|
|
case hook_LinedefExecute:
|
|
lastp = &linedefexecutorhooks;
|
|
break;
|
|
default:
|
|
lastp = &roothook;
|
|
break;
|
|
}
|
|
|
|
// 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;
|
|
|
|
// set the hook function in the registry.
|
|
lua_pushfstring(L, FMT_HOOKID, hook.id);
|
|
lua_pushvalue(L, 1);
|
|
lua_settable(L, LUA_REGISTRYINDEX);
|
|
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);
|
|
return 0;
|
|
}
|
|
|
|
boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
|
|
return false;
|
|
|
|
I_Assert(mo->type < NUMMOBJTYPES);
|
|
|
|
if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type]))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
// Look for all generic mobj hooks
|
|
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != which)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
LUA_PushUserdata(gL, mo, META_MOBJ);
|
|
PushHook(gL, hookp);
|
|
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++;
|
|
if (lua_gettop(gL) == 1)
|
|
LUA_PushUserdata(gL, mo, META_MOBJ);
|
|
PushHook(gL, hookp);
|
|
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;
|
|
if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = playerhooks; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != which)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
LUA_PushUserdata(gL, plr, META_PLAYER);
|
|
PushHook(gL, hookp);
|
|
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 map change (before load)
|
|
void LUAh_MapChange(INT16 mapnumber)
|
|
{
|
|
hook_p hookp;
|
|
if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8))))
|
|
return;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
lua_pushinteger(gL, mapnumber);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_MapChange)
|
|
continue;
|
|
|
|
PushHook(gL, hookp);
|
|
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 map load
|
|
void LUAh_MapLoad(void)
|
|
{
|
|
hook_p hookp;
|
|
if (!gL || !(hooksAvailable[hook_MapLoad/8] & (1<<(hook_MapLoad%8))))
|
|
return;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
lua_pushinteger(gL, gamemap);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_MapLoad)
|
|
continue;
|
|
|
|
PushHook(gL, hookp);
|
|
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 Got_AddPlayer
|
|
void LUAh_PlayerJoin(int playernum)
|
|
{
|
|
hook_p hookp;
|
|
if (!gL || !(hooksAvailable[hook_PlayerJoin/8] & (1<<(hook_PlayerJoin%8))))
|
|
return;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
lua_pushinteger(gL, playernum);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_PlayerJoin)
|
|
continue;
|
|
|
|
PushHook(gL, hookp);
|
|
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;
|
|
if (!gL || !(hooksAvailable[hook_PreThinkFrame/8] & (1<<(hook_PreThinkFrame%8))))
|
|
return;
|
|
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_PreThinkFrame)
|
|
continue;
|
|
|
|
PushHook(gL, hookp);
|
|
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, 1); // Pop error handler
|
|
}
|
|
|
|
// Hook for frame (after mobj and player thinkers)
|
|
void LUAh_ThinkFrame(void)
|
|
{
|
|
hook_p hookp;
|
|
// variables used by perf stats
|
|
int hook_index = 0;
|
|
int time_taken = 0;
|
|
if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8))))
|
|
return;
|
|
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_ThinkFrame)
|
|
continue;
|
|
|
|
if (cv_perfstats.value == 3)
|
|
time_taken = I_GetPreciseTime();
|
|
PushHook(gL, hookp);
|
|
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)
|
|
{
|
|
lua_Debug ar;
|
|
time_taken = I_GetPreciseTime() - time_taken;
|
|
// we need the function, let's just retrieve it again
|
|
PushHook(gL, hookp);
|
|
lua_getinfo(gL, ">S", &ar);
|
|
PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src);
|
|
hook_index++;
|
|
}
|
|
}
|
|
|
|
lua_pop(gL, 1); // Pop error handler
|
|
}
|
|
|
|
// Hook for frame (at end of tick, ie after overlays, precipitation, specials)
|
|
void LUAh_PostThinkFrame(void)
|
|
{
|
|
hook_p hookp;
|
|
if (!gL || !(hooksAvailable[hook_PostThinkFrame/8] & (1<<(hook_PostThinkFrame%8))))
|
|
return;
|
|
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_PostThinkFrame)
|
|
continue;
|
|
|
|
PushHook(gL, hookp);
|
|
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, 1); // Pop error handler
|
|
}
|
|
|
|
// 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.
|
|
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);
|
|
|
|
// Look for all generic mobj collision hooks
|
|
for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != which)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, thing1, META_MOBJ);
|
|
LUA_PushUserdata(gL, thing2, META_MOBJ);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, thing1, META_MOBJ);
|
|
LUA_PushUserdata(gL, thing2, META_MOBJ);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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;
|
|
}
|
|
|
|
UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which)
|
|
{
|
|
hook_p hookp;
|
|
UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no.
|
|
if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
|
|
return 0;
|
|
|
|
I_Assert(thing->type < NUMMOBJTYPES);
|
|
|
|
if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing->type]))
|
|
return 0;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
// Look for all generic mobj collision hooks
|
|
for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != which)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, thing, META_MOBJ);
|
|
LUA_PushUserdata(gL, line, META_LINE);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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[thing->type]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != which)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, thing, META_MOBJ);
|
|
LUA_PushUserdata(gL, line, META_LINE);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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;
|
|
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);
|
|
|
|
// Look for all generic mobj thinker hooks
|
|
for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
|
|
{
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
LUA_PushUserdata(gL, mo, META_MOBJ);
|
|
PushHook(gL, hookp);
|
|
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++;
|
|
if (lua_gettop(gL) == 1)
|
|
LUA_PushUserdata(gL, mo, META_MOBJ);
|
|
PushHook(gL, hookp);
|
|
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;
|
|
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);
|
|
|
|
// 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++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, special, META_MOBJ);
|
|
LUA_PushUserdata(gL, toucher, META_MOBJ);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, special, META_MOBJ);
|
|
LUA_PushUserdata(gL, toucher, META_MOBJ);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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, UINT8 damagetype)
|
|
{
|
|
hook_p hookp;
|
|
UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no.
|
|
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);
|
|
|
|
// 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++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, target, META_MOBJ);
|
|
LUA_PushUserdata(gL, inflictor, META_MOBJ);
|
|
LUA_PushUserdata(gL, source, META_MOBJ);
|
|
lua_pushinteger(gL, damage);
|
|
lua_pushinteger(gL, damagetype);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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_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++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, target, META_MOBJ);
|
|
LUA_PushUserdata(gL, inflictor, META_MOBJ);
|
|
LUA_PushUserdata(gL, source, META_MOBJ);
|
|
lua_pushinteger(gL, damage);
|
|
lua_pushinteger(gL, damagetype);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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_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, UINT8 damagetype)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
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);
|
|
|
|
// 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++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, target, META_MOBJ);
|
|
LUA_PushUserdata(gL, inflictor, META_MOBJ);
|
|
LUA_PushUserdata(gL, source, META_MOBJ);
|
|
lua_pushinteger(gL, damage);
|
|
lua_pushinteger(gL, damagetype);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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);
|
|
}
|
|
|
|
for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_MobjDamage)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, target, META_MOBJ);
|
|
LUA_PushUserdata(gL, inflictor, META_MOBJ);
|
|
LUA_PushUserdata(gL, source, META_MOBJ);
|
|
lua_pushinteger(gL, damage);
|
|
lua_pushinteger(gL, damagetype);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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;
|
|
}
|
|
|
|
// Hook for P_KillMobj by mobj type
|
|
boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
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);
|
|
|
|
// Look for all generic mobj death hooks
|
|
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_MobjDeath)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, target, META_MOBJ);
|
|
LUA_PushUserdata(gL, inflictor, META_MOBJ);
|
|
LUA_PushUserdata(gL, source, META_MOBJ);
|
|
lua_pushinteger(gL, damagetype);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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_MobjDeath)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, target, META_MOBJ);
|
|
LUA_PushUserdata(gL, inflictor, META_MOBJ);
|
|
LUA_PushUserdata(gL, source, META_MOBJ);
|
|
lua_pushinteger(gL, damagetype);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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 B_BuildTiccmd
|
|
boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
if (!gL || !(hooksAvailable[hook_BotTiccmd/8] & (1<<(hook_BotTiccmd%8))))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_BotTiccmd)
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, bot, META_PLAYER);
|
|
LUA_PushUserdata(gL, cmd, META_TICCMD);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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 B_BuildTailsTiccmd by skin name
|
|
boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8))))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_BotAI
|
|
|| (hookp->s.str && strcmp(hookp->s.str, ((skin_t*)tails->skin)->name)))
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, sonic, META_MOBJ);
|
|
LUA_PushUserdata(gL, tails, META_MOBJ);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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, 2+1)) {
|
|
boolean forward=false, backward=false, left=false, right=false, strafeleft=false, straferight=false, jump=false, spin=false;
|
|
#define CHECKFIELD(field) \
|
|
lua_getfield(gL, 2+1, #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, 2+1), lua_toboolean(gL, 2+2), lua_toboolean(gL, 2+3), lua_toboolean(gL, 2+4), lua_toboolean(gL, 2+5), lua_toboolean(gL, 2+6), lua_toboolean(gL, 2+7), lua_toboolean(gL, 2+8));
|
|
|
|
lua_pop(gL, 8);
|
|
hooked = true;
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
return hooked;
|
|
}
|
|
|
|
// Hook for B_CheckRespawn
|
|
boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails)
|
|
{
|
|
hook_p hookp;
|
|
UINT8 shouldRespawn = 0; // 0 = default, 1 = force yes, 2 = force no.
|
|
if (!gL || !(hooksAvailable[hook_BotRespawn/8] & (1<<(hook_BotRespawn%8))))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_BotRespawn)
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, sonic, META_MOBJ);
|
|
LUA_PushUserdata(gL, tails, META_MOBJ);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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 (lua_toboolean(gL, -1))
|
|
shouldRespawn = 1; // Force yes
|
|
else
|
|
shouldRespawn = 2; // Force no
|
|
}
|
|
lua_pop(gL, 1);
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
return shouldRespawn;
|
|
}
|
|
|
|
// Hook for linedef executors
|
|
boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
if (!gL || !(hooksAvailable[hook_LinedefExecute/8] & (1<<(hook_LinedefExecute%8))))
|
|
return 0;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next)
|
|
{
|
|
if (strcmp(hookp->s.str, line->stringargs[0]))
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, line, META_LINE);
|
|
LUA_PushUserdata(gL, mo, META_MOBJ);
|
|
LUA_PushUserdata(gL, sector, META_SECTOR);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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;
|
|
}
|
|
|
|
|
|
boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
if (!gL || !(hooksAvailable[hook_PlayerMsg/8] & (1<<(hook_PlayerMsg%8))))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_PlayerMsg)
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
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
|
|
}
|
|
PushHook(gL, hookp);
|
|
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 hurt messages
|
|
boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
if (!gL || !(hooksAvailable[hook_HurtMsg/8] & (1<<(hook_HurtMsg%8))))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_HurtMsg
|
|
|| (hookp->s.mt && !(inflictor && hookp->s.mt == inflictor->type)))
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
LUA_PushUserdata(gL, inflictor, META_MOBJ);
|
|
LUA_PushUserdata(gL, source, META_MOBJ);
|
|
lua_pushinteger(gL, damagetype);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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;
|
|
}
|
|
|
|
void LUAh_NetArchiveHook(lua_CFunction archFunc)
|
|
{
|
|
hook_p hookp;
|
|
int errorhandlerindex;
|
|
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);
|
|
|
|
// tables becomes an upvalue of archFunc
|
|
lua_pushvalue(gL, -2);
|
|
lua_pushcclosure(gL, archFunc, 1);
|
|
// stack: tables, archFunc
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_NetVars)
|
|
continue;
|
|
|
|
PushHook(gL, hookp);
|
|
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);
|
|
}
|
|
}
|
|
|
|
lua_pop(gL, 2); // Pop archFunc and error handler
|
|
// stack: tables
|
|
}
|
|
|
|
boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
if (!gL || !(hooksAvailable[hook_MapThingSpawn/8] & (1<<(hook_MapThingSpawn%8))))
|
|
return false;
|
|
|
|
if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type]))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
// Look for all generic mobj map thing spawn hooks
|
|
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_MapThingSpawn)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, mo, META_MOBJ);
|
|
LUA_PushUserdata(gL, mthing, META_MAPTHING);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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[mo->type]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_MapThingSpawn)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, mo, META_MOBJ);
|
|
LUA_PushUserdata(gL, mthing, META_MAPTHING);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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_PlayerAfterThink Smiles mobj-following
|
|
boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
if (!gL || !(hooksAvailable[hook_FollowMobj/8] & (1<<(hook_FollowMobj%8))))
|
|
return 0;
|
|
|
|
if (!(mobjhooks[MT_NULL] || mobjhooks[mobj->type]))
|
|
return 0;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
// Look for all generic mobj follow item hooks
|
|
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_FollowMobj)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
LUA_PushUserdata(gL, mobj, META_MOBJ);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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[mobj->type]; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_FollowMobj)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
LUA_PushUserdata(gL, mobj, META_MOBJ);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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_PlayerCanDamage
|
|
UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj)
|
|
{
|
|
hook_p hookp;
|
|
UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no.
|
|
if (!gL || !(hooksAvailable[hook_PlayerCanDamage/8] & (1<<(hook_PlayerCanDamage%8))))
|
|
return 0;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = playerhooks; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_PlayerCanDamage)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
LUA_PushUserdata(gL, mobj, META_MOBJ);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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;
|
|
}
|
|
|
|
void LUAh_PlayerQuit(player_t *plr, kickreason_t reason)
|
|
{
|
|
hook_p hookp;
|
|
if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8))))
|
|
return;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_PlayerQuit)
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit
|
|
lua_pushinteger(gL, reason); // Reason for quitting
|
|
}
|
|
PushHook(gL, hookp);
|
|
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);
|
|
}
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
}
|
|
|
|
// Hook for Y_Ticker
|
|
void LUAh_IntermissionThinker(void)
|
|
{
|
|
hook_p hookp;
|
|
if (!gL || !(hooksAvailable[hook_IntermissionThinker/8] & (1<<(hook_IntermissionThinker%8))))
|
|
return;
|
|
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_IntermissionThinker)
|
|
continue;
|
|
|
|
PushHook(gL, hookp);
|
|
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, 1); // Pop error handler
|
|
}
|
|
|
|
// Hook for team switching
|
|
// It's just an edit of LUAh_ViewpointSwitch.
|
|
boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble)
|
|
{
|
|
hook_p hookp;
|
|
boolean canSwitchTeam = true;
|
|
if (!gL || !(hooksAvailable[hook_TeamSwitch/8] & (1<<(hook_TeamSwitch%8))))
|
|
return true;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = playerhooks; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_TeamSwitch)
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
lua_pushinteger(gL, newteam);
|
|
lua_pushboolean(gL, fromspectators);
|
|
lua_pushboolean(gL, tryingautobalance);
|
|
lua_pushboolean(gL, tryingscramble);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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_isnil(gL, -1) && !lua_toboolean(gL, -1))
|
|
canSwitchTeam = false; // Can't switch team
|
|
lua_pop(gL, 1);
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
return canSwitchTeam;
|
|
}
|
|
|
|
// Hook for spy mode
|
|
UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced)
|
|
{
|
|
hook_p hookp;
|
|
UINT8 canSwitchView = 0; // 0 = default, 1 = force yes, 2 = force no.
|
|
if (!gL || !(hooksAvailable[hook_ViewpointSwitch/8] & (1<<(hook_ViewpointSwitch%8))))
|
|
return 0;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
hud_running = true; // local hook
|
|
|
|
for (hookp = playerhooks; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_ViewpointSwitch)
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER);
|
|
lua_pushboolean(gL, forced);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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 nil, leave canSwitchView = 0.
|
|
if (lua_toboolean(gL, -1))
|
|
canSwitchView = 1; // Force viewpoint switch
|
|
else
|
|
canSwitchView = 2; // Skip viewpoint switch
|
|
}
|
|
lua_pop(gL, 1);
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
|
|
hud_running = false;
|
|
|
|
return canSwitchView;
|
|
}
|
|
|
|
// Hook for MT_NAMECHECK
|
|
boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend)
|
|
{
|
|
hook_p hookp;
|
|
boolean hasSeenPlayer = true;
|
|
if (!gL || !(hooksAvailable[hook_SeenPlayer/8] & (1<<(hook_SeenPlayer%8))))
|
|
return true;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
hud_running = true; // local hook
|
|
|
|
for (hookp = playerhooks; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_SeenPlayer)
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
LUA_PushUserdata(gL, seenfriend, META_PLAYER);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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) && !lua_toboolean(gL, -1))
|
|
hasSeenPlayer = false; // Hasn't seen player
|
|
lua_pop(gL, 1);
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
|
|
hud_running = false;
|
|
|
|
return hasSeenPlayer;
|
|
}
|
|
|
|
boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname)
|
|
{
|
|
hook_p hookp;
|
|
boolean keepplaying = false;
|
|
if (!gL || !(hooksAvailable[hook_ShouldJingleContinue/8] & (1<<(hook_ShouldJingleContinue%8))))
|
|
return true;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
hud_running = true; // local hook
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_ShouldJingleContinue
|
|
|| (hookp->s.str && strcmp(hookp->s.str, musname)))
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
lua_pushstring(gL, musname);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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) && lua_toboolean(gL, -1))
|
|
keepplaying = true; // Keep playing this boolean
|
|
lua_pop(gL, 1);
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
|
|
hud_running = false;
|
|
|
|
return keepplaying;
|
|
}
|
|
|
|
// Hook for game quitting
|
|
void LUAh_GameQuit(boolean quitting)
|
|
{
|
|
hook_p hookp;
|
|
if (!gL || !(hooksAvailable[hook_GameQuit/8] & (1<<(hook_GameQuit%8))))
|
|
return;
|
|
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_GameQuit)
|
|
continue;
|
|
|
|
PushHook(gL, hookp);
|
|
lua_pushboolean(gL, quitting);
|
|
if (lua_pcall(gL, 1, 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, 1); // Pop error handler
|
|
}
|
|
|
|
// Hook for building player's ticcmd struct (Ported from SRB2Kart)
|
|
boolean hook_cmd_running = false;
|
|
boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd)
|
|
{
|
|
hook_p hookp;
|
|
boolean hooked = false;
|
|
if (!gL || !(hooksAvailable[hook_PlayerCmd/8] & (1<<(hook_PlayerCmd%8))))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
hook_cmd_running = true;
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_PlayerCmd)
|
|
continue;
|
|
|
|
if (lua_gettop(gL) == 1)
|
|
{
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
LUA_PushUserdata(gL, cmd, META_TICCMD);
|
|
}
|
|
PushHook(gL, hookp);
|
|
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);
|
|
hook_cmd_running = false;
|
|
return hooked;
|
|
}
|
|
|
|
// 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;
|
|
|
|
if (!gL || !(hooksAvailable[hook_MusicChange/8] & (1<<(hook_MusicChange%8))))
|
|
return false;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = roothook; hookp; hookp = hookp->next)
|
|
if (hookp->type == hook_MusicChange)
|
|
{
|
|
PushHook(gL, hookp);
|
|
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_isboolean(gL, -3))
|
|
*position = lua_tonumber(gL, -3);
|
|
// output 5: prefadems override
|
|
if (lua_isboolean(gL, -2))
|
|
*prefadems = lua_tonumber(gL, -2);
|
|
// output 6: fadeinms override
|
|
if (lua_isboolean(gL, -1))
|
|
*fadeinms = lua_tonumber(gL, -1);
|
|
|
|
lua_pop(gL, 7); // Pop returned values and error handler
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
newname[6] = 0;
|
|
return hooked;
|
|
}
|
|
|
|
// Hook for determining player height
|
|
fixed_t LUAh_PlayerHeight(player_t *player)
|
|
{
|
|
hook_p hookp;
|
|
fixed_t newheight = -1;
|
|
if (!gL || !(hooksAvailable[hook_PlayerHeight/8] & (1<<(hook_PlayerHeight%8))))
|
|
return newheight;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = playerhooks; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_PlayerHeight)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
PushHook(gL, hookp);
|
|
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_isnil(gL, -1))
|
|
{
|
|
fixed_t returnedheight = lua_tonumber(gL, -1);
|
|
// 0 height has... strange results, but it's not problematic like negative heights are.
|
|
// when an object's height is set to a negative number directly with lua, it's forced to 0 instead.
|
|
// here, I think it's better to ignore negatives so that they don't replace any results of previous hooks!
|
|
if (returnedheight >= 0)
|
|
newheight = returnedheight;
|
|
}
|
|
lua_pop(gL, 1);
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
return newheight;
|
|
}
|
|
|
|
// Hook for determining whether players are allowed passage through spin gaps
|
|
UINT8 LUAh_PlayerCanEnterSpinGaps(player_t *player)
|
|
{
|
|
hook_p hookp;
|
|
UINT8 canEnter = 0; // 0 = default, 1 = force yes, 2 = force no.
|
|
if (!gL || !(hooksAvailable[hook_PlayerCanEnterSpinGaps/8] & (1<<(hook_PlayerCanEnterSpinGaps%8))))
|
|
return 0;
|
|
|
|
lua_settop(gL, 0);
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
|
|
|
for (hookp = playerhooks; hookp; hookp = hookp->next)
|
|
{
|
|
if (hookp->type != hook_PlayerCanEnterSpinGaps)
|
|
continue;
|
|
|
|
ps_lua_mobjhooks++;
|
|
if (lua_gettop(gL) == 1)
|
|
LUA_PushUserdata(gL, player, META_PLAYER);
|
|
PushHook(gL, hookp);
|
|
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_isnil(gL, -1))
|
|
{ // if nil, leave canEnter = 0.
|
|
if (lua_toboolean(gL, -1))
|
|
canEnter = 1; // Force yes
|
|
else
|
|
canEnter = 2; // Force no
|
|
}
|
|
lua_pop(gL, 1);
|
|
}
|
|
|
|
lua_settop(gL, 0);
|
|
return canEnter;
|
|
}
|