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_infolib.c
11 /// \brief infotable editing library for Lua scripting
12 
13 #include "doomdef.h"
14 #include "fastcmp.h"
15 #include "info.h"
16 #include "dehacked.h"
17 #include "deh_tables.h"
18 #include "deh_lua.h"
19 #include "p_mobj.h"
20 #include "p_local.h"
21 #include "z_zone.h"
22 #include "r_patch.h"
23 #include "r_picformats.h"
24 #include "r_things.h"
25 #include "r_draw.h" // R_GetColorByName
26 #include "doomstat.h" // luabanks[]
27 
28 #include "lua_script.h"
29 #include "lua_libs.h"
30 #include "lua_hud.h" // hud_running errors
31 #include "lua_hook.h" // hook_cmd_running errors
32 
33 extern CV_PossibleValue_t Color_cons_t[];
34 extern UINT8 skincolor_modified[];
35 
36 boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor);
37 state_t *astate;
38 
39 enum sfxinfo_read {
40 	sfxinfor_name = 0,
41 	sfxinfor_singular,
42 	sfxinfor_priority,
43 	sfxinfor_flags, // "pitch"
44 	sfxinfor_caption,
45 	sfxinfor_skinsound
46 };
47 const char *const sfxinfo_ropt[] = {
48 	"name",
49 	"singular",
50 	"priority",
51 	"flags",
52 	"caption",
53 	"skinsound",
54 	NULL};
55 
56 enum sfxinfo_write {
57 	sfxinfow_singular = 0,
58 	sfxinfow_priority,
59 	sfxinfow_flags, // "pitch"
60 	sfxinfow_caption
61 };
62 const char *const sfxinfo_wopt[] = {
63 	"singular",
64 	"priority",
65 	"flags",
66 	"caption",
67 	NULL};
68 
69 boolean actionsoverridden[NUMACTIONS] = {false};
70 
71 //
72 // Sprite Names
73 //
74 
75 // push sprite name
lib_getSprname(lua_State * L)76 static int lib_getSprname(lua_State *L)
77 {
78 	UINT32 i;
79 
80 	lua_remove(L, 1); // don't care about sprnames[] dummy userdata.
81 
82 	if (lua_isnumber(L, 1))
83 	{
84 		i = lua_tonumber(L, 1);
85 		if (i > NUMSPRITES)
86 			return 0;
87 		lua_pushlstring(L, sprnames[i], 4);
88 		return 1;
89 	}
90 	else if (lua_isstring(L, 1))
91 	{
92 		const char *name = lua_tostring(L, 1);
93 		for (i = 0; i < NUMSPRITES; i++)
94 			if (fastcmp(name, sprnames[i]))
95 			{
96 				lua_pushinteger(L, i);
97 				return 1;
98 			}
99 	}
100 	return 0;
101 }
102 
103 /// \todo Maybe make it tally up the used_spr from dehacked?
lib_sprnamelen(lua_State * L)104 static int lib_sprnamelen(lua_State *L)
105 {
106 	lua_pushinteger(L, NUMSPRITES);
107 	return 1;
108 }
109 
110 //
111 // Player Sprite Names
112 //
113 
114 // push sprite name
lib_getSpr2name(lua_State * L)115 static int lib_getSpr2name(lua_State *L)
116 {
117 	playersprite_t i;
118 
119 	lua_remove(L, 1); // don't care about spr2names[] dummy userdata.
120 
121 	if (lua_isnumber(L, 1))
122 	{
123 		i = lua_tonumber(L, 1);
124 		if (i >= free_spr2)
125 			return 0;
126 		lua_pushlstring(L, spr2names[i], 4);
127 		return 1;
128 	}
129 	else if (lua_isstring(L, 1))
130 	{
131 		const char *name = lua_tostring(L, 1);
132 		for (i = 0; i < free_spr2; i++)
133 			if (fastcmp(name, spr2names[i]))
134 			{
135 				lua_pushinteger(L, i);
136 				return 1;
137 			}
138 	}
139 	return 0;
140 }
141 
lib_getSpr2default(lua_State * L)142 static int lib_getSpr2default(lua_State *L)
143 {
144 	playersprite_t i;
145 
146 	lua_remove(L, 1); // don't care about spr2defaults[] dummy userdata.
147 
148 	if (lua_isnumber(L, 1))
149 		i = lua_tonumber(L, 1);
150 	else if (lua_isstring(L, 1))
151 	{
152 		const char *name = lua_tostring(L, 1);
153 		for (i = 0; i < free_spr2; i++)
154 			if (fastcmp(name, spr2names[i]))
155 				break;
156 	}
157 	else
158 		return luaL_error(L, "spr2defaults[] invalid index");
159 
160 	if (i >= free_spr2)
161 		return luaL_error(L, "spr2defaults[] index %d out of range (%d - %d)", i, 0, free_spr2-1);
162 
163 	lua_pushinteger(L, spr2defaults[i]);
164 	return 1;
165 }
166 
lib_setSpr2default(lua_State * L)167 static int lib_setSpr2default(lua_State *L)
168 {
169 	playersprite_t i;
170 	UINT8 j = 0;
171 
172 	if (hud_running)
173 		return luaL_error(L, "Do not alter spr2defaults[] in HUD rendering code!");
174 	if (hook_cmd_running)
175 		return luaL_error(L, "Do not alter spr2defaults[] in CMD building code!");
176 
177 // todo: maybe allow setting below first freeslot..? step 1 is toggling this, step 2 is testing to see whether it's net-safe
178 #ifdef SETALLSPR2DEFAULTS
179 #define FIRSTMODIFY 0
180 #else
181 #define FIRSTMODIFY SPR2_FIRSTFREESLOT
182 	if (free_spr2 == SPR2_FIRSTFREESLOT)
183 		return luaL_error(L, "You can only modify the spr2defaults[] entries of sprite2 freeslots, and none are currently added.");
184 #endif
185 
186 	lua_remove(L, 1); // don't care about spr2defaults[] dummy userdata.
187 
188 	if (lua_isnumber(L, 1))
189 		i = lua_tonumber(L, 1);
190 	else if (lua_isstring(L, 1))
191 	{
192 		const char *name = lua_tostring(L, 1);
193 		for (i = 0; i < free_spr2; i++)
194 		{
195 			if (fastcmp(name, spr2names[i]))
196 				break;
197 		}
198 		if (i == free_spr2)
199 			return luaL_error(L, "spr2defaults[] invalid index");
200 	}
201 	else
202 		return luaL_error(L, "spr2defaults[] invalid index");
203 
204 	if (i < FIRSTMODIFY || i >= free_spr2)
205 		return luaL_error(L, "spr2defaults[] index %d out of range (%d - %d)", i, FIRSTMODIFY, free_spr2-1);
206 #undef FIRSTMODIFY
207 
208 	if (lua_isnumber(L, 2))
209 		j = lua_tonumber(L, 2);
210 	else if (lua_isstring(L, 2))
211 	{
212 		const char *name = lua_tostring(L, 2);
213 		for (j = 0; j < free_spr2; j++)
214 		{
215 			if (fastcmp(name, spr2names[j]))
216 				break;
217 		}
218 		if (j == free_spr2)
219 			return luaL_error(L, "spr2defaults[] invalid set");
220 	}
221 	else
222 		return luaL_error(L, "spr2defaults[] invalid set");
223 
224 	if (j >= free_spr2)
225 		return luaL_error(L, "spr2defaults[] set %d out of range (%d - %d)", j, 0, free_spr2-1);
226 
227 	spr2defaults[i] = j;
228 	return 0;
229 }
230 
lib_spr2namelen(lua_State * L)231 static int lib_spr2namelen(lua_State *L)
232 {
233 	lua_pushinteger(L, free_spr2);
234 	return 1;
235 }
236 
237 /////////////////
238 // SPRITE INFO //
239 /////////////////
240 
241 // spriteinfo[]
lib_getSpriteInfo(lua_State * L)242 static int lib_getSpriteInfo(lua_State *L)
243 {
244 	UINT32 i = NUMSPRITES;
245 	lua_remove(L, 1);
246 
247 	if (lua_isstring(L, 1))
248 	{
249 		const char *name = lua_tostring(L, 1);
250 		INT32 spr;
251 		for (spr = 0; spr < NUMSPRITES; spr++)
252 		{
253 			if (fastcmp(name, sprnames[spr]))
254 			{
255 				i = spr;
256 				break;
257 			}
258 		}
259 		if (i == NUMSPRITES)
260 		{
261 			char *check;
262 			i = strtol(name, &check, 10);
263 			if (check == name || *check != '\0')
264 				return luaL_error(L, "unknown sprite name %s", name);
265 		}
266 	}
267 	else
268 		i = luaL_checkinteger(L, 1);
269 
270 	if (i == 0 || i >= NUMSPRITES)
271 		return luaL_error(L, "spriteinfo[] index %d out of range (1 - %d)", i, NUMSPRITES-1);
272 
273 	LUA_PushUserdata(L, &spriteinfo[i], META_SPRITEINFO);
274 	return 1;
275 }
276 
277 #define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to spriteinfo[] (%s)", e);
278 #define TYPEERROR(f, t1, t2) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t1), lua_typename(L, t2)))
279 
PopPivotSubTable(spriteframepivot_t * pivot,lua_State * L,int stk,int idx)280 static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, int idx)
281 {
282 	int okcool = 0;
283 	switch (lua_type(L, stk))
284 	{
285 		case LUA_TTABLE:
286 			lua_pushnil(L);
287 			while (lua_next(L, stk))
288 			{
289 				const char *key = NULL;
290 				lua_Integer ikey = -1;
291 				lua_Integer value = 0;
292 				// x or y?
293 				switch (lua_type(L, stk+1))
294 				{
295 					case LUA_TSTRING:
296 						key = lua_tostring(L, stk+1);
297 						break;
298 					case LUA_TNUMBER:
299 						ikey = lua_tointeger(L, stk+1);
300 						break;
301 					default:
302 						FIELDERROR("pivot key", va("string or number expected, got %s", luaL_typename(L, stk+1)))
303 				}
304 				// then get value
305 				switch (lua_type(L, stk+2))
306 				{
307 					case LUA_TNUMBER:
308 						value = lua_tonumber(L, stk+2);
309 						break;
310 					case LUA_TBOOLEAN:
311 						value = (UINT8)lua_toboolean(L, stk+2);
312 						break;
313 					default:
314 						TYPEERROR("pivot value", LUA_TNUMBER, lua_type(L, stk+2))
315 				}
316 				// finally set omg!!!!!!!!!!!!!!!!!!
317 				if (ikey == 1 || (key && fastcmp(key, "x")))
318 					pivot[idx].x = (INT32)value;
319 				else if (ikey == 2 || (key && fastcmp(key, "y")))
320 					pivot[idx].y = (INT32)value;
321 				else if (ikey == 3 || (key && fastcmp(key, "rotaxis")))
322 					pivot[idx].rotaxis = (UINT8)value;
323 				else if (ikey == -1 && (key != NULL))
324 					FIELDERROR("pivot key", va("invalid option %s", key));
325 				okcool = 1;
326 				lua_pop(L, 1);
327 			}
328 			break;
329 		default:
330 			TYPEERROR("sprite pivot", LUA_TTABLE, lua_type(L, stk))
331 	}
332 	return okcool;
333 }
334 
PopPivotTable(spriteinfo_t * info,lua_State * L,int stk)335 static int PopPivotTable(spriteinfo_t *info, lua_State *L, int stk)
336 {
337 	// Just in case?
338 	if (!lua_istable(L, stk))
339 		TYPEERROR("pivot table", LUA_TTABLE, lua_type(L, stk));
340 
341 	lua_pushnil(L);
342 	// stk = 0 has the pivot table
343 	// stk = 1 has the frame key
344 	// stk = 2 has the frame table
345 	// stk = 3 has either a string or a number as key
346 	// stk = 4 has the value for the key mentioned above
347 	while (lua_next(L, stk))
348 	{
349 		int idx = 0;
350 		const char *framestr = NULL;
351 		switch (lua_type(L, stk+1))
352 		{
353 			case LUA_TSTRING:
354 				framestr = lua_tostring(L, stk+1);
355 				idx = R_Char2Frame(framestr[0]);
356 				break;
357 			case LUA_TNUMBER:
358 				idx = lua_tonumber(L, stk+1);
359 				break;
360 			default:
361 				TYPEERROR("pivot frame", LUA_TNUMBER, lua_type(L, stk+1));
362 		}
363 		if ((idx < 0) || (idx >= 64))
364 			return luaL_error(L, "pivot frame %d out of range (0 - %d)", idx, 63);
365 		// the values in pivot[] are also tables
366 		if (PopPivotSubTable(info->pivot, L, stk+2, idx))
367 			info->available = true;
368 		lua_pop(L, 1);
369 	}
370 
371 	return 0;
372 }
373 
lib_setSpriteInfo(lua_State * L)374 static int lib_setSpriteInfo(lua_State *L)
375 {
376 	spriteinfo_t *info;
377 
378 	if (!lua_lumploading)
379 		return luaL_error(L, "Do not alter spriteinfo_t from within a hook or coroutine!");
380 	if (hud_running)
381 		return luaL_error(L, "Do not alter spriteinfo_t in HUD rendering code!");
382 	if (hook_cmd_running)
383 		return luaL_error(L, "Do not alter spriteinfo_t in CMD building code!");
384 
385 	lua_remove(L, 1);
386 	{
387 		UINT32 i = luaL_checkinteger(L, 1);
388 		if (i == 0 || i >= NUMSPRITES)
389 			return luaL_error(L, "spriteinfo[] index %d out of range (1 - %d)", i, NUMSPRITES-1);
390 		info = &spriteinfo[i]; // get the spriteinfo to assign to.
391 	}
392 	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
393 	lua_remove(L, 1); // pop sprite num, don't need it any more.
394 	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the spriteinfo.
395 
396 	lua_pushnil(L);
397 	while (lua_next(L, 1)) {
398 		lua_Integer i = 0;
399 		const char *str = NULL;
400 		if (lua_isnumber(L, 2))
401 			i = lua_tointeger(L, 2);
402 		else
403 			str = luaL_checkstring(L, 2);
404 
405 		if (i == 1 || (str && fastcmp(str, "pivot")))
406 		{
407 			// pivot[] is a table
408 			if (lua_istable(L, 3))
409 				return PopPivotTable(info, L, 3);
410 			else
411 				FIELDERROR("pivot", va("%s expected, got %s", lua_typename(L, LUA_TTABLE), luaL_typename(L, -1)))
412 		}
413 
414 		lua_pop(L, 1);
415 	}
416 
417 	return 0;
418 }
419 
420 #undef FIELDERROR
421 #undef TYPEERROR
422 
lib_spriteinfolen(lua_State * L)423 static int lib_spriteinfolen(lua_State *L)
424 {
425 	lua_pushinteger(L, NUMSPRITES);
426 	return 1;
427 }
428 
429 // spriteinfo_t
spriteinfo_get(lua_State * L)430 static int spriteinfo_get(lua_State *L)
431 {
432 	spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFO));
433 	const char *field = luaL_checkstring(L, 2);
434 
435 	I_Assert(sprinfo != NULL);
436 
437 	// push spriteframepivot_t userdata
438 	if (fastcmp(field, "pivot"))
439 	{
440 		// bypass LUA_PushUserdata
441 		void **userdata = lua_newuserdata(L, sizeof(void *));
442 		*userdata = &sprinfo->pivot;
443 		luaL_getmetatable(L, META_PIVOTLIST);
444 		lua_setmetatable(L, -2);
445 
446 		// stack is left with the userdata on top, as if getting it had originally succeeded.
447 		return 1;
448 	}
449 	else
450 		return luaL_error(L, LUA_QL("spriteinfo_t") " has no field named " LUA_QS, field);
451 
452 	return 0;
453 }
454 
spriteinfo_set(lua_State * L)455 static int spriteinfo_set(lua_State *L)
456 {
457 	spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFO));
458 	const char *field = luaL_checkstring(L, 2);
459 
460 	if (!lua_lumploading)
461 		return luaL_error(L, "Do not alter spriteinfo_t from within a hook or coroutine!");
462 	if (hud_running)
463 		return luaL_error(L, "Do not alter spriteinfo_t in HUD rendering code!");
464 	if (hook_cmd_running)
465 		return luaL_error(L, "Do not alter spriteinfo_t in CMD building code!");
466 
467 	I_Assert(sprinfo != NULL);
468 
469 	lua_remove(L, 1); // remove spriteinfo
470 	lua_remove(L, 1); // remove field
471 	lua_settop(L, 1); // leave only one value
472 
473 	if (fastcmp(field, "pivot"))
474 	{
475 		// pivot[] is a table
476 		if (lua_istable(L, 1))
477 			return PopPivotTable(sprinfo, L, 1);
478 		// pivot[] is userdata
479 		else if (lua_isuserdata(L, 1))
480 		{
481 			spriteframepivot_t *pivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
482 			memcpy(&sprinfo->pivot, pivot, sizeof(spriteframepivot_t));
483 			sprinfo->available = true; // Just in case?
484 		}
485 	}
486 	else
487 		return luaL_error(L, va("Field %s does not exist in spriteinfo_t", field));
488 
489 	return 0;
490 }
491 
spriteinfo_num(lua_State * L)492 static int spriteinfo_num(lua_State *L)
493 {
494 	spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFO));
495 
496 	I_Assert(sprinfo != NULL);
497 	I_Assert(sprinfo >= spriteinfo);
498 
499 	lua_pushinteger(L, (UINT32)(sprinfo-spriteinfo));
500 	return 1;
501 }
502 
503 // framepivot_t
pivotlist_get(lua_State * L)504 static int pivotlist_get(lua_State *L)
505 {
506 	void **userdata;
507 	spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
508 	const char *field = luaL_checkstring(L, 2);
509 	UINT8 frame;
510 
511 	I_Assert(framepivot != NULL);
512 
513 	frame = R_Char2Frame(field[0]);
514 	if (frame == 255)
515 		luaL_error(L, "invalid frame %s", field);
516 
517 	// bypass LUA_PushUserdata
518 	userdata = lua_newuserdata(L, sizeof(void *));
519 	*userdata = &framepivot[frame];
520 	luaL_getmetatable(L, META_FRAMEPIVOT);
521 	lua_setmetatable(L, -2);
522 
523 	// stack is left with the userdata on top, as if getting it had originally succeeded.
524 	return 1;
525 }
526 
pivotlist_set(lua_State * L)527 static int pivotlist_set(lua_State *L)
528 {
529 	// Because I already know it's a spriteframepivot_t anyway
530 	spriteframepivot_t *pivotlist = *((spriteframepivot_t **)lua_touserdata(L, 1));
531 	//spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
532 	const char *field = luaL_checkstring(L, 2);
533 	UINT8 frame;
534 
535 	if (!lua_lumploading)
536 		return luaL_error(L, "Do not alter spriteframepivot_t from within a hook or coroutine!");
537 	if (hud_running)
538 		return luaL_error(L, "Do not alter spriteframepivot_t in HUD rendering code!");
539 	if (hook_cmd_running)
540 		return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!");
541 
542 	I_Assert(pivotlist != NULL);
543 
544 	frame = R_Char2Frame(field[0]);
545 	if (frame == 255)
546 		luaL_error(L, "invalid frame %s", field);
547 
548 	// pivot[] is a table
549 	if (lua_istable(L, 3))
550 		return PopPivotSubTable(pivotlist, L, 3, frame);
551 	// pivot[] is userdata
552 	else if (lua_isuserdata(L, 3))
553 	{
554 		spriteframepivot_t *copypivot = *((spriteframepivot_t **)luaL_checkudata(L, 3, META_FRAMEPIVOT));
555 		memcpy(&pivotlist[frame], copypivot, sizeof(spriteframepivot_t));
556 	}
557 
558 	return 0;
559 }
560 
pivotlist_num(lua_State * L)561 static int pivotlist_num(lua_State *L)
562 {
563 	lua_pushinteger(L, 64);
564 	return 1;
565 }
566 
framepivot_get(lua_State * L)567 static int framepivot_get(lua_State *L)
568 {
569 	spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
570 	const char *field = luaL_checkstring(L, 2);
571 
572 	I_Assert(framepivot != NULL);
573 
574 	if (fastcmp("x", field))
575 		lua_pushinteger(L, framepivot->x);
576 	else if (fastcmp("y", field))
577 		lua_pushinteger(L, framepivot->y);
578 	else if (fastcmp("rotaxis", field))
579 		lua_pushinteger(L, (UINT8)framepivot->rotaxis);
580 	else
581 		return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field));
582 
583 	return 1;
584 }
585 
framepivot_set(lua_State * L)586 static int framepivot_set(lua_State *L)
587 {
588 	spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
589 	const char *field = luaL_checkstring(L, 2);
590 
591 	if (!lua_lumploading)
592 		return luaL_error(L, "Do not alter spriteframepivot_t from within a hook or coroutine!");
593 	if (hud_running)
594 		return luaL_error(L, "Do not alter spriteframepivot_t in HUD rendering code!");
595 	if (hook_cmd_running)
596 		return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!");
597 
598 	I_Assert(framepivot != NULL);
599 
600 	if (fastcmp("x", field))
601 		framepivot->x = luaL_checkinteger(L, 3);
602 	else if (fastcmp("y", field))
603 		framepivot->y = luaL_checkinteger(L, 3);
604 	else if (fastcmp("rotaxis", field))
605 		framepivot->rotaxis = luaL_checkinteger(L, 3);
606 	else
607 		return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field));
608 
609 	return 0;
610 }
611 
framepivot_num(lua_State * L)612 static int framepivot_num(lua_State *L)
613 {
614 	lua_pushinteger(L, 2);
615 	return 1;
616 }
617 
618 ////////////////
619 // STATE INFO //
620 ////////////////
621 
622 // Uses astate to determine which state is calling it
623 // Then looks up which Lua action is assigned to that state and calls it
A_Lua(mobj_t * actor)624 static void A_Lua(mobj_t *actor)
625 {
626 	boolean found = false;
627 	I_Assert(actor != NULL);
628 
629 	lua_settop(gL, 0); // Just in case...
630 	lua_pushcfunction(gL, LUA_GetErrorMessage);
631 
632 	// get the action for this state
633 	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_STATEACTION);
634 	I_Assert(lua_istable(gL, -1));
635 	lua_pushlightuserdata(gL, astate);
636 	lua_rawget(gL, -2);
637 	I_Assert(lua_isfunction(gL, -1));
638 	lua_remove(gL, -2); // pop LREG_STATEACTION
639 
640 	// get the name for this action, if possible.
641 	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
642 	lua_pushnil(gL);
643 	while (lua_next(gL, -2))
644 	{
645 		if (lua_rawequal(gL, -1, -4))
646 		{
647 			found = true;
648 			superactions[superstack] = lua_tostring(gL, -2); // "A_ACTION"
649 			++superstack;
650 			lua_pop(gL, 2); // pop the name and function
651 			break;
652 		}
653 		lua_pop(gL, 1);
654 	}
655 	lua_pop(gL, 1); // pop LREG_ACTION
656 
657 	LUA_PushUserdata(gL, actor, META_MOBJ);
658 	lua_pushinteger(gL, var1);
659 	lua_pushinteger(gL, var2);
660 	LUA_Call(gL, 3, 0, 1);
661 
662 	if (found)
663 	{
664 		--superstack;
665 		superactions[superstack] = NULL;
666 	}
667 }
668 
669 // Arbitrary states[] table index -> state_t *
lib_getState(lua_State * L)670 static int lib_getState(lua_State *L)
671 {
672 	UINT32 i;
673 	lua_remove(L, 1);
674 
675 	i = luaL_checkinteger(L, 1);
676 	if (i >= NUMSTATES)
677 		return luaL_error(L, "states[] index %d out of range (0 - %d)", i, NUMSTATES-1);
678 	LUA_PushUserdata(L, &states[i], META_STATE);
679 	return 1;
680 }
681 
682 // Lua table full of data -> states[] (set the values all at once! :D :D)
lib_setState(lua_State * L)683 static int lib_setState(lua_State *L)
684 {
685 	state_t *state;
686 	lua_remove(L, 1); // don't care about states[] userdata.
687 	{
688 		UINT32 i = luaL_checkinteger(L, 1);
689 		if (i >= NUMSTATES)
690 			return luaL_error(L, "states[] index %d out of range (0 - %d)", i, NUMSTATES-1);
691 		state = &states[i]; // get the state to assign to.
692 	}
693 	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
694 	lua_remove(L, 1); // pop state num, don't need it any more.
695 	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the state.
696 
697 	if (hud_running)
698 		return luaL_error(L, "Do not alter states in HUD rendering code!");
699 	if (hook_cmd_running)
700 		return luaL_error(L, "Do not alter states in CMD building code!");
701 
702 	// clear the state to start with, in case of missing table elements
703 	memset(state,0,sizeof(state_t));
704 	state->tics = -1;
705 
706 	lua_pushnil(L);
707 	while (lua_next(L, 1)) {
708 		lua_Integer i = 0;
709 		const char *str = NULL;
710 		lua_Integer value;
711 		if (lua_isnumber(L, 2))
712 			i = lua_tointeger(L, 2);
713 		else
714 			str = luaL_checkstring(L, 2);
715 
716 		if (i == 1 || (str && fastcmp(str, "sprite"))) {
717 			value = luaL_checkinteger(L, 3);
718 			if (value < SPR_NULL || value >= NUMSPRITES)
719 				return luaL_error(L, "sprite number %d is invalid.", value);
720 			state->sprite = (spritenum_t)value;
721 		} else if (i == 2 || (str && fastcmp(str, "frame"))) {
722 			state->frame = (UINT32)luaL_checkinteger(L, 3);
723 		} else if (i == 3 || (str && fastcmp(str, "tics"))) {
724 			state->tics = (INT32)luaL_checkinteger(L, 3);
725 		} else if (i == 4 || (str && fastcmp(str, "action"))) {
726 			switch(lua_type(L, 3))
727 			{
728 			case LUA_TNIL: // Null? Set the action to nothing, then.
729 				state->action.acp1 = NULL;
730 				break;
731 			case LUA_TSTRING: // It's a string, expect the name of a built-in action
732 				LUA_SetActionByName(state, lua_tostring(L, 3));
733 				break;
734 			case LUA_TUSERDATA: // It's a userdata, expect META_ACTION of a built-in action
735 			{
736 				actionf_t *action = *((actionf_t **)luaL_checkudata(L, 3, META_ACTION));
737 
738 				if (!action)
739 					return luaL_error(L, "not a valid action?");
740 
741 				state->action = *action;
742 				state->action.acv = action->acv;
743 				state->action.acp1 = action->acp1;
744 				break;
745 			}
746 			case LUA_TFUNCTION: // It's a function (a Lua function or a C function? either way!)
747 				lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
748 				I_Assert(lua_istable(L, -1));
749 				lua_pushlightuserdata(L, state); // We'll store this function by the state's pointer in the registry.
750 				lua_pushvalue(L, 3); // Bring it to the top of the stack
751 				lua_rawset(L, -3); // Set it in the registry
752 				lua_pop(L, 1); // pop LREG_STATEACTION
753 				state->action.acp1 = (actionf_p1)A_Lua; // Set the action for the userdata.
754 				break;
755 			default: // ?!
756 				return luaL_typerror(L, 3, "function");
757 			}
758 		} else if (i == 5 || (str && fastcmp(str, "var1"))) {
759 			state->var1 = (INT32)luaL_checkinteger(L, 3);
760 		} else if (i == 6 || (str && fastcmp(str, "var2"))) {
761 			state->var2 = (INT32)luaL_checkinteger(L, 3);
762 		} else if (i == 7 || (str && fastcmp(str, "nextstate"))) {
763 			value = luaL_checkinteger(L, 3);
764 			if (value < S_NULL || value >= NUMSTATES)
765 				return luaL_error(L, "nextstate number %d is invalid.", value);
766 			state->nextstate = (statenum_t)value;
767 		}
768 		lua_pop(L, 1);
769 	}
770 	return 0;
771 }
772 
773 // #states -> NUMSTATES
lib_statelen(lua_State * L)774 static int lib_statelen(lua_State *L)
775 {
776 	lua_pushinteger(L, NUMSTATES);
777 	return 1;
778 }
779 
LUA_SetLuaAction(void * stv,const char * action)780 boolean LUA_SetLuaAction(void *stv, const char *action)
781 {
782 	state_t *st = (state_t *)stv;
783 
784 	I_Assert(st != NULL);
785 	//I_Assert(st >= states && st < states+NUMSTATES); // if you REALLY want to be paranoid...
786 	I_Assert(action != NULL);
787 
788 	if (!gL) // Lua isn't loaded,
789 		return false; // action not set.
790 
791 	// action is assumed to be in all-caps already !!
792 	// the registry is case-sensitive, so we strupr everything that enters it.
793 
794 	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
795 	lua_getfield(gL, -1, action);
796 
797 	if (lua_isnil(gL, -1)) // no match
798 	{
799 		lua_pop(gL, 2); // pop nil and LREG_ACTIONS
800 		return false; // action not set.
801 	}
802 
803 	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_STATEACTION);
804 	I_Assert(lua_istable(gL, -1));
805 	lua_pushlightuserdata(gL, stv); // We'll store this function by the state's pointer in the registry.
806 	lua_pushvalue(gL, -3); // Bring it to the top of the stack
807 	lua_rawset(gL, -3); // Set it in the registry
808 	lua_pop(gL, 1); // pop LREG_STATEACTION
809 
810 	lua_pop(gL, 2); // pop the function and LREG_ACTIONS
811 	st->action.acp1 = (actionf_p1)A_Lua; // Set the action for the userdata.
812 	return true; // action successfully set.
813 }
814 
LUA_CallAction(enum actionnum actionnum,mobj_t * actor)815 boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
816 {
817 	I_Assert(actor != NULL);
818 
819 	if (!actionsoverridden[actionnum]) // The action is not overriden,
820 		return false; // action not called.
821 
822 	if (superstack && fasticmp(actionpointers[actionnum].name, superactions[superstack-1])) // the action is calling itself,
823 		return false; // let it call the hardcoded function instead.
824 
825 	lua_pushcfunction(gL, LUA_GetErrorMessage);
826 
827 	// grab function by uppercase name.
828 	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
829 	lua_getfield(gL, -1, actionpointers[actionnum].name);
830 	lua_remove(gL, -2); // pop LREG_ACTIONS
831 
832 	if (lua_isnil(gL, -1)) // no match
833 	{
834 		lua_pop(gL, 2); // pop nil and error handler
835 		return false; // action not called.
836 	}
837 
838 	if (superstack == MAXRECURSION)
839 	{
840 		CONS_Alert(CONS_WARNING, "Max Lua Action recursion reached! Cool it on the calling A_Action functions from inside A_Action functions!\n");
841 		lua_pop(gL, 2); // pop function and error handler
842 		return true;
843 	}
844 
845 	// Found a function.
846 	// Call it with (actor, var1, var2)
847 	I_Assert(lua_isfunction(gL, -1));
848 	LUA_PushUserdata(gL, actor, META_MOBJ);
849 	lua_pushinteger(gL, var1);
850 	lua_pushinteger(gL, var2);
851 
852 	superactions[superstack] = actionpointers[actionnum].name;
853 	++superstack;
854 
855 	LUA_Call(gL, 3, 0, -(2 + 3));
856 	lua_pop(gL, -1); // Error handler
857 
858 	--superstack;
859 	superactions[superstack] = NULL;
860 	return true; // action successfully called.
861 }
862 
863 // state_t *, field -> number
state_get(lua_State * L)864 static int state_get(lua_State *L)
865 {
866 	state_t *st = *((state_t **)luaL_checkudata(L, 1, META_STATE));
867 	const char *field = luaL_checkstring(L, 2);
868 	lua_Integer number;
869 
870 	if (fastcmp(field,"sprite"))
871 		number = st->sprite;
872 	else if (fastcmp(field,"frame"))
873 		number = st->frame;
874 	else if (fastcmp(field,"tics"))
875 		number = st->tics;
876 	else if (fastcmp(field,"action")) {
877 		const char *name;
878 		if (!st->action.acp1) // Action is NULL.
879 			return 0; // return nil.
880 		if (st->action.acp1 == (actionf_p1)A_Lua) { // This is a Lua function?
881 			lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
882 			I_Assert(lua_istable(L, -1));
883 			lua_pushlightuserdata(L, st); // Push the state pointer and
884 			lua_rawget(L, -2); // use it to get the actual Lua function.
885 			lua_remove(L, -2); // pop LREG_STATEACTION
886 			return 1; // Return the Lua function.
887 		}
888 		name = LUA_GetActionName(&st->action); // find a hardcoded function name
889 		if (!name) // If it's not a hardcoded function and it's not a Lua function...
890 			return 0; // Just what is this??
891 		// get the function from the global
892 		// because the metatable will trigger.
893 		lua_getglobal(L, name); // actually gets from LREG_ACTIONS if applicable, and pushes a META_ACTION userdata if not.
894 		return 1; // return just the function
895 	} else if (fastcmp(field,"var1"))
896 		number = st->var1;
897 	else if (fastcmp(field,"var2"))
898 		number = st->var2;
899 	else if (fastcmp(field,"nextstate"))
900 		number = st->nextstate;
901 	else if (devparm)
902 		return luaL_error(L, LUA_QL("state_t") " has no field named " LUA_QS, field);
903 	else
904 		return 0;
905 
906 	lua_pushinteger(L, number);
907 	return 1;
908 }
909 
910 // state_t *, field, number -> states[]
state_set(lua_State * L)911 static int state_set(lua_State *L)
912 {
913 	state_t *st = *((state_t **)luaL_checkudata(L, 1, META_STATE));
914 	const char *field = luaL_checkstring(L, 2);
915 	lua_Integer value;
916 
917 	if (hud_running)
918 		return luaL_error(L, "Do not alter states in HUD rendering code!");
919 	if (hook_cmd_running)
920 		return luaL_error(L, "Do not alter states in CMD building code!");
921 
922 	if (fastcmp(field,"sprite")) {
923 		value = luaL_checknumber(L, 3);
924 		if (value < SPR_NULL || value >= NUMSPRITES)
925 			return luaL_error(L, "sprite number %d is invalid.", value);
926 		st->sprite = (spritenum_t)value;
927 	} else if (fastcmp(field,"frame"))
928 		st->frame = (UINT32)luaL_checknumber(L, 3);
929 	else if (fastcmp(field,"tics"))
930 		st->tics = (INT32)luaL_checknumber(L, 3);
931 	else if (fastcmp(field,"action")) {
932 		switch(lua_type(L, 3))
933 		{
934 		case LUA_TNIL: // Null? Set the action to nothing, then.
935 			st->action.acp1 = NULL;
936 			break;
937 		case LUA_TSTRING: // It's a string, expect the name of a built-in action
938 			LUA_SetActionByName(st, lua_tostring(L, 3));
939 			break;
940 		case LUA_TUSERDATA: // It's a userdata, expect META_ACTION of a built-in action
941 		{
942 			actionf_t *action = *((actionf_t **)luaL_checkudata(L, 3, META_ACTION));
943 
944 			if (!action)
945 				return luaL_error(L, "not a valid action?");
946 
947 			st->action = *action;
948 			st->action.acv = action->acv;
949 			st->action.acp1 = action->acp1;
950 			break;
951 		}
952 		case LUA_TFUNCTION: // It's a function (a Lua function or a C function? either way!)
953 			lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
954 			I_Assert(lua_istable(L, -1));
955 			lua_pushlightuserdata(L, st); // We'll store this function by the state's pointer in the registry.
956 			lua_pushvalue(L, 3); // Bring it to the top of the stack
957 			lua_rawset(L, -3); // Set it in the registry
958 			lua_pop(L, 1); // pop LREG_STATEACTION
959 			st->action.acp1 = (actionf_p1)A_Lua; // Set the action for the userdata.
960 			break;
961 		default: // ?!
962 			return luaL_typerror(L, 3, "function");
963 		}
964 	} else if (fastcmp(field,"var1"))
965 		st->var1 = (INT32)luaL_checknumber(L, 3);
966 	else if (fastcmp(field,"var2"))
967 		st->var2 = (INT32)luaL_checknumber(L, 3);
968 	else if (fastcmp(field,"nextstate")) {
969 		value = luaL_checkinteger(L, 3);
970 		if (value < S_NULL || value >= NUMSTATES)
971 			return luaL_error(L, "nextstate number %d is invalid.", value);
972 		st->nextstate = (statenum_t)value;
973 	} else
974 		return luaL_error(L, LUA_QL("state_t") " has no field named " LUA_QS, field);
975 
976 	return 0;
977 }
978 
979 // state_t * -> S_*
state_num(lua_State * L)980 static int state_num(lua_State *L)
981 {
982 	state_t *state = *((state_t **)luaL_checkudata(L, 1, META_STATE));
983 	lua_pushinteger(L, state-states);
984 	return 1;
985 }
986 
987 ///////////////
988 // MOBJ INFO //
989 ///////////////
990 
991 // Arbitrary mobjinfo[] table index -> mobjinfo_t *
lib_getMobjInfo(lua_State * L)992 static int lib_getMobjInfo(lua_State *L)
993 {
994 	UINT32 i;
995 	lua_remove(L, 1);
996 
997 	i = luaL_checkinteger(L, 1);
998 	if (i >= NUMMOBJTYPES)
999 		return luaL_error(L, "mobjinfo[] index %d out of range (0 - %d)", i, NUMMOBJTYPES-1);
1000 	LUA_PushUserdata(L, &mobjinfo[i], META_MOBJINFO);
1001 	return 1;
1002 }
1003 
1004 // Lua table full of data -> mobjinfo[]
lib_setMobjInfo(lua_State * L)1005 static int lib_setMobjInfo(lua_State *L)
1006 {
1007 	mobjinfo_t *info;
1008 	lua_remove(L, 1); // don't care about mobjinfo[] userdata.
1009 	{
1010 		UINT32 i = luaL_checkinteger(L, 1);
1011 		if (i >= NUMMOBJTYPES)
1012 			return luaL_error(L, "mobjinfo[] index %d out of range (0 - %d)", i, NUMMOBJTYPES-1);
1013 		info = &mobjinfo[i]; // get the mobjinfo to assign to.
1014 	}
1015 	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
1016 	lua_remove(L, 1); // pop mobjtype num, don't need it any more.
1017 	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the mobjinfo.
1018 
1019 	if (hud_running)
1020 		return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
1021 	if (hook_cmd_running)
1022 		return luaL_error(L, "Do not alter mobjinfo in CMD building code!");
1023 
1024 	// clear the mobjinfo to start with, in case of missing table elements
1025 	memset(info,0,sizeof(mobjinfo_t));
1026 	info->doomednum = -1; // default to no editor value
1027 	info->spawnhealth = 1; // avoid 'dead' noclip behaviors
1028 
1029 	lua_pushnil(L);
1030 	while (lua_next(L, 1)) {
1031 		lua_Integer i = 0;
1032 		const char *str = NULL;
1033 		lua_Integer value;
1034 		if (lua_isnumber(L, 2))
1035 			i = lua_tointeger(L, 2);
1036 		else
1037 			str = luaL_checkstring(L, 2);
1038 
1039 		if (i == 1 || (str && fastcmp(str,"doomednum")))
1040 			info->doomednum = (INT32)luaL_checkinteger(L, 3);
1041 		else if (i == 2 || (str && fastcmp(str,"spawnstate"))) {
1042 			value = luaL_checkinteger(L, 3);
1043 			if (value < S_NULL || value >= NUMSTATES)
1044 				return luaL_error(L, "spawnstate number %d is invalid.", value);
1045 			info->spawnstate = (statenum_t)value;
1046 		} else if (i == 3 || (str && fastcmp(str,"spawnhealth")))
1047 			info->spawnhealth = (INT32)luaL_checkinteger(L, 3);
1048 		else if (i == 4 || (str && fastcmp(str,"seestate"))) {
1049 			value = luaL_checkinteger(L, 3);
1050 			if (value < S_NULL || value >= NUMSTATES)
1051 				return luaL_error(L, "seestate number %d is invalid.", value);
1052 			info->seestate = (statenum_t)value;
1053 		} else if (i == 5 || (str && fastcmp(str,"seesound"))) {
1054 			value = luaL_checkinteger(L, 3);
1055 			if (value < sfx_None || value >= NUMSFX)
1056 				return luaL_error(L, "seesound number %d is invalid.", value);
1057 			info->seesound = (sfxenum_t)value;
1058 		} else if (i == 6 || (str && fastcmp(str,"reactiontime")))
1059 			info->reactiontime = (INT32)luaL_checkinteger(L, 3);
1060 		else if (i == 7 || (str && fastcmp(str,"attacksound")))
1061 			info->attacksound = luaL_checkinteger(L, 3);
1062 		else if (i == 8 || (str && fastcmp(str,"painstate")))
1063 			info->painstate = luaL_checkinteger(L, 3);
1064 		else if (i == 9 || (str && fastcmp(str,"painchance")))
1065 			info->painchance = (INT32)luaL_checkinteger(L, 3);
1066 		else if (i == 10 || (str && fastcmp(str,"painsound")))
1067 			info->painsound = luaL_checkinteger(L, 3);
1068 		else if (i == 11 || (str && fastcmp(str,"meleestate")))
1069 			info->meleestate = luaL_checkinteger(L, 3);
1070 		else if (i == 12 || (str && fastcmp(str,"missilestate")))
1071 			info->missilestate = luaL_checkinteger(L, 3);
1072 		else if (i == 13 || (str && fastcmp(str,"deathstate")))
1073 			info->deathstate = luaL_checkinteger(L, 3);
1074 		else if (i == 14 || (str && fastcmp(str,"xdeathstate")))
1075 			info->xdeathstate = luaL_checkinteger(L, 3);
1076 		else if (i == 15 || (str && fastcmp(str,"deathsound")))
1077 			info->deathsound = luaL_checkinteger(L, 3);
1078 		else if (i == 16 || (str && fastcmp(str,"speed")))
1079 			info->speed = luaL_checkfixed(L, 3);
1080 		else if (i == 17 || (str && fastcmp(str,"radius")))
1081 			info->radius = luaL_checkfixed(L, 3);
1082 		else if (i == 18 || (str && fastcmp(str,"height")))
1083 			info->height = luaL_checkfixed(L, 3);
1084 		else if (i == 19 || (str && fastcmp(str,"dispoffset")))
1085 			info->dispoffset = (INT32)luaL_checkinteger(L, 3);
1086 		else if (i == 20 || (str && fastcmp(str,"mass")))
1087 			info->mass = (INT32)luaL_checkinteger(L, 3);
1088 		else if (i == 21 || (str && fastcmp(str,"damage")))
1089 			info->damage = (INT32)luaL_checkinteger(L, 3);
1090 		else if (i == 22 || (str && fastcmp(str,"activesound")))
1091 			info->activesound = luaL_checkinteger(L, 3);
1092 		else if (i == 23 || (str && fastcmp(str,"flags")))
1093 			info->flags = (INT32)luaL_checkinteger(L, 3);
1094 		else if (i == 24 || (str && fastcmp(str,"raisestate"))) {
1095 			info->raisestate = luaL_checkinteger(L, 3);
1096 		}
1097 		lua_pop(L, 1);
1098 	}
1099 	return 0;
1100 }
1101 
1102 // #mobjinfo -> NUMMOBJTYPES
lib_mobjinfolen(lua_State * L)1103 static int lib_mobjinfolen(lua_State *L)
1104 {
1105 	lua_pushinteger(L, NUMMOBJTYPES);
1106 	return 1;
1107 }
1108 
1109 // mobjinfo_t *, field -> number
mobjinfo_get(lua_State * L)1110 static int mobjinfo_get(lua_State *L)
1111 {
1112 	mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
1113 	const char *field = luaL_checkstring(L, 2);
1114 
1115 	I_Assert(info != NULL);
1116 	I_Assert(info >= mobjinfo);
1117 
1118 	if (fastcmp(field,"doomednum"))
1119 		lua_pushinteger(L, info->doomednum);
1120 	else if (fastcmp(field,"spawnstate"))
1121 		lua_pushinteger(L, info->spawnstate);
1122 	else if (fastcmp(field,"spawnhealth"))
1123 		lua_pushinteger(L, info->spawnhealth);
1124 	else if (fastcmp(field,"seestate"))
1125 		lua_pushinteger(L, info->seestate);
1126 	else if (fastcmp(field,"seesound"))
1127 		lua_pushinteger(L, info->seesound);
1128 	else if (fastcmp(field,"reactiontime"))
1129 		lua_pushinteger(L, info->reactiontime);
1130 	else if (fastcmp(field,"attacksound"))
1131 		lua_pushinteger(L, info->attacksound);
1132 	else if (fastcmp(field,"painstate"))
1133 		lua_pushinteger(L, info->painstate);
1134 	else if (fastcmp(field,"painchance"))
1135 		lua_pushinteger(L, info->painchance);
1136 	else if (fastcmp(field,"painsound"))
1137 		lua_pushinteger(L, info->painsound);
1138 	else if (fastcmp(field,"meleestate"))
1139 		lua_pushinteger(L, info->meleestate);
1140 	else if (fastcmp(field,"missilestate"))
1141 		lua_pushinteger(L, info->missilestate);
1142 	else if (fastcmp(field,"deathstate"))
1143 		lua_pushinteger(L, info->deathstate);
1144 	else if (fastcmp(field,"xdeathstate"))
1145 		lua_pushinteger(L, info->xdeathstate);
1146 	else if (fastcmp(field,"deathsound"))
1147 		lua_pushinteger(L, info->deathsound);
1148 	else if (fastcmp(field,"speed"))
1149 		lua_pushinteger(L, info->speed); // sometimes it's fixed_t, sometimes it's not...
1150 	else if (fastcmp(field,"radius"))
1151 		lua_pushfixed(L, info->radius);
1152 	else if (fastcmp(field,"height"))
1153 		lua_pushfixed(L, info->height);
1154 	else if (fastcmp(field,"dispoffset"))
1155 		lua_pushinteger(L, info->dispoffset);
1156 	else if (fastcmp(field,"mass"))
1157 		lua_pushinteger(L, info->mass);
1158 	else if (fastcmp(field,"damage"))
1159 		lua_pushinteger(L, info->damage);
1160 	else if (fastcmp(field,"activesound"))
1161 		lua_pushinteger(L, info->activesound);
1162 	else if (fastcmp(field,"flags"))
1163 		lua_pushinteger(L, info->flags);
1164 	else if (fastcmp(field,"raisestate"))
1165 		lua_pushinteger(L, info->raisestate);
1166 	else {
1167 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
1168 		I_Assert(lua_istable(L, -1));
1169 		lua_pushlightuserdata(L, info);
1170 		lua_rawget(L, -2);
1171 		if (!lua_istable(L, -1)) { // no extra values table
1172 			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", field);
1173 			return 0;
1174 		}
1175 		lua_getfield(L, -1, field);
1176 		if (lua_isnil(L, -1)) // no value for this field
1177 			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", field);
1178 	}
1179 	return 1;
1180 }
1181 
1182 // mobjinfo_t *, field, number -> mobjinfo[]
mobjinfo_set(lua_State * L)1183 static int mobjinfo_set(lua_State *L)
1184 {
1185 	mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
1186 	const char *field = luaL_checkstring(L, 2);
1187 
1188 	if (hud_running)
1189 		return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
1190 	if (hook_cmd_running)
1191 		return luaL_error(L, "Do not alter mobjinfo in CMD building code!");
1192 
1193 	I_Assert(info != NULL);
1194 	I_Assert(info >= mobjinfo);
1195 
1196 	if (fastcmp(field,"doomednum"))
1197 		info->doomednum = (INT32)luaL_checkinteger(L, 3);
1198 	else if (fastcmp(field,"spawnstate"))
1199 		info->spawnstate = luaL_checkinteger(L, 3);
1200 	else if (fastcmp(field,"spawnhealth"))
1201 		info->spawnhealth = (INT32)luaL_checkinteger(L, 3);
1202 	else if (fastcmp(field,"seestate"))
1203 		info->seestate = luaL_checkinteger(L, 3);
1204 	else if (fastcmp(field,"seesound"))
1205 		info->seesound = luaL_checkinteger(L, 3);
1206 	else if (fastcmp(field,"reactiontime"))
1207 		info->reactiontime = (INT32)luaL_checkinteger(L, 3);
1208 	else if (fastcmp(field,"attacksound"))
1209 		info->attacksound = luaL_checkinteger(L, 3);
1210 	else if (fastcmp(field,"painstate"))
1211 		info->painstate = luaL_checkinteger(L, 3);
1212 	else if (fastcmp(field,"painchance"))
1213 		info->painchance = (INT32)luaL_checkinteger(L, 3);
1214 	else if (fastcmp(field,"painsound"))
1215 		info->painsound = luaL_checkinteger(L, 3);
1216 	else if (fastcmp(field,"meleestate"))
1217 		info->meleestate = luaL_checkinteger(L, 3);
1218 	else if (fastcmp(field,"missilestate"))
1219 		info->missilestate = luaL_checkinteger(L, 3);
1220 	else if (fastcmp(field,"deathstate"))
1221 		info->deathstate = luaL_checkinteger(L, 3);
1222 	else if (fastcmp(field,"xdeathstate"))
1223 		info->xdeathstate = luaL_checkinteger(L, 3);
1224 	else if (fastcmp(field,"deathsound"))
1225 		info->deathsound = luaL_checkinteger(L, 3);
1226 	else if (fastcmp(field,"speed"))
1227 		info->speed = luaL_checkfixed(L, 3);
1228 	else if (fastcmp(field,"radius"))
1229 		info->radius = luaL_checkfixed(L, 3);
1230 	else if (fastcmp(field,"height"))
1231 		info->height = luaL_checkfixed(L, 3);
1232 	else if (fastcmp(field,"dispoffset"))
1233 		info->dispoffset = (INT32)luaL_checkinteger(L, 3);
1234 	else if (fastcmp(field,"mass"))
1235 		info->mass = (INT32)luaL_checkinteger(L, 3);
1236 	else if (fastcmp(field,"damage"))
1237 		info->damage = (INT32)luaL_checkinteger(L, 3);
1238 	else if (fastcmp(field,"activesound"))
1239 		info->activesound = luaL_checkinteger(L, 3);
1240 	else if (fastcmp(field,"flags"))
1241 		info->flags = (INT32)luaL_checkinteger(L, 3);
1242 	else if (fastcmp(field,"raisestate"))
1243 		info->raisestate = luaL_checkinteger(L, 3);
1244 	else {
1245 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
1246 		I_Assert(lua_istable(L, -1));
1247 		lua_pushlightuserdata(L, info);
1248 		lua_rawget(L, -2);
1249 		if (lua_isnil(L, -1)) {
1250 			// This index doesn't have a table for extra values yet, let's make one.
1251 			lua_pop(L, 1);
1252 			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobjinfo_t", field);
1253 			lua_newtable(L);
1254 			lua_pushlightuserdata(L, info);
1255 			lua_pushvalue(L, -2); // ext value table
1256 			lua_rawset(L, -4); // LREG_EXTVARS table
1257 		}
1258 		lua_pushvalue(L, 3); // value to store
1259 		lua_setfield(L, -2, field);
1260 		lua_pop(L, 2);
1261 	}
1262 	//else
1263 		//return luaL_error(L, LUA_QL("mobjinfo_t") " has no field named " LUA_QS, field);
1264 	return 0;
1265 }
1266 
1267 // mobjinfo_t * -> MT_*
mobjinfo_num(lua_State * L)1268 static int mobjinfo_num(lua_State *L)
1269 {
1270 	mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
1271 
1272 	I_Assert(info != NULL);
1273 	I_Assert(info >= mobjinfo);
1274 
1275 	lua_pushinteger(L, info-mobjinfo);
1276 	return 1;
1277 }
1278 
1279 //////////////
1280 // SFX INFO //
1281 //////////////
1282 
1283 // Arbitrary S_sfx[] table index -> sfxinfo_t *
lib_getSfxInfo(lua_State * L)1284 static int lib_getSfxInfo(lua_State *L)
1285 {
1286 	UINT32 i;
1287 	lua_remove(L, 1);
1288 
1289 	i = luaL_checkinteger(L, 1);
1290 	if (i == 0 || i >= NUMSFX)
1291 		return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, NUMSFX-1);
1292 	LUA_PushUserdata(L, &S_sfx[i], META_SFXINFO);
1293 	return 1;
1294 }
1295 
1296 // stack: dummy, S_sfx[] table index, table of values to set.
lib_setSfxInfo(lua_State * L)1297 static int lib_setSfxInfo(lua_State *L)
1298 {
1299 	sfxinfo_t *info;
1300 
1301 	lua_remove(L, 1);
1302 	{
1303 		UINT32 i = luaL_checkinteger(L, 1);
1304 		if (i == 0 || i >= NUMSFX)
1305 			return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, NUMSFX-1);
1306 		info = &S_sfx[i]; // get the sfxinfo to assign to.
1307 	}
1308 	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
1309 	lua_remove(L, 1); // pop sfx num, don't need it any more.
1310 	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the sfx.
1311 
1312 	if (hud_running)
1313 		return luaL_error(L, "Do not alter sfxinfo in HUD rendering code!");
1314 	if (hook_cmd_running)
1315 		return luaL_error(L, "Do not alter sfxinfo in CMD building code!");
1316 
1317 	lua_pushnil(L);
1318 	while (lua_next(L, 1)) {
1319 		enum sfxinfo_write i;
1320 
1321 		if (lua_isnumber(L, 2))
1322 			i = lua_tointeger(L, 2) - 1; // lua is one based, this enum is zero based.
1323 		else
1324 			i = luaL_checkoption(L, 2, NULL, sfxinfo_wopt);
1325 
1326 		switch(i)
1327 		{
1328 		case sfxinfow_singular:
1329 			info->singularity = luaL_checkboolean(L, 3);
1330 			break;
1331 		case sfxinfow_priority:
1332 			info->priority = (INT32)luaL_checkinteger(L, 3);
1333 			break;
1334 		case sfxinfow_flags:
1335 			info->pitch = (INT32)luaL_checkinteger(L, 3);
1336 			break;
1337 		case sfxinfow_caption:
1338 			strlcpy(info->caption, luaL_checkstring(L, 3), sizeof(info->caption));
1339 			break;
1340 		default:
1341 			break;
1342 		}
1343 		lua_pop(L, 1);
1344 	}
1345 
1346 	return 0;
1347 }
1348 
lib_sfxlen(lua_State * L)1349 static int lib_sfxlen(lua_State *L)
1350 {
1351 	lua_pushinteger(L, NUMSFX);
1352 	return 1;
1353 }
1354 
1355 // sfxinfo_t *, field
sfxinfo_get(lua_State * L)1356 static int sfxinfo_get(lua_State *L)
1357 {
1358 	sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
1359 	enum sfxinfo_read field = luaL_checkoption(L, 2, NULL, sfxinfo_ropt);
1360 
1361 	I_Assert(sfx != NULL);
1362 
1363 	switch (field)
1364 	{
1365 	case sfxinfor_name:
1366 		lua_pushstring(L, sfx->name);
1367 		return 1;
1368 	case sfxinfor_singular:
1369 		lua_pushboolean(L, sfx->singularity);
1370 		return 1;
1371 	case sfxinfor_priority:
1372 		lua_pushinteger(L, sfx->priority);
1373 		return 1;
1374 	case sfxinfor_flags:
1375 		lua_pushinteger(L, sfx->pitch);
1376 		return 1;
1377 	case sfxinfor_caption:
1378 		lua_pushstring(L, sfx->caption);
1379 		return 1;
1380 	case sfxinfor_skinsound:
1381 		lua_pushinteger(L, sfx->skinsound);
1382 		return 1;
1383 	default:
1384 		return luaL_error(L, "Field does not exist in sfxinfo_t");
1385 	}
1386 	return 0;
1387 }
1388 
1389 // sfxinfo_t *, field, value
sfxinfo_set(lua_State * L)1390 static int sfxinfo_set(lua_State *L)
1391 {
1392 	sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
1393 	enum sfxinfo_write field = luaL_checkoption(L, 2, NULL, sfxinfo_wopt);
1394 
1395 	if (hud_running)
1396 		return luaL_error(L, "Do not alter S_sfx in HUD rendering code!");
1397 	if (hook_cmd_running)
1398 		return luaL_error(L, "Do not alter S_sfx in CMD building code!");
1399 
1400 	I_Assert(sfx != NULL);
1401 
1402 	lua_remove(L, 1); // remove sfxinfo
1403 	lua_remove(L, 1); // remove field
1404 	lua_settop(L, 1); // leave only one value
1405 
1406 	switch (field)
1407 	{
1408 	case sfxinfow_singular:
1409 		sfx->singularity = luaL_checkboolean(L, 1);
1410 		break;
1411 	case sfxinfow_priority:
1412 		sfx->priority = luaL_checkinteger(L, 1);
1413 		break;
1414 	case sfxinfow_flags:
1415 		sfx->pitch = luaL_checkinteger(L, 1);
1416 		break;
1417 	case sfxinfow_caption:
1418 		strlcpy(sfx->caption, luaL_checkstring(L, 1), sizeof(sfx->caption));
1419 		break;
1420 	default:
1421 		return luaL_error(L, "Field does not exist in sfxinfo_t");
1422 	}
1423 	return 0;
1424 }
1425 
sfxinfo_num(lua_State * L)1426 static int sfxinfo_num(lua_State *L)
1427 {
1428 	sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
1429 
1430 	I_Assert(sfx != NULL);
1431 	I_Assert(sfx >= S_sfx);
1432 
1433 	lua_pushinteger(L, (UINT32)(sfx-S_sfx));
1434 	return 1;
1435 }
1436 
1437 //////////////
1438 // LUABANKS //
1439 //////////////
1440 
lib_getluabanks(lua_State * L)1441 static int lib_getluabanks(lua_State *L)
1442 {
1443 	UINT8 i;
1444 
1445 	lua_remove(L, 1); // don't care about luabanks[] dummy userdata.
1446 
1447 	if (lua_isnumber(L, 1))
1448 		i = lua_tonumber(L, 1);
1449 	else
1450 		return luaL_error(L, "luabanks[] invalid index");
1451 
1452 	if (i >= NUM_LUABANKS)
1453 		luaL_error(L, "luabanks[] index %d out of range (%d - %d)", i, 0, NUM_LUABANKS-1);
1454 
1455 	lua_pushinteger(L, luabanks[i]);
1456 	return 1;
1457 }
1458 
lib_setluabanks(lua_State * L)1459 static int lib_setluabanks(lua_State *L)
1460 {
1461 	UINT8 i;
1462 	INT32 j = 0;
1463 
1464 	if (hud_running)
1465 		return luaL_error(L, "Do not alter luabanks[] in HUD rendering code!");
1466 	if (hook_cmd_running)
1467 		return luaL_error(L, "Do not alter luabanks[] in CMD building code!");
1468 
1469 	lua_remove(L, 1); // don't care about luabanks[] dummy userdata.
1470 
1471 	if (lua_isnumber(L, 1))
1472 		i = lua_tonumber(L, 1);
1473 	else
1474 		return luaL_error(L, "luabanks[] invalid index");
1475 
1476 	if (i >= NUM_LUABANKS)
1477 		luaL_error(L, "luabanks[] index %d out of range (%d - %d)", i, 0, NUM_LUABANKS-1);
1478 
1479 	if (lua_isnumber(L, 2))
1480 		j = lua_tonumber(L, 2);
1481 	else
1482 		return luaL_error(L, "luabanks[] invalid set");
1483 
1484 	luabanks[i] = j;
1485 	return 0;
1486 }
1487 
lib_luabankslen(lua_State * L)1488 static int lib_luabankslen(lua_State *L)
1489 {
1490 	lua_pushinteger(L, NUM_LUABANKS);
1491 	return 1;
1492 }
1493 
1494 ////////////////////
1495 // SKINCOLOR INFO //
1496 ////////////////////
1497 
1498 // Arbitrary skincolors[] table index -> skincolor_t *
lib_getSkinColor(lua_State * L)1499 static int lib_getSkinColor(lua_State *L)
1500 {
1501 	UINT32 i;
1502 	lua_remove(L, 1);
1503 
1504 	i = luaL_checkinteger(L, 1);
1505 	if (!i || i >= numskincolors)
1506 		return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", i, numskincolors-1);
1507 	LUA_PushUserdata(L, &skincolors[i], META_SKINCOLOR);
1508 	return 1;
1509 }
1510 
1511 //Set the entire c->ramp array
setRamp(lua_State * L,skincolor_t * c)1512 static void setRamp(lua_State *L, skincolor_t* c) {
1513 	UINT32 i;
1514 	lua_pushnil(L);
1515 	for (i=0; i<COLORRAMPSIZE; i++) {
1516 		if (lua_objlen(L,-2)!=COLORRAMPSIZE) {
1517 			luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be %d entries long; got %d.", COLORRAMPSIZE, lua_objlen(L,-2));
1518 			break;
1519 		}
1520 		if (lua_next(L, -2) != 0) {
1521 			c->ramp[i] = lua_isnumber(L,-1) ? (UINT8)luaL_checkinteger(L,-1) : 120;
1522 			lua_pop(L, 1);
1523 		} else
1524 			c->ramp[i] = 120;
1525 	}
1526 	lua_pop(L,1);
1527 }
1528 
1529 // Lua table full of data -> skincolors[]
lib_setSkinColor(lua_State * L)1530 static int lib_setSkinColor(lua_State *L)
1531 {
1532 	UINT32 j;
1533 	skincolor_t *info;
1534 	UINT16 cnum; //skincolor num
1535 	lua_remove(L, 1); // don't care about skincolors[] userdata.
1536 	{
1537 		cnum = (UINT16)luaL_checkinteger(L, 1);
1538 		if (!cnum || cnum >= numskincolors)
1539 			return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", cnum, numskincolors-1);
1540 		info = &skincolors[cnum]; // get the skincolor to assign to.
1541 	}
1542 	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
1543 	lua_remove(L, 1); // pop skincolor num, don't need it any more.
1544 	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the skincolor.
1545 
1546 	if (hud_running)
1547 		return luaL_error(L, "Do not alter skincolors in HUD rendering code!");
1548 	if (hook_cmd_running)
1549 		return luaL_error(L, "Do not alter skincolors in CMD building code!");
1550 
1551 	// clear the skincolor to start with, in case of missing table elements
1552 	memset(info,0,sizeof(skincolor_t));
1553 
1554 	Color_cons_t[cnum].value = cnum;
1555 	lua_pushnil(L);
1556 	while (lua_next(L, 1)) {
1557 		lua_Integer i = 0;
1558 		const char *str = NULL;
1559 		if (lua_isnumber(L, 2))
1560 			i = lua_tointeger(L, 2);
1561 		else
1562 			str = luaL_checkstring(L, 2);
1563 
1564 		if (i == 1 || (str && fastcmp(str,"name"))) {
1565 			const char* n = luaL_checkstring(L, 3);
1566 			strlcpy(info->name, n, MAXCOLORNAME+1);
1567 			if (strlen(n) > MAXCOLORNAME)
1568 				CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; clipped to %s.\n", n, MAXCOLORNAME, info->name);
1569 #if 0
1570 			if (strchr(info->name, ' ') != NULL)
1571 				CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') contains spaces.\n", info->name);
1572 #endif
1573 
1574 			if (info->name[0] != '\0') // don't check empty string for dupe
1575 			{
1576 				UINT16 dupecheck = R_GetColorByName(info->name);
1577 				if (!stricmp(info->name, skincolors[SKINCOLOR_NONE].name) || (dupecheck && (dupecheck != info-skincolors)))
1578 					CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') is a duplicate of another skincolor's name.\n", info->name);
1579 			}
1580 		} else if (i == 2 || (str && fastcmp(str,"ramp"))) {
1581 			if (!lua_istable(L, 3) && luaL_checkudata(L, 3, META_COLORRAMP) == NULL)
1582 				return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be a table or array.");
1583 			else if (lua_istable(L, 3))
1584 				setRamp(L, info);
1585 			else
1586 				for (j=0; j<COLORRAMPSIZE; j++)
1587 					info->ramp[j] = (*((UINT8 **)luaL_checkudata(L, 3, META_COLORRAMP)))[j];
1588 			skincolor_modified[cnum] = true;
1589 		} else if (i == 3 || (str && fastcmp(str,"invcolor"))) {
1590 			UINT16 v = (UINT16)luaL_checkinteger(L, 3);
1591 			if (v >= numskincolors)
1592 				return luaL_error(L, "skincolor_t field 'invcolor' out of range (1 - %d)", numskincolors-1);
1593 			info->invcolor = v;
1594 		} else if (i == 4 || (str && fastcmp(str,"invshade")))
1595 			info->invshade = (UINT8)luaL_checkinteger(L, 3)%COLORRAMPSIZE;
1596 		else if (i == 5 || (str && fastcmp(str,"chatcolor")))
1597 			info->chatcolor = (UINT16)luaL_checkinteger(L, 3);
1598 		else if (i == 6 || (str && fastcmp(str,"accessible"))) {
1599 			boolean v = lua_toboolean(L, 3);
1600 			if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
1601 				return luaL_error(L, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
1602 			else
1603 				info->accessible = v;
1604 		}
1605 		lua_pop(L, 1);
1606 	}
1607 	return 0;
1608 }
1609 
1610 // #skincolors -> numskincolors
lib_skincolorslen(lua_State * L)1611 static int lib_skincolorslen(lua_State *L)
1612 {
1613 	lua_pushinteger(L, numskincolors);
1614 	return 1;
1615 }
1616 
1617 // skincolor_t *, field -> number
skincolor_get(lua_State * L)1618 static int skincolor_get(lua_State *L)
1619 {
1620 	skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));
1621 	const char *field = luaL_checkstring(L, 2);
1622 
1623 	I_Assert(info != NULL);
1624 	I_Assert(info >= skincolors);
1625 
1626 	if (fastcmp(field,"name"))
1627 		lua_pushstring(L, info->name);
1628 	else if (fastcmp(field,"ramp"))
1629 		LUA_PushUserdata(L, info->ramp, META_COLORRAMP);
1630 	else if (fastcmp(field,"invcolor"))
1631 		lua_pushinteger(L, info->invcolor);
1632 	else if (fastcmp(field,"invshade"))
1633 		lua_pushinteger(L, info->invshade);
1634 	else if (fastcmp(field,"chatcolor"))
1635 		lua_pushinteger(L, info->chatcolor);
1636 	else if (fastcmp(field,"accessible"))
1637 		lua_pushboolean(L, info->accessible);
1638 	else {
1639 		CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "skincolor_t", field);
1640 		return 0;
1641 	}
1642 	return 1;
1643 }
1644 
1645 // skincolor_t *, field, number -> skincolors[]
skincolor_set(lua_State * L)1646 static int skincolor_set(lua_State *L)
1647 {
1648 	UINT32 i;
1649 	skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));
1650 	const char *field = luaL_checkstring(L, 2);
1651 	UINT16 cnum = (UINT16)(info-skincolors);
1652 
1653 	I_Assert(info != NULL);
1654 	I_Assert(info >= skincolors);
1655 
1656 	if (!cnum || cnum >= numskincolors)
1657 		return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", cnum, numskincolors-1);
1658 
1659 	if (fastcmp(field,"name")) {
1660 		const char* n = luaL_checkstring(L, 3);
1661 		strlcpy(info->name, n, MAXCOLORNAME+1);
1662 		if (strlen(n) > MAXCOLORNAME)
1663 			CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; clipped to %s.\n", n, MAXCOLORNAME, info->name);
1664 #if 0
1665 		if (strchr(info->name, ' ') != NULL)
1666 			CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') contains spaces.\n", info->name);
1667 #endif
1668 
1669 		if (info->name[0] != '\0') // don't check empty string for dupe
1670 		{
1671 			UINT16 dupecheck = R_GetColorByName(info->name);
1672 			if (!stricmp(info->name, skincolors[SKINCOLOR_NONE].name) || (dupecheck && (dupecheck != cnum)))
1673 				CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') is a duplicate of another skincolor's name.\n", info->name);
1674 		}
1675 	} else if (fastcmp(field,"ramp")) {
1676 		if (!lua_istable(L, 3) && luaL_checkudata(L, 3, META_COLORRAMP) == NULL)
1677 			return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be a table or array.");
1678 		else if (lua_istable(L, 3))
1679 			setRamp(L, info);
1680 		else
1681 			for (i=0; i<COLORRAMPSIZE; i++)
1682 				info->ramp[i] = (*((UINT8 **)luaL_checkudata(L, 3, META_COLORRAMP)))[i];
1683 		skincolor_modified[cnum] = true;
1684 	} else if (fastcmp(field,"invcolor")) {
1685 		UINT16 v = (UINT16)luaL_checkinteger(L, 3);
1686 		if (v >= numskincolors)
1687 			return luaL_error(L, "skincolor_t field 'invcolor' out of range (1 - %d)", numskincolors-1);
1688 		info->invcolor = v;
1689 	} else if (fastcmp(field,"invshade"))
1690 		info->invshade = (UINT8)luaL_checkinteger(L, 3)%COLORRAMPSIZE;
1691 	else if (fastcmp(field,"chatcolor"))
1692 		info->chatcolor = (UINT16)luaL_checkinteger(L, 3);
1693 	else if (fastcmp(field,"accessible")) {
1694 		boolean v = lua_toboolean(L, 3);
1695 		if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
1696 			return luaL_error(L, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
1697 		else
1698 			info->accessible = v;
1699 	} else
1700 		CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "skincolor_t", field);
1701 	return 1;
1702 }
1703 
1704 // skincolor_t * -> SKINCOLOR_*
skincolor_num(lua_State * L)1705 static int skincolor_num(lua_State *L)
1706 {
1707 	skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));
1708 
1709 	I_Assert(info != NULL);
1710 	I_Assert(info >= skincolors);
1711 
1712 	lua_pushinteger(L, info-skincolors);
1713 	return 1;
1714 }
1715 
1716 // ramp, n -> ramp[n]
colorramp_get(lua_State * L)1717 static int colorramp_get(lua_State *L)
1718 {
1719 	UINT8 *colorramp = *((UINT8 **)luaL_checkudata(L, 1, META_COLORRAMP));
1720 	UINT32 n = luaL_checkinteger(L, 2);
1721 	if (n >= COLORRAMPSIZE)
1722 		return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' index %d out of range (0 - %d)", n, COLORRAMPSIZE-1);
1723 	lua_pushinteger(L, colorramp[n]);
1724 	return 1;
1725 }
1726 
1727 // ramp, n, value -> ramp[n] = value
colorramp_set(lua_State * L)1728 static int colorramp_set(lua_State *L)
1729 {
1730 	UINT8 *colorramp = *((UINT8 **)luaL_checkudata(L, 1, META_COLORRAMP));
1731 	UINT16 cnum = (UINT16)(((UINT8*)colorramp - (UINT8*)(skincolors[0].ramp))/sizeof(skincolor_t));
1732 	UINT32 n = luaL_checkinteger(L, 2);
1733 	UINT8 i = (UINT8)luaL_checkinteger(L, 3);
1734 	if (!cnum || cnum >= numskincolors)
1735 		return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", cnum, numskincolors-1);
1736 	if (n >= COLORRAMPSIZE)
1737 		return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' index %d out of range (0 - %d)", n, COLORRAMPSIZE-1);
1738 	if (hud_running)
1739 		return luaL_error(L, "Do not alter skincolor_t in HUD rendering code!");
1740 	if (hook_cmd_running)
1741 		return luaL_error(L, "Do not alter skincolor_t in CMD building code!");
1742 	colorramp[n] = i;
1743 	skincolor_modified[cnum] = true;
1744 	return 0;
1745 }
1746 
1747 // #ramp -> COLORRAMPSIZE
colorramp_len(lua_State * L)1748 static int colorramp_len(lua_State *L)
1749 {
1750 	lua_pushinteger(L, COLORRAMPSIZE);
1751 	return 1;
1752 }
1753 
1754 //////////////////////////////
1755 //
1756 // Now push all these functions into the Lua state!
1757 //
1758 //
LUA_InfoLib(lua_State * L)1759 int LUA_InfoLib(lua_State *L)
1760 {
1761 	// index of A_Lua actions to run for each state
1762 	lua_newtable(L);
1763 	lua_setfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
1764 
1765 	// index of globally available Lua actions by function name
1766 	lua_newtable(L);
1767 	lua_setfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS);
1768 
1769 	luaL_newmetatable(L, META_STATE);
1770 		lua_pushcfunction(L, state_get);
1771 		lua_setfield(L, -2, "__index");
1772 
1773 		lua_pushcfunction(L, state_set);
1774 		lua_setfield(L, -2, "__newindex");
1775 
1776 		lua_pushcfunction(L, state_num);
1777 		lua_setfield(L, -2, "__len");
1778 	lua_pop(L, 1);
1779 
1780 	luaL_newmetatable(L, META_MOBJINFO);
1781 		lua_pushcfunction(L, mobjinfo_get);
1782 		lua_setfield(L, -2, "__index");
1783 
1784 		lua_pushcfunction(L, mobjinfo_set);
1785 		lua_setfield(L, -2, "__newindex");
1786 
1787 		lua_pushcfunction(L, mobjinfo_num);
1788 		lua_setfield(L, -2, "__len");
1789 	lua_pop(L, 1);
1790 
1791 	luaL_newmetatable(L, META_SKINCOLOR);
1792 		lua_pushcfunction(L, skincolor_get);
1793 		lua_setfield(L, -2, "__index");
1794 
1795 		lua_pushcfunction(L, skincolor_set);
1796 		lua_setfield(L, -2, "__newindex");
1797 
1798 		lua_pushcfunction(L, skincolor_num);
1799 		lua_setfield(L, -2, "__len");
1800 	lua_pop(L, 1);
1801 
1802 	luaL_newmetatable(L, META_COLORRAMP);
1803 		lua_pushcfunction(L, colorramp_get);
1804 		lua_setfield(L, -2, "__index");
1805 
1806 		lua_pushcfunction(L, colorramp_set);
1807 		lua_setfield(L, -2, "__newindex");
1808 
1809 		lua_pushcfunction(L, colorramp_len);
1810 		lua_setfield(L, -2, "__len");
1811 	lua_pop(L,1);
1812 
1813 	luaL_newmetatable(L, META_SFXINFO);
1814 		lua_pushcfunction(L, sfxinfo_get);
1815 		lua_setfield(L, -2, "__index");
1816 
1817 		lua_pushcfunction(L, sfxinfo_set);
1818 		lua_setfield(L, -2, "__newindex");
1819 
1820 		lua_pushcfunction(L, sfxinfo_num);
1821 		lua_setfield(L, -2, "__len");
1822 	lua_pop(L, 1);
1823 
1824 	luaL_newmetatable(L, META_SPRITEINFO);
1825 		lua_pushcfunction(L, spriteinfo_get);
1826 		lua_setfield(L, -2, "__index");
1827 
1828 		lua_pushcfunction(L, spriteinfo_set);
1829 		lua_setfield(L, -2, "__newindex");
1830 
1831 		lua_pushcfunction(L, spriteinfo_num);
1832 		lua_setfield(L, -2, "__len");
1833 	lua_pop(L, 1);
1834 
1835 	luaL_newmetatable(L, META_PIVOTLIST);
1836 		lua_pushcfunction(L, pivotlist_get);
1837 		lua_setfield(L, -2, "__index");
1838 
1839 		lua_pushcfunction(L, pivotlist_set);
1840 		lua_setfield(L, -2, "__newindex");
1841 
1842 		lua_pushcfunction(L, pivotlist_num);
1843 		lua_setfield(L, -2, "__len");
1844 	lua_pop(L, 1);
1845 
1846 	luaL_newmetatable(L, META_FRAMEPIVOT);
1847 		lua_pushcfunction(L, framepivot_get);
1848 		lua_setfield(L, -2, "__index");
1849 
1850 		lua_pushcfunction(L, framepivot_set);
1851 		lua_setfield(L, -2, "__newindex");
1852 
1853 		lua_pushcfunction(L, framepivot_num);
1854 		lua_setfield(L, -2, "__len");
1855 	lua_pop(L, 1);
1856 
1857 	lua_newuserdata(L, 0);
1858 		lua_createtable(L, 0, 2);
1859 			lua_pushcfunction(L, lib_getSprname);
1860 			lua_setfield(L, -2, "__index");
1861 
1862 			lua_pushcfunction(L, lib_sprnamelen);
1863 			lua_setfield(L, -2, "__len");
1864 		lua_setmetatable(L, -2);
1865 	lua_setglobal(L, "sprnames");
1866 
1867 	lua_newuserdata(L, 0);
1868 		lua_createtable(L, 0, 2);
1869 			lua_pushcfunction(L, lib_getSpr2name);
1870 			lua_setfield(L, -2, "__index");
1871 
1872 			lua_pushcfunction(L, lib_spr2namelen);
1873 			lua_setfield(L, -2, "__len");
1874 		lua_setmetatable(L, -2);
1875 	lua_setglobal(L, "spr2names");
1876 
1877 	lua_newuserdata(L, 0);
1878 		lua_createtable(L, 0, 2);
1879 			lua_pushcfunction(L, lib_getSpr2default);
1880 			lua_setfield(L, -2, "__index");
1881 
1882 			lua_pushcfunction(L, lib_setSpr2default);
1883 			lua_setfield(L, -2, "__newindex");
1884 
1885 			lua_pushcfunction(L, lib_spr2namelen);
1886 			lua_setfield(L, -2, "__len");
1887 		lua_setmetatable(L, -2);
1888 	lua_setglobal(L, "spr2defaults");
1889 
1890 	lua_newuserdata(L, 0);
1891 		lua_createtable(L, 0, 2);
1892 			lua_pushcfunction(L, lib_getState);
1893 			lua_setfield(L, -2, "__index");
1894 
1895 			lua_pushcfunction(L, lib_setState);
1896 			lua_setfield(L, -2, "__newindex");
1897 
1898 			lua_pushcfunction(L, lib_statelen);
1899 			lua_setfield(L, -2, "__len");
1900 		lua_setmetatable(L, -2);
1901 	lua_setglobal(L, "states");
1902 
1903 	lua_newuserdata(L, 0);
1904 		lua_createtable(L, 0, 2);
1905 			lua_pushcfunction(L, lib_getMobjInfo);
1906 			lua_setfield(L, -2, "__index");
1907 
1908 			lua_pushcfunction(L, lib_setMobjInfo);
1909 			lua_setfield(L, -2, "__newindex");
1910 
1911 			lua_pushcfunction(L, lib_mobjinfolen);
1912 			lua_setfield(L, -2, "__len");
1913 		lua_setmetatable(L, -2);
1914 	lua_setglobal(L, "mobjinfo");
1915 
1916 	lua_newuserdata(L, 0);
1917 		lua_createtable(L, 0, 2);
1918 			lua_pushcfunction(L, lib_getSkinColor);
1919 			lua_setfield(L, -2, "__index");
1920 
1921 			lua_pushcfunction(L, lib_setSkinColor);
1922 			lua_setfield(L, -2, "__newindex");
1923 
1924 			lua_pushcfunction(L, lib_skincolorslen);
1925 			lua_setfield(L, -2, "__len");
1926 		lua_setmetatable(L, -2);
1927 	lua_setglobal(L, "skincolors");
1928 
1929 	lua_newuserdata(L, 0);
1930 		lua_createtable(L, 0, 2);
1931 			lua_pushcfunction(L, lib_getSfxInfo);
1932 			lua_setfield(L, -2, "__index");
1933 
1934 			lua_pushcfunction(L, lib_setSfxInfo);
1935 			lua_setfield(L, -2, "__newindex");
1936 
1937 			lua_pushcfunction(L, lib_sfxlen);
1938 			lua_setfield(L, -2, "__len");
1939 		lua_setmetatable(L, -2);
1940 	lua_pushvalue(L, -1);
1941 	lua_setglobal(L, "S_sfx");
1942 	lua_setglobal(L, "sfxinfo");
1943 
1944 	lua_newuserdata(L, 0);
1945 		lua_createtable(L, 0, 2);
1946 			lua_pushcfunction(L, lib_getSpriteInfo);
1947 			lua_setfield(L, -2, "__index");
1948 
1949 			lua_pushcfunction(L, lib_setSpriteInfo);
1950 			lua_setfield(L, -2, "__newindex");
1951 
1952 			lua_pushcfunction(L, lib_spriteinfolen);
1953 			lua_setfield(L, -2, "__len");
1954 		lua_setmetatable(L, -2);
1955 	lua_setglobal(L, "spriteinfo");
1956 
1957 	luaL_newmetatable(L, META_LUABANKS);
1958 		lua_pushcfunction(L, lib_getluabanks);
1959 		lua_setfield(L, -2, "__index");
1960 
1961 		lua_pushcfunction(L, lib_setluabanks);
1962 		lua_setfield(L, -2, "__newindex");
1963 
1964 		lua_pushcfunction(L, lib_luabankslen);
1965 		lua_setfield(L, -2, "__len");
1966 	lua_pop(L, 1);
1967 
1968 	return 0;
1969 }
1970