1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 2014-2016 by John "JTE" Muniz.
4 // Copyright (C) 2014-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_hudlib.c
11 /// \brief custom HUD rendering library for Lua scripting
12
13 #include "doomdef.h"
14 #include "fastcmp.h"
15 #include "r_defs.h"
16 #include "r_local.h"
17 #include "st_stuff.h" // hudinfo[]
18 #include "g_game.h"
19 #include "i_video.h" // rendermode
20 #include "p_local.h" // camera_t
21 #include "screen.h" // screen width/height
22 #include "m_random.h" // m_random
23 #include "v_video.h"
24 #include "w_wad.h"
25 #include "z_zone.h"
26
27 #include "lua_script.h"
28 #include "lua_libs.h"
29 #include "lua_hud.h"
30
31 #define HUDONLY if (!hud_running) return luaL_error(L, "HUD rendering code should not be called outside of rendering hooks!");
32
33 boolean hud_running = false;
34 static UINT8 hud_enabled[(hud_MAX/8)+1];
35
36 static UINT8 hudAvailable; // hud hooks field
37
38 // must match enum hud in lua_hud.h
39 static const char *const hud_disable_options[] = {
40 "stagetitle",
41 "textspectator",
42 "crosshair",
43
44 "score",
45 "time",
46 "rings",
47 "lives",
48
49 "weaponrings",
50 "powerstones",
51 "teamscores",
52
53 "nightslink",
54 "nightsdrill",
55 "nightsrings",
56 "nightsscore",
57 "nightstime",
58 "nightsrecords",
59
60 "rankings",
61 "coopemeralds",
62 "tokens",
63 "tabemblems",
64
65 "intermissiontally",
66 "intermissionmessages",
67 NULL};
68
69 enum hudinfo {
70 hudinfo_x = 0,
71 hudinfo_y,
72 hudinfo_f
73 };
74
75 static const char *const hudinfo_opt[] = {
76 "x",
77 "y",
78 "f",
79 NULL};
80
81 enum patch {
82 patch_valid = 0,
83 patch_width,
84 patch_height,
85 patch_leftoffset,
86 patch_topoffset
87 };
88 static const char *const patch_opt[] = {
89 "valid",
90 "width",
91 "height",
92 "leftoffset",
93 "topoffset",
94 NULL};
95
96 enum hudhook {
97 hudhook_game = 0,
98 hudhook_scores,
99 hudhook_intermission,
100 hudhook_title,
101 hudhook_titlecard
102 };
103 static const char *const hudhook_opt[] = {
104 "game",
105 "scores",
106 "intermission",
107 "title",
108 "titlecard",
109 NULL};
110
111 // alignment types for v.drawString
112 enum align {
113 align_left = 0,
114 align_center,
115 align_right,
116 align_fixed,
117 align_fixedcenter,
118 align_fixedright,
119 align_small,
120 align_smallfixed,
121 align_smallfixedcenter,
122 align_smallfixedright,
123 align_smallcenter,
124 align_smallright,
125 align_smallthin,
126 align_smallthincenter,
127 align_smallthinright,
128 align_smallthinfixed,
129 align_smallthinfixedcenter,
130 align_smallthinfixedright,
131 align_thin,
132 align_thinfixed,
133 align_thinfixedcenter,
134 align_thinfixedright,
135 align_thincenter,
136 align_thinright
137 };
138 static const char *const align_opt[] = {
139 "left",
140 "center",
141 "right",
142 "fixed",
143 "fixed-center",
144 "fixed-right",
145 "small",
146 "small-fixed",
147 "small-fixed-center",
148 "small-fixed-right",
149 "small-center",
150 "small-right",
151 "small-thin",
152 "small-thin-center",
153 "small-thin-right",
154 "small-thin-fixed",
155 "small-thin-fixed-center",
156 "small-thin-fixed-right",
157 "thin",
158 "thin-fixed",
159 "thin-fixed-center",
160 "thin-fixed-right",
161 "thin-center",
162 "thin-right",
163 NULL};
164
165 // width types for v.stringWidth
166 enum widtht {
167 widtht_normal = 0,
168 widtht_small,
169 widtht_thin
170 };
171 static const char *const widtht_opt[] = {
172 "normal",
173 "small",
174 "thin",
175 NULL};
176
177 enum cameraf {
178 camera_chase = 0,
179 camera_aiming,
180 camera_x,
181 camera_y,
182 camera_z,
183 camera_angle,
184 camera_subsector,
185 camera_floorz,
186 camera_ceilingz,
187 camera_radius,
188 camera_height,
189 camera_momx,
190 camera_momy,
191 camera_momz
192 };
193
194
195 static const char *const camera_opt[] = {
196 "chase",
197 "aiming",
198 "x",
199 "y",
200 "z",
201 "angle",
202 "subsector",
203 "floorz",
204 "ceilingz",
205 "radius",
206 "height",
207 "momx",
208 "momy",
209 "momz",
210 NULL};
211
lib_getHudInfo(lua_State * L)212 static int lib_getHudInfo(lua_State *L)
213 {
214 UINT32 i;
215 lua_remove(L, 1);
216
217 i = luaL_checkinteger(L, 1);
218 if (i >= NUMHUDITEMS)
219 return luaL_error(L, "hudinfo[] index %d out of range (0 - %d)", i, NUMHUDITEMS-1);
220 LUA_PushUserdata(L, &hudinfo[i], META_HUDINFO);
221 return 1;
222 }
223
lib_hudinfolen(lua_State * L)224 static int lib_hudinfolen(lua_State *L)
225 {
226 lua_pushinteger(L, NUMHUDITEMS);
227 return 1;
228 }
229
hudinfo_get(lua_State * L)230 static int hudinfo_get(lua_State *L)
231 {
232 hudinfo_t *info = *((hudinfo_t **)luaL_checkudata(L, 1, META_HUDINFO));
233 enum hudinfo field = luaL_checkoption(L, 2, hudinfo_opt[0], hudinfo_opt);
234 I_Assert(info != NULL); // huditems are always valid
235
236 switch(field)
237 {
238 case hudinfo_x:
239 lua_pushinteger(L, info->x);
240 break;
241 case hudinfo_y:
242 lua_pushinteger(L, info->y);
243 break;
244 case hudinfo_f:
245 lua_pushinteger(L, info->f);
246 break;
247 }
248 return 1;
249 }
250
hudinfo_set(lua_State * L)251 static int hudinfo_set(lua_State *L)
252 {
253 hudinfo_t *info = *((hudinfo_t **)luaL_checkudata(L, 1, META_HUDINFO));
254 enum hudinfo field = luaL_checkoption(L, 2, hudinfo_opt[0], hudinfo_opt);
255 I_Assert(info != NULL);
256
257 switch(field)
258 {
259 case hudinfo_x:
260 info->x = (INT32)luaL_checkinteger(L, 3);
261 break;
262 case hudinfo_y:
263 info->y = (INT32)luaL_checkinteger(L, 3);
264 break;
265 case hudinfo_f:
266 info->f = (INT32)luaL_checkinteger(L, 3);
267 break;
268 }
269 return 0;
270 }
271
hudinfo_num(lua_State * L)272 static int hudinfo_num(lua_State *L)
273 {
274 hudinfo_t *info = *((hudinfo_t **)luaL_checkudata(L, 1, META_HUDINFO));
275 lua_pushinteger(L, info-hudinfo);
276 return 1;
277 }
278
colormap_get(lua_State * L)279 static int colormap_get(lua_State *L)
280 {
281 const UINT8 *colormap = *((UINT8 **)luaL_checkudata(L, 1, META_COLORMAP));
282 UINT32 i = luaL_checkinteger(L, 2);
283 if (i >= 256)
284 return luaL_error(L, "colormap index %d out of range (0 - %d)", i, 255);
285 lua_pushinteger(L, colormap[i]);
286 return 1;
287 }
288
patch_get(lua_State * L)289 static int patch_get(lua_State *L)
290 {
291 patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH));
292 enum patch field = luaL_checkoption(L, 2, NULL, patch_opt);
293
294 // patches are invalidated when switching renderers
295 if (!patch) {
296 if (field == patch_valid) {
297 lua_pushboolean(L, 0);
298 return 1;
299 }
300 return LUA_ErrInvalid(L, "patch_t");
301 }
302
303 switch (field)
304 {
305 case patch_valid:
306 lua_pushboolean(L, patch != NULL);
307 break;
308 case patch_width:
309 lua_pushinteger(L, patch->width);
310 break;
311 case patch_height:
312 lua_pushinteger(L, patch->height);
313 break;
314 case patch_leftoffset:
315 lua_pushinteger(L, patch->leftoffset);
316 break;
317 case patch_topoffset:
318 lua_pushinteger(L, patch->topoffset);
319 break;
320 }
321 return 1;
322 }
323
patch_set(lua_State * L)324 static int patch_set(lua_State *L)
325 {
326 return luaL_error(L, LUA_QL("patch_t") " struct cannot be edited by Lua.");
327 }
328
camera_get(lua_State * L)329 static int camera_get(lua_State *L)
330 {
331 camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA));
332 enum cameraf field = luaL_checkoption(L, 2, NULL, camera_opt);
333
334 // cameras should always be valid unless I'm a nutter
335 I_Assert(cam != NULL);
336
337 switch (field)
338 {
339 case camera_chase:
340 lua_pushboolean(L, cam->chase);
341 break;
342 case camera_aiming:
343 lua_pushinteger(L, cam->aiming);
344 break;
345 case camera_x:
346 lua_pushinteger(L, cam->x);
347 break;
348 case camera_y:
349 lua_pushinteger(L, cam->y);
350 break;
351 case camera_z:
352 lua_pushinteger(L, cam->z);
353 break;
354 case camera_angle:
355 lua_pushinteger(L, cam->angle);
356 break;
357 case camera_subsector:
358 LUA_PushUserdata(L, cam->subsector, META_SUBSECTOR);
359 break;
360 case camera_floorz:
361 lua_pushinteger(L, cam->floorz);
362 break;
363 case camera_ceilingz:
364 lua_pushinteger(L, cam->ceilingz);
365 break;
366 case camera_radius:
367 lua_pushinteger(L, cam->radius);
368 break;
369 case camera_height:
370 lua_pushinteger(L, cam->height);
371 break;
372 case camera_momx:
373 lua_pushinteger(L, cam->momx);
374 break;
375 case camera_momy:
376 lua_pushinteger(L, cam->momy);
377 break;
378 case camera_momz:
379 lua_pushinteger(L, cam->momz);
380 break;
381 }
382 return 1;
383 }
384
385 //
386 // lib_draw
387 //
388
libd_patchExists(lua_State * L)389 static int libd_patchExists(lua_State *L)
390 {
391 HUDONLY
392 lua_pushboolean(L, W_LumpExists(luaL_checkstring(L, 1)));
393 return 1;
394 }
395
libd_cachePatch(lua_State * L)396 static int libd_cachePatch(lua_State *L)
397 {
398 HUDONLY
399 LUA_PushUserdata(L, W_CachePatchLongName(luaL_checkstring(L, 1), PU_PATCH), META_PATCH);
400 return 1;
401 }
402
403 // v.getSpritePatch(sprite, [frame, [angle, [rollangle]]])
libd_getSpritePatch(lua_State * L)404 static int libd_getSpritePatch(lua_State *L)
405 {
406 UINT32 i; // sprite prefix
407 UINT32 frame = 0; // 'A'
408 UINT8 angle = 0;
409 spritedef_t *sprdef;
410 spriteframe_t *sprframe;
411 HUDONLY
412
413 if (lua_isnumber(L, 1)) // sprite number given, e.g. SPR_THOK
414 {
415 i = lua_tonumber(L, 1);
416 if (i >= NUMSPRITES)
417 return 0;
418 }
419 else if (lua_isstring(L, 1)) // sprite prefix name given, e.g. "THOK"
420 {
421 const char *name = lua_tostring(L, 1);
422 for (i = 0; i < NUMSPRITES; i++)
423 if (fastcmp(name, sprnames[i]))
424 break;
425 if (i >= NUMSPRITES)
426 return 0;
427 }
428 else
429 return 0;
430
431 if (i == SPR_PLAY) // Use getSprite2Patch instead!
432 return 0;
433
434 sprdef = &sprites[i];
435
436 // set frame number
437 frame = luaL_optinteger(L, 2, 0);
438 frame &= FF_FRAMEMASK; // ignore any bits that are not the actual frame, just in case
439 if (frame >= sprdef->numframes)
440 return 0;
441 // set angle number
442 sprframe = &sprdef->spriteframes[frame];
443 angle = luaL_optinteger(L, 3, 1);
444
445 // convert WAD editor angle numbers (1-8) to internal angle numbers (0-7)
446 // keep 0 the same since we'll make it default to angle 1 (which is internally 0)
447 // in case somebody didn't know that angle 0 really just maps all 8/16 angles to the same patch
448 if (angle != 0)
449 angle--;
450
451 if (angle >= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range?
452 return 0;
453
454 #ifdef ROTSPRITE
455 if (lua_isnumber(L, 4))
456 {
457 // rotsprite?????
458 angle_t rollangle = luaL_checkangle(L, 4);
459 INT32 rot = R_GetRollAngle(rollangle);
460
461 if (rot) {
462 patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), true, &spriteinfo[i], rot);
463 LUA_PushUserdata(L, rotsprite, META_PATCH);
464 lua_pushboolean(L, false);
465 lua_pushboolean(L, true);
466 return 3;
467 }
468 }
469 #endif
470
471 // push both the patch and it's "flip" value
472 LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_SPRITE), META_PATCH);
473 lua_pushboolean(L, (sprframe->flip & (1<<angle)) != 0);
474 return 2;
475 }
476
477 // v.getSprite2Patch(skin, sprite, [super?,] [frame, [angle, [rollangle]]])
libd_getSprite2Patch(lua_State * L)478 static int libd_getSprite2Patch(lua_State *L)
479 {
480 INT32 i; // skin number
481 playersprite_t j; // sprite2 prefix
482 UINT32 frame = 0; // 'A'
483 UINT8 angle = 0;
484 spritedef_t *sprdef;
485 spriteframe_t *sprframe;
486 boolean super = false; // add FF_SPR2SUPER to sprite2 if true
487 HUDONLY
488
489 // get skin first!
490 if (lua_isnumber(L, 1)) // find skin by number
491 {
492 i = lua_tonumber(L, 1);
493 if (i < 0 || i >= MAXSKINS)
494 return luaL_error(L, "skin number %d out of range (0 - %d)", i, MAXSKINS-1);
495 if (i >= numskins)
496 return 0;
497 }
498 else // find skin by name
499 {
500 const char *name = luaL_checkstring(L, 1);
501 for (i = 0; i < numskins; i++)
502 if (fastcmp(skins[i].name, name))
503 break;
504 if (i >= numskins)
505 return 0;
506 }
507
508 lua_remove(L, 1); // remove skin now
509
510 if (lua_isnumber(L, 1)) // sprite number given, e.g. SPR2_STND
511 {
512 j = lua_tonumber(L, 1);
513 if (j & FF_SPR2SUPER) // e.g. SPR2_STND|FF_SPR2SUPER
514 {
515 super = true;
516 j &= ~FF_SPR2SUPER; // remove flag so the next check doesn't fail
517 }
518 if (j >= free_spr2)
519 return 0;
520 }
521 else if (lua_isstring(L, 1)) // sprite prefix name given, e.g. "STND"
522 {
523 const char *name = lua_tostring(L, 1);
524 for (j = 0; j < free_spr2; j++)
525 if (fastcmp(name, spr2names[j]))
526 break;
527 // if you want super flags you'll have to use the optional boolean following this
528 if (j >= free_spr2)
529 return 0;
530 }
531 else
532 return 0;
533
534 if (lua_isboolean(L, 2)) // optional boolean for superness
535 {
536 super = lua_toboolean(L, 2); // note: this can override FF_SPR2SUPER from sprite number
537 lua_remove(L, 2); // remove
538 }
539 // if it's not boolean then just assume it's the frame number
540
541 if (super)
542 j |= FF_SPR2SUPER;
543
544 j = P_GetSkinSprite2(&skins[i], j, NULL); // feed skin and current sprite2 through to change sprite2 used if necessary
545
546 sprdef = &skins[i].sprites[j];
547
548 // set frame number
549 frame = luaL_optinteger(L, 2, 0);
550 frame &= FF_FRAMEMASK; // ignore any bits that are not the actual frame, just in case
551 if (frame >= sprdef->numframes)
552 return 0;
553 // set angle number
554 sprframe = &sprdef->spriteframes[frame];
555 angle = luaL_optinteger(L, 3, 1);
556
557 // convert WAD editor angle numbers (1-8) to internal angle numbers (0-7)
558 // keep 0 the same since we'll make it default to angle 1 (which is internally 0)
559 // in case somebody didn't know that angle 0 really just maps all 8/16 angles to the same patch
560 if (angle != 0)
561 angle--;
562
563 if (angle >= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range?
564 return 0;
565
566 #ifdef ROTSPRITE
567 if (lua_isnumber(L, 4))
568 {
569 // rotsprite?????
570 angle_t rollangle = luaL_checkangle(L, 4);
571 INT32 rot = R_GetRollAngle(rollangle);
572
573 if (rot) {
574 patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), true, &skins[i].sprinfo[j], rot);
575 LUA_PushUserdata(L, rotsprite, META_PATCH);
576 lua_pushboolean(L, false);
577 lua_pushboolean(L, true);
578 return 3;
579 }
580 }
581 #endif
582
583 // push both the patch and it's "flip" value
584 LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_SPRITE), META_PATCH);
585 lua_pushboolean(L, (sprframe->flip & (1<<angle)) != 0);
586 return 2;
587 }
588
libd_draw(lua_State * L)589 static int libd_draw(lua_State *L)
590 {
591 INT32 x, y, flags;
592 patch_t *patch;
593 const UINT8 *colormap = NULL;
594
595 HUDONLY
596 x = luaL_checkinteger(L, 1);
597 y = luaL_checkinteger(L, 2);
598 patch = *((patch_t **)luaL_checkudata(L, 3, META_PATCH));
599 if (!patch)
600 return LUA_ErrInvalid(L, "patch_t");
601 flags = luaL_optinteger(L, 4, 0);
602 if (!lua_isnoneornil(L, 5))
603 colormap = *((UINT8 **)luaL_checkudata(L, 5, META_COLORMAP));
604
605 flags &= ~V_PARAMMASK; // Don't let crashes happen.
606
607 V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT, flags, patch, colormap);
608 return 0;
609 }
610
libd_drawScaled(lua_State * L)611 static int libd_drawScaled(lua_State *L)
612 {
613 fixed_t x, y, scale;
614 INT32 flags;
615 patch_t *patch;
616 const UINT8 *colormap = NULL;
617
618 HUDONLY
619 x = luaL_checkinteger(L, 1);
620 y = luaL_checkinteger(L, 2);
621 scale = luaL_checkinteger(L, 3);
622 if (scale < 0)
623 return luaL_error(L, "negative scale");
624 patch = *((patch_t **)luaL_checkudata(L, 4, META_PATCH));
625 if (!patch)
626 return LUA_ErrInvalid(L, "patch_t");
627 flags = luaL_optinteger(L, 5, 0);
628 if (!lua_isnoneornil(L, 6))
629 colormap = *((UINT8 **)luaL_checkudata(L, 6, META_COLORMAP));
630
631 flags &= ~V_PARAMMASK; // Don't let crashes happen.
632
633 V_DrawFixedPatch(x, y, scale, flags, patch, colormap);
634 return 0;
635 }
636
libd_drawStretched(lua_State * L)637 static int libd_drawStretched(lua_State *L)
638 {
639 fixed_t x, y, hscale, vscale;
640 INT32 flags;
641 patch_t *patch;
642 const UINT8 *colormap = NULL;
643
644 HUDONLY
645 x = luaL_checkinteger(L, 1);
646 y = luaL_checkinteger(L, 2);
647 hscale = luaL_checkinteger(L, 3);
648 if (hscale < 0)
649 return luaL_error(L, "negative horizontal scale");
650 vscale = luaL_checkinteger(L, 4);
651 if (vscale < 0)
652 return luaL_error(L, "negative vertical scale");
653 patch = *((patch_t **)luaL_checkudata(L, 5, META_PATCH));
654 flags = luaL_optinteger(L, 6, 0);
655 if (!lua_isnoneornil(L, 7))
656 colormap = *((UINT8 **)luaL_checkudata(L, 7, META_COLORMAP));
657
658 flags &= ~V_PARAMMASK; // Don't let crashes happen.
659
660 V_DrawStretchyFixedPatch(x, y, hscale, vscale, flags, patch, colormap);
661 return 0;
662 }
663
libd_drawNum(lua_State * L)664 static int libd_drawNum(lua_State *L)
665 {
666 INT32 x, y, flags, num;
667 HUDONLY
668 x = luaL_checkinteger(L, 1);
669 y = luaL_checkinteger(L, 2);
670 num = luaL_checkinteger(L, 3);
671 flags = luaL_optinteger(L, 4, 0);
672 flags &= ~V_PARAMMASK; // Don't let crashes happen.
673
674 V_DrawTallNum(x, y, flags, num);
675 return 0;
676 }
677
libd_drawPaddedNum(lua_State * L)678 static int libd_drawPaddedNum(lua_State *L)
679 {
680 INT32 x, y, flags, num, digits;
681 HUDONLY
682 x = luaL_checkinteger(L, 1);
683 y = luaL_checkinteger(L, 2);
684 num = labs(luaL_checkinteger(L, 3));
685 digits = luaL_optinteger(L, 4, 2);
686 flags = luaL_optinteger(L, 5, 0);
687 flags &= ~V_PARAMMASK; // Don't let crashes happen.
688
689 V_DrawPaddedTallNum(x, y, flags, num, digits);
690 return 0;
691 }
692
libd_drawFill(lua_State * L)693 static int libd_drawFill(lua_State *L)
694 {
695 INT32 x = luaL_optinteger(L, 1, 0);
696 INT32 y = luaL_optinteger(L, 2, 0);
697 INT32 w = luaL_optinteger(L, 3, BASEVIDWIDTH);
698 INT32 h = luaL_optinteger(L, 4, BASEVIDHEIGHT);
699 INT32 c = luaL_optinteger(L, 5, 31);
700
701 HUDONLY
702 V_DrawFill(x, y, w, h, c);
703 return 0;
704 }
705
libd_drawString(lua_State * L)706 static int libd_drawString(lua_State *L)
707 {
708 fixed_t x = luaL_checkinteger(L, 1);
709 fixed_t y = luaL_checkinteger(L, 2);
710 const char *str = luaL_checkstring(L, 3);
711 INT32 flags = luaL_optinteger(L, 4, V_ALLOWLOWERCASE);
712 enum align align = luaL_checkoption(L, 5, "left", align_opt);
713
714 flags &= ~V_PARAMMASK; // Don't let crashes happen.
715
716 HUDONLY
717 switch(align)
718 {
719 // hu_font
720 case align_left:
721 V_DrawString(x, y, flags, str);
722 break;
723 case align_center:
724 V_DrawCenteredString(x, y, flags, str);
725 break;
726 case align_right:
727 V_DrawRightAlignedString(x, y, flags, str);
728 break;
729 case align_fixed:
730 V_DrawStringAtFixed(x, y, flags, str);
731 break;
732 case align_fixedcenter:
733 V_DrawCenteredStringAtFixed(x, y, flags, str);
734 break;
735 case align_fixedright:
736 V_DrawRightAlignedStringAtFixed(x, y, flags, str);
737 break;
738 // hu_font, 0.5x scale
739 case align_small:
740 V_DrawSmallString(x, y, flags, str);
741 break;
742 case align_smallfixed:
743 V_DrawSmallStringAtFixed(x, y, flags, str);
744 break;
745 case align_smallfixedcenter:
746 V_DrawCenteredSmallStringAtFixed(x, y, flags, str);
747 break;
748 case align_smallfixedright:
749 V_DrawRightAlignedSmallStringAtFixed(x, y, flags, str);
750 break;
751 case align_smallcenter:
752 V_DrawCenteredSmallString(x, y, flags, str);
753 break;
754 case align_smallright:
755 V_DrawRightAlignedSmallString(x, y, flags, str);
756 break;
757 case align_smallthin:
758 V_DrawSmallThinString(x, y, flags, str);
759 break;
760 case align_smallthincenter:
761 V_DrawCenteredSmallThinString(x, y, flags, str);
762 break;
763 case align_smallthinright:
764 V_DrawRightAlignedSmallThinString(x, y, flags, str);
765 break;
766 case align_smallthinfixed:
767 V_DrawSmallThinStringAtFixed(x, y, flags, str);
768 break;
769 case align_smallthinfixedcenter:
770 V_DrawCenteredSmallThinStringAtFixed(x, y, flags, str);
771 break;
772 case align_smallthinfixedright:
773 V_DrawRightAlignedSmallThinStringAtFixed(x, y, flags, str);
774 break;
775 // tny_font
776 case align_thin:
777 V_DrawThinString(x, y, flags, str);
778 break;
779 case align_thincenter:
780 V_DrawCenteredThinString(x, y, flags, str);
781 break;
782 case align_thinright:
783 V_DrawRightAlignedThinString(x, y, flags, str);
784 break;
785 case align_thinfixed:
786 V_DrawThinStringAtFixed(x, y, flags, str);
787 break;
788 case align_thinfixedcenter:
789 V_DrawCenteredThinStringAtFixed(x, y, flags, str);
790 break;
791 case align_thinfixedright:
792 V_DrawRightAlignedThinStringAtFixed(x, y, flags, str);
793 break;
794 }
795 return 0;
796 }
797
libd_drawNameTag(lua_State * L)798 static int libd_drawNameTag(lua_State *L)
799 {
800 INT32 x;
801 INT32 y;
802 const char *str;
803 INT32 flags;
804 UINT16 basecolor;
805 UINT16 outlinecolor;
806 UINT8 *basecolormap = NULL;
807 UINT8 *outlinecolormap = NULL;
808
809 HUDONLY
810
811 x = luaL_checkinteger(L, 1);
812 y = luaL_checkinteger(L, 2);
813 str = luaL_checkstring(L, 3);
814 flags = luaL_optinteger(L, 4, 0);
815 basecolor = luaL_optinteger(L, 5, SKINCOLOR_BLUE);
816 outlinecolor = luaL_optinteger(L, 6, SKINCOLOR_ORANGE);
817 if (basecolor != SKINCOLOR_NONE)
818 basecolormap = R_GetTranslationColormap(TC_DEFAULT, basecolor, GTC_CACHE);
819 if (outlinecolor != SKINCOLOR_NONE)
820 outlinecolormap = R_GetTranslationColormap(TC_DEFAULT, outlinecolor, GTC_CACHE);
821
822 flags &= ~V_PARAMMASK; // Don't let crashes happen.
823 V_DrawNameTag(x, y, flags, FRACUNIT, basecolormap, outlinecolormap, str);
824 return 0;
825 }
826
libd_drawScaledNameTag(lua_State * L)827 static int libd_drawScaledNameTag(lua_State *L)
828 {
829 fixed_t x;
830 fixed_t y;
831 const char *str;
832 INT32 flags;
833 fixed_t scale;
834 UINT16 basecolor;
835 UINT16 outlinecolor;
836 UINT8 *basecolormap = NULL;
837 UINT8 *outlinecolormap = NULL;
838
839 HUDONLY
840
841 x = luaL_checkfixed(L, 1);
842 y = luaL_checkfixed(L, 2);
843 str = luaL_checkstring(L, 3);
844 flags = luaL_optinteger(L, 4, 0);
845 scale = luaL_optinteger(L, 5, FRACUNIT);
846 if (scale < 0)
847 return luaL_error(L, "negative scale");
848 basecolor = luaL_optinteger(L, 6, SKINCOLOR_BLUE);
849 outlinecolor = luaL_optinteger(L, 7, SKINCOLOR_ORANGE);
850 if (basecolor != SKINCOLOR_NONE)
851 basecolormap = R_GetTranslationColormap(TC_DEFAULT, basecolor, GTC_CACHE);
852 if (outlinecolor != SKINCOLOR_NONE)
853 outlinecolormap = R_GetTranslationColormap(TC_DEFAULT, outlinecolor, GTC_CACHE);
854
855 flags &= ~V_PARAMMASK; // Don't let crashes happen.
856 V_DrawNameTag(FixedInt(x), FixedInt(y), flags, scale, basecolormap, outlinecolormap, str);
857 return 0;
858 }
859
libd_stringWidth(lua_State * L)860 static int libd_stringWidth(lua_State *L)
861 {
862 const char *str = luaL_checkstring(L, 1);
863 INT32 flags = luaL_optinteger(L, 2, V_ALLOWLOWERCASE);
864 enum widtht widtht = luaL_checkoption(L, 3, "normal", widtht_opt);
865
866 HUDONLY
867 switch(widtht)
868 {
869 case widtht_normal: // hu_font
870 lua_pushinteger(L, V_StringWidth(str, flags));
871 break;
872 case widtht_small: // hu_font, 0.5x scale
873 lua_pushinteger(L, V_SmallStringWidth(str, flags));
874 break;
875 case widtht_thin: // tny_font
876 lua_pushinteger(L, V_ThinStringWidth(str, flags));
877 break;
878 }
879 return 1;
880 }
881
libd_nameTagWidth(lua_State * L)882 static int libd_nameTagWidth(lua_State *L)
883 {
884 HUDONLY
885 lua_pushinteger(L, V_NameTagWidth(luaL_checkstring(L, 1)));
886 return 1;
887 }
888
libd_getColormap(lua_State * L)889 static int libd_getColormap(lua_State *L)
890 {
891 INT32 skinnum = TC_DEFAULT;
892 skincolornum_t color = luaL_optinteger(L, 2, 0);
893 UINT8* colormap = NULL;
894 HUDONLY
895 if (lua_isnoneornil(L, 1))
896 ; // defaults to TC_DEFAULT
897 else if (lua_type(L, 1) == LUA_TNUMBER) // skin number
898 {
899 skinnum = (INT32)luaL_checkinteger(L, 1);
900 if (skinnum >= MAXSKINS)
901 return luaL_error(L, "skin number %d is out of range (>%d)", skinnum, MAXSKINS-1);
902 else if (skinnum < 0 && skinnum > TC_DEFAULT)
903 return luaL_error(L, "translation colormap index is out of range");
904 }
905 else // skin name
906 {
907 const char *skinname = luaL_checkstring(L, 1);
908 INT32 i = R_SkinAvailable(skinname);
909 if (i != -1) // if -1, just default to TC_DEFAULT as above
910 skinnum = i;
911 }
912
913 // all was successful above, now we generate the colormap at last!
914
915 colormap = R_GetTranslationColormap(skinnum, color, GTC_CACHE);
916 LUA_PushUserdata(L, colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use!
917 return 1;
918 }
919
libd_getStringColormap(lua_State * L)920 static int libd_getStringColormap(lua_State *L)
921 {
922 INT32 flags = luaL_checkinteger(L, 1);
923 UINT8* colormap = NULL;
924 HUDONLY
925 colormap = V_GetStringColormap(flags & V_CHARCOLORMASK);
926 if (colormap) {
927 LUA_PushUserdata(L, colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use!
928 return 1;
929 }
930 return 0;
931 }
932
libd_fadeScreen(lua_State * L)933 static int libd_fadeScreen(lua_State *L)
934 {
935 UINT16 color = luaL_checkinteger(L, 1);
936 UINT8 strength = luaL_checkinteger(L, 2);
937 const UINT8 maxstrength = ((color & 0xFF00) ? 32 : 10);
938
939 HUDONLY
940
941 if (!strength)
942 return 0;
943
944 if (strength > maxstrength)
945 return luaL_error(L, "%s fade strength %d out of range (0 - %d)", ((color & 0xFF00) ? "COLORMAP" : "TRANSMAP"), strength, maxstrength);
946
947 if (strength == maxstrength) // Allow as a shortcut for drawfill...
948 {
949 V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color));
950 return 0;
951 }
952
953 V_DrawFadeScreen(color, strength);
954 return 0;
955 }
956
libd_width(lua_State * L)957 static int libd_width(lua_State *L)
958 {
959 HUDONLY
960 lua_pushinteger(L, vid.width); // push screen width
961 return 1;
962 }
963
libd_height(lua_State * L)964 static int libd_height(lua_State *L)
965 {
966 HUDONLY
967 lua_pushinteger(L, vid.height); // push screen height
968 return 1;
969 }
970
libd_dupx(lua_State * L)971 static int libd_dupx(lua_State *L)
972 {
973 HUDONLY
974 lua_pushinteger(L, vid.dupx); // push integral scale (patch scale)
975 lua_pushfixed(L, vid.fdupx); // push fixed point scale (position scale)
976 return 2;
977 }
978
libd_dupy(lua_State * L)979 static int libd_dupy(lua_State *L)
980 {
981 HUDONLY
982 lua_pushinteger(L, vid.dupy); // push integral scale (patch scale)
983 lua_pushfixed(L, vid.fdupy); // push fixed point scale (position scale)
984 return 2;
985 }
986
libd_renderer(lua_State * L)987 static int libd_renderer(lua_State *L)
988 {
989 HUDONLY
990 switch (rendermode) {
991 case render_opengl: lua_pushliteral(L, "opengl"); break; // OpenGL renderer
992 case render_soft: lua_pushliteral(L, "software"); break; // Software renderer
993 default: lua_pushliteral(L, "none"); break; // render_none (for dedicated), in case there's any reason this should be run
994 }
995 return 1;
996 }
997
998 // M_RANDOM
999 //////////////
1000
libd_RandomFixed(lua_State * L)1001 static int libd_RandomFixed(lua_State *L)
1002 {
1003 HUDONLY
1004 lua_pushfixed(L, M_RandomFixed());
1005 return 1;
1006 }
1007
libd_RandomByte(lua_State * L)1008 static int libd_RandomByte(lua_State *L)
1009 {
1010 HUDONLY
1011 lua_pushinteger(L, M_RandomByte());
1012 return 1;
1013 }
1014
libd_RandomKey(lua_State * L)1015 static int libd_RandomKey(lua_State *L)
1016 {
1017 INT32 a = (INT32)luaL_checkinteger(L, 1);
1018
1019 HUDONLY
1020 if (a > 65536)
1021 LUA_UsageWarning(L, "v.RandomKey: range > 65536 is undefined behavior");
1022 lua_pushinteger(L, M_RandomKey(a));
1023 return 1;
1024 }
1025
libd_RandomRange(lua_State * L)1026 static int libd_RandomRange(lua_State *L)
1027 {
1028 INT32 a = (INT32)luaL_checkinteger(L, 1);
1029 INT32 b = (INT32)luaL_checkinteger(L, 2);
1030
1031 HUDONLY
1032 if (b < a) {
1033 INT32 c = a;
1034 a = b;
1035 b = c;
1036 }
1037 if ((b-a+1) > 65536)
1038 LUA_UsageWarning(L, "v.RandomRange: range > 65536 is undefined behavior");
1039 lua_pushinteger(L, M_RandomRange(a, b));
1040 return 1;
1041 }
1042
1043 // Macros.
libd_SignedRandom(lua_State * L)1044 static int libd_SignedRandom(lua_State *L)
1045 {
1046 HUDONLY
1047 lua_pushinteger(L, M_SignedRandom());
1048 return 1;
1049 }
1050
libd_RandomChance(lua_State * L)1051 static int libd_RandomChance(lua_State *L)
1052 {
1053 fixed_t p = luaL_checkfixed(L, 1);
1054 HUDONLY
1055 lua_pushboolean(L, M_RandomChance(p));
1056 return 1;
1057 }
1058
1059 // 30/10/18 Lat': Get st_translucency's value for HUD rendering as a normal V_xxTRANS int
1060 // Could as well be thrown in global vars for ease of access but I guess it makes sense for it to be a HUD fn
libd_getlocaltransflag(lua_State * L)1061 static int libd_getlocaltransflag(lua_State *L)
1062 {
1063 HUDONLY
1064 lua_pushinteger(L, (10-st_translucency)*V_10TRANS);
1065 return 1;
1066 }
1067
1068 // Get cv_translucenthud's value for HUD rendering as a normal V_xxTRANS int
libd_getusertransflag(lua_State * L)1069 static int libd_getusertransflag(lua_State *L)
1070 {
1071 HUDONLY
1072 lua_pushinteger(L, (10-cv_translucenthud.value)*V_10TRANS); // A bit weird that it's called "translucenthud" yet 10 is fully opaque :V
1073 return 1;
1074 }
1075
1076 static luaL_Reg lib_draw[] = {
1077 // cache
1078 {"patchExists", libd_patchExists},
1079 {"cachePatch", libd_cachePatch},
1080 {"getSpritePatch", libd_getSpritePatch},
1081 {"getSprite2Patch", libd_getSprite2Patch},
1082 {"getColormap", libd_getColormap},
1083 {"getStringColormap", libd_getStringColormap},
1084 // drawing
1085 {"draw", libd_draw},
1086 {"drawScaled", libd_drawScaled},
1087 {"drawStretched", libd_drawStretched},
1088 {"drawNum", libd_drawNum},
1089 {"drawPaddedNum", libd_drawPaddedNum},
1090 {"drawFill", libd_drawFill},
1091 {"drawString", libd_drawString},
1092 {"drawNameTag", libd_drawNameTag},
1093 {"drawScaledNameTag", libd_drawScaledNameTag},
1094 {"fadeScreen", libd_fadeScreen},
1095 // misc
1096 {"stringWidth", libd_stringWidth},
1097 {"nameTagWidth", libd_nameTagWidth},
1098 // m_random
1099 {"RandomFixed",libd_RandomFixed},
1100 {"RandomByte",libd_RandomByte},
1101 {"RandomKey",libd_RandomKey},
1102 {"RandomRange",libd_RandomRange},
1103 {"SignedRandom",libd_SignedRandom}, // MACRO
1104 {"RandomChance",libd_RandomChance}, // MACRO
1105 // properties
1106 {"width", libd_width},
1107 {"height", libd_height},
1108 {"dupx", libd_dupx},
1109 {"dupy", libd_dupy},
1110 {"renderer", libd_renderer},
1111 {"localTransFlag", libd_getlocaltransflag},
1112 {"userTransFlag", libd_getusertransflag},
1113 {NULL, NULL}
1114 };
1115
1116 //
1117 // lib_hud
1118 //
1119
1120 // enable vanilla HUD element
lib_hudenable(lua_State * L)1121 static int lib_hudenable(lua_State *L)
1122 {
1123 enum hud option = luaL_checkoption(L, 1, NULL, hud_disable_options);
1124 hud_enabled[option/8] |= 1<<(option%8);
1125 return 0;
1126 }
1127
1128 // disable vanilla HUD element
lib_huddisable(lua_State * L)1129 static int lib_huddisable(lua_State *L)
1130 {
1131 enum hud option = luaL_checkoption(L, 1, NULL, hud_disable_options);
1132 hud_enabled[option/8] &= ~(1<<(option%8));
1133 return 0;
1134 }
1135
1136 // 30/10/18: Lat': How come this wasn't here before?
lib_hudenabled(lua_State * L)1137 static int lib_hudenabled(lua_State *L)
1138 {
1139 enum hud option = luaL_checkoption(L, 1, NULL, hud_disable_options);
1140 if (hud_enabled[option/8] & (1<<(option%8)))
1141 lua_pushboolean(L, true);
1142 else
1143 lua_pushboolean(L, false);
1144
1145 return 1;
1146 }
1147
1148
1149 // add a HUD element for rendering
lib_hudadd(lua_State * L)1150 static int lib_hudadd(lua_State *L)
1151 {
1152 enum hudhook field;
1153
1154 luaL_checktype(L, 1, LUA_TFUNCTION);
1155 field = luaL_checkoption(L, 2, "game", hudhook_opt);
1156
1157 if (!lua_lumploading)
1158 return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
1159
1160 lua_getfield(L, LUA_REGISTRYINDEX, "HUD");
1161 I_Assert(lua_istable(L, -1));
1162 lua_rawgeti(L, -1, field+2); // HUD[2+]
1163 I_Assert(lua_istable(L, -1));
1164 lua_remove(L, -2);
1165
1166 lua_pushvalue(L, 1);
1167 lua_rawseti(L, -2, (int)(lua_objlen(L, -2) + 1));
1168
1169 hudAvailable |= 1<<field;
1170 return 0;
1171 }
1172
1173 static luaL_Reg lib_hud[] = {
1174 {"enable", lib_hudenable},
1175 {"disable", lib_huddisable},
1176 {"enabled", lib_hudenabled},
1177 {"add", lib_hudadd},
1178 {NULL, NULL}
1179 };
1180
1181 //
1182 //
1183 //
1184
LUA_HudLib(lua_State * L)1185 int LUA_HudLib(lua_State *L)
1186 {
1187 memset(hud_enabled, 0xff, (hud_MAX/8)+1);
1188
1189 lua_newtable(L); // HUD registry table
1190 lua_newtable(L);
1191 luaL_register(L, NULL, lib_draw);
1192 lua_rawseti(L, -2, 1); // HUD[1] = lib_draw
1193
1194 lua_newtable(L);
1195 lua_rawseti(L, -2, 2); // HUD[2] = game rendering functions array
1196
1197 lua_newtable(L);
1198 lua_rawseti(L, -2, 3); // HUD[3] = scores rendering functions array
1199
1200 lua_newtable(L);
1201 lua_rawseti(L, -2, 4); // HUD[4] = intermission rendering functions array
1202
1203 lua_newtable(L);
1204 lua_rawseti(L, -2, 5); // HUD[5] = title rendering functions array
1205
1206 lua_newtable(L);
1207 lua_rawseti(L, -2, 6); // HUD[6] = title card rendering functions array
1208 lua_setfield(L, LUA_REGISTRYINDEX, "HUD");
1209
1210 luaL_newmetatable(L, META_HUDINFO);
1211 lua_pushcfunction(L, hudinfo_get);
1212 lua_setfield(L, -2, "__index");
1213
1214 lua_pushcfunction(L, hudinfo_set);
1215 lua_setfield(L, -2, "__newindex");
1216
1217 lua_pushcfunction(L, hudinfo_num);
1218 lua_setfield(L, -2, "__len");
1219 lua_pop(L,1);
1220
1221 lua_newuserdata(L, 0);
1222 lua_createtable(L, 0, 2);
1223 lua_pushcfunction(L, lib_getHudInfo);
1224 lua_setfield(L, -2, "__index");
1225
1226 lua_pushcfunction(L, lib_hudinfolen);
1227 lua_setfield(L, -2, "__len");
1228 lua_setmetatable(L, -2);
1229 lua_setglobal(L, "hudinfo");
1230
1231 luaL_newmetatable(L, META_COLORMAP);
1232 lua_pushcfunction(L, colormap_get);
1233 lua_setfield(L, -2, "__index");
1234 lua_pop(L,1);
1235
1236 luaL_newmetatable(L, META_PATCH);
1237 lua_pushcfunction(L, patch_get);
1238 lua_setfield(L, -2, "__index");
1239
1240 lua_pushcfunction(L, patch_set);
1241 lua_setfield(L, -2, "__newindex");
1242 lua_pop(L,1);
1243
1244 luaL_newmetatable(L, META_CAMERA);
1245 lua_pushcfunction(L, camera_get);
1246 lua_setfield(L, -2, "__index");
1247 lua_pop(L,1);
1248
1249 luaL_register(L, "hud", lib_hud);
1250 return 0;
1251 }
1252
LUA_HudEnabled(enum hud option)1253 boolean LUA_HudEnabled(enum hud option)
1254 {
1255 if (!gL || hud_enabled[option/8] & (1<<(option%8)))
1256 return true;
1257 return false;
1258 }
1259
1260 // Hook for HUD rendering
LUAh_GameHUD(player_t * stplayr)1261 void LUAh_GameHUD(player_t *stplayr)
1262 {
1263 if (!gL || !(hudAvailable & (1<<hudhook_game)))
1264 return;
1265
1266 hud_running = true;
1267 lua_settop(gL, 0);
1268
1269 lua_pushcfunction(gL, LUA_GetErrorMessage);
1270
1271 lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
1272 I_Assert(lua_istable(gL, -1));
1273 lua_rawgeti(gL, -1, 2+hudhook_game); // HUD[2] = rendering funcs
1274 I_Assert(lua_istable(gL, -1));
1275
1276 lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
1277 I_Assert(lua_istable(gL, -1));
1278 lua_remove(gL, -3); // pop HUD
1279 LUA_PushUserdata(gL, stplayr, META_PLAYER);
1280
1281 if (splitscreen && stplayr == &players[secondarydisplayplayer])
1282 LUA_PushUserdata(gL, &camera2, META_CAMERA);
1283 else
1284 LUA_PushUserdata(gL, &camera, META_CAMERA);
1285
1286 lua_pushnil(gL);
1287 while (lua_next(gL, -5) != 0) {
1288 lua_pushvalue(gL, -5); // graphics library (HUD[1])
1289 lua_pushvalue(gL, -5); // stplayr
1290 lua_pushvalue(gL, -5); // camera
1291 LUA_Call(gL, 3, 0, 1);
1292 }
1293 lua_settop(gL, 0);
1294 hud_running = false;
1295 }
1296
LUAh_ScoresHUD(void)1297 void LUAh_ScoresHUD(void)
1298 {
1299 if (!gL || !(hudAvailable & (1<<hudhook_scores)))
1300 return;
1301
1302 hud_running = true;
1303 lua_settop(gL, 0);
1304
1305 lua_pushcfunction(gL, LUA_GetErrorMessage);
1306
1307 lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
1308 I_Assert(lua_istable(gL, -1));
1309 lua_rawgeti(gL, -1, 2+hudhook_scores); // HUD[3] = rendering funcs
1310 I_Assert(lua_istable(gL, -1));
1311
1312 lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
1313 I_Assert(lua_istable(gL, -1));
1314 lua_remove(gL, -3); // pop HUD
1315 lua_pushnil(gL);
1316 while (lua_next(gL, -3) != 0) {
1317 lua_pushvalue(gL, -3); // graphics library (HUD[1])
1318 LUA_Call(gL, 1, 0, 1);
1319 }
1320 lua_settop(gL, 0);
1321 hud_running = false;
1322 }
1323
LUAh_TitleHUD(void)1324 void LUAh_TitleHUD(void)
1325 {
1326 if (!gL || !(hudAvailable & (1<<hudhook_title)))
1327 return;
1328
1329 hud_running = true;
1330 lua_settop(gL, 0);
1331
1332 lua_pushcfunction(gL, LUA_GetErrorMessage);
1333
1334 lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
1335 I_Assert(lua_istable(gL, -1));
1336 lua_rawgeti(gL, -1, 2+hudhook_title); // HUD[5] = rendering funcs
1337 I_Assert(lua_istable(gL, -1));
1338
1339 lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
1340 I_Assert(lua_istable(gL, -1));
1341 lua_remove(gL, -3); // pop HUD
1342 lua_pushnil(gL);
1343 while (lua_next(gL, -3) != 0) {
1344 lua_pushvalue(gL, -3); // graphics library (HUD[1])
1345 LUA_Call(gL, 1, 0, 1);
1346 }
1347 lua_settop(gL, 0);
1348 hud_running = false;
1349 }
1350
LUAh_TitleCardHUD(player_t * stplayr)1351 void LUAh_TitleCardHUD(player_t *stplayr)
1352 {
1353 if (!gL || !(hudAvailable & (1<<hudhook_titlecard)))
1354 return;
1355
1356 hud_running = true;
1357 lua_settop(gL, 0);
1358
1359 lua_pushcfunction(gL, LUA_GetErrorMessage);
1360
1361 lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
1362 I_Assert(lua_istable(gL, -1));
1363 lua_rawgeti(gL, -1, 2+hudhook_titlecard); // HUD[6] = rendering funcs
1364 I_Assert(lua_istable(gL, -1));
1365
1366 lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
1367 I_Assert(lua_istable(gL, -1));
1368 lua_remove(gL, -3); // pop HUD
1369
1370 LUA_PushUserdata(gL, stplayr, META_PLAYER);
1371 lua_pushinteger(gL, lt_ticker);
1372 lua_pushinteger(gL, (lt_endtime + TICRATE));
1373 lua_pushnil(gL);
1374
1375 while (lua_next(gL, -6) != 0) {
1376 lua_pushvalue(gL, -6); // graphics library (HUD[1])
1377 lua_pushvalue(gL, -6); // stplayr
1378 lua_pushvalue(gL, -6); // lt_ticker
1379 lua_pushvalue(gL, -6); // lt_endtime
1380 LUA_Call(gL, 4, 0, 1);
1381 }
1382
1383 lua_settop(gL, 0);
1384 hud_running = false;
1385 }
1386
LUAh_IntermissionHUD(void)1387 void LUAh_IntermissionHUD(void)
1388 {
1389 if (!gL || !(hudAvailable & (1<<hudhook_intermission)))
1390 return;
1391
1392 hud_running = true;
1393 lua_settop(gL, 0);
1394
1395 lua_pushcfunction(gL, LUA_GetErrorMessage);
1396
1397 lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
1398 I_Assert(lua_istable(gL, -1));
1399 lua_rawgeti(gL, -1, 2+hudhook_intermission); // HUD[4] = rendering funcs
1400 I_Assert(lua_istable(gL, -1));
1401
1402 lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
1403 I_Assert(lua_istable(gL, -1));
1404 lua_remove(gL, -3); // pop HUD
1405 lua_pushnil(gL);
1406 while (lua_next(gL, -3) != 0) {
1407 lua_pushvalue(gL, -3); // graphics library (HUD[1])
1408 LUA_Call(gL, 1, 0, 1);
1409 }
1410 lua_settop(gL, 0);
1411 hud_running = false;
1412 }
1413