lua_infolib.c 56.9 KB
Newer Older
Alam Ed Arias committed
1 2
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
3
// Copyright (C) 2012-2016 by John "JTE" Muniz.
James R. committed
4
// Copyright (C) 2012-2020 by Sonic Team Junior.
Alam Ed Arias committed
5 6 7 8 9 10 11 12 13 14 15 16
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file  lua_infolib.c
/// \brief infotable editing library for Lua scripting

#include "doomdef.h"
#include "fastcmp.h"
#include "info.h"
#include "dehacked.h"
17
#include "deh_tables.h"
18
#include "deh_lua.h"
Alam Ed Arias committed
19 20 21
#include "p_mobj.h"
#include "p_local.h"
#include "z_zone.h"
22
#include "r_patch.h"
Jaime Ita Passos committed
23
#include "r_picformats.h"
24
#include "r_things.h"
kaysrishaq committed
25
#include "r_draw.h" // R_GetColorByName
toaster committed
26
#include "doomstat.h" // luabanks[]
Alam Ed Arias committed
27 28 29 30

#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
31
#include "lua_hook.h" // hook_cmd_running errors
Alam Ed Arias committed
32

33 34
extern CV_PossibleValue_t Color_cons_t[];
extern UINT8 skincolor_modified[];
James Hale committed
35

36
boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor);
Alam Ed Arias committed
37 38
state_t *astate;

Alam Ed Arias committed
39 40 41 42 43
enum sfxinfo_read {
	sfxinfor_name = 0,
	sfxinfor_singular,
	sfxinfor_priority,
	sfxinfor_flags, // "pitch"
44
	sfxinfor_caption,
Alam Ed Arias committed
45
	sfxinfor_skinsound
Alam Ed Arias committed
46
};
Alam Ed Arias committed
47
const char *const sfxinfo_ropt[] = {
Alam Ed Arias committed
48
	"name",
Alam Ed Arias committed
49
	"singular",
Alam Ed Arias committed
50 51
	"priority",
	"flags",
52
	"caption",
Alam Ed Arias committed
53 54 55
	"skinsound",
	NULL};

Alam Ed Arias committed
56 57 58
enum sfxinfo_write {
	sfxinfow_singular = 0,
	sfxinfow_priority,
59 60
	sfxinfow_flags, // "pitch"
	sfxinfow_caption
Alam Ed Arias committed
61 62 63 64 65
};
const char *const sfxinfo_wopt[] = {
	"singular",
	"priority",
	"flags",
66
	"caption",
Alam Ed Arias committed
67 68
	NULL};

69 70
boolean actionsoverridden[NUMACTIONS] = {false};

Alam Ed Arias committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
//
// Sprite Names
//

// push sprite name
static int lib_getSprname(lua_State *L)
{
	UINT32 i;

	lua_remove(L, 1); // don't care about sprnames[] dummy userdata.

	if (lua_isnumber(L, 1))
	{
		i = lua_tonumber(L, 1);
		if (i > NUMSPRITES)
			return 0;
		lua_pushlstring(L, sprnames[i], 4);
		return 1;
	}
	else if (lua_isstring(L, 1))
	{
		const char *name = lua_tostring(L, 1);
		for (i = 0; i < NUMSPRITES; i++)
			if (fastcmp(name, sprnames[i]))
			{
				lua_pushinteger(L, i);
				return 1;
			}
	}
	return 0;
}

/// \todo Maybe make it tally up the used_spr from dehacked?
static int lib_sprnamelen(lua_State *L)
{
	lua_pushinteger(L, NUMSPRITES);
	return 1;
}

110 111 112 113 114 115 116
//
// Player Sprite Names
//

// push sprite name
static int lib_getSpr2name(lua_State *L)
{
Alam Ed Arias committed
117
	playersprite_t i;
118 119 120 121 122 123

	lua_remove(L, 1); // don't care about spr2names[] dummy userdata.

	if (lua_isnumber(L, 1))
	{
		i = lua_tonumber(L, 1);
124
		if (i >= free_spr2)
125 126 127 128 129 130 131
			return 0;
		lua_pushlstring(L, spr2names[i], 4);
		return 1;
	}
	else if (lua_isstring(L, 1))
	{
		const char *name = lua_tostring(L, 1);
132
		for (i = 0; i < free_spr2; i++)
133 134 135 136 137 138 139 140 141
			if (fastcmp(name, spr2names[i]))
			{
				lua_pushinteger(L, i);
				return 1;
			}
	}
	return 0;
}

142 143
static int lib_getSpr2default(lua_State *L)
{
Alam Ed Arias committed
144
	playersprite_t i;
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160

	lua_remove(L, 1); // don't care about spr2defaults[] dummy userdata.

	if (lua_isnumber(L, 1))
		i = lua_tonumber(L, 1);
	else if (lua_isstring(L, 1))
	{
		const char *name = lua_tostring(L, 1);
		for (i = 0; i < free_spr2; i++)
			if (fastcmp(name, spr2names[i]))
				break;
	}
	else
		return luaL_error(L, "spr2defaults[] invalid index");

	if (i >= free_spr2)
toaster committed
161
		return luaL_error(L, "spr2defaults[] index %d out of range (%d - %d)", i, 0, free_spr2-1);
162 163 164 165 166 167 168

	lua_pushinteger(L, spr2defaults[i]);
	return 1;
}

static int lib_setSpr2default(lua_State *L)
{
Alam Ed Arias committed
169
	playersprite_t i;
170 171
	UINT8 j = 0;

172 173
	if (hud_running)
		return luaL_error(L, "Do not alter spr2defaults[] in HUD rendering code!");
174 175
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter spr2defaults[] in CMD building code!");
176 177 178 179 180 181 182 183 184 185

// todo: maybe allow setting below first freeslot..? step 1 is toggling this, step 2 is testing to see whether it's net-safe
#ifdef SETALLSPR2DEFAULTS
#define FIRSTMODIFY 0
#else
#define FIRSTMODIFY SPR2_FIRSTFREESLOT
	if (free_spr2 == SPR2_FIRSTFREESLOT)
		return luaL_error(L, "You can only modify the spr2defaults[] entries of sprite2 freeslots, and none are currently added.");
#endif

186 187 188 189 190 191 192 193
	lua_remove(L, 1); // don't care about spr2defaults[] dummy userdata.

	if (lua_isnumber(L, 1))
		i = lua_tonumber(L, 1);
	else if (lua_isstring(L, 1))
	{
		const char *name = lua_tostring(L, 1);
		for (i = 0; i < free_spr2; i++)
Alam Ed Arias committed
194
		{
195 196
			if (fastcmp(name, spr2names[i]))
				break;
Alam Ed Arias committed
197 198 199
		}
		if (i == free_spr2)
			return luaL_error(L, "spr2defaults[] invalid index");
200 201 202 203
	}
	else
		return luaL_error(L, "spr2defaults[] invalid index");

204 205 206
	if (i < FIRSTMODIFY || i >= free_spr2)
		return luaL_error(L, "spr2defaults[] index %d out of range (%d - %d)", i, FIRSTMODIFY, free_spr2-1);
#undef FIRSTMODIFY
207 208 209 210 211 212 213

	if (lua_isnumber(L, 2))
		j = lua_tonumber(L, 2);
	else if (lua_isstring(L, 2))
	{
		const char *name = lua_tostring(L, 2);
		for (j = 0; j < free_spr2; j++)
Alam Ed Arias committed
214
		{
215 216
			if (fastcmp(name, spr2names[j]))
				break;
Alam Ed Arias committed
217 218
		}
		if (j == free_spr2)
219
			return luaL_error(L, "spr2defaults[] invalid set");
220
	}
221 222
	else
		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 228 229 230

	spr2defaults[i] = j;
	return 0;
}

231 232
static int lib_spr2namelen(lua_State *L)
{
233
	lua_pushinteger(L, free_spr2);
234 235 236
	return 1;
}

237 238 239 240
/////////////////
// SPRITE INFO //
/////////////////

241
// spriteinfo[]
242 243
static int lib_getSpriteInfo(lua_State *L)
{
244
	UINT32 i = NUMSPRITES;
245 246
	lua_remove(L, 1);

247
	if (lua_isstring(L, 1))
248 249 250 251 252 253 254 255 256 257 258
	{
		const char *name = lua_tostring(L, 1);
		INT32 spr;
		for (spr = 0; spr < NUMSPRITES; spr++)
		{
			if (fastcmp(name, sprnames[spr]))
			{
				i = spr;
				break;
			}
		}
259 260 261 262 263 264 265
		if (i == NUMSPRITES)
		{
			char *check;
			i = strtol(name, &check, 10);
			if (check == name || *check != '\0')
				return luaL_error(L, "unknown sprite name %s", name);
		}
266
	}
267
	else
268 269 270 271 272 273 274 275 276 277 278 279
		i = luaL_checkinteger(L, 1);

	if (i == 0 || i >= NUMSPRITES)
		return luaL_error(L, "spriteinfo[] index %d out of range (1 - %d)", i, NUMSPRITES-1);

	LUA_PushUserdata(L, &spriteinfo[i], META_SPRITEINFO);
	return 1;
}

#define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to spriteinfo[] (%s)", e);
#define TYPEERROR(f, t1, t2) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t1), lua_typename(L, t2)))

280 281
static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, int idx)
{
Jaime Ita Passos committed
282
	int okcool = 0;
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	switch (lua_type(L, stk))
	{
		case LUA_TTABLE:
			lua_pushnil(L);
			while (lua_next(L, stk))
			{
				const char *key = NULL;
				lua_Integer ikey = -1;
				lua_Integer value = 0;
				// x or y?
				switch (lua_type(L, stk+1))
				{
					case LUA_TSTRING:
						key = lua_tostring(L, stk+1);
						break;
					case LUA_TNUMBER:
						ikey = lua_tointeger(L, stk+1);
						break;
					default:
						FIELDERROR("pivot key", va("string or number expected, got %s", luaL_typename(L, stk+1)))
				}
				// then get value
				switch (lua_type(L, stk+2))
				{
					case LUA_TNUMBER:
						value = lua_tonumber(L, stk+2);
						break;
310 311 312
					case LUA_TBOOLEAN:
						value = (UINT8)lua_toboolean(L, stk+2);
						break;
313 314 315 316 317 318 319 320
					default:
						TYPEERROR("pivot value", LUA_TNUMBER, lua_type(L, stk+2))
				}
				// finally set omg!!!!!!!!!!!!!!!!!!
				if (ikey == 1 || (key && fastcmp(key, "x")))
					pivot[idx].x = (INT32)value;
				else if (ikey == 2 || (key && fastcmp(key, "y")))
					pivot[idx].y = (INT32)value;
321 322
				else if (ikey == 3 || (key && fastcmp(key, "rotaxis")))
					pivot[idx].rotaxis = (UINT8)value;
323
				else if (ikey == -1 && (key != NULL))
324
					FIELDERROR("pivot key", va("invalid option %s", key));
Jaime Ita Passos committed
325
				okcool = 1;
326 327 328 329 330 331
				lua_pop(L, 1);
			}
			break;
		default:
			TYPEERROR("sprite pivot", LUA_TTABLE, lua_type(L, stk))
	}
Jaime Ita Passos committed
332
	return okcool;
333 334
}

335 336 337 338 339 340 341 342 343 344
static int PopPivotTable(spriteinfo_t *info, lua_State *L, int stk)
{
	// Just in case?
	if (!lua_istable(L, stk))
		TYPEERROR("pivot table", LUA_TTABLE, lua_type(L, stk));

	lua_pushnil(L);
	// stk = 0 has the pivot table
	// stk = 1 has the frame key
	// stk = 2 has the frame table
345
	// stk = 3 has either a string or a number as key
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	// stk = 4 has the value for the key mentioned above
	while (lua_next(L, stk))
	{
		int idx = 0;
		const char *framestr = NULL;
		switch (lua_type(L, stk+1))
		{
			case LUA_TSTRING:
				framestr = lua_tostring(L, stk+1);
				idx = R_Char2Frame(framestr[0]);
				break;
			case LUA_TNUMBER:
				idx = lua_tonumber(L, stk+1);
				break;
			default:
				TYPEERROR("pivot frame", LUA_TNUMBER, lua_type(L, stk+1));
		}
		if ((idx < 0) || (idx >= 64))
			return luaL_error(L, "pivot frame %d out of range (0 - %d)", idx, 63);
		// the values in pivot[] are also tables
366 367
		if (PopPivotSubTable(info->pivot, L, stk+2, idx))
			info->available = true;
368 369 370 371 372 373 374 375 376 377
		lua_pop(L, 1);
	}

	return 0;
}

static int lib_setSpriteInfo(lua_State *L)
{
	spriteinfo_t *info;

378 379 380 381
	if (!lua_lumploading)
		return luaL_error(L, "Do not alter spriteinfo_t from within a hook or coroutine!");
	if (hud_running)
		return luaL_error(L, "Do not alter spriteinfo_t in HUD rendering code!");
382
	if (hook_cmd_running)
LJ Sonic committed
383
		return luaL_error(L, "Do not alter spriteinfo_t in CMD building code!");
384

385 386 387 388 389
	lua_remove(L, 1);
	{
		UINT32 i = luaL_checkinteger(L, 1);
		if (i == 0 || i >= NUMSPRITES)
			return luaL_error(L, "spriteinfo[] index %d out of range (1 - %d)", i, NUMSPRITES-1);
Jaime Ita Passos committed
390
		info = &spriteinfo[i]; // get the spriteinfo to assign to.
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
	}
	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
	lua_remove(L, 1); // pop sprite num, don't need it any more.
	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the spriteinfo.

	lua_pushnil(L);
	while (lua_next(L, 1)) {
		lua_Integer i = 0;
		const char *str = NULL;
		if (lua_isnumber(L, 2))
			i = lua_tointeger(L, 2);
		else
			str = luaL_checkstring(L, 2);

		if (i == 1 || (str && fastcmp(str, "pivot")))
		{
			// pivot[] is a table
			if (lua_istable(L, 3))
				return PopPivotTable(info, L, 3);
			else
				FIELDERROR("pivot", va("%s expected, got %s", lua_typename(L, LUA_TTABLE), luaL_typename(L, -1)))
		}

		lua_pop(L, 1);
	}

	return 0;
}

#undef FIELDERROR
#undef TYPEERROR

static int lib_spriteinfolen(lua_State *L)
{
	lua_pushinteger(L, NUMSPRITES);
	return 1;
}

429
// spriteinfo_t
430 431 432 433 434 435 436
static int spriteinfo_get(lua_State *L)
{
	spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFO));
	const char *field = luaL_checkstring(L, 2);

	I_Assert(sprinfo != NULL);

437
	// push spriteframepivot_t userdata
438 439
	if (fastcmp(field, "pivot"))
	{
440 441 442 443 444
		// bypass LUA_PushUserdata
		void **userdata = lua_newuserdata(L, sizeof(void *));
		*userdata = &sprinfo->pivot;
		luaL_getmetatable(L, META_PIVOTLIST);
		lua_setmetatable(L, -2);
445

446
		// stack is left with the userdata on top, as if getting it had originally succeeded.
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
		return 1;
	}
	else
		return luaL_error(L, LUA_QL("spriteinfo_t") " has no field named " LUA_QS, field);

	return 0;
}

static int spriteinfo_set(lua_State *L)
{
	spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFO));
	const char *field = luaL_checkstring(L, 2);

	if (!lua_lumploading)
		return luaL_error(L, "Do not alter spriteinfo_t from within a hook or coroutine!");
	if (hud_running)
		return luaL_error(L, "Do not alter spriteinfo_t in HUD rendering code!");
464 465
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter spriteinfo_t in CMD building code!");
466 467 468 469 470 471 472 473

	I_Assert(sprinfo != NULL);

	lua_remove(L, 1); // remove spriteinfo
	lua_remove(L, 1); // remove field
	lua_settop(L, 1); // leave only one value

	if (fastcmp(field, "pivot"))
474 475 476 477 478 479 480 481 482 483 484 485
	{
		// pivot[] is a table
		if (lua_istable(L, 1))
			return PopPivotTable(sprinfo, L, 1);
		// pivot[] is userdata
		else if (lua_isuserdata(L, 1))
		{
			spriteframepivot_t *pivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
			memcpy(&sprinfo->pivot, pivot, sizeof(spriteframepivot_t));
			sprinfo->available = true; // Just in case?
		}
	}
486 487 488 489 490 491 492 493
	else
		return luaL_error(L, va("Field %s does not exist in spriteinfo_t", field));

	return 0;
}

static int spriteinfo_num(lua_State *L)
{
494
	spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFO));
495 496 497 498 499 500 501 502

	I_Assert(sprinfo != NULL);
	I_Assert(sprinfo >= spriteinfo);

	lua_pushinteger(L, (UINT32)(sprinfo-spriteinfo));
	return 1;
}

503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
// framepivot_t
static int pivotlist_get(lua_State *L)
{
	void **userdata;
	spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
	const char *field = luaL_checkstring(L, 2);
	UINT8 frame;

	I_Assert(framepivot != NULL);

	frame = R_Char2Frame(field[0]);
	if (frame == 255)
		luaL_error(L, "invalid frame %s", field);

	// bypass LUA_PushUserdata
	userdata = lua_newuserdata(L, sizeof(void *));
	*userdata = &framepivot[frame];
	luaL_getmetatable(L, META_FRAMEPIVOT);
	lua_setmetatable(L, -2);

	// stack is left with the userdata on top, as if getting it had originally succeeded.
	return 1;
}

static int pivotlist_set(lua_State *L)
{
	// Because I already know it's a spriteframepivot_t anyway
	spriteframepivot_t *pivotlist = *((spriteframepivot_t **)lua_touserdata(L, 1));
	//spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
	const char *field = luaL_checkstring(L, 2);
	UINT8 frame;

535 536 537 538
	if (!lua_lumploading)
		return luaL_error(L, "Do not alter spriteframepivot_t from within a hook or coroutine!");
	if (hud_running)
		return luaL_error(L, "Do not alter spriteframepivot_t in HUD rendering code!");
539 540
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!");
541

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
	I_Assert(pivotlist != NULL);

	frame = R_Char2Frame(field[0]);
	if (frame == 255)
		luaL_error(L, "invalid frame %s", field);

	// pivot[] is a table
	if (lua_istable(L, 3))
		return PopPivotSubTable(pivotlist, L, 3, frame);
	// pivot[] is userdata
	else if (lua_isuserdata(L, 3))
	{
		spriteframepivot_t *copypivot = *((spriteframepivot_t **)luaL_checkudata(L, 3, META_FRAMEPIVOT));
		memcpy(&pivotlist[frame], copypivot, sizeof(spriteframepivot_t));
	}

	return 0;
}

static int pivotlist_num(lua_State *L)
{
	lua_pushinteger(L, 64);
	return 1;
}

static int framepivot_get(lua_State *L)
{
	spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
	const char *field = luaL_checkstring(L, 2);

	I_Assert(framepivot != NULL);

	if (fastcmp("x", field))
		lua_pushinteger(L, framepivot->x);
	else if (fastcmp("y", field))
		lua_pushinteger(L, framepivot->y);
578 579
	else if (fastcmp("rotaxis", field))
		lua_pushinteger(L, (UINT8)framepivot->rotaxis);
580 581 582 583 584 585 586 587 588 589 590
	else
		return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field));

	return 1;
}

static int framepivot_set(lua_State *L)
{
	spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
	const char *field = luaL_checkstring(L, 2);

591 592 593 594
	if (!lua_lumploading)
		return luaL_error(L, "Do not alter spriteframepivot_t from within a hook or coroutine!");
	if (hud_running)
		return luaL_error(L, "Do not alter spriteframepivot_t in HUD rendering code!");
595 596
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!");
597

598 599 600 601 602 603
	I_Assert(framepivot != NULL);

	if (fastcmp("x", field))
		framepivot->x = luaL_checkinteger(L, 3);
	else if (fastcmp("y", field))
		framepivot->y = luaL_checkinteger(L, 3);
604
	else if (fastcmp("rotaxis", field))
Jaime Ita Passos committed
605
		framepivot->rotaxis = luaL_checkinteger(L, 3);
606 607 608 609 610 611 612 613 614 615 616 617
	else
		return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field));

	return 0;
}

static int framepivot_num(lua_State *L)
{
	lua_pushinteger(L, 2);
	return 1;
}

Alam Ed Arias committed
618 619 620 621 622 623 624 625 626 627 628
////////////////
// STATE INFO //
////////////////

// Uses astate to determine which state is calling it
// Then looks up which Lua action is assigned to that state and calls it
static void A_Lua(mobj_t *actor)
{
	boolean found = false;
	I_Assert(actor != NULL);

629 630 631
	lua_settop(gL, 0); // Just in case...
	lua_pushcfunction(gL, LUA_GetErrorMessage);

Alam Ed Arias committed
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
	// get the action for this state
	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_STATEACTION);
	I_Assert(lua_istable(gL, -1));
	lua_pushlightuserdata(gL, astate);
	lua_rawget(gL, -2);
	I_Assert(lua_isfunction(gL, -1));
	lua_remove(gL, -2); // pop LREG_STATEACTION

	// get the name for this action, if possible.
	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
	lua_pushnil(gL);
	while (lua_next(gL, -2))
	{
		if (lua_rawequal(gL, -1, -4))
		{
			found = true;
			superactions[superstack] = lua_tostring(gL, -2); // "A_ACTION"
			++superstack;
			lua_pop(gL, 2); // pop the name and function
			break;
		}
		lua_pop(gL, 1);
	}
	lua_pop(gL, 1); // pop LREG_ACTION

	LUA_PushUserdata(gL, actor, META_MOBJ);
	lua_pushinteger(gL, var1);
	lua_pushinteger(gL, var2);
660
	LUA_Call(gL, 3, 0, 1);
Alam Ed Arias committed
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675

	if (found)
	{
		--superstack;
		superactions[superstack] = NULL;
	}
}

// Arbitrary states[] table index -> state_t *
static int lib_getState(lua_State *L)
{
	UINT32 i;
	lua_remove(L, 1);

	i = luaL_checkinteger(L, 1);
676 677
	if (i >= NUMSTATES)
		return luaL_error(L, "states[] index %d out of range (0 - %d)", i, NUMSTATES-1);
Alam Ed Arias committed
678 679 680 681 682 683 684 685 686
	LUA_PushUserdata(L, &states[i], META_STATE);
	return 1;
}

// Lua table full of data -> states[] (set the values all at once! :D :D)
static int lib_setState(lua_State *L)
{
	state_t *state;
	lua_remove(L, 1); // don't care about states[] userdata.
687 688 689 690 691 692
	{
		UINT32 i = luaL_checkinteger(L, 1);
		if (i >= NUMSTATES)
			return luaL_error(L, "states[] index %d out of range (0 - %d)", i, NUMSTATES-1);
		state = &states[i]; // get the state to assign to.
	}
Alam Ed Arias committed
693 694 695 696 697 698
	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
	lua_remove(L, 1); // pop state num, don't need it any more.
	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the state.

	if (hud_running)
		return luaL_error(L, "Do not alter states in HUD rendering code!");
699 700
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter states in CMD building code!");
Alam Ed Arias committed
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733

	// clear the state to start with, in case of missing table elements
	memset(state,0,sizeof(state_t));
	state->tics = -1;

	lua_pushnil(L);
	while (lua_next(L, 1)) {
		lua_Integer i = 0;
		const char *str = NULL;
		lua_Integer value;
		if (lua_isnumber(L, 2))
			i = lua_tointeger(L, 2);
		else
			str = luaL_checkstring(L, 2);

		if (i == 1 || (str && fastcmp(str, "sprite"))) {
			value = luaL_checkinteger(L, 3);
			if (value < SPR_NULL || value >= NUMSPRITES)
				return luaL_error(L, "sprite number %d is invalid.", value);
			state->sprite = (spritenum_t)value;
		} else if (i == 2 || (str && fastcmp(str, "frame"))) {
			state->frame = (UINT32)luaL_checkinteger(L, 3);
		} else if (i == 3 || (str && fastcmp(str, "tics"))) {
			state->tics = (INT32)luaL_checkinteger(L, 3);
		} else if (i == 4 || (str && fastcmp(str, "action"))) {
			switch(lua_type(L, 3))
			{
			case LUA_TNIL: // Null? Set the action to nothing, then.
				state->action.acp1 = NULL;
				break;
			case LUA_TSTRING: // It's a string, expect the name of a built-in action
				LUA_SetActionByName(state, lua_tostring(L, 3));
				break;
734 735 736 737 738
			case LUA_TUSERDATA: // It's a userdata, expect META_ACTION of a built-in action
			{
				actionf_t *action = *((actionf_t **)luaL_checkudata(L, 3, META_ACTION));

				if (!action)
739
					return luaL_error(L, "not a valid action?");
740 741 742 743

				state->action = *action;
				state->action.acv = action->acv;
				state->action.acp1 = action->acp1;
744
				break;
745
			}
Alam Ed Arias committed
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
			case LUA_TFUNCTION: // It's a function (a Lua function or a C function? either way!)
				lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
				I_Assert(lua_istable(L, -1));
				lua_pushlightuserdata(L, state); // We'll store this function by the state's pointer in the registry.
				lua_pushvalue(L, 3); // Bring it to the top of the stack
				lua_rawset(L, -3); // Set it in the registry
				lua_pop(L, 1); // pop LREG_STATEACTION
				state->action.acp1 = (actionf_p1)A_Lua; // Set the action for the userdata.
				break;
			default: // ?!
				return luaL_typerror(L, 3, "function");
			}
		} else if (i == 5 || (str && fastcmp(str, "var1"))) {
			state->var1 = (INT32)luaL_checkinteger(L, 3);
		} else if (i == 6 || (str && fastcmp(str, "var2"))) {
			state->var2 = (INT32)luaL_checkinteger(L, 3);
		} else if (i == 7 || (str && fastcmp(str, "nextstate"))) {
			value = luaL_checkinteger(L, 3);
			if (value < S_NULL || value >= NUMSTATES)
				return luaL_error(L, "nextstate number %d is invalid.", value);
			state->nextstate = (statenum_t)value;
		}
		lua_pop(L, 1);
	}
	return 0;
}

// #states -> NUMSTATES
static int lib_statelen(lua_State *L)
{
	lua_pushinteger(L, NUMSTATES);
	return 1;
}

boolean LUA_SetLuaAction(void *stv, const char *action)
{
	state_t *st = (state_t *)stv;

	I_Assert(st != NULL);
	//I_Assert(st >= states && st < states+NUMSTATES); // if you REALLY want to be paranoid...
	I_Assert(action != NULL);

	if (!gL) // Lua isn't loaded,
		return false; // action not set.

	// action is assumed to be in all-caps already !!
	// the registry is case-sensitive, so we strupr everything that enters it.

	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
	lua_getfield(gL, -1, action);

	if (lua_isnil(gL, -1)) // no match
	{
		lua_pop(gL, 2); // pop nil and LREG_ACTIONS
		return false; // action not set.
	}

	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_STATEACTION);
	I_Assert(lua_istable(gL, -1));
	lua_pushlightuserdata(gL, stv); // We'll store this function by the state's pointer in the registry.
	lua_pushvalue(gL, -3); // Bring it to the top of the stack
	lua_rawset(gL, -3); // Set it in the registry
	lua_pop(gL, 1); // pop LREG_STATEACTION

	lua_pop(gL, 2); // pop the function and LREG_ACTIONS
	st->action.acp1 = (actionf_p1)A_Lua; // Set the action for the userdata.
	return true; // action successfully set.
}

815
boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
Alam Ed Arias committed
816 817 818
{
	I_Assert(actor != NULL);

LJ Sonic committed
819
	if (!actionsoverridden[actionnum]) // The action is not overriden,
Alam Ed Arias committed
820 821
		return false; // action not called.

822
	if (superstack && fasticmp(actionpointers[actionnum].name, superactions[superstack-1])) // the action is calling itself,
Alam Ed Arias committed
823 824
		return false; // let it call the hardcoded function instead.

825 826
	lua_pushcfunction(gL, LUA_GetErrorMessage);

Alam Ed Arias committed
827 828
	// grab function by uppercase name.
	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
829
	lua_getfield(gL, -1, actionpointers[actionnum].name);
Alam Ed Arias committed
830 831 832 833
	lua_remove(gL, -2); // pop LREG_ACTIONS

	if (lua_isnil(gL, -1)) // no match
	{
834
		lua_pop(gL, 2); // pop nil and error handler
Alam Ed Arias committed
835 836 837 838 839 840
		return false; // action not called.
	}

	if (superstack == MAXRECURSION)
	{
		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
Alam Ed Arias committed
842 843 844 845 846 847 848 849 850 851
		return true;
	}

	// Found a function.
	// Call it with (actor, var1, var2)
	I_Assert(lua_isfunction(gL, -1));
	LUA_PushUserdata(gL, actor, META_MOBJ);
	lua_pushinteger(gL, var1);
	lua_pushinteger(gL, var2);

852
	superactions[superstack] = actionpointers[actionnum].name;
Alam Ed Arias committed
853 854
	++superstack;

855 856
	LUA_Call(gL, 3, 0, -(2 + 3));
	lua_pop(gL, -1); // Error handler
Alam Ed Arias committed
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892

	--superstack;
	superactions[superstack] = NULL;
	return true; // action successfully called.
}

// state_t *, field -> number
static int state_get(lua_State *L)
{
	state_t *st = *((state_t **)luaL_checkudata(L, 1, META_STATE));
	const char *field = luaL_checkstring(L, 2);
	lua_Integer number;

	if (fastcmp(field,"sprite"))
		number = st->sprite;
	else if (fastcmp(field,"frame"))
		number = st->frame;
	else if (fastcmp(field,"tics"))
		number = st->tics;
	else if (fastcmp(field,"action")) {
		const char *name;
		if (!st->action.acp1) // Action is NULL.
			return 0; // return nil.
		if (st->action.acp1 == (actionf_p1)A_Lua) { // This is a Lua function?
			lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
			I_Assert(lua_istable(L, -1));
			lua_pushlightuserdata(L, st); // Push the state pointer and
			lua_rawget(L, -2); // use it to get the actual Lua function.
			lua_remove(L, -2); // pop LREG_STATEACTION
			return 1; // Return the Lua function.
		}
		name = LUA_GetActionName(&st->action); // find a hardcoded function name
		if (!name) // If it's not a hardcoded function and it's not a Lua function...
			return 0; // Just what is this??
		// get the function from the global
		// because the metatable will trigger.
893 894
		lua_getglobal(L, name); // actually gets from LREG_ACTIONS if applicable, and pushes a META_ACTION userdata if not.
		return 1; // return just the function
Alam Ed Arias committed
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
	} else if (fastcmp(field,"var1"))
		number = st->var1;
	else if (fastcmp(field,"var2"))
		number = st->var2;
	else if (fastcmp(field,"nextstate"))
		number = st->nextstate;
	else if (devparm)
		return luaL_error(L, LUA_QL("state_t") " has no field named " LUA_QS, field);
	else
		return 0;

	lua_pushinteger(L, number);
	return 1;
}

// state_t *, field, number -> states[]
static int state_set(lua_State *L)
{
	state_t *st = *((state_t **)luaL_checkudata(L, 1, META_STATE));
	const char *field = luaL_checkstring(L, 2);
	lua_Integer value;

	if (hud_running)
		return luaL_error(L, "Do not alter states in HUD rendering code!");
919 920
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter states in CMD building code!");
Alam Ed Arias committed
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939

	if (fastcmp(field,"sprite")) {
		value = luaL_checknumber(L, 3);
		if (value < SPR_NULL || value >= NUMSPRITES)
			return luaL_error(L, "sprite number %d is invalid.", value);
		st->sprite = (spritenum_t)value;
	} else if (fastcmp(field,"frame"))
		st->frame = (UINT32)luaL_checknumber(L, 3);
	else if (fastcmp(field,"tics"))
		st->tics = (INT32)luaL_checknumber(L, 3);
	else if (fastcmp(field,"action")) {
		switch(lua_type(L, 3))
		{
		case LUA_TNIL: // Null? Set the action to nothing, then.
			st->action.acp1 = NULL;
			break;
		case LUA_TSTRING: // It's a string, expect the name of a built-in action
			LUA_SetActionByName(st, lua_tostring(L, 3));
			break;
940 941 942 943 944
		case LUA_TUSERDATA: // It's a userdata, expect META_ACTION of a built-in action
		{
			actionf_t *action = *((actionf_t **)luaL_checkudata(L, 3, META_ACTION));

			if (!action)
945
				return luaL_error(L, "not a valid action?");
946 947 948 949

			st->action = *action;
			st->action.acv = action->acv;
			st->action.acp1 = action->acp1;
950
			break;
951
		}
Alam Ed Arias committed
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
		case LUA_TFUNCTION: // It's a function (a Lua function or a C function? either way!)
			lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
			I_Assert(lua_istable(L, -1));
			lua_pushlightuserdata(L, st); // We'll store this function by the state's pointer in the registry.
			lua_pushvalue(L, 3); // Bring it to the top of the stack
			lua_rawset(L, -3); // Set it in the registry
			lua_pop(L, 1); // pop LREG_STATEACTION
			st->action.acp1 = (actionf_p1)A_Lua; // Set the action for the userdata.
			break;
		default: // ?!
			return luaL_typerror(L, 3, "function");
		}
	} else if (fastcmp(field,"var1"))
		st->var1 = (INT32)luaL_checknumber(L, 3);
	else if (fastcmp(field,"var2"))
		st->var2 = (INT32)luaL_checknumber(L, 3);
	else if (fastcmp(field,"nextstate")) {
		value = luaL_checkinteger(L, 3);
		if (value < S_NULL || value >= NUMSTATES)
			return luaL_error(L, "nextstate number %d is invalid.", value);
		st->nextstate = (statenum_t)value;
	} else
		return luaL_error(L, LUA_QL("state_t") " has no field named " LUA_QS, field);

	return 0;
}

// state_t * -> S_*
static int state_num(lua_State *L)
{
	state_t *state = *((state_t **)luaL_checkudata(L, 1, META_STATE));
	lua_pushinteger(L, state-states);
	return 1;
}

///////////////
// MOBJ INFO //
///////////////

// Arbitrary mobjinfo[] table index -> mobjinfo_t *
static int lib_getMobjInfo(lua_State *L)
{
	UINT32 i;
	lua_remove(L, 1);

	i = luaL_checkinteger(L, 1);
998 999
	if (i >= NUMMOBJTYPES)
		return luaL_error(L, "mobjinfo[] index %d out of range (0 - %d)", i, NUMMOBJTYPES-1);
Alam Ed Arias committed
1000 1001 1002 1003 1004 1005 1006 1007 1008
	LUA_PushUserdata(L, &mobjinfo[i], META_MOBJINFO);
	return 1;
}

// Lua table full of data -> mobjinfo[]
static int lib_setMobjInfo(lua_State *L)
{
	mobjinfo_t *info;
	lua_remove(L, 1); // don't care about mobjinfo[] userdata.
1009 1010 1011 1012 1013 1014
	{
		UINT32 i = luaL_checkinteger(L, 1);
		if (i >= NUMMOBJTYPES)
			return luaL_error(L, "mobjinfo[] index %d out of range (0 - %d)", i, NUMMOBJTYPES-1);
		info = &mobjinfo[i]; // get the mobjinfo to assign to.
	}
Alam Ed Arias committed
1015 1016 1017 1018 1019 1020
	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
	lua_remove(L, 1); // pop mobjtype num, don't need it any more.
	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the mobjinfo.

	if (hud_running)
		return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
1021 1022
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter mobjinfo in CMD building code!");
Alam Ed Arias committed
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078

	// clear the mobjinfo to start with, in case of missing table elements
	memset(info,0,sizeof(mobjinfo_t));
	info->doomednum = -1; // default to no editor value
	info->spawnhealth = 1; // avoid 'dead' noclip behaviors

	lua_pushnil(L);
	while (lua_next(L, 1)) {
		lua_Integer i = 0;
		const char *str = NULL;
		lua_Integer value;
		if (lua_isnumber(L, 2))
			i = lua_tointeger(L, 2);
		else
			str = luaL_checkstring(L, 2);

		if (i == 1 || (str && fastcmp(str,"doomednum")))
			info->doomednum = (INT32)luaL_checkinteger(L, 3);
		else if (i == 2 || (str && fastcmp(str,"spawnstate"))) {
			value = luaL_checkinteger(L, 3);
			if (value < S_NULL || value >= NUMSTATES)
				return luaL_error(L, "spawnstate number %d is invalid.", value);
			info->spawnstate = (statenum_t)value;
		} else if (i == 3 || (str && fastcmp(str,"spawnhealth")))
			info->spawnhealth = (INT32)luaL_checkinteger(L, 3);
		else if (i == 4 || (str && fastcmp(str,"seestate"))) {
			value = luaL_checkinteger(L, 3);
			if (value < S_NULL || value >= NUMSTATES)
				return luaL_error(L, "seestate number %d is invalid.", value);
			info->seestate = (statenum_t)value;
		} else if (i == 5 || (str && fastcmp(str,"seesound"))) {
			value = luaL_checkinteger(L, 3);
			if (value < sfx_None || value >= NUMSFX)
				return luaL_error(L, "seesound number %d is invalid.", value);
			info->seesound = (sfxenum_t)value;
		} else if (i == 6 || (str && fastcmp(str,"reactiontime")))
			info->reactiontime = (INT32)luaL_checkinteger(L, 3);
		else if (i == 7 || (str && fastcmp(str,"attacksound")))
			info->attacksound = luaL_checkinteger(L, 3);
		else if (i == 8 || (str && fastcmp(str,"painstate")))
			info->painstate = luaL_checkinteger(L, 3);
		else if (i == 9 || (str && fastcmp(str,"painchance")))
			info->painchance = (INT32)luaL_checkinteger(L, 3);
		else if (i == 10 || (str && fastcmp(str,"painsound")))
			info->painsound = luaL_checkinteger(L, 3);
		else if (i == 11 || (str && fastcmp(str,"meleestate")))
			info->meleestate = luaL_checkinteger(L, 3);
		else if (i == 12 || (str && fastcmp(str,"missilestate")))
			info->missilestate = luaL_checkinteger(L, 3);
		else if (i == 13 || (str && fastcmp(str,"deathstate")))
			info->deathstate = luaL_checkinteger(L, 3);
		else if (i == 14 || (str && fastcmp(str,"xdeathstate")))
			info->xdeathstate = luaL_checkinteger(L, 3);
		else if (i == 15 || (str && fastcmp(str,"deathsound")))
			info->deathsound = luaL_checkinteger(L, 3);
		else if (i == 16 || (str && fastcmp(str,"speed")))
1079
			info->speed = luaL_checkfixed(L, 3);
Alam Ed Arias committed
1080
		else if (i == 17 || (str && fastcmp(str,"radius")))
1081
			info->radius = luaL_checkfixed(L, 3);
Alam Ed Arias committed
1082
		else if (i == 18 || (str && fastcmp(str,"height")))
1083
			info->height = luaL_checkfixed(L, 3);
Alam Ed Arias committed
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
		else if (i == 19 || (str && fastcmp(str,"dispoffset")))
			info->dispoffset = (INT32)luaL_checkinteger(L, 3);
		else if (i == 20 || (str && fastcmp(str,"mass")))
			info->mass = (INT32)luaL_checkinteger(L, 3);
		else if (i == 21 || (str && fastcmp(str,"damage")))
			info->damage = (INT32)luaL_checkinteger(L, 3);
		else if (i == 22 || (str && fastcmp(str,"activesound")))
			info->activesound = luaL_checkinteger(L, 3);
		else if (i == 23 || (str && fastcmp(str,"flags")))
			info->flags = (INT32)luaL_checkinteger(L, 3);
		else if (i == 24 || (str && fastcmp(str,"raisestate"))) {
			info->raisestate = luaL_checkinteger(L, 3);
		}
		lua_pop(L, 1);
	}
	return 0;
}

// #mobjinfo -> NUMMOBJTYPES
static int lib_mobjinfolen(lua_State *L)
{
	lua_pushinteger(L, NUMMOBJTYPES);
	return 1;
}

// mobjinfo_t *, field -> number
static int mobjinfo_get(lua_State *L)
{
	mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
	const char *field = luaL_checkstring(L, 2);

	I_Assert(info != NULL);
	I_Assert(info >= mobjinfo);

	if (fastcmp(field,"doomednum"))
		lua_pushinteger(L, info->doomednum);
	else if (fastcmp(field,"spawnstate"))
		lua_pushinteger(L, info->spawnstate);
	else if (fastcmp(field,"spawnhealth"))
		lua_pushinteger(L, info->spawnhealth);
	else if (fastcmp(field,"seestate"))
		lua_pushinteger(L, info->seestate);
	else if (fastcmp(field,"seesound"))
		lua_pushinteger(L, info->seesound);
	else if (fastcmp(field,"reactiontime"))
		lua_pushinteger(L, info->reactiontime);
	else if (fastcmp(field,"attacksound"))
		lua_pushinteger(L, info->attacksound);
	else if (fastcmp(field,"painstate"))
		lua_pushinteger(L, info->painstate);
	else if (fastcmp(field,"painchance"))
		lua_pushinteger(L, info->painchance);
	else if (fastcmp(field,"painsound"))
		lua_pushinteger(L, info->painsound);
	else if (fastcmp(field,"meleestate"))
		lua_pushinteger(L, info->meleestate);
	else if (fastcmp(field,"missilestate"))
		lua_pushinteger(L, info->missilestate);
	else if (fastcmp(field,"deathstate"))
		lua_pushinteger(L, info->deathstate);
	else if (fastcmp(field,"xdeathstate"))
		lua_pushinteger(L, info->xdeathstate);
	else if (fastcmp(field,"deathsound"))
		lua_pushinteger(L, info->deathsound);
	else if (fastcmp(field,"speed"))
1149
		lua_pushinteger(L, info->speed); // sometimes it's fixed_t, sometimes it's not...
Alam Ed Arias committed
1150
	else if (fastcmp(field,"radius"))
1151
		lua_pushfixed(L, info->radius);
Alam Ed Arias committed
1152
	else if (fastcmp(field,"height"))
1153
		lua_pushfixed(L, info->height);
Alam Ed Arias committed
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
	else if (fastcmp(field,"dispoffset"))
		lua_pushinteger(L, info->dispoffset);
	else if (fastcmp(field,"mass"))
		lua_pushinteger(L, info->mass);
	else if (fastcmp(field,"damage"))
		lua_pushinteger(L, info->damage);
	else if (fastcmp(field,"activesound"))
		lua_pushinteger(L, info->activesound);
	else if (fastcmp(field,"flags"))
		lua_pushinteger(L, info->flags);
	else if (fastcmp(field,"raisestate"))
		lua_pushinteger(L, info->raisestate);
	else {
		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
		I_Assert(lua_istable(L, -1));
		lua_pushlightuserdata(L, info);
		lua_rawget(L, -2);
		if (!lua_istable(L, -1)) { // no extra values table
			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", field);
			return 0;
		}
		lua_getfield(L, -1, field);
		if (lua_isnil(L, -1)) // no value for this field
			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", field);
	}
	return 1;
}

// mobjinfo_t *, field, number -> mobjinfo[]
static int mobjinfo_set(lua_State *L)
{
	mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
	const char *field = luaL_checkstring(L, 2);

	if (hud_running)
		return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
1190 1191
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter mobjinfo in CMD building code!");
Alam Ed Arias committed
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226

	I_Assert(info != NULL);
	I_Assert(info >= mobjinfo);

	if (fastcmp(field,"doomednum"))
		info->doomednum = (INT32)luaL_checkinteger(L, 3);
	else if (fastcmp(field,"spawnstate"))
		info->spawnstate = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"spawnhealth"))
		info->spawnhealth = (INT32)luaL_checkinteger(L, 3);
	else if (fastcmp(field,"seestate"))
		info->seestate = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"seesound"))
		info->seesound = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"reactiontime"))
		info->reactiontime = (INT32)luaL_checkinteger(L, 3);
	else if (fastcmp(field,"attacksound"))
		info->attacksound = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"painstate"))
		info->painstate = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"painchance"))
		info->painchance = (INT32)luaL_checkinteger(L, 3);
	else if (fastcmp(field,"painsound"))
		info->painsound = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"meleestate"))
		info->meleestate = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"missilestate"))
		info->missilestate = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"deathstate"))
		info->deathstate = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"xdeathstate"))
		info->xdeathstate = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"deathsound"))
		info->deathsound = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"speed"))
1227
		info->speed = luaL_checkfixed(L, 3);
Alam Ed Arias committed
1228
	else if (fastcmp(field,"radius"))
1229
		info->radius = luaL_checkfixed(L, 3);
Alam Ed Arias committed
1230
	else if (fastcmp(field,"height"))
1231
		info->height = luaL_checkfixed(L, 3);
Alam Ed Arias committed
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
	else if (fastcmp(field,"dispoffset"))
		info->dispoffset = (INT32)luaL_checkinteger(L, 3);
	else if (fastcmp(field,"mass"))
		info->mass = (INT32)luaL_checkinteger(L, 3);
	else if (fastcmp(field,"damage"))
		info->damage = (INT32)luaL_checkinteger(L, 3);
	else if (fastcmp(field,"activesound"))
		info->activesound = luaL_checkinteger(L, 3);
	else if (fastcmp(field,"flags"))
		info->flags = (INT32)luaL_checkinteger(L, 3);
	else if (fastcmp(field,"raisestate"))
		info->raisestate = luaL_checkinteger(L, 3);
	else {
		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
		I_Assert(lua_istable(L, -1));
		lua_pushlightuserdata(L, info);
		lua_rawget(L, -2);
		if (lua_isnil(L, -1)) {
			// This index doesn't have a table for extra values yet, let's make one.
			lua_pop(L, 1);
			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobjinfo_t", field);
			lua_newtable(L);
			lua_pushlightuserdata(L, info);
			lua_pushvalue(L, -2); // ext value table
			lua_rawset(L, -4); // LREG_EXTVARS table
		}
		lua_pushvalue(L, 3); // value to store
		lua_setfield(L, -2, field);
		lua_pop(L, 2);
	}
	//else
		//return luaL_error(L, LUA_QL("mobjinfo_t") " has no field named " LUA_QS, field);
	return 0;
}

// mobjinfo_t * -> MT_*
static int mobjinfo_num(lua_State *L)
{
	mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));

	I_Assert(info != NULL);
	I_Assert(info >= mobjinfo);

	lua_pushinteger(L, info-mobjinfo);
	return 1;
}

//////////////
// SFX INFO //
//////////////

// Arbitrary S_sfx[] table index -> sfxinfo_t *
static int lib_getSfxInfo(lua_State *L)
{
	UINT32 i;
	lua_remove(L, 1);

	i = luaL_checkinteger(L, 1);
1290 1291
	if (i == 0 || i >= NUMSFX)
		return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, NUMSFX-1);
Alam Ed Arias committed
1292 1293 1294 1295 1296 1297 1298 1299
	LUA_PushUserdata(L, &S_sfx[i], META_SFXINFO);
	return 1;
}

// stack: dummy, S_sfx[] table index, table of values to set.
static int lib_setSfxInfo(lua_State *L)
{
	sfxinfo_t *info;
Alam Ed Arias committed
1300

Alam Ed Arias committed
1301
	lua_remove(L, 1);
1302 1303
	{
		UINT32 i = luaL_checkinteger(L, 1);
1304 1305 1306
		if (i == 0 || i >= NUMSFX)
			return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, NUMSFX-1);
		info = &S_sfx[i]; // get the sfxinfo to assign to.
1307
	}
Alam Ed Arias committed
1308
	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
1309 1310
	lua_remove(L, 1); // pop sfx num, don't need it any more.
	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the sfx.
Alam Ed Arias committed
1311 1312 1313

	if (hud_running)
		return luaL_error(L, "Do not alter sfxinfo in HUD rendering code!");
1314 1315
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter sfxinfo in CMD building code!");
Alam Ed Arias committed
1316 1317 1318

	lua_pushnil(L);
	while (lua_next(L, 1)) {
Alam Ed Arias committed
1319
		enum sfxinfo_write i;
Alam Ed Arias committed
1320 1321

		if (lua_isnumber(L, 2))
Alam Ed Arias committed
1322
			i = lua_tointeger(L, 2) - 1; // lua is one based, this enum is zero based.
Alam Ed Arias committed
1323
		else
Alam Ed Arias committed
1324
			i = luaL_checkoption(L, 2, NULL, sfxinfo_wopt);
Alam Ed Arias committed
1325

Alam Ed Arias committed
1326 1327 1328
		switch(i)
		{
		case sfxinfow_singular:
Alam Ed Arias committed
1329
			info->singularity = luaL_checkboolean(L, 3);
Alam Ed Arias committed
1330 1331
			break;
		case sfxinfow_priority:
Alam Ed Arias committed
1332
			info->priority = (INT32)luaL_checkinteger(L, 3);
Alam Ed Arias committed
1333 1334
			break;
		case sfxinfow_flags:
Alam Ed Arias committed
1335
			info->pitch = (INT32)luaL_checkinteger(L, 3);
Alam Ed Arias committed
1336
			break;
1337 1338 1339
		case sfxinfow_caption:
			strlcpy(info->caption, luaL_checkstring(L, 3), sizeof(info->caption));
			break;
Alam Ed Arias committed
1340 1341 1342
		default:
			break;
		}
Alam Ed Arias committed
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358
		lua_pop(L, 1);
	}

	return 0;
}

static int lib_sfxlen(lua_State *L)
{
	lua_pushinteger(L, NUMSFX);
	return 1;
}

// sfxinfo_t *, field
static int sfxinfo_get(lua_State *L)
{
	sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
Alam Ed Arias committed
1359
	enum sfxinfo_read field = luaL_checkoption(L, 2, NULL, sfxinfo_ropt);
Alam Ed Arias committed
1360 1361 1362 1363 1364

	I_Assert(sfx != NULL);

	switch (field)
	{
Alam Ed Arias committed
1365
	case sfxinfor_name:
Alam Ed Arias committed
1366 1367
		lua_pushstring(L, sfx->name);
		return 1;
Alam Ed Arias committed
1368
	case sfxinfor_singular:
Alam Ed Arias committed
1369 1370
		lua_pushboolean(L, sfx->singularity);
		return 1;
Alam Ed Arias committed
1371
	case sfxinfor_priority:
Alam Ed Arias committed
1372 1373
		lua_pushinteger(L, sfx->priority);
		return 1;
Alam Ed Arias committed
1374
	case sfxinfor_flags:
Alam Ed Arias committed
1375 1376
		lua_pushinteger(L, sfx->pitch);
		return 1;
1377 1378 1379
	case sfxinfor_caption:
		lua_pushstring(L, sfx->caption);
		return 1;
Alam Ed Arias committed
1380
	case sfxinfor_skinsound:
Alam Ed Arias committed
1381 1382
		lua_pushinteger(L, sfx->skinsound);
		return 1;
Alam Ed Arias committed
1383
	default:
1384
		return luaL_error(L, "Field does not exist in sfxinfo_t");
Alam Ed Arias committed
1385
	}
Alam Ed Arias committed
1386
	return 0;
Alam Ed Arias committed
1387 1388 1389 1390 1391 1392
}

// sfxinfo_t *, field, value
static int sfxinfo_set(lua_State *L)
{
	sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
Alam Ed Arias committed
1393
	enum sfxinfo_write field = luaL_checkoption(L, 2, NULL, sfxinfo_wopt);
Alam Ed Arias committed
1394 1395 1396

	if (hud_running)
		return luaL_error(L, "Do not alter S_sfx in HUD rendering code!");
1397 1398
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter S_sfx in CMD building code!");
Alam Ed Arias committed
1399 1400 1401 1402 1403 1404 1405 1406 1407

	I_Assert(sfx != NULL);

	lua_remove(L, 1); // remove sfxinfo
	lua_remove(L, 1); // remove field
	lua_settop(L, 1); // leave only one value

	switch (field)
	{
Alam Ed Arias committed
1408
	case sfxinfow_singular:
Alam Ed Arias committed
1409 1410
		sfx->singularity = luaL_checkboolean(L, 1);
		break;
Alam Ed Arias committed
1411
	case sfxinfow_priority:
Alam Ed Arias committed
1412 1413
		sfx->priority = luaL_checkinteger(L, 1);
		break;
Alam Ed Arias committed
1414
	case sfxinfow_flags:
Alam Ed Arias committed
1415 1416
		sfx->pitch = luaL_checkinteger(L, 1);
		break;
1417 1418 1419
	case sfxinfow_caption:
		strlcpy(sfx->caption, luaL_checkstring(L, 1), sizeof(sfx->caption));
		break;
Alam Ed Arias committed
1420
	default:
1421
		return luaL_error(L, "Field does not exist in sfxinfo_t");
Alam Ed Arias committed
1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436
	}
	return 0;
}

static int sfxinfo_num(lua_State *L)
{
	sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));

	I_Assert(sfx != NULL);
	I_Assert(sfx >= S_sfx);

	lua_pushinteger(L, (UINT32)(sfx-S_sfx));
	return 1;
}

toaster committed
1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
//////////////
// LUABANKS //
//////////////

static int lib_getluabanks(lua_State *L)
{
	UINT8 i;

	lua_remove(L, 1); // don't care about luabanks[] dummy userdata.

	if (lua_isnumber(L, 1))
		i = lua_tonumber(L, 1);
	else
		return luaL_error(L, "luabanks[] invalid index");

	if (i >= NUM_LUABANKS)
1453
		luaL_error(L, "luabanks[] index %d out of range (%d - %d)", i, 0, NUM_LUABANKS-1);
toaster committed
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465

	lua_pushinteger(L, luabanks[i]);
	return 1;
}

static int lib_setluabanks(lua_State *L)
{
	UINT8 i;
	INT32 j = 0;

	if (hud_running)
		return luaL_error(L, "Do not alter luabanks[] in HUD rendering code!");
1466 1467
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter luabanks[] in CMD building code!");
toaster committed
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493

	lua_remove(L, 1); // don't care about luabanks[] dummy userdata.

	if (lua_isnumber(L, 1))
		i = lua_tonumber(L, 1);
	else
		return luaL_error(L, "luabanks[] invalid index");

	if (i >= NUM_LUABANKS)
		luaL_error(L, "luabanks[] index %d out of range (%d - %d)", i, 0, NUM_LUABANKS-1);

	if (lua_isnumber(L, 2))
		j = lua_tonumber(L, 2);
	else
		return luaL_error(L, "luabanks[] invalid set");

	luabanks[i] = j;
	return 0;
}

static int lib_luabankslen(lua_State *L)
{
	lua_pushinteger(L, NUM_LUABANKS);
	return 1;
}

James Hale committed
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515
////////////////////
// SKINCOLOR INFO //
////////////////////

// Arbitrary skincolors[] table index -> skincolor_t *
static int lib_getSkinColor(lua_State *L)
{
	UINT32 i;
	lua_remove(L, 1);

	i = luaL_checkinteger(L, 1);
	if (!i || i >= numskincolors)
		return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", i, numskincolors-1);
	LUA_PushUserdata(L, &skincolors[i], META_SKINCOLOR);
	return 1;
}

//Set the entire c->ramp array
static void setRamp(lua_State *L, skincolor_t* c) {
	UINT32 i;
	lua_pushnil(L);
	for (i=0; i<COLORRAMPSIZE; i++) {
1516
		if (lua_objlen(L,-2)!=COLORRAMPSIZE) {
James Hale committed
1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533
			luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be %d entries long; got %d.", COLORRAMPSIZE, lua_objlen(L,-2));
			break;
		}
		if (lua_next(L, -2) != 0) {
			c->ramp[i] = lua_isnumber(L,-1) ? (UINT8)luaL_checkinteger(L,-1) : 120;
			lua_pop(L, 1);
		} else
			c->ramp[i] = 120;
	}
	lua_pop(L,1);
}

// Lua table full of data -> skincolors[]
static int lib_setSkinColor(lua_State *L)
{
	UINT32 j;
	skincolor_t *info;
1534
	UINT16 cnum; //skincolor num
James Hale committed
1535 1536
	lua_remove(L, 1); // don't care about skincolors[] userdata.
	{
1537
		cnum = (UINT16)luaL_checkinteger(L, 1);
1538 1539
		if (!cnum || cnum >= numskincolors)
			return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", cnum, numskincolors-1);
James Hale committed
1540 1541 1542 1543 1544 1545 1546 1547
		info = &skincolors[cnum]; // get the skincolor to assign to.
	}
	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
	lua_remove(L, 1); // pop skincolor num, don't need it any more.
	lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the skincolor.

	if (hud_running)
		return luaL_error(L, "Do not alter skincolors in HUD rendering code!");
1548 1549
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter skincolors in CMD building code!");
James Hale committed
1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567

	// clear the skincolor to start with, in case of missing table elements
	memset(info,0,sizeof(skincolor_t));

	Color_cons_t[cnum].value = cnum;
	lua_pushnil(L);
	while (lua_next(L, 1)) {
		lua_Integer i = 0;
		const char *str = NULL;
		if (lua_isnumber(L, 2))
			i = lua_tointeger(L, 2);
		else
			str = luaL_checkstring(L, 2);

		if (i == 1 || (str && fastcmp(str,"name"))) {
			const char* n = luaL_checkstring(L, 3);
			strlcpy(info->name, n, MAXCOLORNAME+1);
			if (strlen(n) > MAXCOLORNAME)
kaysrishaq committed
1568
				CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; clipped to %s.\n", n, MAXCOLORNAME, info->name);
kaysrishaq committed
1569
#if 0
kaysrishaq committed
1570 1571
			if (strchr(info->name, ' ') != NULL)
				CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') contains spaces.\n", info->name);
kaysrishaq committed
1572
#endif
kaysrishaq committed
1573

1574 1575 1576 1577 1578 1579
			if (info->name[0] != '\0') // don't check empty string for dupe
			{
				UINT16 dupecheck = R_GetColorByName(info->name);
				if (!stricmp(info->name, skincolors[SKINCOLOR_NONE].name) || (dupecheck && (dupecheck != info-skincolors)))
					CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') is a duplicate of another skincolor's name.\n", info->name);
			}
James Hale committed
1580 1581 1582 1583 1584 1585 1586 1587
		} else if (i == 2 || (str && fastcmp(str,"ramp"))) {
			if (!lua_istable(L, 3) && luaL_checkudata(L, 3, META_COLORRAMP) == NULL)
				return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be a table or array.");
			else if (lua_istable(L, 3))
				setRamp(L, info);
			else
				for (j=0; j<COLORRAMPSIZE; j++)
					info->ramp[j] = (*((UINT8 **)luaL_checkudata(L, 3, META_COLORRAMP)))[j];
1588
			skincolor_modified[cnum] = true;
kaysrishaq committed
1589 1590 1591
		} else if (i == 3 || (str && fastcmp(str,"invcolor"))) {
			UINT16 v = (UINT16)luaL_checkinteger(L, 3);
			if (v >= numskincolors)
1592
				return luaL_error(L, "skincolor_t field 'invcolor' out of range (1 - %d)", numskincolors-1);
kaysrishaq committed
1593
			info->invcolor = v;
kaysrishaq committed
1594
		} else if (i == 4 || (str && fastcmp(str,"invshade")))
1595
			info->invshade = (UINT8)luaL_checkinteger(L, 3)%COLORRAMPSIZE;
James Hale committed
1596 1597
		else if (i == 5 || (str && fastcmp(str,"chatcolor")))
			info->chatcolor = (UINT16)luaL_checkinteger(L, 3);
1598 1599 1600 1601 1602 1603 1604
		else if (i == 6 || (str && fastcmp(str,"accessible"))) {
			boolean v = lua_toboolean(L, 3);
			if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
				return luaL_error(L, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
			else
				info->accessible = v;
		}
James Hale committed
1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637
		lua_pop(L, 1);
	}
	return 0;
}

// #skincolors -> numskincolors
static int lib_skincolorslen(lua_State *L)
{
	lua_pushinteger(L, numskincolors);
	return 1;
}

// skincolor_t *, field -> number
static int skincolor_get(lua_State *L)
{
	skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));
	const char *field = luaL_checkstring(L, 2);

	I_Assert(info != NULL);
	I_Assert(info >= skincolors);

	if (fastcmp(field,"name"))
		lua_pushstring(L, info->name);
	else if (fastcmp(field,"ramp"))
		LUA_PushUserdata(L, info->ramp, META_COLORRAMP);
	else if (fastcmp(field,"invcolor"))
		lua_pushinteger(L, info->invcolor);
	else if (fastcmp(field,"invshade"))
		lua_pushinteger(L, info->invshade);
	else if (fastcmp(field,"chatcolor"))
		lua_pushinteger(L, info->chatcolor);
	else if (fastcmp(field,"accessible"))
		lua_pushboolean(L, info->accessible);
1638
	else {
James Hale committed
1639
		CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "skincolor_t", field);
1640 1641
		return 0;
	}
James Hale committed
1642 1643 1644 1645 1646 1647 1648 1649 1650
	return 1;
}

// skincolor_t *, field, number -> skincolors[]
static int skincolor_set(lua_State *L)
{
	UINT32 i;
	skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));
	const char *field = luaL_checkstring(L, 2);
1651
	UINT16 cnum = (UINT16)(info-skincolors);
James Hale committed
1652 1653 1654 1655

	I_Assert(info != NULL);
	I_Assert(info >= skincolors);

1656 1657
	if (!cnum || cnum >= numskincolors)
		return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", cnum, numskincolors-1);
James Hale committed
1658 1659 1660 1661 1662 1663

	if (fastcmp(field,"name")) {
		const char* n = luaL_checkstring(L, 3);
		strlcpy(info->name, n, MAXCOLORNAME+1);
		if (strlen(n) > MAXCOLORNAME)
			CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; clipped to %s.\n", n, MAXCOLORNAME, info->name);
kaysrishaq committed
1664
#if 0
kaysrishaq committed
1665 1666
		if (strchr(info->name, ' ') != NULL)
			CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') contains spaces.\n", info->name);
kaysrishaq committed
1667
#endif
kaysrishaq committed
1668

1669 1670 1671
		if (info->name[0] != '\0') // don't check empty string for dupe
		{
			UINT16 dupecheck = R_GetColorByName(info->name);
1672
			if (!stricmp(info->name, skincolors[SKINCOLOR_NONE].name) || (dupecheck && (dupecheck != cnum)))
1673 1674
				CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') is a duplicate of another skincolor's name.\n", info->name);
		}
James Hale committed
1675 1676 1677 1678 1679 1680 1681 1682
	} else if (fastcmp(field,"ramp")) {
		if (!lua_istable(L, 3) && luaL_checkudata(L, 3, META_COLORRAMP) == NULL)
			return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be a table or array.");
		else if (lua_istable(L, 3))
			setRamp(L, info);
		else
			for (i=0; i<COLORRAMPSIZE; i++)
				info->ramp[i] = (*((UINT8 **)luaL_checkudata(L, 3, META_COLORRAMP)))[i];
1683
		skincolor_modified[cnum] = true;
kaysrishaq committed
1684 1685 1686
	} else if (fastcmp(field,"invcolor")) {
		UINT16 v = (UINT16)luaL_checkinteger(L, 3);
		if (v >= numskincolors)
1687
			return luaL_error(L, "skincolor_t field 'invcolor' out of range (1 - %d)", numskincolors-1);
kaysrishaq committed
1688
		info->invcolor = v;
kaysrishaq committed
1689
	} else if (fastcmp(field,"invshade"))
1690
		info->invshade = (UINT8)luaL_checkinteger(L, 3)%COLORRAMPSIZE;
James Hale committed
1691 1692
	else if (fastcmp(field,"chatcolor"))
		info->chatcolor = (UINT16)luaL_checkinteger(L, 3);
1693 1694 1695 1696 1697 1698 1699
	else if (fastcmp(field,"accessible")) {
		boolean v = lua_toboolean(L, 3);
		if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
			return luaL_error(L, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
		else
			info->accessible = v;
	} else
James Hale committed
1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730
		CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "skincolor_t", field);
	return 1;
}

// skincolor_t * -> SKINCOLOR_*
static int skincolor_num(lua_State *L)
{
	skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));

	I_Assert(info != NULL);
	I_Assert(info >= skincolors);

	lua_pushinteger(L, info-skincolors);
	return 1;
}

// ramp, n -> ramp[n]
static int colorramp_get(lua_State *L)
{
	UINT8 *colorramp = *((UINT8 **)luaL_checkudata(L, 1, META_COLORRAMP));
	UINT32 n = luaL_checkinteger(L, 2);
	if (n >= COLORRAMPSIZE)
		return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' index %d out of range (0 - %d)", n, COLORRAMPSIZE-1);
	lua_pushinteger(L, colorramp[n]);
	return 1;
}

// ramp, n, value -> ramp[n] = value
static int colorramp_set(lua_State *L)
{
	UINT8 *colorramp = *((UINT8 **)luaL_checkudata(L, 1, META_COLORRAMP));
1731
	UINT16 cnum = (UINT16)(((UINT8*)colorramp - (UINT8*)(skincolors[0].ramp))/sizeof(skincolor_t));
James Hale committed
1732 1733
	UINT32 n = luaL_checkinteger(L, 2);
	UINT8 i = (UINT8)luaL_checkinteger(L, 3);
1734 1735
	if (!cnum || cnum >= numskincolors)
		return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", cnum, numskincolors-1);
James Hale committed
1736 1737 1738 1739
	if (n >= COLORRAMPSIZE)
		return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' index %d out of range (0 - %d)", n, COLORRAMPSIZE-1);
	if (hud_running)
		return luaL_error(L, "Do not alter skincolor_t in HUD rendering code!");
1740 1741
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter skincolor_t in CMD building code!");
James Hale committed
1742
	colorramp[n] = i;
1743
	skincolor_modified[cnum] = true;
James Hale committed
1744 1745 1746 1747 1748 1749 1750 1751 1752 1753
	return 0;
}

// #ramp -> COLORRAMPSIZE
static int colorramp_len(lua_State *L)
{
	lua_pushinteger(L, COLORRAMPSIZE);
	return 1;
}

Alam Ed Arias committed
1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790
//////////////////////////////
//
// Now push all these functions into the Lua state!
//
//
int LUA_InfoLib(lua_State *L)
{
	// index of A_Lua actions to run for each state
	lua_newtable(L);
	lua_setfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);

	// index of globally available Lua actions by function name
	lua_newtable(L);
	lua_setfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS);

	luaL_newmetatable(L, META_STATE);
		lua_pushcfunction(L, state_get);
		lua_setfield(L, -2, "__index");

		lua_pushcfunction(L, state_set);
		lua_setfield(L, -2, "__newindex");

		lua_pushcfunction(L, state_num);
		lua_setfield(L, -2, "__len");
	lua_pop(L, 1);

	luaL_newmetatable(L, META_MOBJINFO);
		lua_pushcfunction(L, mobjinfo_get);
		lua_setfield(L, -2, "__index");

		lua_pushcfunction(L, mobjinfo_set);
		lua_setfield(L, -2, "__newindex");

		lua_pushcfunction(L, mobjinfo_num);
		lua_setfield(L, -2, "__len");
	lua_pop(L, 1);

James Hale committed
1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
	luaL_newmetatable(L, META_SKINCOLOR);
		lua_pushcfunction(L, skincolor_get);
		lua_setfield(L, -2, "__index");

		lua_pushcfunction(L, skincolor_set);
		lua_setfield(L, -2, "__newindex");

		lua_pushcfunction(L, skincolor_num);
		lua_setfield(L, -2, "__len");
	lua_pop(L, 1);

	luaL_newmetatable(L, META_COLORRAMP);
		lua_pushcfunction(L, colorramp_get);
		lua_setfield(L, -2, "__index");

		lua_pushcfunction(L, colorramp_set);
		lua_setfield(L, -2, "__newindex");

		lua_pushcfunction(L, colorramp_len);
		lua_setfield(L, -2, "__len");
	lua_pop(L,1);

Alam Ed Arias committed
1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823
	luaL_newmetatable(L, META_SFXINFO);
		lua_pushcfunction(L, sfxinfo_get);
		lua_setfield(L, -2, "__index");

		lua_pushcfunction(L, sfxinfo_set);
		lua_setfield(L, -2, "__newindex");

		lua_pushcfunction(L, sfxinfo_num);
		lua_setfield(L, -2, "__len");
	lua_pop(L, 1);

1824 1825 1826 1827 1828 1829 1830 1831