1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 2012-2016 by John "JTE" Muniz.
4 // Copyright (C) 2012-2020 by Sonic Team Junior.
5 //
6 // This program is free software distributed under the
7 // terms of the GNU General Public License, version 2.
8 // See the 'LICENSE' file for more details.
9 //-----------------------------------------------------------------------------
10 /// \file  lua_hooklib.c
11 /// \brief hooks for Lua scripting
12 
13 #include "doomdef.h"
14 #include "doomstat.h"
15 #include "p_mobj.h"
16 #include "g_game.h"
17 #include "r_skins.h"
18 #include "b_bot.h"
19 #include "z_zone.h"
20 
21 #include "lua_script.h"
22 #include "lua_libs.h"
23 #include "lua_hook.h"
24 #include "lua_hud.h" // hud_running errors
25 
26 #include "m_perfstats.h"
27 #include "d_netcmd.h" // for cv_perfstats
28 #include "i_system.h" // I_GetPreciseTime
29 
30 static UINT8 hooksAvailable[(hook_MAX/8)+1];
31 
32 const char *const hookNames[hook_MAX+1] = {
33 	"NetVars",
34 	"MapChange",
35 	"MapLoad",
36 	"PlayerJoin",
37 	"PreThinkFrame",
38 	"ThinkFrame",
39 	"PostThinkFrame",
40 	"MobjSpawn",
41 	"MobjCollide",
42 	"MobjLineCollide",
43 	"MobjMoveCollide",
44 	"TouchSpecial",
45 	"MobjFuse",
46 	"MobjThinker",
47 	"BossThinker",
48 	"ShouldDamage",
49 	"MobjDamage",
50 	"MobjDeath",
51 	"BossDeath",
52 	"MobjRemoved",
53 	"JumpSpecial",
54 	"AbilitySpecial",
55 	"SpinSpecial",
56 	"JumpSpinSpecial",
57 	"BotTiccmd",
58 	"BotAI",
59 	"BotRespawn",
60 	"LinedefExecute",
61 	"PlayerMsg",
62 	"HurtMsg",
63 	"PlayerSpawn",
64 	"ShieldSpawn",
65 	"ShieldSpecial",
66 	"MobjMoveBlocked",
67 	"MapThingSpawn",
68 	"FollowMobj",
69 	"PlayerCanDamage",
70 	"PlayerQuit",
71 	"IntermissionThinker",
72 	"TeamSwitch",
73 	"ViewpointSwitch",
74 	"SeenPlayer",
75 	"PlayerThink",
76 	"ShouldJingleContinue",
77 	"GameQuit",
78 	"PlayerCmd",
79 	"MusicChange",
80 	"PlayerHeight",
81 	"PlayerCanEnterSpinGaps",
82 	NULL
83 };
84 
85 // Hook metadata
86 struct hook_s
87 {
88 	struct hook_s *next;
89 	enum hook type;
90 	UINT16 id;
91 	union {
92 		mobjtype_t mt;
93 		char *str;
94 	} s;
95 	boolean error;
96 };
97 typedef struct hook_s* hook_p;
98 
99 #define FMT_HOOKID "hook_%d"
100 
101 // For each mobj type, a linked list to its thinker and collision hooks.
102 // That way, we don't have to iterate through all the hooks.
103 // We could do that with all other mobj hooks, but it would probably just be
104 // a waste of memory since they are only called occasionally. Probably...
105 static hook_p mobjthinkerhooks[NUMMOBJTYPES];
106 static hook_p mobjcollidehooks[NUMMOBJTYPES];
107 
108 // For each mobj type, a linked list for other mobj hooks
109 static hook_p mobjhooks[NUMMOBJTYPES];
110 
111 // A linked list for player hooks
112 static hook_p playerhooks;
113 
114 // A linked list for linedef executor hooks
115 static hook_p linedefexecutorhooks;
116 
117 // For other hooks, a unique linked list
118 hook_p roothook;
119 
PushHook(lua_State * L,hook_p hookp)120 static void PushHook(lua_State *L, hook_p hookp)
121 {
122 	lua_pushfstring(L, FMT_HOOKID, hookp->id);
123 	lua_gettable(L, LUA_REGISTRYINDEX);
124 }
125 
126 // Takes hook, function, and additional arguments (mobj type to act on, etc.)
lib_addHook(lua_State * L)127 static int lib_addHook(lua_State *L)
128 {
129 	static struct hook_s hook = {NULL, 0, 0, {0}, false};
130 	static UINT32 nextid;
131 	hook_p hookp, *lastp;
132 
133 	hook.type = luaL_checkoption(L, 1, NULL, hookNames);
134 	lua_remove(L, 1);
135 
136 	luaL_checktype(L, 1, LUA_TFUNCTION);
137 
138 	if (!lua_lumploading)
139 		return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
140 
141 	switch(hook.type)
142 	{
143 	// Take a mobjtype enum which this hook is specifically for.
144 	case hook_MobjSpawn:
145 	case hook_MobjCollide:
146 	case hook_MobjLineCollide:
147 	case hook_MobjMoveCollide:
148 	case hook_TouchSpecial:
149 	case hook_MobjFuse:
150 	case hook_MobjThinker:
151 	case hook_BossThinker:
152 	case hook_ShouldDamage:
153 	case hook_MobjDamage:
154 	case hook_MobjDeath:
155 	case hook_BossDeath:
156 	case hook_MobjRemoved:
157 	case hook_HurtMsg:
158 	case hook_MobjMoveBlocked:
159 	case hook_MapThingSpawn:
160 	case hook_FollowMobj:
161 		hook.s.mt = MT_NULL;
162 		if (lua_isnumber(L, 2))
163 			hook.s.mt = lua_tonumber(L, 2);
164 		luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t");
165 		break;
166 	case hook_BotAI:
167 	case hook_ShouldJingleContinue:
168 		hook.s.str = NULL;
169 		if (lua_isstring(L, 2))
170 		{ // lowercase copy
171 			hook.s.str = Z_StrDup(lua_tostring(L, 2));
172 			strlwr(hook.s.str);
173 		}
174 		break;
175 	case hook_LinedefExecute: // Linedef executor functions
176 		hook.s.str = Z_StrDup(luaL_checkstring(L, 2));
177 		strupr(hook.s.str);
178 		break;
179 	default:
180 		break;
181 	}
182 	lua_settop(L, 1); // lua stack contains only the function now.
183 
184 	hooksAvailable[hook.type/8] |= 1<<(hook.type%8);
185 
186 	// set hook.id to the highest id + 1
187 	hook.id = nextid++;
188 
189 	// Special cases for some hook types (see the comments above mobjthinkerhooks declaration)
190 	switch(hook.type)
191 	{
192 	case hook_MobjThinker:
193 		lastp = &mobjthinkerhooks[hook.s.mt];
194 		break;
195 	case hook_MobjCollide:
196 	case hook_MobjLineCollide:
197 	case hook_MobjMoveCollide:
198 		lastp = &mobjcollidehooks[hook.s.mt];
199 		break;
200 	case hook_MobjSpawn:
201 	case hook_TouchSpecial:
202 	case hook_MobjFuse:
203 	case hook_BossThinker:
204 	case hook_ShouldDamage:
205 	case hook_MobjDamage:
206 	case hook_MobjDeath:
207 	case hook_BossDeath:
208 	case hook_MobjRemoved:
209 	case hook_MobjMoveBlocked:
210 	case hook_MapThingSpawn:
211 	case hook_FollowMobj:
212 		lastp = &mobjhooks[hook.s.mt];
213 		break;
214 	case hook_JumpSpecial:
215 	case hook_AbilitySpecial:
216 	case hook_SpinSpecial:
217 	case hook_JumpSpinSpecial:
218 	case hook_PlayerSpawn:
219 	case hook_PlayerCanDamage:
220 	case hook_TeamSwitch:
221 	case hook_ViewpointSwitch:
222 	case hook_SeenPlayer:
223 	case hook_ShieldSpawn:
224 	case hook_ShieldSpecial:
225 	case hook_PlayerThink:
226 	case hook_PlayerHeight:
227 	case hook_PlayerCanEnterSpinGaps:
228 		lastp = &playerhooks;
229 		break;
230 	case hook_LinedefExecute:
231 		lastp = &linedefexecutorhooks;
232 		break;
233 	default:
234 		lastp = &roothook;
235 		break;
236 	}
237 
238 	// iterate the hook metadata structs
239 	// set lastp to the last hook struct's "next" pointer.
240 	for (hookp = *lastp; hookp; hookp = hookp->next)
241 		lastp = &hookp->next;
242 	// allocate a permanent memory struct to stuff hook.
243 	hookp = ZZ_Alloc(sizeof(struct hook_s));
244 	memcpy(hookp, &hook, sizeof(struct hook_s));
245 	// tack it onto the end of the linked list.
246 	*lastp = hookp;
247 
248 	// set the hook function in the registry.
249 	lua_pushfstring(L, FMT_HOOKID, hook.id);
250 	lua_pushvalue(L, 1);
251 	lua_settable(L, LUA_REGISTRYINDEX);
252 	return 0;
253 }
254 
LUA_HookLib(lua_State * L)255 int LUA_HookLib(lua_State *L)
256 {
257 	memset(hooksAvailable,0,sizeof(UINT8[(hook_MAX/8)+1]));
258 	roothook = NULL;
259 	lua_register(L, "addHook", lib_addHook);
260 	return 0;
261 }
262 
LUAh_MobjHook(mobj_t * mo,enum hook which)263 boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
264 {
265 	hook_p hookp;
266 	boolean hooked = false;
267 	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
268 		return false;
269 
270 	I_Assert(mo->type < NUMMOBJTYPES);
271 
272 	if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type]))
273 		return false;
274 
275 	lua_settop(gL, 0);
276 	lua_pushcfunction(gL, LUA_GetErrorMessage);
277 
278 	// Look for all generic mobj hooks
279 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
280 	{
281 		if (hookp->type != which)
282 			continue;
283 
284 		ps_lua_mobjhooks++;
285 		if (lua_gettop(gL) == 1)
286 			LUA_PushUserdata(gL, mo, META_MOBJ);
287 		PushHook(gL, hookp);
288 		lua_pushvalue(gL, -2);
289 		if (lua_pcall(gL, 1, 1, 1)) {
290 			if (!hookp->error || cv_debug & DBG_LUA)
291 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
292 			lua_pop(gL, 1);
293 			hookp->error = true;
294 			continue;
295 		}
296 		if (lua_toboolean(gL, -1))
297 			hooked = true;
298 		lua_pop(gL, 1);
299 	}
300 
301 	for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next)
302 	{
303 		if (hookp->type != which)
304 			continue;
305 
306 		ps_lua_mobjhooks++;
307 		if (lua_gettop(gL) == 1)
308 			LUA_PushUserdata(gL, mo, META_MOBJ);
309 		PushHook(gL, hookp);
310 		lua_pushvalue(gL, -2);
311 		if (lua_pcall(gL, 1, 1, 1)) {
312 			if (!hookp->error || cv_debug & DBG_LUA)
313 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
314 			lua_pop(gL, 1);
315 			hookp->error = true;
316 			continue;
317 		}
318 		if (lua_toboolean(gL, -1))
319 			hooked = true;
320 		lua_pop(gL, 1);
321 	}
322 
323 	lua_settop(gL, 0);
324 	return hooked;
325 }
326 
LUAh_PlayerHook(player_t * plr,enum hook which)327 boolean LUAh_PlayerHook(player_t *plr, enum hook which)
328 {
329 	hook_p hookp;
330 	boolean hooked = false;
331 	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
332 		return false;
333 
334 	lua_settop(gL, 0);
335 	lua_pushcfunction(gL, LUA_GetErrorMessage);
336 
337 	for (hookp = playerhooks; hookp; hookp = hookp->next)
338 	{
339 		if (hookp->type != which)
340 			continue;
341 
342 		ps_lua_mobjhooks++;
343 		if (lua_gettop(gL) == 1)
344 			LUA_PushUserdata(gL, plr, META_PLAYER);
345 		PushHook(gL, hookp);
346 		lua_pushvalue(gL, -2);
347 		if (lua_pcall(gL, 1, 1, 1)) {
348 			if (!hookp->error || cv_debug & DBG_LUA)
349 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
350 			lua_pop(gL, 1);
351 			hookp->error = true;
352 			continue;
353 		}
354 		if (lua_toboolean(gL, -1))
355 			hooked = true;
356 		lua_pop(gL, 1);
357 	}
358 
359 	lua_settop(gL, 0);
360 	return hooked;
361 }
362 
363 // Hook for map change (before load)
LUAh_MapChange(INT16 mapnumber)364 void LUAh_MapChange(INT16 mapnumber)
365 {
366 	hook_p hookp;
367 	if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8))))
368 		return;
369 
370 	lua_settop(gL, 0);
371 	lua_pushcfunction(gL, LUA_GetErrorMessage);
372 	lua_pushinteger(gL, mapnumber);
373 
374 	for (hookp = roothook; hookp; hookp = hookp->next)
375 	{
376 		if (hookp->type != hook_MapChange)
377 			continue;
378 
379 		PushHook(gL, hookp);
380 		lua_pushvalue(gL, -2);
381 		if (lua_pcall(gL, 1, 0, 1)) {
382 			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
383 			lua_pop(gL, 1);
384 		}
385 	}
386 
387 	lua_settop(gL, 0);
388 }
389 
390 // Hook for map load
LUAh_MapLoad(void)391 void LUAh_MapLoad(void)
392 {
393 	hook_p hookp;
394 	if (!gL || !(hooksAvailable[hook_MapLoad/8] & (1<<(hook_MapLoad%8))))
395 		return;
396 
397 	lua_settop(gL, 0);
398 	lua_pushcfunction(gL, LUA_GetErrorMessage);
399 	lua_pushinteger(gL, gamemap);
400 
401 	for (hookp = roothook; hookp; hookp = hookp->next)
402 	{
403 		if (hookp->type != hook_MapLoad)
404 			continue;
405 
406 		PushHook(gL, hookp);
407 		lua_pushvalue(gL, -2);
408 		if (lua_pcall(gL, 1, 0, 1)) {
409 			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
410 			lua_pop(gL, 1);
411 		}
412 	}
413 
414 	lua_settop(gL, 0);
415 }
416 
417 // Hook for Got_AddPlayer
LUAh_PlayerJoin(int playernum)418 void LUAh_PlayerJoin(int playernum)
419 {
420 	hook_p hookp;
421 	if (!gL || !(hooksAvailable[hook_PlayerJoin/8] & (1<<(hook_PlayerJoin%8))))
422 		return;
423 
424 	lua_settop(gL, 0);
425 	lua_pushcfunction(gL, LUA_GetErrorMessage);
426 	lua_pushinteger(gL, playernum);
427 
428 	for (hookp = roothook; hookp; hookp = hookp->next)
429 	{
430 		if (hookp->type != hook_PlayerJoin)
431 			continue;
432 
433 		PushHook(gL, hookp);
434 		lua_pushvalue(gL, -2);
435 		if (lua_pcall(gL, 1, 0, 1)) {
436 			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
437 			lua_pop(gL, 1);
438 		}
439 	}
440 
441 	lua_settop(gL, 0);
442 }
443 
444 // Hook for frame (before mobj and player thinkers)
LUAh_PreThinkFrame(void)445 void LUAh_PreThinkFrame(void)
446 {
447 	hook_p hookp;
448 	if (!gL || !(hooksAvailable[hook_PreThinkFrame/8] & (1<<(hook_PreThinkFrame%8))))
449 		return;
450 
451 	lua_pushcfunction(gL, LUA_GetErrorMessage);
452 
453 	for (hookp = roothook; hookp; hookp = hookp->next)
454 	{
455 		if (hookp->type != hook_PreThinkFrame)
456 			continue;
457 
458 		PushHook(gL, hookp);
459 		if (lua_pcall(gL, 0, 0, 1)) {
460 			if (!hookp->error || cv_debug & DBG_LUA)
461 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
462 			lua_pop(gL, 1);
463 			hookp->error = true;
464 		}
465 	}
466 
467 	lua_pop(gL, 1); // Pop error handler
468 }
469 
470 // Hook for frame (after mobj and player thinkers)
LUAh_ThinkFrame(void)471 void LUAh_ThinkFrame(void)
472 {
473 	hook_p hookp;
474 	// variables used by perf stats
475 	int hook_index = 0;
476 	precise_t time_taken = 0;
477 	if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8))))
478 		return;
479 
480 	lua_pushcfunction(gL, LUA_GetErrorMessage);
481 
482 	for (hookp = roothook; hookp; hookp = hookp->next)
483 	{
484 		if (hookp->type != hook_ThinkFrame)
485 			continue;
486 
487 		if (cv_perfstats.value == 3)
488 			time_taken = I_GetPreciseTime();
489 		PushHook(gL, hookp);
490 		if (lua_pcall(gL, 0, 0, 1)) {
491 			if (!hookp->error || cv_debug & DBG_LUA)
492 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
493 			lua_pop(gL, 1);
494 			hookp->error = true;
495 		}
496 		if (cv_perfstats.value == 3)
497 		{
498 			lua_Debug ar;
499 			time_taken = I_GetPreciseTime() - time_taken;
500 			// we need the function, let's just retrieve it again
501 			PushHook(gL, hookp);
502 			lua_getinfo(gL, ">S", &ar);
503 			PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src);
504 			hook_index++;
505 		}
506 	}
507 
508 	lua_pop(gL, 1); // Pop error handler
509 }
510 
511 // Hook for frame (at end of tick, ie after overlays, precipitation, specials)
LUAh_PostThinkFrame(void)512 void LUAh_PostThinkFrame(void)
513 {
514 	hook_p hookp;
515 	if (!gL || !(hooksAvailable[hook_PostThinkFrame/8] & (1<<(hook_PostThinkFrame%8))))
516 		return;
517 
518 	lua_pushcfunction(gL, LUA_GetErrorMessage);
519 
520 	for (hookp = roothook; hookp; hookp = hookp->next)
521 	{
522 		if (hookp->type != hook_PostThinkFrame)
523 			continue;
524 
525 		PushHook(gL, hookp);
526 		if (lua_pcall(gL, 0, 0, 1)) {
527 			if (!hookp->error || cv_debug & DBG_LUA)
528 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
529 			lua_pop(gL, 1);
530 			hookp->error = true;
531 		}
532 	}
533 
534 	lua_pop(gL, 1); // Pop error handler
535 }
536 
537 // Hook for mobj collisions
LUAh_MobjCollideHook(mobj_t * thing1,mobj_t * thing2,enum hook which)538 UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
539 {
540 	hook_p hookp;
541 	UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no.
542 	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
543 		return 0;
544 
545 	I_Assert(thing1->type < NUMMOBJTYPES);
546 
547 	if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing1->type]))
548 		return 0;
549 
550 	lua_settop(gL, 0);
551 	lua_pushcfunction(gL, LUA_GetErrorMessage);
552 
553 	// Look for all generic mobj collision hooks
554 	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
555 	{
556 		if (hookp->type != which)
557 			continue;
558 
559 		ps_lua_mobjhooks++;
560 		if (lua_gettop(gL) == 1)
561 		{
562 			LUA_PushUserdata(gL, thing1, META_MOBJ);
563 			LUA_PushUserdata(gL, thing2, META_MOBJ);
564 		}
565 		PushHook(gL, hookp);
566 		lua_pushvalue(gL, -3);
567 		lua_pushvalue(gL, -3);
568 		if (lua_pcall(gL, 2, 1, 1)) {
569 			if (!hookp->error || cv_debug & DBG_LUA)
570 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
571 			lua_pop(gL, 1);
572 			hookp->error = true;
573 			continue;
574 		}
575 		if (!lua_isnil(gL, -1))
576 		{ // if nil, leave shouldCollide = 0.
577 			if (lua_toboolean(gL, -1))
578 				shouldCollide = 1; // Force yes
579 			else
580 				shouldCollide = 2; // Force no
581 		}
582 		lua_pop(gL, 1);
583 	}
584 
585 	for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next)
586 	{
587 		if (hookp->type != which)
588 			continue;
589 
590 		ps_lua_mobjhooks++;
591 		if (lua_gettop(gL) == 1)
592 		{
593 			LUA_PushUserdata(gL, thing1, META_MOBJ);
594 			LUA_PushUserdata(gL, thing2, META_MOBJ);
595 		}
596 		PushHook(gL, hookp);
597 		lua_pushvalue(gL, -3);
598 		lua_pushvalue(gL, -3);
599 		if (lua_pcall(gL, 2, 1, 1)) {
600 			if (!hookp->error || cv_debug & DBG_LUA)
601 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
602 			lua_pop(gL, 1);
603 			hookp->error = true;
604 			continue;
605 		}
606 		if (!lua_isnil(gL, -1))
607 		{ // if nil, leave shouldCollide = 0.
608 			if (lua_toboolean(gL, -1))
609 				shouldCollide = 1; // Force yes
610 			else
611 				shouldCollide = 2; // Force no
612 		}
613 		lua_pop(gL, 1);
614 	}
615 
616 	lua_settop(gL, 0);
617 	return shouldCollide;
618 }
619 
LUAh_MobjLineCollideHook(mobj_t * thing,line_t * line,enum hook which)620 UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which)
621 {
622 	hook_p hookp;
623 	UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no.
624 	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
625 		return 0;
626 
627 	I_Assert(thing->type < NUMMOBJTYPES);
628 
629 	if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing->type]))
630 		return 0;
631 
632 	lua_settop(gL, 0);
633 	lua_pushcfunction(gL, LUA_GetErrorMessage);
634 
635 	// Look for all generic mobj collision hooks
636 	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
637 	{
638 		if (hookp->type != which)
639 			continue;
640 
641 		ps_lua_mobjhooks++;
642 		if (lua_gettop(gL) == 1)
643 		{
644 			LUA_PushUserdata(gL, thing, META_MOBJ);
645 			LUA_PushUserdata(gL, line, META_LINE);
646 		}
647 		PushHook(gL, hookp);
648 		lua_pushvalue(gL, -3);
649 		lua_pushvalue(gL, -3);
650 		if (lua_pcall(gL, 2, 1, 1)) {
651 			if (!hookp->error || cv_debug & DBG_LUA)
652 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
653 			lua_pop(gL, 1);
654 			hookp->error = true;
655 			continue;
656 		}
657 		if (!lua_isnil(gL, -1))
658 		{ // if nil, leave shouldCollide = 0.
659 			if (lua_toboolean(gL, -1))
660 				shouldCollide = 1; // Force yes
661 			else
662 				shouldCollide = 2; // Force no
663 		}
664 		lua_pop(gL, 1);
665 	}
666 
667 	for (hookp = mobjcollidehooks[thing->type]; hookp; hookp = hookp->next)
668 	{
669 		if (hookp->type != which)
670 			continue;
671 
672 		ps_lua_mobjhooks++;
673 		if (lua_gettop(gL) == 1)
674 		{
675 			LUA_PushUserdata(gL, thing, META_MOBJ);
676 			LUA_PushUserdata(gL, line, META_LINE);
677 		}
678 		PushHook(gL, hookp);
679 		lua_pushvalue(gL, -3);
680 		lua_pushvalue(gL, -3);
681 		if (lua_pcall(gL, 2, 1, 1)) {
682 			if (!hookp->error || cv_debug & DBG_LUA)
683 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
684 			lua_pop(gL, 1);
685 			hookp->error = true;
686 			continue;
687 		}
688 		if (!lua_isnil(gL, -1))
689 		{ // if nil, leave shouldCollide = 0.
690 			if (lua_toboolean(gL, -1))
691 				shouldCollide = 1; // Force yes
692 			else
693 				shouldCollide = 2; // Force no
694 		}
695 		lua_pop(gL, 1);
696 	}
697 
698 	lua_settop(gL, 0);
699 	return shouldCollide;
700 }
701 
702 // Hook for mobj thinkers
LUAh_MobjThinker(mobj_t * mo)703 boolean LUAh_MobjThinker(mobj_t *mo)
704 {
705 	hook_p hookp;
706 	boolean hooked = false;
707 	if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8))))
708 		return false;
709 
710 	I_Assert(mo->type < NUMMOBJTYPES);
711 
712 	if (!(mobjthinkerhooks[MT_NULL] || mobjthinkerhooks[mo->type]))
713 		return false;
714 
715 	lua_settop(gL, 0);
716 	lua_pushcfunction(gL, LUA_GetErrorMessage);
717 
718 	// Look for all generic mobj thinker hooks
719 	for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
720 	{
721 		ps_lua_mobjhooks++;
722 		if (lua_gettop(gL) == 1)
723 			LUA_PushUserdata(gL, mo, META_MOBJ);
724 		PushHook(gL, hookp);
725 		lua_pushvalue(gL, -2);
726 		if (lua_pcall(gL, 1, 1, 1)) {
727 			if (!hookp->error || cv_debug & DBG_LUA)
728 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
729 			lua_pop(gL, 1);
730 			hookp->error = true;
731 			continue;
732 		}
733 		if (lua_toboolean(gL, -1))
734 			hooked = true;
735 		lua_pop(gL, 1);
736 	}
737 
738 	for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next)
739 	{
740 		ps_lua_mobjhooks++;
741 		if (lua_gettop(gL) == 1)
742 			LUA_PushUserdata(gL, mo, META_MOBJ);
743 		PushHook(gL, hookp);
744 		lua_pushvalue(gL, -2);
745 		if (lua_pcall(gL, 1, 1, 1)) {
746 			if (!hookp->error || cv_debug & DBG_LUA)
747 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
748 			lua_pop(gL, 1);
749 			hookp->error = true;
750 			continue;
751 		}
752 		if (lua_toboolean(gL, -1))
753 			hooked = true;
754 		lua_pop(gL, 1);
755 	}
756 
757 	lua_settop(gL, 0);
758 	return hooked;
759 }
760 
761 // Hook for P_TouchSpecialThing by mobj type
LUAh_TouchSpecial(mobj_t * special,mobj_t * toucher)762 boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
763 {
764 	hook_p hookp;
765 	boolean hooked = false;
766 	if (!gL || !(hooksAvailable[hook_TouchSpecial/8] & (1<<(hook_TouchSpecial%8))))
767 		return false;
768 
769 	I_Assert(special->type < NUMMOBJTYPES);
770 
771 	if (!(mobjhooks[MT_NULL] || mobjhooks[special->type]))
772 		return false;
773 
774 	lua_settop(gL, 0);
775 	lua_pushcfunction(gL, LUA_GetErrorMessage);
776 
777 	// Look for all generic touch special hooks
778 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
779 	{
780 		if (hookp->type != hook_TouchSpecial)
781 			continue;
782 
783 		ps_lua_mobjhooks++;
784 		if (lua_gettop(gL) == 1)
785 		{
786 			LUA_PushUserdata(gL, special, META_MOBJ);
787 			LUA_PushUserdata(gL, toucher, META_MOBJ);
788 		}
789 		PushHook(gL, hookp);
790 		lua_pushvalue(gL, -3);
791 		lua_pushvalue(gL, -3);
792 		if (lua_pcall(gL, 2, 1, 1)) {
793 			if (!hookp->error || cv_debug & DBG_LUA)
794 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
795 			lua_pop(gL, 1);
796 			hookp->error = true;
797 			continue;
798 		}
799 		if (lua_toboolean(gL, -1))
800 			hooked = true;
801 		lua_pop(gL, 1);
802 	}
803 
804 	for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next)
805 	{
806 		if (hookp->type != hook_TouchSpecial)
807 			continue;
808 
809 		ps_lua_mobjhooks++;
810 		if (lua_gettop(gL) == 1)
811 		{
812 			LUA_PushUserdata(gL, special, META_MOBJ);
813 			LUA_PushUserdata(gL, toucher, META_MOBJ);
814 		}
815 		PushHook(gL, hookp);
816 		lua_pushvalue(gL, -3);
817 		lua_pushvalue(gL, -3);
818 		if (lua_pcall(gL, 2, 1, 1)) {
819 			if (!hookp->error || cv_debug & DBG_LUA)
820 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
821 			lua_pop(gL, 1);
822 			hookp->error = true;
823 			continue;
824 		}
825 		if (lua_toboolean(gL, -1))
826 			hooked = true;
827 		lua_pop(gL, 1);
828 	}
829 
830 	lua_settop(gL, 0);
831 	return hooked;
832 }
833 
834 // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
LUAh_ShouldDamage(mobj_t * target,mobj_t * inflictor,mobj_t * source,INT32 damage,UINT8 damagetype)835 UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
836 {
837 	hook_p hookp;
838 	UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no.
839 	if (!gL || !(hooksAvailable[hook_ShouldDamage/8] & (1<<(hook_ShouldDamage%8))))
840 		return 0;
841 
842 	I_Assert(target->type < NUMMOBJTYPES);
843 
844 	if (!(mobjhooks[MT_NULL] || mobjhooks[target->type]))
845 		return 0;
846 
847 	lua_settop(gL, 0);
848 	lua_pushcfunction(gL, LUA_GetErrorMessage);
849 
850 	// Look for all generic should damage hooks
851 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
852 	{
853 		if (hookp->type != hook_ShouldDamage)
854 			continue;
855 
856 		ps_lua_mobjhooks++;
857 		if (lua_gettop(gL) == 1)
858 		{
859 			LUA_PushUserdata(gL, target, META_MOBJ);
860 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
861 			LUA_PushUserdata(gL, source, META_MOBJ);
862 			lua_pushinteger(gL, damage);
863 			lua_pushinteger(gL, damagetype);
864 		}
865 		PushHook(gL, hookp);
866 		lua_pushvalue(gL, -6);
867 		lua_pushvalue(gL, -6);
868 		lua_pushvalue(gL, -6);
869 		lua_pushvalue(gL, -6);
870 		lua_pushvalue(gL, -6);
871 		if (lua_pcall(gL, 5, 1, 1)) {
872 			if (!hookp->error || cv_debug & DBG_LUA)
873 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
874 			lua_pop(gL, 1);
875 			hookp->error = true;
876 			continue;
877 		}
878 		if (!lua_isnil(gL, -1))
879 		{
880 			if (lua_toboolean(gL, -1))
881 				shouldDamage = 1; // Force yes
882 			else
883 				shouldDamage = 2; // Force no
884 		}
885 		lua_pop(gL, 1);
886 	}
887 
888 	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
889 	{
890 		if (hookp->type != hook_ShouldDamage)
891 			continue;
892 		ps_lua_mobjhooks++;
893 		if (lua_gettop(gL) == 1)
894 		{
895 			LUA_PushUserdata(gL, target, META_MOBJ);
896 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
897 			LUA_PushUserdata(gL, source, META_MOBJ);
898 			lua_pushinteger(gL, damage);
899 			lua_pushinteger(gL, damagetype);
900 		}
901 		PushHook(gL, hookp);
902 		lua_pushvalue(gL, -6);
903 		lua_pushvalue(gL, -6);
904 		lua_pushvalue(gL, -6);
905 		lua_pushvalue(gL, -6);
906 		lua_pushvalue(gL, -6);
907 		if (lua_pcall(gL, 5, 1, 1)) {
908 			if (!hookp->error || cv_debug & DBG_LUA)
909 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
910 			lua_pop(gL, 1);
911 			hookp->error = true;
912 			continue;
913 		}
914 		if (!lua_isnil(gL, -1))
915 		{
916 			if (lua_toboolean(gL, -1))
917 				shouldDamage = 1; // Force yes
918 			else
919 				shouldDamage = 2; // Force no
920 		}
921 		lua_pop(gL, 1);
922 	}
923 
924 	lua_settop(gL, 0);
925 	return shouldDamage;
926 }
927 
928 // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
LUAh_MobjDamage(mobj_t * target,mobj_t * inflictor,mobj_t * source,INT32 damage,UINT8 damagetype)929 boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
930 {
931 	hook_p hookp;
932 	boolean hooked = false;
933 	if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8))))
934 		return false;
935 
936 	I_Assert(target->type < NUMMOBJTYPES);
937 
938 	if (!(mobjhooks[MT_NULL] || mobjhooks[target->type]))
939 		return false;
940 
941 	lua_settop(gL, 0);
942 	lua_pushcfunction(gL, LUA_GetErrorMessage);
943 
944 	// Look for all generic mobj damage hooks
945 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
946 	{
947 		if (hookp->type != hook_MobjDamage)
948 			continue;
949 
950 		ps_lua_mobjhooks++;
951 		if (lua_gettop(gL) == 1)
952 		{
953 			LUA_PushUserdata(gL, target, META_MOBJ);
954 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
955 			LUA_PushUserdata(gL, source, META_MOBJ);
956 			lua_pushinteger(gL, damage);
957 			lua_pushinteger(gL, damagetype);
958 		}
959 		PushHook(gL, hookp);
960 		lua_pushvalue(gL, -6);
961 		lua_pushvalue(gL, -6);
962 		lua_pushvalue(gL, -6);
963 		lua_pushvalue(gL, -6);
964 		lua_pushvalue(gL, -6);
965 		if (lua_pcall(gL, 5, 1, 1)) {
966 			if (!hookp->error || cv_debug & DBG_LUA)
967 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
968 			lua_pop(gL, 1);
969 			hookp->error = true;
970 			continue;
971 		}
972 		if (lua_toboolean(gL, -1))
973 			hooked = true;
974 		lua_pop(gL, 1);
975 	}
976 
977 	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
978 	{
979 		if (hookp->type != hook_MobjDamage)
980 			continue;
981 
982 		ps_lua_mobjhooks++;
983 		if (lua_gettop(gL) == 1)
984 		{
985 			LUA_PushUserdata(gL, target, META_MOBJ);
986 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
987 			LUA_PushUserdata(gL, source, META_MOBJ);
988 			lua_pushinteger(gL, damage);
989 			lua_pushinteger(gL, damagetype);
990 		}
991 		PushHook(gL, hookp);
992 		lua_pushvalue(gL, -6);
993 		lua_pushvalue(gL, -6);
994 		lua_pushvalue(gL, -6);
995 		lua_pushvalue(gL, -6);
996 		lua_pushvalue(gL, -6);
997 		if (lua_pcall(gL, 5, 1, 1)) {
998 			if (!hookp->error || cv_debug & DBG_LUA)
999 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1000 			lua_pop(gL, 1);
1001 			hookp->error = true;
1002 			continue;
1003 		}
1004 		if (lua_toboolean(gL, -1))
1005 			hooked = true;
1006 		lua_pop(gL, 1);
1007 	}
1008 
1009 	lua_settop(gL, 0);
1010 	return hooked;
1011 }
1012 
1013 // Hook for P_KillMobj by mobj type
LUAh_MobjDeath(mobj_t * target,mobj_t * inflictor,mobj_t * source,UINT8 damagetype)1014 boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
1015 {
1016 	hook_p hookp;
1017 	boolean hooked = false;
1018 	if (!gL || !(hooksAvailable[hook_MobjDeath/8] & (1<<(hook_MobjDeath%8))))
1019 		return false;
1020 
1021 	I_Assert(target->type < NUMMOBJTYPES);
1022 
1023 	if (!(mobjhooks[MT_NULL] || mobjhooks[target->type]))
1024 		return false;
1025 
1026 	lua_settop(gL, 0);
1027 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1028 
1029 	// Look for all generic mobj death hooks
1030 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
1031 	{
1032 		if (hookp->type != hook_MobjDeath)
1033 			continue;
1034 
1035 		ps_lua_mobjhooks++;
1036 		if (lua_gettop(gL) == 1)
1037 		{
1038 			LUA_PushUserdata(gL, target, META_MOBJ);
1039 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
1040 			LUA_PushUserdata(gL, source, META_MOBJ);
1041 			lua_pushinteger(gL, damagetype);
1042 		}
1043 		PushHook(gL, hookp);
1044 		lua_pushvalue(gL, -5);
1045 		lua_pushvalue(gL, -5);
1046 		lua_pushvalue(gL, -5);
1047 		lua_pushvalue(gL, -5);
1048 		if (lua_pcall(gL, 4, 1, 1)) {
1049 			if (!hookp->error || cv_debug & DBG_LUA)
1050 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1051 			lua_pop(gL, 1);
1052 			hookp->error = true;
1053 			continue;
1054 		}
1055 		if (lua_toboolean(gL, -1))
1056 			hooked = true;
1057 		lua_pop(gL, 1);
1058 	}
1059 
1060 	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
1061 	{
1062 		if (hookp->type != hook_MobjDeath)
1063 			continue;
1064 
1065 		ps_lua_mobjhooks++;
1066 		if (lua_gettop(gL) == 1)
1067 		{
1068 			LUA_PushUserdata(gL, target, META_MOBJ);
1069 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
1070 			LUA_PushUserdata(gL, source, META_MOBJ);
1071 			lua_pushinteger(gL, damagetype);
1072 		}
1073 		PushHook(gL, hookp);
1074 		lua_pushvalue(gL, -5);
1075 		lua_pushvalue(gL, -5);
1076 		lua_pushvalue(gL, -5);
1077 		lua_pushvalue(gL, -5);
1078 		if (lua_pcall(gL, 4, 1, 1)) {
1079 			if (!hookp->error || cv_debug & DBG_LUA)
1080 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1081 			lua_pop(gL, 1);
1082 			hookp->error = true;
1083 			continue;
1084 		}
1085 		if (lua_toboolean(gL, -1))
1086 			hooked = true;
1087 		lua_pop(gL, 1);
1088 	}
1089 
1090 	lua_settop(gL, 0);
1091 	return hooked;
1092 }
1093 
1094 // Hook for B_BuildTiccmd
LUAh_BotTiccmd(player_t * bot,ticcmd_t * cmd)1095 boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd)
1096 {
1097 	hook_p hookp;
1098 	boolean hooked = false;
1099 	if (!gL || !(hooksAvailable[hook_BotTiccmd/8] & (1<<(hook_BotTiccmd%8))))
1100 		return false;
1101 
1102 	lua_settop(gL, 0);
1103 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1104 
1105 	for (hookp = roothook; hookp; hookp = hookp->next)
1106 	{
1107 		if (hookp->type != hook_BotTiccmd)
1108 			continue;
1109 
1110 		if (lua_gettop(gL) == 1)
1111 		{
1112 			LUA_PushUserdata(gL, bot, META_PLAYER);
1113 			LUA_PushUserdata(gL, cmd, META_TICCMD);
1114 		}
1115 		PushHook(gL, hookp);
1116 		lua_pushvalue(gL, -3);
1117 		lua_pushvalue(gL, -3);
1118 		if (lua_pcall(gL, 2, 1, 1)) {
1119 			if (!hookp->error || cv_debug & DBG_LUA)
1120 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1121 			lua_pop(gL, 1);
1122 			hookp->error = true;
1123 			continue;
1124 		}
1125 		if (lua_toboolean(gL, -1))
1126 			hooked = true;
1127 		lua_pop(gL, 1);
1128 	}
1129 
1130 	lua_settop(gL, 0);
1131 	return hooked;
1132 }
1133 
1134 // Hook for B_BuildTailsTiccmd by skin name
LUAh_BotAI(mobj_t * sonic,mobj_t * tails,ticcmd_t * cmd)1135 boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
1136 {
1137 	hook_p hookp;
1138 	boolean hooked = false;
1139 	if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8))))
1140 		return false;
1141 
1142 	lua_settop(gL, 0);
1143 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1144 
1145 	for (hookp = roothook; hookp; hookp = hookp->next)
1146 	{
1147 		if (hookp->type != hook_BotAI
1148 		|| (hookp->s.str && strcmp(hookp->s.str, ((skin_t*)tails->skin)->name)))
1149 			continue;
1150 
1151 		if (lua_gettop(gL) == 1)
1152 		{
1153 			LUA_PushUserdata(gL, sonic, META_MOBJ);
1154 			LUA_PushUserdata(gL, tails, META_MOBJ);
1155 		}
1156 		PushHook(gL, hookp);
1157 		lua_pushvalue(gL, -3);
1158 		lua_pushvalue(gL, -3);
1159 		if (lua_pcall(gL, 2, 8, 1)) {
1160 			if (!hookp->error || cv_debug & DBG_LUA)
1161 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1162 			lua_pop(gL, 1);
1163 			hookp->error = true;
1164 			continue;
1165 		}
1166 
1167 		// This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails.
1168 		if (lua_istable(gL, 2+1)) {
1169 			boolean forward=false, backward=false, left=false, right=false, strafeleft=false, straferight=false, jump=false, spin=false;
1170 #define CHECKFIELD(field) \
1171 			lua_getfield(gL, 2+1, #field);\
1172 			if (lua_toboolean(gL, -1))\
1173 				field = true;\
1174 			lua_pop(gL, 1);
1175 
1176 			CHECKFIELD(forward)
1177 			CHECKFIELD(backward)
1178 			CHECKFIELD(left)
1179 			CHECKFIELD(right)
1180 			CHECKFIELD(strafeleft)
1181 			CHECKFIELD(straferight)
1182 			CHECKFIELD(jump)
1183 			CHECKFIELD(spin)
1184 #undef CHECKFIELD
1185 			B_KeysToTiccmd(tails, cmd, forward, backward, left, right, strafeleft, straferight, jump, spin);
1186 		} else
1187 			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));
1188 
1189 		lua_pop(gL, 8);
1190 		hooked = true;
1191 	}
1192 
1193 	lua_settop(gL, 0);
1194 	return hooked;
1195 }
1196 
1197 // Hook for B_CheckRespawn
LUAh_BotRespawn(mobj_t * sonic,mobj_t * tails)1198 boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails)
1199 {
1200 	hook_p hookp;
1201 	UINT8 shouldRespawn = 0; // 0 = default, 1 = force yes, 2 = force no.
1202 	if (!gL || !(hooksAvailable[hook_BotRespawn/8] & (1<<(hook_BotRespawn%8))))
1203 		return false;
1204 
1205 	lua_settop(gL, 0);
1206 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1207 
1208 	for (hookp = roothook; hookp; hookp = hookp->next)
1209 	{
1210 		if (hookp->type != hook_BotRespawn)
1211 			continue;
1212 
1213 		if (lua_gettop(gL) == 1)
1214 		{
1215 			LUA_PushUserdata(gL, sonic, META_MOBJ);
1216 			LUA_PushUserdata(gL, tails, META_MOBJ);
1217 		}
1218 		PushHook(gL, hookp);
1219 		lua_pushvalue(gL, -3);
1220 		lua_pushvalue(gL, -3);
1221 		if (lua_pcall(gL, 2, 1, 1)) {
1222 			if (!hookp->error || cv_debug & DBG_LUA)
1223 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1224 			lua_pop(gL, 1);
1225 			hookp->error = true;
1226 			continue;
1227 		}
1228 		if (!lua_isnil(gL, -1))
1229 		{
1230 			if (lua_toboolean(gL, -1))
1231 				shouldRespawn = 1; // Force yes
1232 			else
1233 				shouldRespawn = 2; // Force no
1234 		}
1235 		lua_pop(gL, 1);
1236 	}
1237 
1238 	lua_settop(gL, 0);
1239 	return shouldRespawn;
1240 }
1241 
1242 // Hook for linedef executors
LUAh_LinedefExecute(line_t * line,mobj_t * mo,sector_t * sector)1243 boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
1244 {
1245 	hook_p hookp;
1246 	boolean hooked = false;
1247 	if (!gL || !(hooksAvailable[hook_LinedefExecute/8] & (1<<(hook_LinedefExecute%8))))
1248 		return 0;
1249 
1250 	lua_settop(gL, 0);
1251 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1252 
1253 	for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next)
1254 	{
1255 		if (strcmp(hookp->s.str, line->stringargs[0]))
1256 			continue;
1257 
1258 		ps_lua_mobjhooks++;
1259 		if (lua_gettop(gL) == 1)
1260 		{
1261 			LUA_PushUserdata(gL, line, META_LINE);
1262 			LUA_PushUserdata(gL, mo, META_MOBJ);
1263 			LUA_PushUserdata(gL, sector, META_SECTOR);
1264 		}
1265 		PushHook(gL, hookp);
1266 		lua_pushvalue(gL, -4);
1267 		lua_pushvalue(gL, -4);
1268 		lua_pushvalue(gL, -4);
1269 		if (lua_pcall(gL, 3, 0, 1)) {
1270 			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1271 			lua_pop(gL, 1);
1272 		}
1273 		hooked = true;
1274 	}
1275 
1276 	lua_settop(gL, 0);
1277 	return hooked;
1278 }
1279 
1280 
LUAh_PlayerMsg(int source,int target,int flags,char * msg)1281 boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
1282 {
1283 	hook_p hookp;
1284 	boolean hooked = false;
1285 	if (!gL || !(hooksAvailable[hook_PlayerMsg/8] & (1<<(hook_PlayerMsg%8))))
1286 		return false;
1287 
1288 	lua_settop(gL, 0);
1289 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1290 
1291 	for (hookp = roothook; hookp; hookp = hookp->next)
1292 	{
1293 		if (hookp->type != hook_PlayerMsg)
1294 			continue;
1295 
1296 		if (lua_gettop(gL) == 1)
1297 		{
1298 			LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player
1299 			if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c
1300 				lua_pushinteger(gL, 3); // type
1301 				lua_pushnil(gL); // target
1302 			} else if (target == -1) { // sayteam
1303 				lua_pushinteger(gL, 1); // type
1304 				lua_pushnil(gL); // target
1305 			} else if (target == 0) { // say
1306 				lua_pushinteger(gL, 0); // type
1307 				lua_pushnil(gL); // target
1308 			} else { // sayto
1309 				lua_pushinteger(gL, 2); // type
1310 				LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target
1311 			}
1312 			lua_pushstring(gL, msg); // msg
1313 		}
1314 		PushHook(gL, hookp);
1315 		lua_pushvalue(gL, -5);
1316 		lua_pushvalue(gL, -5);
1317 		lua_pushvalue(gL, -5);
1318 		lua_pushvalue(gL, -5);
1319 		if (lua_pcall(gL, 4, 1, 1)) {
1320 			if (!hookp->error || cv_debug & DBG_LUA)
1321 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1322 			lua_pop(gL, 1);
1323 			hookp->error = true;
1324 			continue;
1325 		}
1326 		if (lua_toboolean(gL, -1))
1327 			hooked = true;
1328 		lua_pop(gL, 1);
1329 	}
1330 
1331 	lua_settop(gL, 0);
1332 	return hooked;
1333 }
1334 
1335 
1336 // Hook for hurt messages
LUAh_HurtMsg(player_t * player,mobj_t * inflictor,mobj_t * source,UINT8 damagetype)1337 boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
1338 {
1339 	hook_p hookp;
1340 	boolean hooked = false;
1341 	if (!gL || !(hooksAvailable[hook_HurtMsg/8] & (1<<(hook_HurtMsg%8))))
1342 		return false;
1343 
1344 	lua_settop(gL, 0);
1345 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1346 
1347 	for (hookp = roothook; hookp; hookp = hookp->next)
1348 	{
1349 		if (hookp->type != hook_HurtMsg
1350 		|| (hookp->s.mt && !(inflictor && hookp->s.mt == inflictor->type)))
1351 			continue;
1352 
1353 		if (lua_gettop(gL) == 1)
1354 		{
1355 			LUA_PushUserdata(gL, player, META_PLAYER);
1356 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
1357 			LUA_PushUserdata(gL, source, META_MOBJ);
1358 			lua_pushinteger(gL, damagetype);
1359 		}
1360 		PushHook(gL, hookp);
1361 		lua_pushvalue(gL, -5);
1362 		lua_pushvalue(gL, -5);
1363 		lua_pushvalue(gL, -5);
1364 		lua_pushvalue(gL, -5);
1365 		if (lua_pcall(gL, 4, 1, 1)) {
1366 			if (!hookp->error || cv_debug & DBG_LUA)
1367 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1368 			lua_pop(gL, 1);
1369 			hookp->error = true;
1370 			continue;
1371 		}
1372 		if (lua_toboolean(gL, -1))
1373 			hooked = true;
1374 		lua_pop(gL, 1);
1375 	}
1376 
1377 	lua_settop(gL, 0);
1378 	return hooked;
1379 }
1380 
LUAh_NetArchiveHook(lua_CFunction archFunc)1381 void LUAh_NetArchiveHook(lua_CFunction archFunc)
1382 {
1383 	hook_p hookp;
1384 	int errorhandlerindex;
1385 	if (!gL || !(hooksAvailable[hook_NetVars/8] & (1<<(hook_NetVars%8))))
1386 		return;
1387 
1388 	// stack: tables
1389 	I_Assert(lua_gettop(gL) > 0);
1390 	I_Assert(lua_istable(gL, -1));
1391 
1392 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1393 	errorhandlerindex = lua_gettop(gL);
1394 
1395 	// tables becomes an upvalue of archFunc
1396 	lua_pushvalue(gL, -2);
1397 	lua_pushcclosure(gL, archFunc, 1);
1398 	// stack: tables, archFunc
1399 
1400 	for (hookp = roothook; hookp; hookp = hookp->next)
1401 	{
1402 		if (hookp->type != hook_NetVars)
1403 			continue;
1404 
1405 		PushHook(gL, hookp);
1406 		lua_pushvalue(gL, -2); // archFunc
1407 		if (lua_pcall(gL, 1, 0, errorhandlerindex)) {
1408 			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1409 			lua_pop(gL, 1);
1410 		}
1411 	}
1412 
1413 	lua_pop(gL, 2); // Pop archFunc and error handler
1414 	// stack: tables
1415 }
1416 
LUAh_MapThingSpawn(mobj_t * mo,mapthing_t * mthing)1417 boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing)
1418 {
1419 	hook_p hookp;
1420 	boolean hooked = false;
1421 	if (!gL || !(hooksAvailable[hook_MapThingSpawn/8] & (1<<(hook_MapThingSpawn%8))))
1422 		return false;
1423 
1424 	if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type]))
1425 		return false;
1426 
1427 	lua_settop(gL, 0);
1428 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1429 
1430 	// Look for all generic mobj map thing spawn hooks
1431 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
1432 	{
1433 		if (hookp->type != hook_MapThingSpawn)
1434 			continue;
1435 
1436 		ps_lua_mobjhooks++;
1437 		if (lua_gettop(gL) == 1)
1438 		{
1439 			LUA_PushUserdata(gL, mo, META_MOBJ);
1440 			LUA_PushUserdata(gL, mthing, META_MAPTHING);
1441 		}
1442 		PushHook(gL, hookp);
1443 		lua_pushvalue(gL, -3);
1444 		lua_pushvalue(gL, -3);
1445 		if (lua_pcall(gL, 2, 1, 1)) {
1446 			if (!hookp->error || cv_debug & DBG_LUA)
1447 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1448 			lua_pop(gL, 1);
1449 			hookp->error = true;
1450 			continue;
1451 		}
1452 		if (lua_toboolean(gL, -1))
1453 			hooked = true;
1454 		lua_pop(gL, 1);
1455 	}
1456 
1457 	for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next)
1458 	{
1459 		if (hookp->type != hook_MapThingSpawn)
1460 			continue;
1461 
1462 		ps_lua_mobjhooks++;
1463 		if (lua_gettop(gL) == 1)
1464 		{
1465 			LUA_PushUserdata(gL, mo, META_MOBJ);
1466 			LUA_PushUserdata(gL, mthing, META_MAPTHING);
1467 		}
1468 		PushHook(gL, hookp);
1469 		lua_pushvalue(gL, -3);
1470 		lua_pushvalue(gL, -3);
1471 		if (lua_pcall(gL, 2, 1, 1)) {
1472 			if (!hookp->error || cv_debug & DBG_LUA)
1473 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1474 			lua_pop(gL, 1);
1475 			hookp->error = true;
1476 			continue;
1477 		}
1478 		if (lua_toboolean(gL, -1))
1479 			hooked = true;
1480 		lua_pop(gL, 1);
1481 	}
1482 
1483 	lua_settop(gL, 0);
1484 	return hooked;
1485 }
1486 
1487 // Hook for P_PlayerAfterThink Smiles mobj-following
LUAh_FollowMobj(player_t * player,mobj_t * mobj)1488 boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj)
1489 {
1490 	hook_p hookp;
1491 	boolean hooked = false;
1492 	if (!gL || !(hooksAvailable[hook_FollowMobj/8] & (1<<(hook_FollowMobj%8))))
1493 		return 0;
1494 
1495 	if (!(mobjhooks[MT_NULL] || mobjhooks[mobj->type]))
1496 		return 0;
1497 
1498 	lua_settop(gL, 0);
1499 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1500 
1501 	// Look for all generic mobj follow item hooks
1502 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
1503 	{
1504 		if (hookp->type != hook_FollowMobj)
1505 			continue;
1506 
1507 		ps_lua_mobjhooks++;
1508 		if (lua_gettop(gL) == 1)
1509 		{
1510 			LUA_PushUserdata(gL, player, META_PLAYER);
1511 			LUA_PushUserdata(gL, mobj, META_MOBJ);
1512 		}
1513 		PushHook(gL, hookp);
1514 		lua_pushvalue(gL, -3);
1515 		lua_pushvalue(gL, -3);
1516 		if (lua_pcall(gL, 2, 1, 1)) {
1517 			if (!hookp->error || cv_debug & DBG_LUA)
1518 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1519 			lua_pop(gL, 1);
1520 			hookp->error = true;
1521 			continue;
1522 		}
1523 		if (lua_toboolean(gL, -1))
1524 			hooked = true;
1525 		lua_pop(gL, 1);
1526 	}
1527 
1528 	for (hookp = mobjhooks[mobj->type]; hookp; hookp = hookp->next)
1529 	{
1530 		if (hookp->type != hook_FollowMobj)
1531 			continue;
1532 
1533 		ps_lua_mobjhooks++;
1534 		if (lua_gettop(gL) == 1)
1535 		{
1536 			LUA_PushUserdata(gL, player, META_PLAYER);
1537 			LUA_PushUserdata(gL, mobj, META_MOBJ);
1538 		}
1539 		PushHook(gL, hookp);
1540 		lua_pushvalue(gL, -3);
1541 		lua_pushvalue(gL, -3);
1542 		if (lua_pcall(gL, 2, 1, 1)) {
1543 			if (!hookp->error || cv_debug & DBG_LUA)
1544 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1545 			lua_pop(gL, 1);
1546 			hookp->error = true;
1547 			continue;
1548 		}
1549 		if (lua_toboolean(gL, -1))
1550 			hooked = true;
1551 		lua_pop(gL, 1);
1552 	}
1553 
1554 	lua_settop(gL, 0);
1555 	return hooked;
1556 }
1557 
1558 // Hook for P_PlayerCanDamage
LUAh_PlayerCanDamage(player_t * player,mobj_t * mobj)1559 UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj)
1560 {
1561 	hook_p hookp;
1562 	UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no.
1563 	if (!gL || !(hooksAvailable[hook_PlayerCanDamage/8] & (1<<(hook_PlayerCanDamage%8))))
1564 		return 0;
1565 
1566 	lua_settop(gL, 0);
1567 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1568 
1569 	for (hookp = playerhooks; hookp; hookp = hookp->next)
1570 	{
1571 		if (hookp->type != hook_PlayerCanDamage)
1572 			continue;
1573 
1574 		ps_lua_mobjhooks++;
1575 		if (lua_gettop(gL) == 1)
1576 		{
1577 			LUA_PushUserdata(gL, player, META_PLAYER);
1578 			LUA_PushUserdata(gL, mobj, META_MOBJ);
1579 		}
1580 		PushHook(gL, hookp);
1581 		lua_pushvalue(gL, -3);
1582 		lua_pushvalue(gL, -3);
1583 		if (lua_pcall(gL, 2, 1, 1)) {
1584 			if (!hookp->error || cv_debug & DBG_LUA)
1585 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1586 			lua_pop(gL, 1);
1587 			hookp->error = true;
1588 			continue;
1589 		}
1590 		if (!lua_isnil(gL, -1))
1591 		{ // if nil, leave shouldCollide = 0.
1592 			if (lua_toboolean(gL, -1))
1593 				shouldCollide = 1; // Force yes
1594 			else
1595 				shouldCollide = 2; // Force no
1596 		}
1597 		lua_pop(gL, 1);
1598 	}
1599 
1600 	lua_settop(gL, 0);
1601 	return shouldCollide;
1602 }
1603 
LUAh_PlayerQuit(player_t * plr,kickreason_t reason)1604 void LUAh_PlayerQuit(player_t *plr, kickreason_t reason)
1605 {
1606 	hook_p hookp;
1607 	if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8))))
1608 		return;
1609 
1610 	lua_settop(gL, 0);
1611 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1612 
1613 	for (hookp = roothook; hookp; hookp = hookp->next)
1614 	{
1615 		if (hookp->type != hook_PlayerQuit)
1616 			continue;
1617 
1618 	    if (lua_gettop(gL) == 1)
1619 	    {
1620 	        LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit
1621 	        lua_pushinteger(gL, reason); // Reason for quitting
1622 	    }
1623 		PushHook(gL, hookp);
1624 		lua_pushvalue(gL, -3);
1625 		lua_pushvalue(gL, -3);
1626 		if (lua_pcall(gL, 2, 0, 1)) {
1627 			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1628 			lua_pop(gL, 1);
1629 		}
1630 	}
1631 
1632 	lua_settop(gL, 0);
1633 }
1634 
1635 // Hook for Y_Ticker
LUAh_IntermissionThinker(void)1636 void LUAh_IntermissionThinker(void)
1637 {
1638 	hook_p hookp;
1639 	if (!gL || !(hooksAvailable[hook_IntermissionThinker/8] & (1<<(hook_IntermissionThinker%8))))
1640 		return;
1641 
1642 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1643 
1644 	for (hookp = roothook; hookp; hookp = hookp->next)
1645 	{
1646 		if (hookp->type != hook_IntermissionThinker)
1647 			continue;
1648 
1649 		PushHook(gL, hookp);
1650 		if (lua_pcall(gL, 0, 0, 1)) {
1651 			if (!hookp->error || cv_debug & DBG_LUA)
1652 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1653 			lua_pop(gL, 1);
1654 			hookp->error = true;
1655 		}
1656 	}
1657 
1658 	lua_pop(gL, 1); // Pop error handler
1659 }
1660 
1661 // Hook for team switching
1662 // It's just an edit of LUAh_ViewpointSwitch.
LUAh_TeamSwitch(player_t * player,int newteam,boolean fromspectators,boolean tryingautobalance,boolean tryingscramble)1663 boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble)
1664 {
1665 	hook_p hookp;
1666 	boolean canSwitchTeam = true;
1667 	if (!gL || !(hooksAvailable[hook_TeamSwitch/8] & (1<<(hook_TeamSwitch%8))))
1668 		return true;
1669 
1670 	lua_settop(gL, 0);
1671 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1672 
1673 	for (hookp = playerhooks; hookp; hookp = hookp->next)
1674 	{
1675 		if (hookp->type != hook_TeamSwitch)
1676 			continue;
1677 
1678 		if (lua_gettop(gL) == 1)
1679 		{
1680 			LUA_PushUserdata(gL, player, META_PLAYER);
1681 			lua_pushinteger(gL, newteam);
1682 			lua_pushboolean(gL, fromspectators);
1683 			lua_pushboolean(gL, tryingautobalance);
1684 			lua_pushboolean(gL, tryingscramble);
1685 		}
1686 		PushHook(gL, hookp);
1687 		lua_pushvalue(gL, -6);
1688 		lua_pushvalue(gL, -6);
1689 		lua_pushvalue(gL, -6);
1690 		lua_pushvalue(gL, -6);
1691 		lua_pushvalue(gL, -6);
1692 		if (lua_pcall(gL, 5, 1, 1)) {
1693 			if (!hookp->error || cv_debug & DBG_LUA)
1694 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1695 			lua_pop(gL, 1);
1696 			hookp->error = true;
1697 			continue;
1698 		}
1699 		if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1))
1700 			canSwitchTeam = false; // Can't switch team
1701 		lua_pop(gL, 1);
1702 	}
1703 
1704 	lua_settop(gL, 0);
1705 	return canSwitchTeam;
1706 }
1707 
1708 // Hook for spy mode
LUAh_ViewpointSwitch(player_t * player,player_t * newdisplayplayer,boolean forced)1709 UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced)
1710 {
1711 	hook_p hookp;
1712 	UINT8 canSwitchView = 0; // 0 = default, 1 = force yes, 2 = force no.
1713 	if (!gL || !(hooksAvailable[hook_ViewpointSwitch/8] & (1<<(hook_ViewpointSwitch%8))))
1714 		return 0;
1715 
1716 	lua_settop(gL, 0);
1717 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1718 
1719 	hud_running = true; // local hook
1720 
1721 	for (hookp = playerhooks; hookp; hookp = hookp->next)
1722 	{
1723 		if (hookp->type != hook_ViewpointSwitch)
1724 			continue;
1725 
1726 		if (lua_gettop(gL) == 1)
1727 		{
1728 			LUA_PushUserdata(gL, player, META_PLAYER);
1729 			LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER);
1730 			lua_pushboolean(gL, forced);
1731 		}
1732 		PushHook(gL, hookp);
1733 		lua_pushvalue(gL, -4);
1734 		lua_pushvalue(gL, -4);
1735 		lua_pushvalue(gL, -4);
1736 		if (lua_pcall(gL, 3, 1, 1)) {
1737 			if (!hookp->error || cv_debug & DBG_LUA)
1738 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1739 			lua_pop(gL, 1);
1740 			hookp->error = true;
1741 			continue;
1742 		}
1743 		if (!lua_isnil(gL, -1))
1744 		{ // if nil, leave canSwitchView = 0.
1745 			if (lua_toboolean(gL, -1))
1746 				canSwitchView = 1; // Force viewpoint switch
1747 			else
1748 				canSwitchView = 2; // Skip viewpoint switch
1749 		}
1750 		lua_pop(gL, 1);
1751 	}
1752 
1753 	lua_settop(gL, 0);
1754 
1755 	hud_running = false;
1756 
1757 	return canSwitchView;
1758 }
1759 
1760 // Hook for MT_NAMECHECK
LUAh_SeenPlayer(player_t * player,player_t * seenfriend)1761 boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend)
1762 {
1763 	hook_p hookp;
1764 	boolean hasSeenPlayer = true;
1765 	if (!gL || !(hooksAvailable[hook_SeenPlayer/8] & (1<<(hook_SeenPlayer%8))))
1766 		return true;
1767 
1768 	lua_settop(gL, 0);
1769 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1770 
1771 	hud_running = true; // local hook
1772 
1773 	for (hookp = playerhooks; hookp; hookp = hookp->next)
1774 	{
1775 		if (hookp->type != hook_SeenPlayer)
1776 			continue;
1777 
1778 		if (lua_gettop(gL) == 1)
1779 		{
1780 			LUA_PushUserdata(gL, player, META_PLAYER);
1781 			LUA_PushUserdata(gL, seenfriend, META_PLAYER);
1782 		}
1783 		PushHook(gL, hookp);
1784 		lua_pushvalue(gL, -3);
1785 		lua_pushvalue(gL, -3);
1786 		if (lua_pcall(gL, 2, 1, 1)) {
1787 			if (!hookp->error || cv_debug & DBG_LUA)
1788 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1789 			lua_pop(gL, 1);
1790 			hookp->error = true;
1791 			continue;
1792 		}
1793 		if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1))
1794 			hasSeenPlayer = false; // Hasn't seen player
1795 		lua_pop(gL, 1);
1796 	}
1797 
1798 	lua_settop(gL, 0);
1799 
1800 	hud_running = false;
1801 
1802 	return hasSeenPlayer;
1803 }
1804 
LUAh_ShouldJingleContinue(player_t * player,const char * musname)1805 boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname)
1806 {
1807 	hook_p hookp;
1808 	boolean keepplaying = false;
1809 	if (!gL || !(hooksAvailable[hook_ShouldJingleContinue/8] & (1<<(hook_ShouldJingleContinue%8))))
1810 		return true;
1811 
1812 	lua_settop(gL, 0);
1813 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1814 
1815 	hud_running = true; // local hook
1816 
1817 	for (hookp = roothook; hookp; hookp = hookp->next)
1818 	{
1819 		if (hookp->type != hook_ShouldJingleContinue
1820 			|| (hookp->s.str && strcmp(hookp->s.str, musname)))
1821 			continue;
1822 
1823 		if (lua_gettop(gL) == 1)
1824 		{
1825 			LUA_PushUserdata(gL, player, META_PLAYER);
1826 			lua_pushstring(gL, musname);
1827 		}
1828 		PushHook(gL, hookp);
1829 		lua_pushvalue(gL, -3);
1830 		lua_pushvalue(gL, -3);
1831 		if (lua_pcall(gL, 2, 1, 1)) {
1832 			if (!hookp->error || cv_debug & DBG_LUA)
1833 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1834 			lua_pop(gL, 1);
1835 			hookp->error = true;
1836 			continue;
1837 		}
1838 		if (!lua_isnil(gL, -1) && lua_toboolean(gL, -1))
1839 			keepplaying = true; // Keep playing this boolean
1840 		lua_pop(gL, 1);
1841 	}
1842 
1843 	lua_settop(gL, 0);
1844 
1845 	hud_running = false;
1846 
1847 	return keepplaying;
1848 }
1849 
1850 // Hook for game quitting
LUAh_GameQuit(boolean quitting)1851 void LUAh_GameQuit(boolean quitting)
1852 {
1853 	hook_p hookp;
1854 	if (!gL || !(hooksAvailable[hook_GameQuit/8] & (1<<(hook_GameQuit%8))))
1855 		return;
1856 
1857 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1858 
1859 	for (hookp = roothook; hookp; hookp = hookp->next)
1860 	{
1861 		if (hookp->type != hook_GameQuit)
1862 			continue;
1863 
1864 		PushHook(gL, hookp);
1865 		lua_pushboolean(gL, quitting);
1866 		if (lua_pcall(gL, 1, 0, 1)) {
1867 			if (!hookp->error || cv_debug & DBG_LUA)
1868 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1869 			lua_pop(gL, 1);
1870 			hookp->error = true;
1871 		}
1872 	}
1873 
1874 	lua_pop(gL, 1); // Pop error handler
1875 }
1876 
1877 // Hook for building player's ticcmd struct (Ported from SRB2Kart)
1878 boolean hook_cmd_running = false;
LUAh_PlayerCmd(player_t * player,ticcmd_t * cmd)1879 boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd)
1880 {
1881 	hook_p hookp;
1882 	boolean hooked = false;
1883 	if (!gL || !(hooksAvailable[hook_PlayerCmd/8] & (1<<(hook_PlayerCmd%8))))
1884 		return false;
1885 
1886 	lua_settop(gL, 0);
1887 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1888 
1889 	hook_cmd_running = true;
1890 	for (hookp = roothook; hookp; hookp = hookp->next)
1891 	{
1892 		if (hookp->type != hook_PlayerCmd)
1893 			continue;
1894 
1895 		if (lua_gettop(gL) == 1)
1896 		{
1897 			LUA_PushUserdata(gL, player, META_PLAYER);
1898 			LUA_PushUserdata(gL, cmd, META_TICCMD);
1899 		}
1900 		PushHook(gL, hookp);
1901 		lua_pushvalue(gL, -3);
1902 		lua_pushvalue(gL, -3);
1903 		if (lua_pcall(gL, 2, 1, 1)) {
1904 			if (!hookp->error || cv_debug & DBG_LUA)
1905 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
1906 			lua_pop(gL, 1);
1907 			hookp->error = true;
1908 			continue;
1909 		}
1910 		if (lua_toboolean(gL, -1))
1911 			hooked = true;
1912 		lua_pop(gL, 1);
1913 	}
1914 
1915 	lua_settop(gL, 0);
1916 	hook_cmd_running = false;
1917 	return hooked;
1918 }
1919 
1920 // Hook for music changes
LUAh_MusicChange(const char * oldname,char * newname,UINT16 * mflags,boolean * looping,UINT32 * position,UINT32 * prefadems,UINT32 * fadeinms)1921 boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping,
1922 	UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms)
1923 {
1924 	hook_p hookp;
1925 	boolean hooked = false;
1926 
1927 	if (!gL || !(hooksAvailable[hook_MusicChange/8] & (1<<(hook_MusicChange%8))))
1928 		return false;
1929 
1930 	lua_settop(gL, 0);
1931 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1932 
1933 	for (hookp = roothook; hookp; hookp = hookp->next)
1934 		if (hookp->type == hook_MusicChange)
1935 		{
1936 			PushHook(gL, hookp);
1937 			lua_pushstring(gL, oldname);
1938 			lua_pushstring(gL, newname);
1939 			lua_pushinteger(gL, *mflags);
1940 			lua_pushboolean(gL, *looping);
1941 			lua_pushinteger(gL, *position);
1942 			lua_pushinteger(gL, *prefadems);
1943 			lua_pushinteger(gL, *fadeinms);
1944 			if (lua_pcall(gL, 7, 6, 1)) {
1945 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1));
1946 				lua_pop(gL, 1);
1947 				continue;
1948 			}
1949 
1950 			// output 1: true, false, or string musicname override
1951 			if (lua_isboolean(gL, -6) && lua_toboolean(gL, -6))
1952 				hooked = true;
1953 			else if (lua_isstring(gL, -6))
1954 				strncpy(newname, lua_tostring(gL, -6), 7);
1955 			// output 2: mflags override
1956 			if (lua_isnumber(gL, -5))
1957 				*mflags = lua_tonumber(gL, -5);
1958 			// output 3: looping override
1959 			if (lua_isboolean(gL, -4))
1960 				*looping = lua_toboolean(gL, -4);
1961 			// output 4: position override
1962 			if (lua_isnumber(gL, -3))
1963 				*position = lua_tonumber(gL, -3);
1964 			// output 5: prefadems override
1965 			if (lua_isnumber(gL, -2))
1966 				*prefadems = lua_tonumber(gL, -2);
1967 			// output 6: fadeinms override
1968 			if (lua_isnumber(gL, -1))
1969 				*fadeinms = lua_tonumber(gL, -1);
1970 
1971 			lua_pop(gL, 7);  // Pop returned values and error handler
1972 		}
1973 
1974 	lua_settop(gL, 0);
1975 	newname[6] = 0;
1976 	return hooked;
1977 }
1978 
1979 // Hook for determining player height
LUAh_PlayerHeight(player_t * player)1980 fixed_t LUAh_PlayerHeight(player_t *player)
1981 {
1982 	hook_p hookp;
1983 	fixed_t newheight = -1;
1984 	if (!gL || !(hooksAvailable[hook_PlayerHeight/8] & (1<<(hook_PlayerHeight%8))))
1985 		return newheight;
1986 
1987 	lua_settop(gL, 0);
1988 	lua_pushcfunction(gL, LUA_GetErrorMessage);
1989 
1990 	for (hookp = playerhooks; hookp; hookp = hookp->next)
1991 	{
1992 		if (hookp->type != hook_PlayerHeight)
1993 			continue;
1994 
1995 		ps_lua_mobjhooks++;
1996 		if (lua_gettop(gL) == 1)
1997 			LUA_PushUserdata(gL, player, META_PLAYER);
1998 		PushHook(gL, hookp);
1999 		lua_pushvalue(gL, -2);
2000 		if (lua_pcall(gL, 1, 1, 1)) {
2001 			if (!hookp->error || cv_debug & DBG_LUA)
2002 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
2003 			lua_pop(gL, 1);
2004 			hookp->error = true;
2005 			continue;
2006 		}
2007 		if (!lua_isnil(gL, -1))
2008 		{
2009 			fixed_t returnedheight = lua_tonumber(gL, -1);
2010 			// 0 height has... strange results, but it's not problematic like negative heights are.
2011 			// when an object's height is set to a negative number directly with lua, it's forced to 0 instead.
2012 			// here, I think it's better to ignore negatives so that they don't replace any results of previous hooks!
2013 			if (returnedheight >= 0)
2014 				newheight = returnedheight;
2015 		}
2016 		lua_pop(gL, 1);
2017 	}
2018 
2019 	lua_settop(gL, 0);
2020 	return newheight;
2021 }
2022 
2023 // Hook for determining whether players are allowed passage through spin gaps
LUAh_PlayerCanEnterSpinGaps(player_t * player)2024 UINT8 LUAh_PlayerCanEnterSpinGaps(player_t *player)
2025 {
2026 	hook_p hookp;
2027 	UINT8 canEnter = 0; // 0 = default, 1 = force yes, 2 = force no.
2028 	if (!gL || !(hooksAvailable[hook_PlayerCanEnterSpinGaps/8] & (1<<(hook_PlayerCanEnterSpinGaps%8))))
2029 		return 0;
2030 
2031 	lua_settop(gL, 0);
2032 	lua_pushcfunction(gL, LUA_GetErrorMessage);
2033 
2034 	for (hookp = playerhooks; hookp; hookp = hookp->next)
2035 	{
2036 		if (hookp->type != hook_PlayerCanEnterSpinGaps)
2037 			continue;
2038 
2039 		ps_lua_mobjhooks++;
2040 		if (lua_gettop(gL) == 1)
2041 			LUA_PushUserdata(gL, player, META_PLAYER);
2042 		PushHook(gL, hookp);
2043 		lua_pushvalue(gL, -2);
2044 		if (lua_pcall(gL, 1, 1, 1)) {
2045 			if (!hookp->error || cv_debug & DBG_LUA)
2046 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
2047 			lua_pop(gL, 1);
2048 			hookp->error = true;
2049 			continue;
2050 		}
2051 		if (!lua_isnil(gL, -1))
2052 		{ // if nil, leave canEnter = 0.
2053 			if (lua_toboolean(gL, -1))
2054 				canEnter = 1; // Force yes
2055 			else
2056 				canEnter = 2; // Force no
2057 		}
2058 		lua_pop(gL, 1);
2059 	}
2060 
2061 	lua_settop(gL, 0);
2062 	return canEnter;
2063 }
2064