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