Skip to content
Snippets Groups Projects
Select Git revision
  • fixopengl2
  • master default protected
  • fixopengl
  • appveyor-exename
  • sal-oglshaderport
  • lua-io
  • key-out-of-range
  • keep-body
  • software-clownery-mkii
  • point-in-subsector-or-null
  • ploadflat-bruh-bruh
  • opengl-garbage-fade
  • opengl-skipintro-sigabrt
  • trim-floats
  • musicdef-versioning
  • next
  • pictureformats
  • more-lua-map-names
  • fix-mouse-controls-setup
  • push-special-params
  • SRB2_release_2.2.0
  • SRB2_release_2.1.25
  • SRB2_release_2.1.24
  • SRB2_release_2.1.23
  • SRB2_release_2.1.22
  • SRB2_release_2.1.21
  • SRB2_release_2.1.20
  • SRB2_release_2.1.19
  • SRB2_release_2.1.18
  • td-release-v1.0.0
  • SRB2_release_2.1.17
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.16
  • SRB2_release_2.1.15
  • SRB2_release_2.1.14
  • SRB2_release_2.1.12
  • SRB2_release_2.1.11
  • SRB2_release_2.1.10
  • SRB2_release_2.1.9
  • SRB2_release_2.1.8
40 results

lua_hooklib.c

Blame
  • Forked from STJr / SRB2
    15971 commits behind the upstream repository.
    wolfy852's avatar
    wolfs authored
    db3797fd
    History
    lua_hooklib.c 18.98 KiB
    // SONIC ROBO BLAST 2
    //-----------------------------------------------------------------------------
    // Copyright (C) 2012-2014 by John "JTE" Muniz.
    // Copyright (C) 2012-2014 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"
    #ifdef HAVE_BLUA
    #include "doomstat.h"
    #include "p_mobj.h"
    #include "g_game.h"
    #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
    
    static UINT8 hooksAvailable[(hook_MAX/8)+1];
    
    const char *const hookNames[hook_MAX+1] = {
    	"NetVars",
    	"MapChange",
    	"MapLoad",
    	"PlayerJoin",
    	"ThinkFrame",
    	"MobjSpawn",
    	"MobjCollide",
    	"MobjMoveCollide",
    	"TouchSpecial",
    	"MobjFuse",
    	"MobjThinker",
    	"BossThinker",
    	"ShouldDamage",
    	"MobjDamage",
    	"MobjDeath",
    	"BossDeath",
    	"MobjRemoved",
    	"JumpSpecial",
    	"AbilitySpecial",
    	"SpinSpecial",
    	"JumpSpinSpecial",
    	"BotTiccmd",
    	"BotAI",
    	"LinedefExecute",
    	"PlayerMsg",
    	"HurtMsg",
    	"PlayerSpawn",
    	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"
    
    hook_p roothook;
    
    // 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);
    
    	luaL_checktype(L, 1, LUA_TFUNCTION);
    
    	if (hud_running)
    		return luaL_error(L, "HUD rendering code should not call this function!");
    
    	switch(hook.type)
    	{
    	// 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);
    		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;
    	default:
    		break;
    	}
    	lua_settop(L, 1); // lua stack contains only the function now.
    
    	hooksAvailable[hook.type/8] |= 1<<(hook.type%8);
    
    	// iterate the hook metadata structs
    	// set hook.id to the highest id + 1
    	// set lastp to the last hook struct's "next" pointer.
    	lastp = &roothook;
    	hook.id = 0;
    	for (hookp = roothook; hookp; hookp = hookp->next)
    	{
    		if (hookp->id >= hook.id)
    			hook.id = hookp->id+1;
    		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;
    
    	lua_settop(gL, 0);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == which
    		&& (hookp->s.mt == MT_NULL || hookp->s.mt == mo->type))
    		{
    			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;
    }
    
    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);
    
    	for (hookp = roothook; 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;
    			lua_pop(gL, 1);
    		}
    
    	lua_settop(gL, 0);
    	return hooked;
    }
    
    // Hook for map change (before load)
    void LUAh_MapChange(void)
    {
    	hook_p hookp;
    	if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8))))
    		return;
    
    	lua_settop(gL, 0);
    	lua_pushinteger(gL, gamemap);
    
    	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);
    }
    
    // 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_pushinteger(gL, gamemap);
    
    	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);
    		}
    
    	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_pushinteger(gL, playernum);
    
    	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);
    		}
    
    	lua_settop(gL, 0);
    }
    
    // Hook for frame (after mobj and player thinkers)
    void LUAh_ThinkFrame(void)
    {
    	hook_p hookp;
    	if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8))))
    		return;
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == hook_ThinkFrame)
    		{
    			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)
    {
    	hook_p hookp;
    	UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no.
    	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
    		return 0;
    
    	lua_settop(gL, 0);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == which
    		&& (hookp->s.mt == MT_NULL || hookp->s.mt == thing1->type))
    		{
    			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);
    		}
    
    	lua_settop(gL, 0);
    	return shouldCollide;
    }
    
    // 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 0;
    
    	lua_settop(gL, 0);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == hook_TouchSpecial
    		&& (hookp->s.mt == MT_NULL || hookp->s.mt == special->type))
    		{
    			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);
    		}
    
    	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.
    	if (!gL || !(hooksAvailable[hook_ShouldDamage/8] & (1<<(hook_ShouldDamage%8))))
    		return 0;
    
    	lua_settop(gL, 0);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == hook_ShouldDamage
    		&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
    		{
    			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);
    		}
    
    	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;
    	if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8))))
    		return 0;
    
    	lua_settop(gL, 0);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == hook_MobjDamage
    		&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
    		{
    			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);
    		}
    
    	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;
    	if (!gL || !(hooksAvailable[hook_MobjDeath/8] & (1<<(hook_MobjDeath%8))))
    		return 0;
    
    	lua_settop(gL, 0);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == hook_MobjDeath
    		&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
    		{
    			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);
    		}
    
    	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);
    
    	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;
    			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);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == hook_BotAI
    		&& (hookp->s.skinname == NULL || !strcmp(hookp->s.skinname, ((skin_t*)tails->skin)->name)))
    		{
    			if (lua_gettop(gL) == 0)
    			{
    				LUA_PushUserdata(gL, sonic, META_MOBJ);
    				LUA_PushUserdata(gL, tails, 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, 8, 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;
    			}
    
    			// 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 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);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == hook_LinedefExecute
    		&& !strcmp(hookp->s.funcname, line->text))
    		{
    			if (lua_gettop(gL) == 0)
    			{
    				LUA_PushUserdata(gL, line, META_LINE);
    				LUA_PushUserdata(gL, mo, META_MOBJ);
    				LUA_PushUserdata(gL, sector, META_SECTOR);
    			}
    			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
    			lua_gettable(gL, LUA_REGISTRYINDEX);
    			lua_pushvalue(gL, -4);
    			lua_pushvalue(gL, -4);
    			lua_pushvalue(gL, -4);
    			LUA_Call(gL, 3);
    			hooked = true;
    		}
    
    	lua_settop(gL, 0);
    	return hooked;
    }
    
    // Hook for player chat
    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);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == hook_PlayerMsg)
    		{
    			if (lua_gettop(gL) == 0)
    			{
    				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
    			}
    			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);
    		}
    
    	lua_settop(gL, 0);
    	return hooked;
    }
    
    // Hook for hurt messages
    boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source)
    {
    	hook_p hookp;
    	boolean hooked = false;
    	if (!gL || !(hooksAvailable[hook_HurtMsg/8] & (1<<(hook_HurtMsg%8))))
    		return false;
    
    	lua_settop(gL, 0);
    
    	for (hookp = roothook; hookp; hookp = hookp->next)
    		if (hookp->type == hook_HurtMsg
    		&& (hookp->s.mt == MT_NULL || (inflictor && hookp->s.mt == inflictor->type)))
    		{
    			if (lua_gettop(gL) == 0)
    			{
    				LUA_PushUserdata(gL, player, META_PLAYER);
    				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);
    		}
    
    	lua_settop(gL, 0);
    	return hooked;
    }
    
    #endif