Skip to content
Snippets Groups Projects
lua_hooklib.c 40.9 KiB
Newer Older
Alam Ed Arias's avatar
Alam Ed Arias committed
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2018 by Sonic Team Junior.
Alam Ed Arias's avatar
Alam Ed Arias committed
//
// 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"
#ifdef HAVE_BLUA
#include "doomstat.h"
#include "p_mobj.h"
#include "g_game.h"
Alam Ed Arias's avatar
Alam Ed Arias committed
#include "r_things.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

Alam Ed Arias's avatar
Alam Ed Arias committed
static UINT8 hooksAvailable[(hook_MAX/8)+1];

Alam Ed Arias's avatar
Alam Ed Arias committed
const char *const hookNames[hook_MAX+1] = {
	"NetVars",
	"MapChange",
	"MapLoad",
	"PlayerJoin",
	"PreThinkFrame",
Alam Ed Arias's avatar
Alam Ed Arias committed
	"ThinkFrame",
	"PostThinkFrame",
Alam Ed Arias's avatar
Alam Ed Arias committed
	"MobjSpawn",
	"MobjCollide",
	"MobjMoveCollide",
	"TouchSpecial",
	"MobjFuse",
	"MobjThinker",
	"BossThinker",
	"ShouldDamage",
	"MobjDamage",
	"MobjDeath",
	"BossDeath",
	"MobjRemoved",
Alam Ed Arias's avatar
Alam Ed Arias committed
	"JumpSpecial",
	"AbilitySpecial",
	"SpinSpecial",
	"JumpSpinSpecial",
Alam Ed Arias's avatar
Alam Ed Arias committed
	"BotTiccmd",
	"BotAI",
	"LinedefExecute",
Alam Ed Arias's avatar
Alam Ed Arias committed
	"HurtMsg",
wolfs's avatar
wolfs committed
	"PlayerSpawn",
Prisima the Fox's avatar
Prisima the Fox committed
	"PlayerQuit",
	"PlayerThink",
wolfs's avatar
wolfs committed
	"MusicChange",
	"ShouldSpin",
	"ShouldExplode",
	"ShouldSquish",
	"PlayerSpin",
	"PlayerExplode",
	"PlayerSquish",
SteelT's avatar
SteelT committed
	"IntermissionThinker",
Latapostrophe's avatar
Latapostrophe committed
	"VoteThinker",
Alam Ed Arias's avatar
Alam Ed Arias committed
	NULL
};

// Hook metadata
struct hook_s
{
	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;

#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
Alam Ed Arias's avatar
Alam Ed Arias committed
// 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};
	hook_p hookp, *lastp;

	hook.type = luaL_checkoption(L, 1, NULL, hookNames);
	lua_remove(L, 1);
Alam Ed Arias's avatar
Alam Ed Arias committed

	luaL_checktype(L, 1, LUA_TFUNCTION);
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (hud_running)
		return luaL_error(L, "HUD rendering code should not call this function!");

	switch(hook.type)
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
	// 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");
Alam Ed Arias's avatar
Alam Ed Arias committed
		break;
	case hook_BotAI:
		hook.s.skinname = NULL;
		if (lua_isstring(L, 2))
Alam Ed Arias's avatar
Alam Ed Arias committed
		{ // lowercase copy
			const char *s = lua_tostring(L, 2);
			char *p = hook.s.skinname = ZZ_Alloc(strlen(s)+1);
Alam Ed Arias's avatar
Alam Ed Arias committed
			do {
				*p = tolower(*s);
				++p;
			} while(*(++s));
			*p = 0;
		}
		break;
	case hook_LinedefExecute: // Linedef executor functions
Alam Ed Arias's avatar
Alam Ed Arias committed
		{ // uppercase copy
			const char *s = luaL_checkstring(L, 2);
			char *p = hook.s.funcname = ZZ_Alloc(strlen(s)+1);
Alam Ed Arias's avatar
Alam Ed Arias committed
			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:
Alam Ed Arias's avatar
Alam Ed Arias committed
	default:
		break;
	}
	lua_settop(L, 1); // lua stack contains only the function now.
Alam Ed Arias's avatar
Alam Ed Arias committed

	hooksAvailable[hook.type/8] |= 1<<(hook.type%8);
Alam Ed Arias's avatar
Alam Ed Arias committed

	// set hook.id to the highest id + 1
	// Special cases for some hook types (see the comments above mobjthinkerhooks declaration)
	switch(hook.type)
	{
	case hook_MobjThinker:
		break;
	case hook_MobjCollide:
	case hook_MobjMoveCollide:
		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:
	// 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);
Alam Ed Arias's avatar
Alam Ed Arias committed
	return 0;
}

int LUA_HookLib(lua_State *L)
{
Alam Ed Arias's avatar
Alam Ed Arias committed
	memset(hooksAvailable,0,sizeof(UINT8[(hook_MAX/8)+1]));
	roothook = NULL;
Alam Ed Arias's avatar
Alam Ed Arias committed
	lua_register(L, "addHook", lib_addHook);
	return 0;
}

boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
{
	hook_p hookp;
Alam Ed Arias's avatar
Alam Ed Arias committed
	boolean hooked = false;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed
		return false;

	lua_settop(gL, 0);
	// Look for all generic mobj hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
		if (hookp->type == which)
Alam Ed Arias's avatar
Alam Ed Arias committed
		{
			if (lua_gettop(gL) == 0)
				LUA_PushUserdata(gL, mo, META_MOBJ);
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -2);
			if (lua_pcall(gL, 1, 1, 0)) {
				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;
			}
Alam Ed Arias's avatar
Alam Ed Arias committed
			if (lua_toboolean(gL, -1))
				hooked = true;
			lua_pop(gL, 1);
		}

	for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next)
		if (hookp->type == which)
		{
			if (lua_gettop(gL) == 0)
				LUA_PushUserdata(gL, mo, META_MOBJ);
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -2);
			if (lua_pcall(gL, 1, 1, 0)) {
				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);
Alam Ed Arias's avatar
Alam Ed Arias committed
	return hooked;
}

Alam Ed Arias's avatar
Alam Ed Arias committed
boolean LUAh_PlayerHook(player_t *plr, enum hook which)
{
	hook_p hookp;
Alam Ed Arias's avatar
Alam Ed Arias committed
	boolean hooked = false;
	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
		return false;

	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed

	for (hookp = playerhooks; hookp; hookp = hookp->next)
		if (hookp->type == which)
		{
			if (lua_gettop(gL) == 0)
				LUA_PushUserdata(gL, plr, META_PLAYER);
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -2);
			if (lua_pcall(gL, 1, 1, 0)) {
				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;
Alam Ed Arias's avatar
Alam Ed Arias committed
			lua_pop(gL, 1);
		}

	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed
	return hooked;
}

Alam Ed Arias's avatar
Alam Ed Arias committed
// Hook for map change (before load)
void LUAh_MapChange(INT16 mapnumber)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	hook_p hookp;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed
		return;

	lua_settop(gL, 0);
	lua_pushinteger(gL, mapnumber);

	for (hookp = roothook; hookp; hookp = hookp->next)
		if (hookp->type == hook_MapChange)
		{
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -2);
			LUA_Call(gL, 1);
		}

	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// Hook for map load
void LUAh_MapLoad(void)
{
	hook_p hookp;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[hook_MapLoad/8] & (1<<(hook_MapLoad%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed
		return;

	lua_settop(gL, 0);
	lua_pushinteger(gL, gamemap);
Alam Ed Arias's avatar
Alam Ed Arias committed

	for (hookp = roothook; hookp; hookp = hookp->next)
		if (hookp->type == hook_MapLoad)
		{
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -2);
			LUA_Call(gL, 1);
		}
Alam Ed Arias's avatar
Alam Ed Arias committed

	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// Hook for Got_AddPlayer
void LUAh_PlayerJoin(int playernum)
{
	hook_p hookp;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[hook_PlayerJoin/8] & (1<<(hook_PlayerJoin%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed
		return;

	lua_settop(gL, 0);
	lua_pushinteger(gL, playernum);
Alam Ed Arias's avatar
Alam Ed Arias committed

	for (hookp = roothook; hookp; hookp = hookp->next)
		if (hookp->type == hook_PlayerJoin)
		{
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -2);
			LUA_Call(gL, 1);
		}
Alam Ed Arias's avatar
Alam Ed Arias committed

	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// 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;

	for (hookp = roothook; hookp; hookp = hookp->next)
	{
		if (hookp->type != hook_PreThinkFrame)
			continue;

		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
		lua_gettable(gL, LUA_REGISTRYINDEX);
		if (lua_pcall(gL, 0, 0, 0)) {
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
			lua_pop(gL, 1);
			hookp->error = true;
		}
	}
}

Alam Ed Arias's avatar
Alam Ed Arias committed
// Hook for frame (after mobj and player thinkers)
void LUAh_ThinkFrame(void)
{
	hook_p hookp;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed
		return;

	for (hookp = roothook; hookp; hookp = hookp->next)
		if (hookp->type == hook_ThinkFrame)
Alam Ed Arias's avatar
Alam Ed Arias committed
		{
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			if (lua_pcall(gL, 0, 0, 0)) {
				if (!hookp->error || cv_debug & DBG_LUA)
					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
				lua_pop(gL, 1);
				hookp->error = true;
			}
// 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;

	for (hookp = roothook; hookp; hookp = hookp->next)
	{
		if (hookp->type != hook_PostThinkFrame)
			continue;

		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
		lua_gettable(gL, LUA_REGISTRYINDEX);
		if (lua_pcall(gL, 0, 0, 0)) {
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
			lua_pop(gL, 1);
			hookp->error = true;
		}
	}
}

SteelT's avatar
SteelT committed
// Hook for Y_Ticker
void LUAh_IntermissionThinker(void)
{
	hook_p hookp;
	if (!gL || !(hooksAvailable[hook_IntermissionThinker/8] & (1<<(hook_IntermissionThinker%8))))
		return;

	for (hookp = roothook; hookp; hookp = hookp->next)
		if (hookp->type == hook_IntermissionThinker)
		{
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			if (lua_pcall(gL, 0, 0, 0)) {
				if (!hookp->error || cv_debug & DBG_LUA)
					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
				lua_pop(gL, 1);
				hookp->error = true;
			}
		}
}

Latapostrophe's avatar
Latapostrophe committed
// Hook for Y_VoteTicker
void LUAh_VoteThinker(void)
{
	hook_p hookp;
	if (!gL || !(hooksAvailable[hook_VoteThinker/8] & (1<<(hook_VoteThinker%8))))
		return;

	for (hookp = roothook; hookp; hookp = hookp->next)
		if (hookp->type == hook_VoteThinker)
		{
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			if (lua_pcall(gL, 0, 0, 0)) {
				if (!hookp->error || cv_debug & DBG_LUA)
					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
				lua_pop(gL, 1);
				hookp->error = true;
			}
		}
}

// Hook for mobj collisions
UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	hook_p hookp;
Alam Ed Arias's avatar
Alam Ed Arias committed
	UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no.
	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed
		return 0;

	lua_settop(gL, 0);
	// Look for all generic mobj collision hooks
	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
		if (hookp->type == which)
		{
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, thing1, META_MOBJ);
				LUA_PushUserdata(gL, thing2, META_MOBJ);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -3);
			lua_pushvalue(gL, -3);
			if (lua_pcall(gL, 2, 1, 0)) {
				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 (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, thing1, META_MOBJ);
				LUA_PushUserdata(gL, thing2, META_MOBJ);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -3);
			lua_pushvalue(gL, -3);
			if (lua_pcall(gL, 2, 1, 0)) {
				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
			}
Alam Ed Arias's avatar
Alam Ed Arias committed
			lua_pop(gL, 1);
		}
	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed
	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;

	lua_settop(gL, 0);

	// Look for all generic mobj thinker hooks
	for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
	{
		if (lua_gettop(gL) == 0)
			LUA_PushUserdata(gL, mo, META_MOBJ);
		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
		lua_gettable(gL, LUA_REGISTRYINDEX);
		lua_pushvalue(gL, -2);
		if (lua_pcall(gL, 1, 1, 0)) {
			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)
	{
		if (lua_gettop(gL) == 0)
			LUA_PushUserdata(gL, mo, META_MOBJ);
		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
		lua_gettable(gL, LUA_REGISTRYINDEX);
		lua_pushvalue(gL, -2);
		if (lua_pcall(gL, 1, 1, 0)) {
			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;
}

Alam Ed Arias's avatar
Alam Ed Arias committed
// Hook for P_TouchSpecialThing by mobj type
boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
{
	hook_p hookp;
Alam Ed Arias's avatar
Alam Ed Arias committed
	boolean hooked = false;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[hook_TouchSpecial/8] & (1<<(hook_TouchSpecial%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed

	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed

	// Look for all generic touch special hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
		if (hookp->type == hook_TouchSpecial)
		{
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, special, META_MOBJ);
				LUA_PushUserdata(gL, toucher, META_MOBJ);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -3);
			lua_pushvalue(gL, -3);
			if (lua_pcall(gL, 2, 1, 0)) {
				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)
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, special, META_MOBJ);
				LUA_PushUserdata(gL, toucher, META_MOBJ);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -3);
			lua_pushvalue(gL, -3);
			if (lua_pcall(gL, 2, 1, 0)) {
				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;
Alam Ed Arias's avatar
Alam Ed Arias committed
			lua_pop(gL, 1);
		}
Alam Ed Arias's avatar
Alam Ed Arias committed

	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed
	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;
Alam Ed Arias's avatar
Alam Ed Arias committed
	UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no.
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[hook_ShouldDamage/8] & (1<<(hook_ShouldDamage%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed
		return 0;

	lua_settop(gL, 0);
	// Look for all generic should damage hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
		if (hookp->type == hook_ShouldDamage)
		{
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, target, META_MOBJ);
				LUA_PushUserdata(gL, inflictor, META_MOBJ);
				LUA_PushUserdata(gL, source, META_MOBJ);
				lua_pushinteger(gL, damage);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			if (lua_pcall(gL, 4, 1, 0)) {
				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)
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, target, META_MOBJ);
				LUA_PushUserdata(gL, inflictor, META_MOBJ);
				LUA_PushUserdata(gL, source, META_MOBJ);
				lua_pushinteger(gL, damage);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			if (lua_pcall(gL, 4, 1, 0)) {
				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
			}
Alam Ed Arias's avatar
Alam Ed Arias committed
			lua_pop(gL, 1);
		}
	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed
	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;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed

	lua_settop(gL, 0);
	// Look for all generic mobj damage hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
		if (hookp->type == hook_MobjDamage)
		{
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, target, META_MOBJ);
				LUA_PushUserdata(gL, inflictor, META_MOBJ);
				LUA_PushUserdata(gL, source, META_MOBJ);
				lua_pushinteger(gL, damage);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			if (lua_pcall(gL, 4, 1, 0)) {
				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)
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, target, META_MOBJ);
				LUA_PushUserdata(gL, inflictor, META_MOBJ);
				LUA_PushUserdata(gL, source, META_MOBJ);
				lua_pushinteger(gL, damage);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			lua_pushvalue(gL, -5);
			if (lua_pcall(gL, 4, 1, 0)) {
				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;
Alam Ed Arias's avatar
Alam Ed Arias committed
			lua_pop(gL, 1);
		}
	lua_settop(gL, 0);
	return hooked;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// 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;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[hook_MobjDeath/8] & (1<<(hook_MobjDeath%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed

	lua_settop(gL, 0);
	// Look for all generic mobj death hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
		if (hookp->type == hook_MobjDeath)
		{
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, target, META_MOBJ);
				LUA_PushUserdata(gL, inflictor, META_MOBJ);
				LUA_PushUserdata(gL, source, META_MOBJ);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -4);
			lua_pushvalue(gL, -4);
			lua_pushvalue(gL, -4);
			if (lua_pcall(gL, 3, 1, 0)) {
				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)
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, target, META_MOBJ);
				LUA_PushUserdata(gL, inflictor, META_MOBJ);
				LUA_PushUserdata(gL, source, META_MOBJ);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -4);
			lua_pushvalue(gL, -4);
			lua_pushvalue(gL, -4);
			if (lua_pcall(gL, 3, 1, 0)) {
				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;
Alam Ed Arias's avatar
Alam Ed Arias committed
			lua_pop(gL, 1);
		}
	lua_settop(gL, 0);
	return hooked;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// Hook for B_BuildTiccmd
boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd)
{
	hook_p hookp;
Alam Ed Arias's avatar
Alam Ed Arias committed
	boolean hooked = false;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!gL || !(hooksAvailable[hook_BotTiccmd/8] & (1<<(hook_BotTiccmd%8))))
Alam Ed Arias's avatar
Alam Ed Arias committed
		return false;

	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed

	for (hookp = roothook; hookp; hookp = hookp->next)
		if (hookp->type == hook_BotTiccmd)
		{
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, bot, META_PLAYER);
				LUA_PushUserdata(gL, cmd, META_TICCMD);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -3);
			lua_pushvalue(gL, -3);
			if (lua_pcall(gL, 2, 1, 0)) {
				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;
Alam Ed Arias's avatar
Alam Ed Arias committed
			lua_pop(gL, 1);
		}
Alam Ed Arias's avatar
Alam Ed Arias committed

	lua_settop(gL, 0);
Alam Ed Arias's avatar
Alam Ed Arias committed
	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;
	if (!gL || !(hooksAvailable[hook_PlayerCmd/8] & (1<<(hook_PlayerCmd%8))))
		return false;

	lua_settop(gL, 0);

	hook_cmd_running = true;
	for (hookp = roothook; hookp; hookp = hookp->next)
		if (hookp->type == hook_PlayerCmd)
		{
			if (lua_gettop(gL) == 0)
			{
				LUA_PushUserdata(gL, player, META_PLAYER);
				LUA_PushUserdata(gL, cmd, META_TICCMD);
			}
			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
			lua_gettable(gL, LUA_REGISTRYINDEX);
			lua_pushvalue(gL, -3);
			lua_pushvalue(gL, -3);