Skip to content
Snippets Groups Projects
Select Git revision
  • e7b4beba20c49e0943be587e5792b6398d5642d1
  • next default protected
  • precipoptimizations
  • lightdithering
  • renderdistance
  • thinfps
  • spriteshadows
  • secbright
  • lift-freeslot-limits-2
  • lift-maxsend-limits
  • add-forth-interpreter
  • close-connection-timeout
  • lift-netxcmd-limits
  • add-textinput-hook
  • add-namechange-lua-hook
  • inline-mobjwasremoved
  • fix-bot-2pai-desync
  • avoid-double-checkmobjtrigger-call
  • remove-scale-deadcode
  • remove-duplicate-mobjthinker-call
  • master
  • SRB2_release_2.2.10
  • SRB2_release_2.2.9
  • SRB2_release_2.2.8
  • SRB2_release_2.2.7
  • SRB2_release_2.2.6
  • SRB2_release_2.2.5
  • SRB2_release_2.2.4
  • SRB2_release_2.2.3
  • SRB2_release_2.2.2
  • SRB2_release_2.2.1
  • SRB2_release_2.2.0
  • SRB2_release_2.1.25
  • SRB2_release_2.1.24
  • SRB2_release_2.1.23
  • SRB2_release_2.1.22
  • SRB2_release_2.1.21
  • SRB2_release_2.1.20
  • SRB2_release_2.1.19
  • SRB2_release_2.1.18
  • td-release-v1.0.0
41 results

command.c

Blame
  • Forked from STJr / SRB2
    Source project has a limited visibility.
    lua_maplib.c 31.49 KiB
    // SONIC ROBO BLAST 2
    //-----------------------------------------------------------------------------
    // Copyright (C) 2012-2014 by John "JTE" Muniz.
    // Copyright (C) 2012-2014 by Sonic Team Junior.
    //
    // 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_maplib.c
    /// \brief game map library for Lua scripting
    
    #include "doomdef.h"
    #ifdef HAVE_BLUA
    #include "r_state.h"
    #include "p_local.h"
    #include "p_setup.h"
    #include "z_zone.h"
    
    #include "lua_script.h"
    #include "lua_libs.h"
    #include "lua_hud.h" // hud_running errors
    
    #include "dehacked.h"
    #include "fastcmp.h"
    #include "doomstat.h"
    
    enum sector_e {
    	sector_valid = 0,
    	sector_floorheight,
    	sector_ceilingheight,
    	sector_floorpic,
    	sector_ceilingpic,
    	sector_lightlevel,
    	sector_special,
    	sector_tag,
    	sector_thinglist,
    	sector_heightsec,
    	sector_camsec,
    	sector_ffloors
    };
    
    static const char *const sector_opt[] = {
    	"valid",
    	"floorheight",
    	"ceilingheight",
    	"floorpic",
    	"ceilingpic",
    	"lightlevel",
    	"special",
    	"tag",
    	"thinglist",
    	"heightsec",
    	"camsec",
    	"ffloors",
    	NULL};
    
    enum subsector_e {
    	subsector_valid = 0,
    	subsector_sector,
    	subsector_numlines,
    	subsector_firstline,
    };
    
    static const char *const subsector_opt[] = {
    	"valid",
    	"sector",
    	"numlines",
    	"firstline",
    	NULL};
    
    enum line_e {
    	line_valid = 0,
    	line_v1,
    	line_v2,
    	line_dx,
    	line_dy,
    	line_flags,
    	line_special,
    	line_tag,
    	line_sidenum,
    	line_frontside,
    	line_backside,
    	line_slopetype,
    	line_frontsector,
    	line_backsector,
    	line_firsttag,
    	line_nexttag,
    	line_text,
    	line_callcount
    };
    
    static const char *const line_opt[] = {
    	"valid",
    	"v1",
    	"v2",
    	"dx",
    	"dy",
    	"flags",
    	"special",
    	"tag",
    	"sidenum",
    	"frontside",
    	"backside",
    	"slopetype",
    	"frontsector",
    	"backsector",
    	"firsttag",
    	"nexttag",
    	"text",
    	"callcount",
    	NULL};
    
    enum side_e {
    	side_valid = 0,
    	side_textureoffset,
    	side_rowoffset,
    	side_toptexture,
    	side_bottomtexture,
    	side_midtexture,
    	side_sector,
    	side_special,
    	side_repeatcnt,
    	side_text
    };
    
    static const char *const side_opt[] = {
    	"valid",
    	"textureoffset",
    	"rowoffset",
    	"toptexture",
    	"bottomtexture",
    	"midtexture",
    	"sector",
    	"special",
    	"repeatcnt",
    	"text",
    	NULL};
    
    enum vertex_e {
    	vertex_valid = 0,
    	vertex_x,
    	vertex_y,
    	vertex_z
    };
    
    static const char *const vertex_opt[] = {
    	"valid",
    	"x",
    	"y",
    	"z",
    	NULL};
    
    enum ffloor_e {
    	ffloor_valid = 0,
    	ffloor_topheight,
    	ffloor_toppic,
    	ffloor_toplightlevel,
    	ffloor_bottomheight,
    	ffloor_bottompic,
    	ffloor_sector,
    	ffloor_flags,
    	ffloor_master,
    	ffloor_target,
    	ffloor_next,
    	ffloor_prev,
    	ffloor_alpha,
    };
    
    static const char *const ffloor_opt[] = {
    	"valid",
    	"topheight",
    	"toppic",
    	"toplightlevel",
    	"bottomheight",
    	"bottompic",
    	"sector", // secnum pushed as control sector userdata
    	"flags",
    	"master", // control linedef
    	"target", // target sector
    	"next",
    	"prev",
    	"alpha",
    	NULL};
    
    static const char *const array_opt[] ={"iterate",NULL};
    static const char *const valid_opt[] ={"valid",NULL};
    
    // iterates through a sector's thinglist!
    static int lib_iterateSectorThinglist(lua_State *L)
    {
    	mobj_t *state = NULL;
    	mobj_t *thing = NULL;
    
    	if (lua_gettop(L) < 2)
    		return luaL_error(L, "Don't call sector.thinglist() directly, use it as 'for rover in sector.thinglist do <block> end'.");
    
    	if (!lua_isnil(L, 1))
    		state = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
    	else
    		return 0; // no thinglist to iterate through sorry!
    
    	lua_settop(L, 2);
    	lua_remove(L, 1); // remove state now.
    
    	if (!lua_isnil(L, 1))
    	{
    		thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
    		thing = thing->snext;
    	}
    	else
    		thing = state; // state is used as the "start" of the thinglist
    
    	if (thing)
    	{
    		LUA_PushUserdata(L, thing, META_MOBJ);
    		return 1;
    	}
    	return 0;
    }
    
    // iterates through the ffloors list in a sector!
    static int lib_iterateSectorFFloors(lua_State *L)
    {
    	ffloor_t *state = NULL;
    	ffloor_t *ffloor = NULL;
    
    	if (lua_gettop(L) < 2)
    		return luaL_error(L, "Don't call sector.ffloors() directly, use it as 'for rover in sector.ffloors do <block> end'.");
    
    	if (!lua_isnil(L, 1))
    		state = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
    	else
    		return 0; // no ffloors to iterate through sorry!
    
    	lua_settop(L, 2);
    	lua_remove(L, 1); // remove state now.
    
    	if (!lua_isnil(L, 1))
    	{
    		ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
    		ffloor = ffloor->next;
    	}
    	else
    		ffloor = state; // state is used as the "start" of the ffloor list
    
    	if (ffloor)
    	{
    		LUA_PushUserdata(L, ffloor, META_FFLOOR);
    		return 1;
    	}
    	return 0;
    }
    
    static int sector_iterate(lua_State *L)
    {
    	lua_pushvalue(L, lua_upvalueindex(1)); // iterator function, or the "generator"
    	lua_pushvalue(L, lua_upvalueindex(2)); // state (used as the "start" of the list for our purposes
    	lua_pushnil(L); // initial value (unused)
    	return 3;
    }
    
    static int sector_get(lua_State *L)
    {
    	sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
    	enum sector_e field = luaL_checkoption(L, 2, sector_opt[0], sector_opt);
    
    	if (!sector)
    	{
    		if (field == sector_valid) {
    			lua_pushboolean(L, 0);
    			return 1;
    		}
    		return luaL_error(L, "accessed sector_t doesn't exist anymore.");
    	}
    
    	switch(field)
    	{
    	case sector_valid: // valid
    		lua_pushboolean(L, 1);
    		return 1;
    	case sector_floorheight:
    		lua_pushfixed(L, sector->floorheight);
    		return 1;
    	case sector_ceilingheight:
    		lua_pushfixed(L, sector->ceilingheight);
    		return 1;
    	case sector_floorpic: { // floorpic
    		levelflat_t *levelflat;
    		INT16 i;
    		for (i = 0, levelflat = levelflats; i != sector->floorpic; i++, levelflat++)
    			;
    		lua_pushlstring(L, levelflat->name, 8);
    		return 1;
    	}
    	case sector_ceilingpic: { // ceilingpic
    		levelflat_t *levelflat;
    		INT16 i;
    		for (i = 0, levelflat = levelflats; i != sector->ceilingpic; i++, levelflat++)
    			;
    		lua_pushlstring(L, levelflat->name, 8);
    		return 1;
    	}
    	case sector_lightlevel:
    		lua_pushinteger(L, sector->lightlevel);
    		return 1;
    	case sector_special:
    		lua_pushinteger(L, sector->special);
    		return 1;
    	case sector_tag:
    		lua_pushinteger(L, sector->tag);
    		return 1;
    	case sector_thinglist: // thinglist
    		lua_pushcfunction(L, lib_iterateSectorThinglist);
    		LUA_PushUserdata(L, sector->thinglist, META_MOBJ);
    		lua_pushcclosure(L, sector_iterate, 2); // push lib_iterateSectorThinglist and sector->thinglist as upvalues for the function
    		return 1;
    	case sector_heightsec: // heightsec - fake floor heights
    		if (sector->heightsec < 0)
    			return 0;
    		LUA_PushUserdata(L, &sectors[sector->heightsec], META_SECTOR);
    		return 1;
    	case sector_camsec: // camsec - camera clipping heights
    		if (sector->camsec < 0)
    			return 0;
    		LUA_PushUserdata(L, &sectors[sector->camsec], META_SECTOR);
    		return 1;
    	case sector_ffloors: // ffloors
    		lua_pushcfunction(L, lib_iterateSectorFFloors);
    		LUA_PushUserdata(L, sector->ffloors, META_FFLOOR);
    		lua_pushcclosure(L, sector_iterate, 2); // push lib_iterateFFloors and sector->ffloors as upvalues for the function
    		return 1;
    	}
    	return 0;
    }
    
    // help function for P_LoadSectors, find a flat in the active wad files,
    // allocate an id for it, and set the levelflat (to speedup search)
    //
    static INT32 P_AddLevelFlatRuntime(const char *flatname)
    {
    	size_t i;
    	levelflat_t *levelflat = levelflats;
    
    	//
    	//  first scan through the already found flats
    	//
    	for (i = 0; i < numlevelflats; i++, levelflat++)
    		if (strnicmp(levelflat->name,flatname,8)==0)
    			break;
    
    	// that flat was already found in the level, return the id
    	if (i == numlevelflats)
    	{
    		// allocate new flat memory
    		levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL);
    		levelflat = levelflats+i;
    
    		// store the name
    		strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
    		strupr(levelflat->name);
    
    		// store the flat lump number
    		levelflat->lumpnum = R_GetFlatNumForName(flatname);
    
    #ifndef ZDEBUG
    		CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
    #endif
    
    		numlevelflats++;
    	}
    
    	// level flat id
    	return (INT32)i;
    }
    
    static int sector_set(lua_State *L)
    {
    	sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
    	enum sector_e field = luaL_checkoption(L, 2, sector_opt[0], sector_opt);
    
    	if (!sector)
    		return luaL_error(L, "accessed sector_t doesn't exist anymore.");
    
    	if (hud_running)
    		return luaL_error(L, "Do not alter sector_t in HUD rendering code!");
    
    	switch(field)
    	{
    	case sector_valid: // valid
    	case sector_thinglist: // thinglist
    	case sector_heightsec: // heightsec
    	case sector_camsec: // camsec
    	case sector_ffloors: // ffloors
    	default:
    		return luaL_error(L, "sector_t field " LUA_QS " cannot be set.", sector_opt[field]);
    	case sector_floorheight: { // floorheight
    		boolean flag;
    		mobj_t *ptmthing = tmthing;
    		fixed_t lastpos = sector->floorheight;
    		sector->floorheight = luaL_checkfixed(L, 3);
    		flag = P_CheckSector(sector, true);
    		if (flag && sector->numattached)
    		{
    			sector->floorheight = lastpos;
    			P_CheckSector(sector, true);
    		}
    		P_SetTarget(&tmthing, ptmthing);
    		break;
    	}
    	case sector_ceilingheight: { // ceilingheight
    		boolean flag;
    		mobj_t *ptmthing = tmthing;
    		fixed_t lastpos = sector->ceilingheight;
    		sector->ceilingheight = luaL_checkfixed(L, 3);
    		flag = P_CheckSector(sector, true);
    		if (flag && sector->numattached)
    		{
    			sector->ceilingheight = lastpos;
    			P_CheckSector(sector, true);
    		}
    		P_SetTarget(&tmthing, ptmthing);
    		break;
    	}
    	case sector_floorpic:
    		sector->floorpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
    		break;
    	case sector_ceilingpic:
    		sector->ceilingpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
    		break;
    	case sector_lightlevel:
    		sector->lightlevel = (INT16)luaL_checkinteger(L, 3);
    		break;
    	case sector_special:
    		sector->special = (INT16)luaL_checkinteger(L, 3);
    		break;
    	case sector_tag:
    		P_ChangeSectorTag((UINT32)(sector - sectors), (INT16)luaL_checkinteger(L, 3));
    		break;
    	}
    	return 0;
    }
    
    static int sector_num(lua_State *L)
    {
    	sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
    	lua_pushinteger(L, sector-sectors);
    	return 1;
    }
    
    static int subsector_get(lua_State *L)
    {
    	subsector_t *subsector = *((subsector_t **)luaL_checkudata(L, 1, META_SUBSECTOR));
    	enum subsector_e field = luaL_checkoption(L, 2, subsector_opt[0], subsector_opt);
    
    	if (!subsector)
    	{
    		if (field == subsector_valid) {
    			lua_pushboolean(L, 0);
    			return 1;
    		}
    		return luaL_error(L, "accessed subsector_t doesn't exist anymore.");
    	}
    
    	switch(field)
    	{
    	case subsector_valid: // valid
    		lua_pushboolean(L, 1);
    		return 1;
    	case subsector_sector:
    		LUA_PushUserdata(L, subsector->sector, META_SECTOR);
    		return 1;
    	case subsector_numlines:
    		lua_pushinteger(L, subsector->numlines);
    		return 1;
    	case subsector_firstline:
    		lua_pushinteger(L, subsector->firstline);
    		return 1;
    	}
    	return 0;
    }
    
    static int subsector_num(lua_State *L)
    {
    	subsector_t *subsector = *((subsector_t **)luaL_checkudata(L, 1, META_SUBSECTOR));
    	lua_pushinteger(L, subsector-subsectors);
    	return 1;
    }
    
    static int line_get(lua_State *L)
    {
    	line_t *line = *((line_t **)luaL_checkudata(L, 1, META_LINE));
    	enum line_e field = luaL_checkoption(L, 2, line_opt[0], line_opt);
    
    	if (!line)
    	{
    		if (field == line_valid) {
    			lua_pushboolean(L, 0);
    			return 1;
    		}
    		return luaL_error(L, "accessed line_t doesn't exist anymore.");
    	}
    
    	switch(field)
    	{
    	case line_valid: // valid
    		lua_pushboolean(L, 1);
    		return 1;
    	case line_v1:
    		LUA_PushUserdata(L, line->v1, META_VERTEX);
    		return 1;
    	case line_v2:
    		LUA_PushUserdata(L, line->v2, META_VERTEX);
    		return 1;
    	case line_dx:
    		lua_pushfixed(L, line->dx);
    		return 1;
    	case line_dy:
    		lua_pushfixed(L, line->dy);
    		return 1;
    	case line_flags:
    		lua_pushinteger(L, line->flags);
    		return 1;
    	case line_special:
    		lua_pushinteger(L, line->special);
    		return 1;
    	case line_tag:
    		lua_pushinteger(L, line->tag);
    		return 1;
    	case line_sidenum:
    		LUA_PushUserdata(L, line->sidenum, META_SIDENUM);
    		return 1;
    	case line_frontside: // frontside
    		LUA_PushUserdata(L, &sides[line->sidenum[0]], META_SIDE);
    		return 1;
    	case line_backside: // backside
    		if (line->sidenum[1] == 0xffff)
    			return 0;
    		LUA_PushUserdata(L, &sides[line->sidenum[1]], META_SIDE);
    		return 1;
    	case line_slopetype:
    		switch(line->slopetype)
    		{
    		case ST_HORIZONTAL:
    			lua_pushliteral(L, "horizontal");
    			break;
    		case ST_VERTICAL:
    			lua_pushliteral(L, "vertical");
    			break;
    		case ST_POSITIVE:
    			lua_pushliteral(L, "positive");
    			break;
    		case ST_NEGATIVE:
    			lua_pushliteral(L, "negative");
    			break;
    		}
    		return 1;
    	case line_frontsector:
    		LUA_PushUserdata(L, line->frontsector, META_SECTOR);
    		return 1;
    	case line_backsector:
    		LUA_PushUserdata(L, line->backsector, META_SECTOR);
    		return 1;
    	case line_firsttag:
    		lua_pushinteger(L, line->firsttag);
    		return 1;
    	case line_nexttag:
    		lua_pushinteger(L, line->nexttag);
    		return 1;
    	case line_text:
    		lua_pushstring(L, line->text);
    		return 1;
    	case line_callcount:
    		lua_pushinteger(L, line->callcount);
    		return 1;
    	}
    	return 0;
    }
    
    static int line_num(lua_State *L)
    {
    	line_t *line = *((line_t **)luaL_checkudata(L, 1, META_LINE));
    	lua_pushinteger(L, line-lines);
    	return 1;
    }
    
    static int sidenum_get(lua_State *L)
    {
    	UINT16 *sidenum = *((UINT16 **)luaL_checkudata(L, 1, META_SIDENUM));
    	int i;
    	lua_settop(L, 2);
    	if (!lua_isnumber(L, 2))
    	{
    		int field = luaL_checkoption(L, 2, NULL, valid_opt);
    		if (!sidenum)
    		{
    			if (field == 0) {
    				lua_pushboolean(L, 0);
    				return 1;
    			}
    			return luaL_error(L, "accessed line_t doesn't exist anymore.");
    		} else if (field == 0) {
    			lua_pushboolean(L, 1);
    			return 1;
    		}
    	}
    
    	i = lua_tointeger(L, 2);
    	if (i < 0 || i > 1)
    		return 0;
    	lua_pushinteger(L, sidenum[i]);
    	return 1;
    }
    
    static int side_get(lua_State *L)
    {
    	side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE));
    	enum side_e field = luaL_checkoption(L, 2, side_opt[0], side_opt);
    
    	if (!side)
    	{
    		if (field == side_valid) {
    			lua_pushboolean(L, 0);
    			return 1;
    		}
    		return luaL_error(L, "accessed side_t doesn't exist anymore.");
    	}
    
    	switch(field)
    	{
    	case side_valid: // valid
    		lua_pushboolean(L, 1);
    		return 1;
    	case side_textureoffset:
    		lua_pushfixed(L, side->textureoffset);
    		return 1;
    	case side_rowoffset:
    		lua_pushfixed(L, side->rowoffset);
    		return 1;
    	case side_toptexture:
    		lua_pushinteger(L, side->toptexture);
    		return 1;
    	case side_bottomtexture:
    		lua_pushinteger(L, side->bottomtexture);
    		return 1;
    	case side_midtexture:
    		lua_pushinteger(L, side->midtexture);
    		return 1;
    	case side_sector:
    		LUA_PushUserdata(L, side->sector, META_SECTOR);
    		return 1;
    	case side_special:
    		lua_pushinteger(L, side->special);
    		return 1;
    	case side_repeatcnt:
    		lua_pushinteger(L, side->repeatcnt);
    		return 1;
    	case side_text:
    		lua_pushstring(L, side->text);
    		return 1;
    	}
    	return 0;
    }
    
    static int side_set(lua_State *L)
    {
    	side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE));
    	enum side_e field = luaL_checkoption(L, 2, side_opt[0], side_opt);
    
    	if (!side)
    	{
    		if (field == side_valid) {
    			lua_pushboolean(L, 0);
    			return 1;
    		}
    		return luaL_error(L, "accessed side_t doesn't exist anymore.");
    	}
    
    	switch(field)
    	{
    	case side_valid: // valid
    	case side_sector:
    	case side_special:
    	case side_text:
    	default:
    		return luaL_error(L, "side_t field " LUA_QS " cannot be set.", side_opt[field]);
    	case side_textureoffset:
    		side->textureoffset = luaL_checkfixed(L, 3);
    		break;
    	case side_rowoffset:
    		side->rowoffset = luaL_checkfixed(L, 3);
    		break;
    	case side_toptexture:
            side->toptexture = luaL_checkinteger(L, 3);
    		break;
    	case side_bottomtexture:
            side->bottomtexture = luaL_checkinteger(L, 3);
    		break;
    	case side_midtexture:
            side->midtexture = luaL_checkinteger(L, 3);
    		break;
    	case side_repeatcnt:
            side->repeatcnt = luaL_checkinteger(L, 3);
    		break;
    	}
    	return 0;
    }
    
    static int side_num(lua_State *L)
    {
    	side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE));
    	lua_pushinteger(L, side-sides);
    	return 1;
    }
    
    static int vertex_get(lua_State *L)
    {
    	vertex_t *vertex = *((vertex_t **)luaL_checkudata(L, 1, META_VERTEX));
    	enum vertex_e field = luaL_checkoption(L, 2, vertex_opt[0], vertex_opt);
    
    	if (!vertex)
    	{
    		if (field == vertex_valid) {
    			lua_pushboolean(L, 0);
    			return 1;
    		}
    		return luaL_error(L, "accessed vertex_t doesn't exist anymore.");
    	}
    
    	switch(field)
    	{
    	case vertex_valid: // valid
    		lua_pushboolean(L, 1);
    		return 1;
    	case vertex_x:
    		lua_pushfixed(L, vertex->x);
    		return 1;
    	case vertex_y:
    		lua_pushfixed(L, vertex->y);
    		return 1;
    	case vertex_z:
    		lua_pushfixed(L, vertex->z);
    		return 1;
    	}
    	return 0;
    }
    
    static int vertex_num(lua_State *L)
    {
    	vertex_t *vertex = *((vertex_t **)luaL_checkudata(L, 1, META_VERTEX));
    	lua_pushinteger(L, vertex-vertexes);
    	return 1;
    }
    
    static int lib_iterateSectors(lua_State *L)
    {
    	size_t i = 0;
    	if (lua_gettop(L) < 2)
    		return luaL_error(L, "Don't call sectors.iterate() directly, use it as 'for sector in sectors.iterate do <block> end'.");
    	lua_settop(L, 2);
    	lua_remove(L, 1); // state is unused.
    	if (!lua_isnil(L, 1))
    		i = (size_t)(*((sector_t **)luaL_checkudata(L, 1, META_SECTOR)) - sectors)+1;
    	if (i < numsectors)
    	{
    		LUA_PushUserdata(L, &sectors[i], META_SECTOR);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_getSector(lua_State *L)
    {
    	int field;
    	lua_settop(L, 2);
    	lua_remove(L, 1); // dummy userdata table is unused.
    	if (lua_isnumber(L, 1))
    	{
    		size_t i = lua_tointeger(L, 1);
    		if (i >= numsectors)
    			return 0;
    		LUA_PushUserdata(L, &sectors[i], META_SECTOR);
    		return 1;
    	}
    	field = luaL_checkoption(L, 1, NULL, array_opt);
    	switch(field)
    	{
    	case 0: // iterate
    		lua_pushcfunction(L, lib_iterateSectors);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_numsectors(lua_State *L)
    {
    	lua_pushinteger(L, numsectors);
    	return 1;
    }
    
    static int lib_iterateSubsectors(lua_State *L)
    {
    	size_t i = 0;
    	if (lua_gettop(L) < 2)
    		return luaL_error(L, "Don't call subsectors.iterate() directly, use it as 'for subsector in subsectors.iterate do <block> end'.");
    	lua_settop(L, 2);
    	lua_remove(L, 1); // state is unused.
    	if (!lua_isnil(L, 1))
    		i = (size_t)(*((subsector_t **)luaL_checkudata(L, 1, META_SUBSECTOR)) - subsectors)+1;
    	if (i < numsubsectors)
    	{
    		LUA_PushUserdata(L, &subsectors[i], META_SUBSECTOR);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_getSubsector(lua_State *L)
    {
    	int field;
    	lua_settop(L, 2);
    	lua_remove(L, 1); // dummy userdata table is unused.
    	if (lua_isnumber(L, 1))
    	{
    		size_t i = lua_tointeger(L, 1);
    		if (i >= numsubsectors)
    			return 0;
    		LUA_PushUserdata(L, &subsectors[i], META_SUBSECTOR);
    		return 1;
    	}
    	field = luaL_checkoption(L, 1, NULL, array_opt);
    	switch(field)
    	{
    	case 0: // iterate
    		lua_pushcfunction(L, lib_iterateSubsectors);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_numsubsectors(lua_State *L)
    {
    	lua_pushinteger(L, numsubsectors);
    	return 1;
    }
    
    static int lib_iterateLines(lua_State *L)
    {
    	size_t i = 0;
    	if (lua_gettop(L) < 2)
    		return luaL_error(L, "Don't call lines.iterate() directly, use it as 'for line in lines.iterate do <block> end'.");
    	lua_settop(L, 2);
    	lua_remove(L, 1); // state is unused.
    	if (!lua_isnil(L, 1))
    		i = (size_t)(*((line_t **)luaL_checkudata(L, 1, META_LINE)) - lines)+1;
    	if (i < numlines)
    	{
    		LUA_PushUserdata(L, &lines[i], META_LINE);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_getLine(lua_State *L)
    {
    	int field;
    	lua_settop(L, 2);
    	lua_remove(L, 1); // dummy userdata table is unused.
    	if (lua_isnumber(L, 1))
    	{
    		size_t i = lua_tointeger(L, 1);
    		if (i >= numlines)
    			return 0;
    		LUA_PushUserdata(L, &lines[i], META_LINE);
    		return 1;
    	}
    	field = luaL_checkoption(L, 1, NULL, array_opt);
    	switch(field)
    	{
    	case 0: // iterate
    		lua_pushcfunction(L, lib_iterateLines);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_numlines(lua_State *L)
    {
    	lua_pushinteger(L, numlines);
    	return 1;
    }
    
    static int lib_iterateSides(lua_State *L)
    {
    	size_t i = 0;
    	if (lua_gettop(L) < 2)
    		return luaL_error(L, "Don't call sides.iterate() directly, use it as 'for side in sides.iterate do <block> end'.");
    	lua_settop(L, 2);
    	lua_remove(L, 1); // state is unused.
    	if (!lua_isnil(L, 1))
    		i = (size_t)(*((side_t **)luaL_checkudata(L, 1, META_SIDE)) - sides)+1;
    	if (i < numsides)
    	{
    		LUA_PushUserdata(L, &sides[i], META_SIDE);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_getSide(lua_State *L)
    {
    	int field;
    	lua_settop(L, 2);
    	lua_remove(L, 1); // dummy userdata table is unused.
    	if (lua_isnumber(L, 1))
    	{
    		size_t i = lua_tointeger(L, 1);
    		if (i >= numsides)
    			return 0;
    		LUA_PushUserdata(L, &sides[i], META_SIDE);
    		return 1;
    	}
    	field = luaL_checkoption(L, 1, NULL, array_opt);
    	switch(field)
    	{
    	case 0: // iterate
    		lua_pushcfunction(L, lib_iterateSides);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_numsides(lua_State *L)
    {
    	lua_pushinteger(L, numsides);
    	return 1;
    }
    
    static int lib_iterateVertexes(lua_State *L)
    {
    	size_t i = 0;
    	if (lua_gettop(L) < 2)
    		return luaL_error(L, "Don't call vertexes.iterate() directly, use it as 'for vertex in vertexes.iterate do <block> end'.");
    	lua_settop(L, 2);
    	lua_remove(L, 1); // state is unused.
    	if (!lua_isnil(L, 1))
    		i = (size_t)(*((vertex_t **)luaL_checkudata(L, 1, META_VERTEX)) - vertexes)+1;
    	if (i < numvertexes)
    	{
    		LUA_PushUserdata(L, &vertexes[i], META_VERTEX);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_getVertex(lua_State *L)
    {
    	int field;
    	lua_settop(L, 2);
    	lua_remove(L, 1); // dummy userdata table is unused.
    	if (lua_isnumber(L, 1))
    	{
    		size_t i = lua_tointeger(L, 1);
    		if (i >= numvertexes)
    			return 0;
    		LUA_PushUserdata(L, &vertexes[i], META_VERTEX);
    		return 1;
    	}
    	field = luaL_checkoption(L, 1, NULL, array_opt);
    	switch(field)
    	{
    	case 0: // iterate
    		lua_pushcfunction(L, lib_iterateVertexes);
    		return 1;
    	}
    	return 0;
    }
    
    static int lib_numvertexes(lua_State *L)
    {
    	lua_pushinteger(L, numvertexes);
    	return 1;
    }
    
    static int ffloor_get(lua_State *L)
    {
    	ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
    	enum ffloor_e field = luaL_checkoption(L, 2, ffloor_opt[0], ffloor_opt);
    
    	if (!ffloor)
    	{
    		if (field == ffloor_valid) {
    			lua_pushboolean(L, 0);
    			return 1;
    		}
    		return luaL_error(L, "accessed ffloor_t doesn't exist anymore.");
    	}
    
    	switch(field)
    	{
    	case ffloor_valid: // valid
    		lua_pushboolean(L, 1);
    		return 1;
    	case ffloor_topheight:
    		lua_pushfixed(L, *ffloor->topheight);
    		return 1;
    	case ffloor_toppic: { // toppic
    		levelflat_t *levelflat;
    		INT16 i;
    		for (i = 0, levelflat = levelflats; i != *ffloor->toppic; i++, levelflat++)
    			;
    		lua_pushlstring(L, levelflat->name, 8);
    		return 1;
    	}
    	case ffloor_toplightlevel:
    		lua_pushinteger(L, *ffloor->toplightlevel);
    		return 1;
    	case ffloor_bottomheight:
    		lua_pushfixed(L, *ffloor->bottomheight);
    		return 1;
    	case ffloor_bottompic: { // bottompic
    		levelflat_t *levelflat;
    		INT16 i;
    		for (i = 0, levelflat = levelflats; i != *ffloor->bottompic; i++, levelflat++)
    			;
    		lua_pushlstring(L, levelflat->name, 8);
    		return 1;
    	}
    	case ffloor_sector:
    		LUA_PushUserdata(L, &sectors[ffloor->secnum], META_SECTOR);
    		return 1;
    	case ffloor_flags:
    		lua_pushinteger(L, ffloor->flags);
    		return 1;
    	case ffloor_master:
    		LUA_PushUserdata(L, ffloor->master, META_LINE);
    		return 1;
    	case ffloor_target:
    		LUA_PushUserdata(L, ffloor->target, META_SECTOR);
    		return 1;
    	case ffloor_next:
    		LUA_PushUserdata(L, ffloor->next, META_FFLOOR);
    		return 1;
    	case ffloor_prev:
    		LUA_PushUserdata(L, ffloor->prev, META_FFLOOR);
    		return 1;
    	case ffloor_alpha:
    		lua_pushinteger(L, ffloor->alpha);
    		return 1;
    	}
    	return 0;
    }
    
    static int ffloor_set(lua_State *L)
    {
    	ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
    	enum ffloor_e field = luaL_checkoption(L, 2, ffloor_opt[0], ffloor_opt);
    
    	if (!ffloor)
    		return luaL_error(L, "accessed ffloor_t doesn't exist anymore.");
    
    	if (hud_running)
    		return luaL_error(L, "Do not alter ffloor_t in HUD rendering code!");
    
    	switch(field)
    	{
    	case ffloor_valid: // valid
    	case ffloor_sector: // sector
    	case ffloor_master: // master
    	case ffloor_target: // target
    	case ffloor_next: // next
    	case ffloor_prev: // prev
    	default:
    		return luaL_error(L, "ffloor_t field " LUA_QS " cannot be set.", ffloor_opt[field]);
    	case ffloor_topheight: { // topheight
    		boolean flag;
    		fixed_t lastpos = *ffloor->topheight;
    		mobj_t *ptmthing = tmthing;
    		sector_t *sector = &sectors[ffloor->secnum];
    		sector->ceilingheight = luaL_checkfixed(L, 3);
    		flag = P_CheckSector(sector, true);
    		if (flag && sector->numattached)
    		{
    			*ffloor->topheight = lastpos;
    			P_CheckSector(sector, true);
    		}
    		P_SetTarget(&tmthing, ptmthing);
    		break;
    	}
    	case ffloor_toppic:
    		*ffloor->toppic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
    		break;
    	case ffloor_toplightlevel:
    		*ffloor->toplightlevel = (INT16)luaL_checkinteger(L, 3);
    		break;
    	case ffloor_bottomheight: { // bottomheight
    		boolean flag;
    		fixed_t lastpos = *ffloor->bottomheight;
    		mobj_t *ptmthing = tmthing;
    		sector_t *sector = &sectors[ffloor->secnum];
    		sector->floorheight = luaL_checkfixed(L, 3);
    		flag = P_CheckSector(sector, true);
    		if (flag && sector->numattached)
    		{
    			*ffloor->bottomheight = lastpos;
    			P_CheckSector(sector, true);
    		}
    		P_SetTarget(&tmthing, ptmthing);
    		break;
    	}
    	case ffloor_bottompic:
    		*ffloor->bottompic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
    		break;
    	case ffloor_flags:
    		ffloor->flags = luaL_checkinteger(L, 3);
    		break;
    	case ffloor_alpha:
    		ffloor->alpha = (INT32)luaL_checkinteger(L, 3);
    		break;
    	}
    	return 0;
    }
    
    static int lib_getMapheaderinfo(lua_State *L)
    {
    	// i -> mapheaderinfo[i-1]
    
    	//int field;
    	lua_settop(L, 2);
    	lua_remove(L, 1); // dummy userdata table is unused.
    	if (lua_isnumber(L, 1))
    	{
    		size_t i = lua_tointeger(L, 1)-1;
    		if (i >= NUMMAPS)
    			return 0;
    		LUA_PushUserdata(L, mapheaderinfo[i], META_MAPHEADER);
    		//CONS_Printf(mapheaderinfo[i]->lvlttl);
    		return 1;
    	}/*
    	field = luaL_checkoption(L, 1, NULL, array_opt);
    	switch(field)
    	{
    	case 0: // iterate
    		lua_pushcfunction(L, lib_iterateSubsectors);
    		return 1;
    	}*/
    	return 0;
    }
    
    static int lib_nummapheaders(lua_State *L)
    {
    	lua_pushinteger(L, NUMMAPS);
    	return 1;
    }
    
    static int mapheaderinfo_get(lua_State *L)
    {
    	mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER));
    	const char *field = luaL_checkstring(L, 2);
    	//INT16 i;
    	if (fastcmp(field,"lvlttl")) {
    		//for (i = 0; i < 21; i++)
    		//	if (!header->lvlttl[i])
    		//		break;
    		lua_pushlstring(L, header->lvlttl, 21);
    	} else if (fastcmp(field,"subttl"))
    		lua_pushlstring(L, header->subttl, 32);
    	else if (fastcmp(field,"actnum"))
    		lua_pushinteger(L, header->actnum);
    	else if (fastcmp(field,"typeoflevel"))
    		lua_pushinteger(L, header->typeoflevel);
    	else if (fastcmp(field,"nextlevel"))
    		lua_pushinteger(L, header->nextlevel);
    	else if (fastcmp(field,"musicslot"))
    		lua_pushinteger(L, header->musicslot);
    	else if (fastcmp(field,"musicslottrack"))
    		lua_pushinteger(L, header->musicslottrack);
    	else if (fastcmp(field,"forcecharacter"))
    		lua_pushlstring(L, header->forcecharacter, 16);
    	else if (fastcmp(field,"weather"))
    		lua_pushinteger(L, header->weather);
    	else if (fastcmp(field,"skynum"))
    		lua_pushinteger(L, header->skynum);
    	else if (fastcmp(field,"skybox_scalex"))
    		lua_pushinteger(L, header->skybox_scalex);
    	else if (fastcmp(field,"skybox_scaley"))
    		lua_pushinteger(L, header->skybox_scaley);
    	else if (fastcmp(field,"skybox_scalez"))
    		lua_pushinteger(L, header->skybox_scalez);
    	else if (fastcmp(field,"interscreen"))
    		lua_pushlstring(L, header->interscreen, 8);
    	else if (fastcmp(field,"runsoc"))
    		lua_pushlstring(L, header->runsoc, 32);
    	else if (fastcmp(field,"scriptname"))
    		lua_pushlstring(L, header->scriptname, 32);
    	else if (fastcmp(field,"precutscenenum"))
    		lua_pushinteger(L, header->precutscenenum);
    	else if (fastcmp(field,"cutscenenum"))
    		lua_pushinteger(L, header->cutscenenum);
    	else if (fastcmp(field,"countdown"))
    		lua_pushinteger(L, header->countdown);
    	else if (fastcmp(field,"palette"))
    		lua_pushinteger(L, header->palette);
    	else if (fastcmp(field,"numlaps"))
    		lua_pushinteger(L, header->numlaps);
    	else if (fastcmp(field,"unlockrequired"))
    		lua_pushinteger(L, header->unlockrequired);
    	else if (fastcmp(field,"levelselect"))
    		lua_pushinteger(L, header->levelselect);
    	else if (fastcmp(field,"bonustype"))
    		lua_pushinteger(L, header->bonustype);
    	else if (fastcmp(field,"levelflags"))
    		lua_pushinteger(L, header->levelflags);
    	else if (fastcmp(field,"menuflags"))
    		lua_pushinteger(L, header->menuflags);
    	// TODO add support for reading numGradedMares and grades
    	else {
    		// Read custom vars now
    		// (note: don't include the "LUA." in your lua scripts!)
    		UINT8 i = 0;
    		for (;i < header->numCustomOptions && !fastcmp(field, header->customopts[i].option); ++i);
    
    		if(i < header->numCustomOptions)
    			lua_pushlstring(L, header->customopts[i].value, 255);
    		else
    			lua_pushnil(L);
    	}
    	return 1;
    }
    
    int LUA_MapLib(lua_State *L)
    {
    	luaL_newmetatable(L, META_SECTOR);
    		lua_pushcfunction(L, sector_get);
    		lua_setfield(L, -2, "__index");
    
    		lua_pushcfunction(L, sector_set);
    		lua_setfield(L, -2, "__newindex");
    
    		lua_pushcfunction(L, sector_num);
    		lua_setfield(L, -2, "__len");
    	lua_pop(L, 1);
    
    	luaL_newmetatable(L, META_SUBSECTOR);
    		lua_pushcfunction(L, subsector_get);
    		lua_setfield(L, -2, "__index");
    
    		lua_pushcfunction(L, subsector_num);
    		lua_setfield(L, -2, "__len");
    	lua_pop(L, 1);
    
    	luaL_newmetatable(L, META_LINE);
    		lua_pushcfunction(L, line_get);
    		lua_setfield(L, -2, "__index");
    
    		lua_pushcfunction(L, line_num);
    		lua_setfield(L, -2, "__len");
    	lua_pop(L, 1);
    
    	luaL_newmetatable(L, META_SIDENUM);
    		lua_pushcfunction(L, sidenum_get);
    		lua_setfield(L, -2, "__index");
    	lua_pop(L, 1);
    
    	luaL_newmetatable(L, META_SIDE);
    		lua_pushcfunction(L, side_get);
    		lua_setfield(L, -2, "__index");
    
    		lua_pushcfunction(L, side_set);
    		lua_setfield(L, -2, "__newindex");
    
    		lua_pushcfunction(L, side_num);
    		lua_setfield(L, -2, "__len");
    	lua_pop(L, 1);
    
    	luaL_newmetatable(L, META_VERTEX);
    		lua_pushcfunction(L, vertex_get);
    		lua_setfield(L, -2, "__index");
    
    		lua_pushcfunction(L, vertex_num);
    		lua_setfield(L, -2, "__len");
    	lua_pop(L, 1);
    
    	luaL_newmetatable(L, META_FFLOOR);
    		lua_pushcfunction(L, ffloor_get);
    		lua_setfield(L, -2, "__index");
    
    		lua_pushcfunction(L, ffloor_set);
    		lua_setfield(L, -2, "__newindex");
    	lua_pop(L, 1);
    
    	luaL_newmetatable(L, META_MAPHEADER);
    		lua_pushcfunction(L, mapheaderinfo_get);
    		lua_setfield(L, -2, "__index");
    
    		//lua_pushcfunction(L, mapheaderinfo_num);
    		//lua_setfield(L, -2, "__len");
    	lua_pop(L, 1);
    
    	lua_newuserdata(L, 0);
    		lua_createtable(L, 0, 2);
    			lua_pushcfunction(L, lib_getSector);
    			lua_setfield(L, -2, "__index");
    
    			lua_pushcfunction(L, lib_numsectors);
    			lua_setfield(L, -2, "__len");
    		lua_setmetatable(L, -2);
    	lua_setglobal(L, "sectors");
    
    	lua_newuserdata(L, 0);
    		lua_createtable(L, 0, 2);
    			lua_pushcfunction(L, lib_getSubsector);
    			lua_setfield(L, -2, "__index");
    
    			lua_pushcfunction(L, lib_numsubsectors);
    			lua_setfield(L, -2, "__len");
    		lua_setmetatable(L, -2);
    	lua_setglobal(L, "subsectors");
    
    	lua_newuserdata(L, 0);
    		lua_createtable(L, 0, 2);
    			lua_pushcfunction(L, lib_getLine);
    			lua_setfield(L, -2, "__index");
    
    			lua_pushcfunction(L, lib_numlines);
    			lua_setfield(L, -2, "__len");
    		lua_setmetatable(L, -2);
    	lua_setglobal(L, "lines");
    
    	lua_newuserdata(L, 0);
    		lua_createtable(L, 0, 2);
    			lua_pushcfunction(L, lib_getSide);
    			lua_setfield(L, -2, "__index");
    
    			lua_pushcfunction(L, lib_numsides);
    			lua_setfield(L, -2, "__len");
    		lua_setmetatable(L, -2);
    	lua_setglobal(L, "sides");
    
    	lua_newuserdata(L, 0);
    		lua_createtable(L, 0, 2);
    			lua_pushcfunction(L, lib_getVertex);
    			lua_setfield(L, -2, "__index");
    
    			lua_pushcfunction(L, lib_numvertexes);
    			lua_setfield(L, -2, "__len");
    		lua_setmetatable(L, -2);
    	lua_setglobal(L, "vertexes");
    
    	lua_newuserdata(L, 0);
    		lua_createtable(L, 0, 2);
    			lua_pushcfunction(L, lib_getMapheaderinfo);
    			lua_setfield(L, -2, "__index");
    
    			lua_pushcfunction(L, lib_nummapheaders);
    			lua_setfield(L, -2, "__len");
    		lua_setmetatable(L, -2);
    	lua_setglobal(L, "mapheaderinfo");
    	return 0;
    }
    
    #endif