lua_hooklib.c 46.6 KB
Newer Older
Alam Ed Arias committed
1 2
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
3
// Copyright (C) 2012-2016 by John "JTE" Muniz.
James R. committed
4
// Copyright (C) 2012-2020 by Sonic Team Junior.
Alam Ed Arias committed
5 6 7 8 9 10 11 12 13 14 15
//
// 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"
16
#include "g_game.h"
17
#include "r_skins.h"
Alam Ed Arias committed
18 19 20 21 22 23 24 25
#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

Hannu Hanhi committed
26 27
#include "m_perfstats.h"
#include "d_netcmd.h" // for cv_perfstats
James R. committed
28
#include "i_system.h" // I_GetPreciseTime
Hannu Hanhi committed
29

Alam Ed Arias committed
30 31
static UINT8 hooksAvailable[(hook_MAX/8)+1];

Alam Ed Arias committed
32 33 34 35 36
const char *const hookNames[hook_MAX+1] = {
	"NetVars",
	"MapChange",
	"MapLoad",
	"PlayerJoin",
Nami committed
37
	"PreThinkFrame",
Alam Ed Arias committed
38
	"ThinkFrame",
Nami committed
39
	"PostThinkFrame",
Alam Ed Arias committed
40 41
	"MobjSpawn",
	"MobjCollide",
Nami committed
42
	"MobjLineCollide",
Alam Ed Arias committed
43 44 45 46 47 48 49 50 51 52
	"MobjMoveCollide",
	"TouchSpecial",
	"MobjFuse",
	"MobjThinker",
	"BossThinker",
	"ShouldDamage",
	"MobjDamage",
	"MobjDeath",
	"BossDeath",
	"MobjRemoved",
Alam Ed Arias committed
53 54 55 56
	"JumpSpecial",
	"AbilitySpecial",
	"SpinSpecial",
	"JumpSpinSpecial",
Alam Ed Arias committed
57 58
	"BotTiccmd",
	"BotAI",
Sal committed
59
	"BotRespawn",
Alam Ed Arias committed
60
	"LinedefExecute",
61
	"PlayerMsg",
Alam Ed Arias committed
62
	"HurtMsg",
wolfs committed
63
	"PlayerSpawn",
64
	"ShieldSpawn",
65
	"ShieldSpecial",
66
	"MobjMoveBlocked",
67
	"MapThingSpawn",
68
	"FollowMobj",
toaster committed
69
	"PlayerCanDamage",
Prisima the Fox committed
70
	"PlayerQuit",
SteelT committed
71
	"IntermissionThinker",
72
	"TeamSwitch",
73
	"ViewpointSwitch",
74
	"SeenPlayer",
Zachary McAlpin committed
75
	"PlayerThink",
76
	"ShouldJingleContinue",
77
	"GameQuit",
78
	"PlayerCmd",
79
	"MusicChange",
Alam Ed Arias committed
80 81 82
	NULL
};

83 84 85 86 87 88 89 90
// Hook metadata
struct hook_s
{
	struct hook_s *next;
	enum hook type;
	UINT16 id;
	union {
		mobjtype_t mt;
LJ Sonic committed
91
		char *str;
92 93 94 95 96
	} s;
	boolean error;
};
typedef struct hook_s* hook_p;

97
#define FMT_HOOKID "hook_%d"
98

99 100 101 102 103 104 105
// 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];

106 107 108 109 110 111 112 113 114
// 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;

115
// For other hooks, a unique linked list
116 117
hook_p roothook;

118 119 120 121 122 123
static void PushHook(lua_State *L, hook_p hookp)
{
	lua_pushfstring(L, FMT_HOOKID, hookp->id);
	lua_gettable(L, LUA_REGISTRYINDEX);
}

Alam Ed Arias committed
124 125 126
// Takes hook, function, and additional arguments (mobj type to act on, etc.)
static int lib_addHook(lua_State *L)
{
127
	static struct hook_s hook = {NULL, 0, 0, {0}, false};
128
	static UINT32 nextid;
129 130 131 132
	hook_p hookp, *lastp;

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

134
	luaL_checktype(L, 1, LUA_TFUNCTION);
Alam Ed Arias committed
135

136 137
	if (!lua_lumploading)
		return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
Alam Ed Arias committed
138

139
	switch(hook.type)
Alam Ed Arias committed
140 141 142 143
	{
	// Take a mobjtype enum which this hook is specifically for.
	case hook_MobjSpawn:
	case hook_MobjCollide:
Nami committed
144
	case hook_MobjLineCollide:
Alam Ed Arias committed
145 146 147 148 149 150 151 152 153 154
	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:
155
	case hook_HurtMsg:
156
	case hook_MobjMoveBlocked:
157
	case hook_MapThingSpawn:
158
	case hook_FollowMobj:
159 160 161
		hook.s.mt = MT_NULL;
		if (lua_isnumber(L, 2))
			hook.s.mt = lua_tonumber(L, 2);
162
		luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t");
Alam Ed Arias committed
163
		break;
164
	case hook_BotAI:
165
	case hook_ShouldJingleContinue:
LJ Sonic committed
166
		hook.s.str = NULL;
167
		if (lua_isstring(L, 2))
Alam Ed Arias committed
168
		{ // lowercase copy
LJ Sonic committed
169 170
			hook.s.str = Z_StrDup(lua_tostring(L, 2));
			strlwr(hook.s.str);
Alam Ed Arias committed
171 172
		}
		break;
173
	case hook_LinedefExecute: // Linedef executor functions
LJ Sonic committed
174 175
		hook.s.str = Z_StrDup(luaL_checkstring(L, 2));
		strupr(hook.s.str);
Alam Ed Arias committed
176 177 178 179
		break;
	default:
		break;
	}
180
	lua_settop(L, 1); // lua stack contains only the function now.
Alam Ed Arias committed
181

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

184
	// set hook.id to the highest id + 1
185 186
	hook.id = nextid++;

187 188
	// Special cases for some hook types (see the comments above mobjthinkerhooks declaration)
	switch(hook.type)
Alam Ed Arias committed
189
	{
190
	case hook_MobjThinker:
191
		lastp = &mobjthinkerhooks[hook.s.mt];
192 193
		break;
	case hook_MobjCollide:
Nami committed
194
	case hook_MobjLineCollide:
195
	case hook_MobjMoveCollide:
196
		lastp = &mobjcollidehooks[hook.s.mt];
197 198 199 200 201 202 203 204 205 206
		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:
207
	case hook_MobjMoveBlocked:
208
	case hook_MapThingSpawn:
209
	case hook_FollowMobj:
210 211 212 213 214 215 216
		lastp = &mobjhooks[hook.s.mt];
		break;
	case hook_JumpSpecial:
	case hook_AbilitySpecial:
	case hook_SpinSpecial:
	case hook_JumpSpinSpecial:
	case hook_PlayerSpawn:
toaster committed
217
	case hook_PlayerCanDamage:
218
	case hook_TeamSwitch:
219
	case hook_ViewpointSwitch:
220
	case hook_SeenPlayer:
221 222
	case hook_ShieldSpawn:
	case hook_ShieldSpecial:
Zachary McAlpin committed
223
	case hook_PlayerThink:
224 225 226 227 228 229
		lastp = &playerhooks;
		break;
	case hook_LinedefExecute:
		lastp = &linedefexecutorhooks;
		break;
	default:
230
		lastp = &roothook;
231
		break;
Alam Ed Arias committed
232 233
	}

234
	// iterate the hook metadata structs
235
	// set lastp to the last hook struct's "next" pointer.
236
	for (hookp = *lastp; hookp; hookp = hookp->next)
237 238 239 240 241 242 243 244 245 246 247
		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 committed
248 249 250 251 252
	return 0;
}

int LUA_HookLib(lua_State *L)
{
Alam Ed Arias committed
253
	memset(hooksAvailable,0,sizeof(UINT8[(hook_MAX/8)+1]));
254
	roothook = NULL;
Alam Ed Arias committed
255 256 257 258 259 260
	lua_register(L, "addHook", lib_addHook);
	return 0;
}

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

266 267
	I_Assert(mo->type < NUMMOBJTYPES);

268 269 270
	if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type]))
		return false;

271
	lua_settop(gL, 0);
272
	lua_pushcfunction(gL, LUA_GetErrorMessage);
273

274 275
	// Look for all generic mobj hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
276 277 278 279
	{
		if (hookp->type != which)
			continue;

Hannu Hanhi committed
280
		ps_lua_mobjhooks++;
281
		if (lua_gettop(gL) == 1)
282
			LUA_PushUserdata(gL, mo, META_MOBJ);
283
		PushHook(gL, hookp);
284
		lua_pushvalue(gL, -2);
285
		if (lua_pcall(gL, 1, 1, 1)) {
286 287
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
Alam Ed Arias committed
288
			lua_pop(gL, 1);
289 290
			hookp->error = true;
			continue;
Alam Ed Arias committed
291
		}
292 293 294 295
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
Alam Ed Arias committed
296

297
	for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next)
298 299 300 301
	{
		if (hookp->type != which)
			continue;

Hannu Hanhi committed
302
		ps_lua_mobjhooks++;
303
		if (lua_gettop(gL) == 1)
304
			LUA_PushUserdata(gL, mo, META_MOBJ);
305
		PushHook(gL, hookp);
306
		lua_pushvalue(gL, -2);
307
		if (lua_pcall(gL, 1, 1, 1)) {
308 309
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
Alam Ed Arias committed
310
			lua_pop(gL, 1);
311 312
			hookp->error = true;
			continue;
Alam Ed Arias committed
313
		}
314 315 316 317
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
Alam Ed Arias committed
318

319
	lua_settop(gL, 0);
Alam Ed Arias committed
320 321 322
	return hooked;
}

Alam Ed Arias committed
323 324
boolean LUAh_PlayerHook(player_t *plr, enum hook which)
{
325
	hook_p hookp;
Alam Ed Arias committed
326 327 328 329
	boolean hooked = false;
	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
		return false;

330
	lua_settop(gL, 0);
331
	lua_pushcfunction(gL, LUA_GetErrorMessage);
Alam Ed Arias committed
332

333
	for (hookp = playerhooks; hookp; hookp = hookp->next)
334 335 336 337
	{
		if (hookp->type != which)
			continue;

Hannu Hanhi committed
338
		ps_lua_mobjhooks++;
339
		if (lua_gettop(gL) == 1)
340
			LUA_PushUserdata(gL, plr, META_PLAYER);
341
		PushHook(gL, hookp);
342
		lua_pushvalue(gL, -2);
343
		if (lua_pcall(gL, 1, 1, 1)) {
344 345
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
Alam Ed Arias committed
346
			lua_pop(gL, 1);
347 348
			hookp->error = true;
			continue;
Alam Ed Arias committed
349
		}
350 351 352 353
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
Alam Ed Arias committed
354

355
	lua_settop(gL, 0);
Alam Ed Arias committed
356 357 358
	return hooked;
}

Alam Ed Arias committed
359
// Hook for map change (before load)
360
void LUAh_MapChange(INT16 mapnumber)
Alam Ed Arias committed
361
{
362
	hook_p hookp;
Alam Ed Arias committed
363
	if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8))))
Alam Ed Arias committed
364 365
		return;

366
	lua_settop(gL, 0);
367
	lua_pushcfunction(gL, LUA_GetErrorMessage);
368
	lua_pushinteger(gL, mapnumber);
369 370

	for (hookp = roothook; hookp; hookp = hookp->next)
371 372 373 374
	{
		if (hookp->type != hook_MapChange)
			continue;

375
		PushHook(gL, hookp);
376
		lua_pushvalue(gL, -2);
377 378 379 380
		if (lua_pcall(gL, 1, 0, 1)) {
			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
			lua_pop(gL, 1);
		}
381
	}
382

383
	lua_settop(gL, 0);
Alam Ed Arias committed
384 385 386 387 388
}

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

393
	lua_settop(gL, 0);
394
	lua_pushcfunction(gL, LUA_GetErrorMessage);
395
	lua_pushinteger(gL, gamemap);
Alam Ed Arias committed
396

397
	for (hookp = roothook; hookp; hookp = hookp->next)
398 399 400 401
	{
		if (hookp->type != hook_MapLoad)
			continue;

402
		PushHook(gL, hookp);
403
		lua_pushvalue(gL, -2);
404 405 406 407
		if (lua_pcall(gL, 1, 0, 1)) {
			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
			lua_pop(gL, 1);
		}
408
	}
Alam Ed Arias committed
409

410
	lua_settop(gL, 0);
Alam Ed Arias committed
411 412 413 414 415
}

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

420
	lua_settop(gL, 0);
421
	lua_pushcfunction(gL, LUA_GetErrorMessage);
422
	lua_pushinteger(gL, playernum);
Alam Ed Arias committed
423

424
	for (hookp = roothook; hookp; hookp = hookp->next)
425 426 427 428
	{
		if (hookp->type != hook_PlayerJoin)
			continue;

429
		PushHook(gL, hookp);
430
		lua_pushvalue(gL, -2);
431 432 433 434
		if (lua_pcall(gL, 1, 0, 1)) {
			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
			lua_pop(gL, 1);
		}
435
	}
Alam Ed Arias committed
436

437
	lua_settop(gL, 0);
Alam Ed Arias committed
438 439
}

Nami committed
440 441 442 443 444 445 446
// 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;

447 448
	lua_pushcfunction(gL, LUA_GetErrorMessage);

Nami committed
449 450 451 452 453
	for (hookp = roothook; hookp; hookp = hookp->next)
	{
		if (hookp->type != hook_PreThinkFrame)
			continue;

454
		PushHook(gL, hookp);
455
		if (lua_pcall(gL, 0, 0, 1)) {
Nami committed
456 457 458 459 460 461
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
			lua_pop(gL, 1);
			hookp->error = true;
		}
	}
462 463

	lua_pop(gL, 1); // Pop error handler
Nami committed
464 465
}

Alam Ed Arias committed
466 467 468
// Hook for frame (after mobj and player thinkers)
void LUAh_ThinkFrame(void)
{
469
	hook_p hookp;
Hannu Hanhi committed
470 471 472
	// variables used by perf stats
	int hook_index = 0;
	int time_taken = 0;
Alam Ed Arias committed
473
	if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8))))
Alam Ed Arias committed
474 475
		return;

476 477
	lua_pushcfunction(gL, LUA_GetErrorMessage);

478
	for (hookp = roothook; hookp; hookp = hookp->next)
479 480 481 482
	{
		if (hookp->type != hook_ThinkFrame)
			continue;

Hannu Hanhi committed
483
		if (cv_perfstats.value == 3)
James R. committed
484
			time_taken = I_GetPreciseTime();
485
		PushHook(gL, hookp);
486
		if (lua_pcall(gL, 0, 0, 1)) {
487 488 489 490
			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 committed
491
		}
Hannu Hanhi committed
492 493 494
		if (cv_perfstats.value == 3)
		{
			lua_Debug ar;
James R. committed
495
			time_taken = I_GetPreciseTime() - time_taken;
Hannu Hanhi committed
496 497 498 499 500 501
			// 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++;
		}
502
	}
Alam Ed Arias committed
503

504 505
	lua_pop(gL, 1); // Pop error handler
}
506 507

// Hook for frame (at end of tick, ie after overlays, precipitation, specials)
Nami committed
508
void LUAh_PostThinkFrame(void)
Alam Ed Arias committed
509
{
510
	hook_p hookp;
Nami committed
511
	if (!gL || !(hooksAvailable[hook_PostThinkFrame/8] & (1<<(hook_PostThinkFrame%8))))
Alam Ed Arias committed
512 513
		return;

514 515
	lua_pushcfunction(gL, LUA_GetErrorMessage);

516
	for (hookp = roothook; hookp; hookp = hookp->next)
517
	{
Nami committed
518
		if (hookp->type != hook_PostThinkFrame)
519 520
			continue;

521
		PushHook(gL, hookp);
522
		if (lua_pcall(gL, 0, 0, 1)) {
523 524 525 526
			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 committed
527
		}
528
	}
529 530

	lua_pop(gL, 1); // Pop error handler
Alam Ed Arias committed
531 532
}

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

541 542
	I_Assert(thing1->type < NUMMOBJTYPES);

LJ Sonic committed
543
	if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing1->type]))
544 545
		return 0;

546
	lua_settop(gL, 0);
547
	lua_pushcfunction(gL, LUA_GetErrorMessage);
548

549 550
	// Look for all generic mobj collision hooks
	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
551 552 553 554
	{
		if (hookp->type != which)
			continue;

Hannu Hanhi committed
555
		ps_lua_mobjhooks++;
556
		if (lua_gettop(gL) == 1)
557
		{
558 559 560
			LUA_PushUserdata(gL, thing1, META_MOBJ);
			LUA_PushUserdata(gL, thing2, META_MOBJ);
		}
561
		PushHook(gL, hookp);
562 563
		lua_pushvalue(gL, -3);
		lua_pushvalue(gL, -3);
564
		if (lua_pcall(gL, 2, 1, 1)) {
565 566
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
567
			lua_pop(gL, 1);
568 569
			hookp->error = true;
			continue;
570
		}
571 572 573 574 575 576 577 578 579
		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);
	}
580

581
	for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next)
582 583 584 585
	{
		if (hookp->type != which)
			continue;

Hannu Hanhi committed
586
		ps_lua_mobjhooks++;
587
		if (lua_gettop(gL) == 1)
588
		{
589 590 591
			LUA_PushUserdata(gL, thing1, META_MOBJ);
			LUA_PushUserdata(gL, thing2, META_MOBJ);
		}
592
		PushHook(gL, hookp);
593 594
		lua_pushvalue(gL, -3);
		lua_pushvalue(gL, -3);
595
		if (lua_pcall(gL, 2, 1, 1)) {
596 597
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
Alam Ed Arias committed
598
			lua_pop(gL, 1);
599 600
			hookp->error = true;
			continue;
Alam Ed Arias committed
601
		}
602 603 604 605 606 607 608 609 610
		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);
	}
611

612
	lua_settop(gL, 0);
Alam Ed Arias committed
613 614 615
	return shouldCollide;
}

Nami committed
616 617 618 619 620 621 622 623 624
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);

LJ Sonic committed
625
	if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing->type]))
626 627
		return 0;

Nami committed
628
	lua_settop(gL, 0);
629
	lua_pushcfunction(gL, LUA_GetErrorMessage);
Nami committed
630 631 632 633 634 635 636

	// Look for all generic mobj collision hooks
	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
	{
		if (hookp->type != which)
			continue;

Hannu Hanhi committed
637
		ps_lua_mobjhooks++;
638
		if (lua_gettop(gL) == 1)
Nami committed
639 640 641 642
		{
			LUA_PushUserdata(gL, thing, META_MOBJ);
			LUA_PushUserdata(gL, line, META_LINE);
		}
643
		PushHook(gL, hookp);
Nami committed
644 645
		lua_pushvalue(gL, -3);
		lua_pushvalue(gL, -3);
646
		if (lua_pcall(gL, 2, 1, 1)) {
Nami committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
			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;

Hannu Hanhi committed
668
		ps_lua_mobjhooks++;
669
		if (lua_gettop(gL) == 1)
Nami committed
670 671 672 673
		{
			LUA_PushUserdata(gL, thing, META_MOBJ);
			LUA_PushUserdata(gL, line, META_LINE);
		}
674
		PushHook(gL, hookp);
Nami committed
675 676
		lua_pushvalue(gL, -3);
		lua_pushvalue(gL, -3);
677
		if (lua_pcall(gL, 2, 1, 1)) {
Nami committed
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
			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;
}

698 699 700 701 702 703 704 705
// 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;

706 707
	I_Assert(mo->type < NUMMOBJTYPES);

LJ Sonic committed
708
	if (!(mobjthinkerhooks[MT_NULL] || mobjthinkerhooks[mo->type]))
709 710
		return false;

711
	lua_settop(gL, 0);
712
	lua_pushcfunction(gL, LUA_GetErrorMessage);
713 714 715 716

	// Look for all generic mobj thinker hooks
	for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
	{
Hannu Hanhi committed
717
		ps_lua_mobjhooks++;
718
		if (lua_gettop(gL) == 1)
719
			LUA_PushUserdata(gL, mo, META_MOBJ);
720
		PushHook(gL, hookp);
721
		lua_pushvalue(gL, -2);
722
		if (lua_pcall(gL, 1, 1, 1)) {
723 724 725 726 727 728 729 730 731 732 733 734 735
			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)
	{
Hannu Hanhi committed
736
		ps_lua_mobjhooks++;
737
		if (lua_gettop(gL) == 1)
738
			LUA_PushUserdata(gL, mo, META_MOBJ);
739
		PushHook(gL, hookp);
740
		lua_pushvalue(gL, -2);
741
		if (lua_pcall(gL, 1, 1, 1)) {
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
			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 committed
757 758 759
// Hook for P_TouchSpecialThing by mobj type
boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
{
760
	hook_p hookp;
Alam Ed Arias committed
761
	boolean hooked = false;
Alam Ed Arias committed
762
	if (!gL || !(hooksAvailable[hook_TouchSpecial/8] & (1<<(hook_TouchSpecial%8))))
763
		return false;
Alam Ed Arias committed
764

765 766
	I_Assert(special->type < NUMMOBJTYPES);

767 768 769
	if (!(mobjhooks[MT_NULL] || mobjhooks[special->type]))
		return false;

770
	lua_settop(gL, 0);
771
	lua_pushcfunction(gL, LUA_GetErrorMessage);
Alam Ed Arias committed
772

773 774
	// Look for all generic touch special hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
775 776 777 778
	{
		if (hookp->type != hook_TouchSpecial)
			continue;

Hannu Hanhi committed
779
		ps_lua_mobjhooks++;
780
		if (lua_gettop(gL) == 1)
781
		{
782 783 784
			LUA_PushUserdata(gL, special, META_MOBJ);
			LUA_PushUserdata(gL, toucher, META_MOBJ);
		}
785
		PushHook(gL, hookp);
786 787
		lua_pushvalue(gL, -3);
		lua_pushvalue(gL, -3);
788
		if (lua_pcall(gL, 2, 1, 1)) {
789 790
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
791
			lua_pop(gL, 1);
792 793
			hookp->error = true;
			continue;
794
		}
795 796 797 798
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
799 800

	for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next)
801 802 803 804
	{
		if (hookp->type != hook_TouchSpecial)
			continue;

Hannu Hanhi committed
805
		ps_lua_mobjhooks++;
806
		if (lua_gettop(gL) == 1)
807
		{
808 809 810
			LUA_PushUserdata(gL, special, META_MOBJ);
			LUA_PushUserdata(gL, toucher, META_MOBJ);
		}
811
		PushHook(gL, hookp);
812 813
		lua_pushvalue(gL, -3);
		lua_pushvalue(gL, -3);
814
		if (lua_pcall(gL, 2, 1, 1)) {
815 816
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
Alam Ed Arias committed
817
			lua_pop(gL, 1);
818 819
			hookp->error = true;
			continue;
Alam Ed Arias committed
820
		}
821 822 823 824
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
Alam Ed Arias committed
825

826
	lua_settop(gL, 0);
Alam Ed Arias committed
827 828 829 830
	return hooked;
}

// Hook for P_DamageMobj by mobj type (Should mobj take damage?)
831
UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
Alam Ed Arias committed
832
{
833
	hook_p hookp;
Alam Ed Arias committed
834
	UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no.
Alam Ed Arias committed
835
	if (!gL || !(hooksAvailable[hook_ShouldDamage/8] & (1<<(hook_ShouldDamage%8))))
Alam Ed Arias committed
836 837
		return 0;

838 839
	I_Assert(target->type < NUMMOBJTYPES);

840 841 842
	if (!(mobjhooks[MT_NULL] || mobjhooks[target->type]))
		return 0;

843
	lua_settop(gL, 0);
844
	lua_pushcfunction(gL, LUA_GetErrorMessage);
845

846 847
	// Look for all generic should damage hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
848 849 850 851
	{
		if (hookp->type != hook_ShouldDamage)
			continue;

Hannu Hanhi committed
852
		ps_lua_mobjhooks++;
853
		if (lua_gettop(gL) == 1)
854
		{
855 856 857 858
			LUA_PushUserdata(gL, target, META_MOBJ);
			LUA_PushUserdata(gL, inflictor, META_MOBJ);
			LUA_PushUserdata(gL, source, META_MOBJ);
			lua_pushinteger(gL, damage);
859
			lua_pushinteger(gL, damagetype);
860
		}
861
		PushHook(gL, hookp);
862 863 864 865 866
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
867
		if (lua_pcall(gL, 5, 1, 1)) {
868 869
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
870
			lua_pop(gL, 1);
871 872
			hookp->error = true;
			continue;
873
		}
874 875 876 877 878 879 880 881 882
		if (!lua_isnil(gL, -1))
		{
			if (lua_toboolean(gL, -1))
				shouldDamage = 1; // Force yes
			else
				shouldDamage = 2; // Force no
		}
		lua_pop(gL, 1);
	}
883 884

	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
885 886 887
	{
		if (hookp->type != hook_ShouldDamage)
			continue;
Hannu Hanhi committed
888
		ps_lua_mobjhooks++;
889
		if (lua_gettop(gL) == 1)
890
		{
891 892 893 894 895 896
			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);
		}
897
		PushHook(gL, hookp);
898 899 900 901 902
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
903
		if (lua_pcall(gL, 5, 1, 1)) {
904 905
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
Alam Ed Arias committed
906
			lua_pop(gL, 1);
907 908
			hookp->error = true;
			continue;
Alam Ed Arias committed
909
		}
910 911 912 913 914 915 916 917 918
		if (!lua_isnil(gL, -1))
		{
			if (lua_toboolean(gL, -1))
				shouldDamage = 1; // Force yes
			else
				shouldDamage = 2; // Force no
		}
		lua_pop(gL, 1);
	}
919

920
	lua_settop(gL, 0);
Alam Ed Arias committed
921 922 923 924
	return shouldDamage;
}

// Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
925
boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
Alam Ed Arias committed
926
{
927 928
	hook_p hookp;
	boolean hooked = false;
Alam Ed Arias committed
929
	if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8))))
930
		return false;
Alam Ed Arias committed
931

932 933
	I_Assert(target->type < NUMMOBJTYPES);

934 935 936
	if (!(mobjhooks[MT_NULL] || mobjhooks[target->type]))
		return false;

937
	lua_settop(gL, 0);
938
	lua_pushcfunction(gL, LUA_GetErrorMessage);
939

940 941
	// Look for all generic mobj damage hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
942 943 944 945
	{
		if (hookp->type != hook_MobjDamage)
			continue;

Hannu Hanhi committed
946
		ps_lua_mobjhooks++;
947
		if (lua_gettop(gL) == 1)
948
		{
949 950 951 952
			LUA_PushUserdata(gL, target, META_MOBJ);
			LUA_PushUserdata(gL, inflictor, META_MOBJ);
			LUA_PushUserdata(gL, source, META_MOBJ);
			lua_pushinteger(gL, damage);
953
			lua_pushinteger(gL, damagetype);
954
		}
955
		PushHook(gL, hookp);
956 957 958 959 960
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
961
		if (lua_pcall(gL, 5, 1, 1)) {
962 963
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
964
			lua_pop(gL, 1);
965 966
			hookp->error = true;
			continue;
967
		}
968 969 970 971
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
972 973

	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
974 975 976 977
	{
		if (hookp->type != hook_MobjDamage)
			continue;

Hannu Hanhi committed
978
		ps_lua_mobjhooks++;
979
		if (lua_gettop(gL) == 1)
980
		{
981 982 983 984 985 986
			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);
		}
987
		PushHook(gL, hookp);
988 989 990 991 992
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
		lua_pushvalue(gL, -6);
993
		if (lua_pcall(gL, 5, 1, 1)) {
994 995
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
Alam Ed Arias committed
996
			lua_pop(gL, 1);
997 998
			hookp->error = true;
			continue;
Alam Ed Arias committed
999
		}
1000 1001 1002 1003
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
1004

1005
	lua_settop(gL, 0);
1006
	return hooked;
Alam Ed Arias committed
1007 1008 1009
}

// Hook for P_KillMobj by mobj type
1010
boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
Alam Ed Arias committed
1011
{
1012 1013
	hook_p hookp;
	boolean hooked = false;
Alam Ed Arias committed
1014
	if (!gL || !(hooksAvailable[hook_MobjDeath/8] & (1<<(hook_MobjDeath%8))))
1015
		return false;
Alam Ed Arias committed
1016

1017 1018
	I_Assert(target->type < NUMMOBJTYPES);

1019 1020 1021
	if (!(mobjhooks[MT_NULL] || mobjhooks[target->type]))
		return false;

1022
	lua_settop(gL, 0);
1023
	lua_pushcfunction(gL, LUA_GetErrorMessage);
1024

1025 1026
	// Look for all generic mobj death hooks
	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
1027 1028 1029 1030
	{
		if (hookp->type != hook_MobjDeath)
			continue;

Hannu Hanhi committed
1031
		ps_lua_mobjhooks++;
1032
		if (lua_gettop(gL) == 1)
1033
		{
1034 1035 1036
			LUA_PushUserdata(gL, target, META_MOBJ);
			LUA_PushUserdata(gL, inflictor, META_MOBJ);
			LUA_PushUserdata(gL, source, META_MOBJ);
1037
			lua_pushinteger(gL, damagetype);
1038
		}
1039
		PushHook(gL, hookp);
1040 1041 1042 1043
		lua_pushvalue(gL, -5);
		lua_pushvalue(gL, -5);
		lua_pushvalue(gL, -5);
		lua_pushvalue(gL, -5);
1044
		if (lua_pcall(gL, 4, 1, 1)) {
1045 1046
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1047
			lua_pop(gL, 1);
1048 1049
			hookp->error = true;
			continue;
1050
		}
1051 1052 1053 1054
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
1055 1056

	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
1057 1058 1059 1060
	{
		if (hookp->type != hook_MobjDeath)
			continue;

Hannu Hanhi committed
1061
		ps_lua_mobjhooks++;
1062
		if (lua_gettop(gL) == 1)
1063
		{
1064 1065 1066 1067 1068
			LUA_PushUserdata(gL, target, META_MOBJ);
			LUA_PushUserdata(gL, inflictor, META_MOBJ);
			LUA_PushUserdata(gL, source, META_MOBJ);
			lua_pushinteger(gL, damagetype);
		}
1069
		PushHook(gL, hookp);
1070 1071 1072 1073
		lua_pushvalue(gL, -5);
		lua_pushvalue(gL, -5);
		lua_pushvalue(gL, -5);
		lua_pushvalue(gL, -5);
1074
		if (lua_pcall(gL, 4, 1, 1)) {
1075 1076
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
Alam Ed Arias committed
1077
			lua_pop(gL, 1);
1078 1079
			hookp->error = true;
			continue;
Alam Ed Arias committed
1080
		}
1081 1082 1083 1084
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
1085

1086
	lua_settop(gL, 0);
1087
	return hooked;
Alam Ed Arias committed
1088 1089 1090 1091 1092
}

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

1098
	lua_settop(gL, 0);
1099
	lua_pushcfunction(gL, LUA_GetErrorMessage);
Alam Ed Arias committed
1100

1101
	for (hookp = roothook; hookp; hookp = hookp->next)
1102 1103 1104 1105
	{
		if (hookp->type != hook_BotTiccmd)
			continue;

1106
		if (lua_gettop(gL) == 1)
1107
		{
1108 1109 1110
			LUA_PushUserdata(gL, bot, META_PLAYER);
			LUA_PushUserdata(gL, cmd, META_TICCMD);
		}
1111
		PushHook(gL, hookp);
1112 1113
		lua_pushvalue(gL, -3);
		lua_pushvalue(gL, -3);
1114
		if (lua_pcall(gL, 2, 1, 1)) {
1115 1116
			if (!hookp->error || cv_debug & DBG_LUA)
				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
Alam Ed Arias committed
1117
			lua_pop(gL, 1);
1118 1119
			hookp->error = true;
			continue;
Alam Ed Arias committed
1120
		}
1121 1122 1123 1124
		if (lua_toboolean(gL, -1))
			hooked = true;
		lua_pop(gL, 1);
	}
Alam Ed Arias committed
1125

1126
	lua_settop(gL, 0);
Alam Ed Arias committed
1127 1128 1129 1130 1131 1132
	return hooked;
}

// Hook for B_BuildTailsTiccmd by skin name
boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
{
1133 1134 1135
	hook_p hookp;
	boolean hooked = false;
	if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8))))
Alam Ed Arias committed
1136 1137
		return false;

1138
	lua_settop(gL, 0);
1139
	lua_pushcfunction(gL, LUA_GetErrorMessage);
Alam Ed Arias committed
1140

1141
	for (hookp = roothook; hookp; hookp = hookp->next)