diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index dcaad1416011941230285723037a193ade75102e..1d15b3b145c5e5d0f2bccdd055ff1dcde60056b0 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -566,27 +566,59 @@ static luaL_Reg lib[] = {
 	{NULL, NULL}
 };
 
+enum cvar_e
+{
+	cvar_name,
+	cvar_defaultvalue,
+	cvar_flags,
+	cvar_value,
+	cvar_string,
+	cvar_changed,
+};
+
+static const char *const cvar_opt[] = {
+	"name",
+	"defaultvalue",
+	"flags",
+	"value",
+	"string",
+	"changed",
+	NULL,
+};
+
+static int cvar_fields_ref = LUA_NOREF;
+
 static int cvar_get(lua_State *L)
 {
 	consvar_t *cvar = *(consvar_t **)luaL_checkudata(L, 1, META_CVAR);
-	const char *field = luaL_checkstring(L, 2);
+	enum cvar_e field = Lua_optoption(L, 2, -1, cvar_fields_ref);
 
-	if(fastcmp(field,"name"))
+	switch (field)
+	{
+	case cvar_name:
 		lua_pushstring(L, cvar->name);
-	else if(fastcmp(field,"defaultvalue"))
+		break;
+	case cvar_defaultvalue:
 		lua_pushstring(L, cvar->defaultvalue);
-	else if(fastcmp(field,"flags"))
+		break;
+	case cvar_flags:
 		lua_pushinteger(L, cvar->flags);
-	else if(fastcmp(field,"value"))
+		break;
+	case cvar_value:
 		lua_pushinteger(L, cvar->value);
-	else if(fastcmp(field,"string"))
+		break;
+	case cvar_string:
 		lua_pushstring(L, cvar->string);
-	else if(fastcmp(field,"changed"))
+		break;
+	case cvar_changed:
 		lua_pushboolean(L, cvar->changed);
-	else if (devparm)
-		return luaL_error(L, LUA_QL("consvar_t") " has no field named " LUA_QS, field);
-	else
-		return 0;
+		break;
+	default:
+		if (devparm)
+			return luaL_error(L, LUA_QL("consvar_t") " has no field named " LUA_QS, field);
+		else
+			return 0;
+	}
 	return 1;
 }
 
@@ -598,6 +630,8 @@ int LUA_ConsoleLib(lua_State *L)
 		lua_setfield(L, -2, "__index");
 	lua_pop(L,1);
 
+	cvar_fields_ref = Lua_CreateFieldTable(L, cvar_opt);
+
 	// Set empty registry tables
 	lua_newtable(L);
 	lua_setfield(L, LUA_REGISTRYINDEX, "COM_Command");
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 1a05997572f60542ca12d195b697f7aad87da564..c7f67e93ac07d194a25a2ce365726219d52a3ee4 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -95,6 +95,8 @@ static const char *const patch_opt[] = {
 	"topoffset",
 	NULL};
 
+static int patch_fields_ref = LUA_NOREF;
+
 // alignment types for v.drawString
 enum align {
 	align_left = 0,
@@ -196,6 +198,8 @@ static const char *const camera_opt[] = {
 	"momz",
 	NULL};
 
+static int camera_fields_ref = LUA_NOREF;
+
 static int lib_getHudInfo(lua_State *L)
 {
 	UINT32 i;
@@ -276,7 +280,7 @@ static int colormap_get(lua_State *L)
 static int patch_get(lua_State *L)
 {
 	patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH));
-	enum patch field = luaL_checkoption(L, 2, NULL, patch_opt);
+	enum patch field = Lua_optoption(L, 2, -1, patch_fields_ref);
 
 	// patches are invalidated when switching renderers
 	if (!patch) {
@@ -316,7 +320,7 @@ static int patch_set(lua_State *L)
 static int camera_get(lua_State *L)
 {
 	camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA));
-	enum cameraf field = luaL_checkoption(L, 2, NULL, camera_opt);
+	enum cameraf field = Lua_optoption(L, 2, -1, camera_fields_ref);
 
 	// cameras should always be valid unless I'm a nutter
 	I_Assert(cam != NULL);
@@ -372,7 +376,7 @@ static int camera_get(lua_State *L)
 static int camera_set(lua_State *L)
 {
 	camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA));
-	enum cameraf field = luaL_checkoption(L, 2, NULL, camera_opt);
+	enum cameraf field = Lua_optoption(L, 2, -1, camera_fields_ref);
 
 	I_Assert(cam != NULL);
 
@@ -1444,6 +1448,8 @@ int LUA_HudLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L,1);
 
+	patch_fields_ref = Lua_CreateFieldTable(L, patch_opt);
+
 	luaL_newmetatable(L, META_CAMERA);
 		lua_pushcfunction(L, camera_get);
 		lua_setfield(L, -2, "__index");
@@ -1452,6 +1458,8 @@ int LUA_HudLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L,1);
 
+	camera_fields_ref = Lua_CreateFieldTable(L, camera_opt);
+
 	luaL_register(L, "hud", lib_hud);
 	return 0;
 }
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 7388632d3c6cac8e74619ff4b5ac9bfca8ddb2e8..fb07ccebb53dc00a7c93a2be141d05e54b24baa9 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1106,75 +1106,161 @@ static int lib_mobjinfolen(lua_State *L)
 	return 1;
 }
 
+enum mobjinfo_e
+{
+	mobjinfo_doomednum,
+	mobjinfo_spawnstate,
+	mobjinfo_spawnhealth,
+	mobjinfo_seestate,
+	mobjinfo_seesound,
+	mobjinfo_reactiontime,
+	mobjinfo_attacksound,
+	mobjinfo_painstate,
+	mobjinfo_painchance,
+	mobjinfo_painsound,
+	mobjinfo_meleestate,
+	mobjinfo_missilestate,
+	mobjinfo_deathstate,
+	mobjinfo_xdeathstate,
+	mobjinfo_deathsound,
+	mobjinfo_speed,
+	mobjinfo_radius,
+	mobjinfo_height,
+	mobjinfo_dispoffset,
+	mobjinfo_mass,
+	mobjinfo_damage,
+	mobjinfo_activesound,
+	mobjinfo_flags,
+	mobjinfo_raisestate,
+};
+
+const char *const mobjinfo_opt[] = {
+	"doomednum",
+	"spawnstate",
+	"spawnhealth",
+	"seestate",
+	"seesound",
+	"reactiontime",
+	"attacksound",
+	"painstate",
+	"painchance",
+	"painsound",
+	"meleestate",
+	"missilestate",
+	"deathstate",
+	"xdeathstate",
+	"deathsound",
+	"speed",
+	"radius",
+	"height",
+	"dispoffset",
+	"mass",
+	"damage",
+	"activesound",
+	"flags",
+	"raisestate",
+	NULL,
+};
+
+static int mobjinfo_fields_ref = LUA_NOREF;
+
 // 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);
+	enum mobjinfo_e field = luaL_checkoption(L, 2, mobjinfo_opt[0], mobjinfo_opt);
 
 	I_Assert(info != NULL);
 	I_Assert(info >= mobjinfo);
 
-	if (fastcmp(field,"doomednum"))
+	switch (field)
+	{
+	case mobjinfo_doomednum:
 		lua_pushinteger(L, info->doomednum);
-	else if (fastcmp(field,"spawnstate"))
+		break;
+	case mobjinfo_spawnstate:
 		lua_pushinteger(L, info->spawnstate);
-	else if (fastcmp(field,"spawnhealth"))
+		break;
+	case mobjinfo_spawnhealth:
 		lua_pushinteger(L, info->spawnhealth);
-	else if (fastcmp(field,"seestate"))
+		break;
+	case mobjinfo_seestate:
 		lua_pushinteger(L, info->seestate);
-	else if (fastcmp(field,"seesound"))
+		break;
+	case mobjinfo_seesound:
 		lua_pushinteger(L, info->seesound);
-	else if (fastcmp(field,"reactiontime"))
+		break;
+	case mobjinfo_reactiontime:
 		lua_pushinteger(L, info->reactiontime);
-	else if (fastcmp(field,"attacksound"))
+		break;
+	case mobjinfo_attacksound:
 		lua_pushinteger(L, info->attacksound);
-	else if (fastcmp(field,"painstate"))
+		break;
+	case mobjinfo_painstate:
 		lua_pushinteger(L, info->painstate);
-	else if (fastcmp(field,"painchance"))
+		break;
+	case mobjinfo_painchance:
 		lua_pushinteger(L, info->painchance);
-	else if (fastcmp(field,"painsound"))
+		break;
+	case mobjinfo_painsound:
 		lua_pushinteger(L, info->painsound);
-	else if (fastcmp(field,"meleestate"))
+		break;
+	case mobjinfo_meleestate:
 		lua_pushinteger(L, info->meleestate);
-	else if (fastcmp(field,"missilestate"))
+		break;
+	case mobjinfo_missilestate:
 		lua_pushinteger(L, info->missilestate);
-	else if (fastcmp(field,"deathstate"))
+		break;
+	case mobjinfo_deathstate:
 		lua_pushinteger(L, info->deathstate);
-	else if (fastcmp(field,"xdeathstate"))
+		break;
+	case mobjinfo_xdeathstate:
 		lua_pushinteger(L, info->xdeathstate);
-	else if (fastcmp(field,"deathsound"))
+		break;
+	case mobjinfo_deathsound:
 		lua_pushinteger(L, info->deathsound);
-	else if (fastcmp(field,"speed"))
+		break;
+	case mobjinfo_speed:
 		lua_pushinteger(L, info->speed); // sometimes it's fixed_t, sometimes it's not...
-	else if (fastcmp(field,"radius"))
+		break;
+	case mobjinfo_radius:
 		lua_pushfixed(L, info->radius);
-	else if (fastcmp(field,"height"))
+		break;
+	case mobjinfo_height:
 		lua_pushfixed(L, info->height);
-	else if (fastcmp(field,"dispoffset"))
+		break;
+	case mobjinfo_dispoffset:
 		lua_pushinteger(L, info->dispoffset);
-	else if (fastcmp(field,"mass"))
+		break;
+	case mobjinfo_mass:
 		lua_pushinteger(L, info->mass);
-	else if (fastcmp(field,"damage"))
+		break;
+	case mobjinfo_damage:
 		lua_pushinteger(L, info->damage);
-	else if (fastcmp(field,"activesound"))
+		break;
+	case mobjinfo_activesound:
 		lua_pushinteger(L, info->activesound);
-	else if (fastcmp(field,"flags"))
+		break;
+	case mobjinfo_flags:
 		lua_pushinteger(L, info->flags);
-	else if (fastcmp(field,"raisestate"))
+		break;
+	case mobjinfo_raisestate:
 		lua_pushinteger(L, info->raisestate);
-	else {
+		break;
+	default:
 		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);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", lua_tostring(L, 2));
 			return 0;
 		}
-		lua_getfield(L, -1, field);
+		lua_pushvalue(L, 2); // field name
+		lua_gettable(L, -2);
 		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);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", lua_tostring(L, 2));
+		break;
 	}
 	return 1;
 }
@@ -1183,7 +1269,7 @@ static int mobjinfo_get(lua_State *L)
 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);
+	enum mobjinfo_e field = Lua_optoption(L, 2, -1, mobjinfo_fields_ref);
 
 	if (hud_running)
 		return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
@@ -1193,55 +1279,81 @@ static int mobjinfo_set(lua_State *L)
 	I_Assert(info != NULL);
 	I_Assert(info >= mobjinfo);
 
-	if (fastcmp(field,"doomednum"))
+	switch (field)
+	{
+	case mobjinfo_doomednum:
 		info->doomednum = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"spawnstate"))
+		break;
+	case mobjinfo_spawnstate:
 		info->spawnstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"spawnhealth"))
+		break;
+	case mobjinfo_spawnhealth:
 		info->spawnhealth = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"seestate"))
+		break;
+	case mobjinfo_seestate:
 		info->seestate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"seesound"))
+		break;
+	case mobjinfo_seesound:
 		info->seesound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"reactiontime"))
+		break;
+	case mobjinfo_reactiontime:
 		info->reactiontime = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"attacksound"))
+		break;
+	case mobjinfo_attacksound:
 		info->attacksound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"painstate"))
+		break;
+	case mobjinfo_painstate:
 		info->painstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"painchance"))
+		break;
+	case mobjinfo_painchance:
 		info->painchance = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"painsound"))
+		break;
+	case mobjinfo_painsound:
 		info->painsound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"meleestate"))
+		break;
+	case mobjinfo_meleestate:
 		info->meleestate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"missilestate"))
+		break;
+	case mobjinfo_missilestate:
 		info->missilestate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"deathstate"))
+		break;
+	case mobjinfo_deathstate:
 		info->deathstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"xdeathstate"))
+		break;
+	case mobjinfo_xdeathstate:
 		info->xdeathstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"deathsound"))
+		break;
+	case mobjinfo_deathsound:
 		info->deathsound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"speed"))
+		break;
+	case mobjinfo_speed:
 		info->speed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"radius"))
+		break;
+	case mobjinfo_radius:
 		info->radius = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"height"))
+		break;
+	case mobjinfo_height:
 		info->height = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"dispoffset"))
+		break;
+	case mobjinfo_dispoffset:
 		info->dispoffset = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"mass"))
+		break;
+	case mobjinfo_mass:
 		info->mass = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"damage"))
+		break;
+	case mobjinfo_damage:
 		info->damage = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"activesound"))
+		break;
+	case mobjinfo_activesound:
 		info->activesound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"flags"))
+		break;
+	case mobjinfo_flags:
 		info->flags = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"raisestate"))
+		break;
+	case mobjinfo_raisestate:
 		info->raisestate = luaL_checkinteger(L, 3);
-	else {
+		break;
+	default:
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
 		I_Assert(lua_istable(L, -1));
 		lua_pushlightuserdata(L, info);
@@ -1249,18 +1361,17 @@ static int mobjinfo_set(lua_State *L)
 		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);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobjinfo_t", lua_tostring(L, 2));
 			lua_newtable(L);
 			lua_pushlightuserdata(L, info);
 			lua_pushvalue(L, -2); // ext value table
 			lua_rawset(L, -4); // LREG_EXTVARS table
 		}
+		lua_pushvalue(L, 2); // key
 		lua_pushvalue(L, 3); // value to store
-		lua_setfield(L, -2, field);
+		lua_settable(L, -3);
 		lua_pop(L, 2);
 	}
-	//else
-		//return luaL_error(L, LUA_QL("mobjinfo_t") " has no field named " LUA_QS, field);
 	return 0;
 }
 
@@ -1788,6 +1899,8 @@ int LUA_InfoLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	mobjinfo_fields_ref = Lua_CreateFieldTable(L, mobjinfo_opt);
+
 	luaL_newmetatable(L, META_SKINCOLOR);
 		lua_pushcfunction(L, skincolor_get);
 		lua_setfield(L, -2, "__index");
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 898651520d77ead06f05ccbe767fe618708e4c20..dc477c81f53bbc8c4e3faa51b89c12d58710fe0c 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -88,6 +88,8 @@ static const char *const sector_opt[] = {
 	"gravity",
 	NULL};
 
+static int sector_fields_ref = LUA_NOREF;
+
 enum subsector_e {
 	subsector_valid = 0,
 	subsector_sector,
@@ -104,6 +106,8 @@ static const char *const subsector_opt[] = {
 	"polyList",
 	NULL};
 
+static int subsector_fields_ref = LUA_NOREF;
+
 enum line_e {
 	line_valid = 0,
 	line_v1,
@@ -156,6 +160,8 @@ static const char *const line_opt[] = {
 	"callcount",
 	NULL};
 
+static int line_fields_ref = LUA_NOREF;
+
 enum side_e {
 	side_valid = 0,
 	side_textureoffset,
@@ -184,6 +190,8 @@ static const char *const side_opt[] = {
 	"text",
 	NULL};
 
+static int side_fields_ref = LUA_NOREF;
+
 enum vertex_e {
 	vertex_valid = 0,
 	vertex_x,
@@ -204,6 +212,8 @@ static const char *const vertex_opt[] = {
 	"ceilingzset",
 	NULL};
 
+static int vertex_fields_ref = LUA_NOREF;
+
 enum ffloor_e {
 	ffloor_valid = 0,
 	ffloor_topheight,
@@ -256,6 +266,8 @@ static const char *const ffloor_opt[] = {
 	"bouncestrength",
 	NULL};
 
+static int ffloor_fields_ref = LUA_NOREF;
+
 #ifdef HAVE_LUA_SEGS
 enum seg_e {
 	seg_valid = 0,
@@ -285,6 +297,8 @@ static const char *const seg_opt[] = {
 	"polyseg",
 	NULL};
 
+static int seg_fields_ref = LUA_NOREF;
+
 enum node_e {
 	node_valid = 0,
 	node_x,
@@ -305,6 +319,8 @@ static const char *const node_opt[] = {
 	"children",
 	NULL};
 
+static int node_fields_ref = LUA_NOREF;
+
 enum nodechild_e {
 	nodechild_valid = 0,
 	nodechild_right,
@@ -356,6 +372,8 @@ static const char *const slope_opt[] = {
 	"flags",
 	NULL};
 
+static int slope_fields_ref = LUA_NOREF;
+
 // shared by both vector2_t and vector3_t
 enum vector_e {
 	vector_x = 0,
@@ -575,7 +593,7 @@ static int sectorlines_num(lua_State *L)
 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);
+	enum sector_e field = Lua_optoption(L, 2, sector_valid, sector_fields_ref);
 	INT16 i;
 
 	if (!sector)
@@ -697,7 +715,7 @@ static int sector_get(lua_State *L)
 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);
+	enum sector_e field = Lua_optoption(L, 2, sector_valid, sector_fields_ref);
 
 	if (!sector)
 		return luaL_error(L, "accessed sector_t doesn't exist anymore.");
@@ -814,7 +832,7 @@ static int sector_num(lua_State *L)
 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);
+	enum subsector_e field = Lua_optoption(L, 2, subsector_valid, subsector_fields_ref);
 
 	if (!subsector)
 	{
@@ -898,7 +916,7 @@ static int linestringargs_len(lua_State *L)
 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);
+	enum line_e field = Lua_optoption(L, 2, line_valid, line_fields_ref);
 
 	if (!line)
 	{
@@ -1057,7 +1075,7 @@ static int sidenum_get(lua_State *L)
 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);
+	enum side_e field = Lua_optoption(L, 2, side_valid, side_fields_ref);
 
 	if (!side)
 	{
@@ -1110,7 +1128,7 @@ static int side_get(lua_State *L)
 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);
+	enum side_e field = Lua_optoption(L, 2, side_valid, side_fields_ref);
 
 	if (!side)
 	{
@@ -1166,7 +1184,7 @@ static int side_num(lua_State *L)
 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);
+	enum vertex_e field = Lua_optoption(L, 2, vertex_valid, vertex_fields_ref);
 
 	if (!vertex)
 	{
@@ -1220,7 +1238,7 @@ static int vertex_num(lua_State *L)
 static int seg_get(lua_State *L)
 {
 	seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG));
-	enum seg_e field = luaL_checkoption(L, 2, seg_opt[0], seg_opt);
+	enum seg_e field = Lua_optoption(L, 2, seg_valid, seg_fields_ref);
 
 	if (!seg)
 	{
@@ -1284,7 +1302,7 @@ static int seg_num(lua_State *L)
 static int node_get(lua_State *L)
 {
 	node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE));
-	enum node_e field = luaL_checkoption(L, 2, node_opt[0], node_opt);
+	enum node_e field = Lua_optoption(L, 2, node_valid, node_fields_ref);
 
 	if (!node)
 	{
@@ -1920,7 +1938,7 @@ static INT32 P_GetOldFOFFlags(ffloor_t *fflr)
 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);
+	enum ffloor_e field = Lua_optoption(L, 2, ffloor_valid, ffloor_fields_ref);
 	INT16 i;
 
 	if (!ffloor)
@@ -2102,7 +2120,7 @@ static void P_SetOldFOFFlags(ffloor_t *fflr, oldffloortype_e oldflags)
 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);
+	enum ffloor_e field = Lua_optoption(L, 2, ffloor_valid, ffloor_fields_ref);
 
 	if (!ffloor)
 		return luaL_error(L, "accessed ffloor_t doesn't exist anymore.");
@@ -2197,7 +2215,7 @@ static int ffloor_set(lua_State *L)
 static int slope_get(lua_State *L)
 {
 	pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
-	enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt);
+	enum slope_e field = Lua_optoption(L, 2, slope_valid, slope_fields_ref);
 
 	if (!slope)
 	{
@@ -2241,7 +2259,7 @@ static int slope_get(lua_State *L)
 static int slope_set(lua_State *L)
 {
 	pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
-	enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt);
+	enum slope_e field = Lua_optoption(L, 2, slope_valid, slope_fields_ref);
 
 	if (!slope)
 		return luaL_error(L, "accessed pslope_t doesn't exist anymore.");
@@ -2405,110 +2423,258 @@ static int lib_nummapheaders(lua_State *L)
 // mapheader_t //
 /////////////////
 
+enum mapheaderinfo_e
+{
+	mapheaderinfo_lvlttl,
+	mapheaderinfo_subttl,
+	mapheaderinfo_actnum,
+	mapheaderinfo_typeoflevel,
+	mapheaderinfo_nextlevel,
+	mapheaderinfo_marathonnext,
+	mapheaderinfo_keywords,
+	mapheaderinfo_musname,
+	mapheaderinfo_mustrack,
+	mapheaderinfo_muspos,
+	mapheaderinfo_musinterfadeout,
+	mapheaderinfo_musintername,
+	mapheaderinfo_muspostbossname,
+	mapheaderinfo_muspostbosstrack,
+	mapheaderinfo_muspostbosspos,
+	mapheaderinfo_muspostbossfadein,
+	mapheaderinfo_musforcereset,
+	mapheaderinfo_forcecharacter,
+	mapheaderinfo_weather,
+	mapheaderinfo_skynum,
+	mapheaderinfo_skybox_scalex,
+	mapheaderinfo_skybox_scaley,
+	mapheaderinfo_skybox_scalez,
+	mapheaderinfo_interscreen,
+	mapheaderinfo_runsoc,
+	mapheaderinfo_scriptname,
+	mapheaderinfo_precutscenenum,
+	mapheaderinfo_cutscenenum,
+	mapheaderinfo_countdown,
+	mapheaderinfo_palette,
+	mapheaderinfo_numlaps,
+	mapheaderinfo_unlockrequired,
+	mapheaderinfo_levelselect,
+	mapheaderinfo_bonustype,
+	mapheaderinfo_ltzzpatch,
+	mapheaderinfo_ltzztext,
+	mapheaderinfo_ltactdiamond,
+	mapheaderinfo_maxbonuslives,
+	mapheaderinfo_levelflags,
+	mapheaderinfo_menuflags,
+	mapheaderinfo_selectheading,
+	mapheaderinfo_startrings,
+	mapheaderinfo_sstimer,
+	mapheaderinfo_ssspheres,
+	mapheaderinfo_gravity,
+};
+
+static const char *const mapheaderinfo_opt[] = {
+	"lvlttl",
+	"subttl",
+	"actnum",
+	"typeoflevel",
+	"nextlevel",
+	"marathonnext",
+	"keywords",
+	"musname",
+	"mustrack",
+	"muspos",
+	"musinterfadeout",
+	"musintername",
+	"muspostbossname",
+	"muspostbosstrack",
+	"muspostbosspos",
+	"muspostbossfadein",
+	"musforcereset",
+	"forcecharacter",
+	"weather",
+	"skynum",
+	"skybox_scalex",
+	"skybox_scaley",
+	"skybox_scalez",
+	"interscreen",
+	"runsoc",
+	"scriptname",
+	"precutscenenum",
+	"cutscenenum",
+	"countdown",
+	"palette",
+	"numlaps",
+	"unlockrequired",
+	"levelselect",
+	"bonustype",
+	"ltzzpatch",
+	"ltzztext",
+	"ltactdiamond",
+	"maxbonuslives",
+	"levelflags",
+	"menuflags",
+	"selectheading",
+	"startrings",
+	"sstimer",
+	"ssspheres",
+	"gravity",
+	NULL,
+};
+
+static int mapheaderinfo_fields_ref = LUA_NOREF;
+
 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);
+	enum mapheaderinfo_e field = Lua_optoption(L, 2, -1, mapheaderinfo_fields_ref);
 	INT16 i;
-	if (fastcmp(field,"lvlttl"))
+
+	switch (field)
+	{
+	case mapheaderinfo_lvlttl:
 		lua_pushstring(L, header->lvlttl);
-	else if (fastcmp(field,"subttl"))
+		break;
+	case mapheaderinfo_subttl:
 		lua_pushstring(L, header->subttl);
-	else if (fastcmp(field,"actnum"))
+		break;
+	case mapheaderinfo_actnum:
 		lua_pushinteger(L, header->actnum);
-	else if (fastcmp(field,"typeoflevel"))
+		break;
+	case mapheaderinfo_typeoflevel:
 		lua_pushinteger(L, header->typeoflevel);
-	else if (fastcmp(field,"nextlevel"))
+		break;
+	case mapheaderinfo_nextlevel:
 		lua_pushinteger(L, header->nextlevel);
-	else if (fastcmp(field,"marathonnext"))
+		break;
+	case mapheaderinfo_marathonnext:
 		lua_pushinteger(L, header->marathonnext);
-	else if (fastcmp(field,"keywords"))
+		break;
+	case mapheaderinfo_keywords:
 		lua_pushstring(L, header->keywords);
-	else if (fastcmp(field,"musname"))
+		break;
+	case mapheaderinfo_musname:
 		lua_pushstring(L, header->musname);
-	else if (fastcmp(field,"mustrack"))
+		break;
+	case mapheaderinfo_mustrack:
 		lua_pushinteger(L, header->mustrack);
-	else if (fastcmp(field,"muspos"))
+		break;
+	case mapheaderinfo_muspos:
 		lua_pushinteger(L, header->muspos);
-	else if (fastcmp(field,"musinterfadeout"))
+		break;
+	case mapheaderinfo_musinterfadeout:
 		lua_pushinteger(L, header->musinterfadeout);
-	else if (fastcmp(field,"musintername"))
+		break;
+	case mapheaderinfo_musintername:
 		lua_pushstring(L, header->musintername);
-	else if (fastcmp(field,"muspostbossname"))
+		break;
+	case mapheaderinfo_muspostbossname:
 		lua_pushstring(L, header->muspostbossname);
-	else if (fastcmp(field,"muspostbosstrack"))
+		break;
+	case mapheaderinfo_muspostbosstrack:
 		lua_pushinteger(L, header->muspostbosstrack);
-	else if (fastcmp(field,"muspostbosspos"))
+		break;
+	case mapheaderinfo_muspostbosspos:
 		lua_pushinteger(L, header->muspostbosspos);
-	else if (fastcmp(field,"muspostbossfadein"))
+		break;
+	case mapheaderinfo_muspostbossfadein:
 		lua_pushinteger(L, header->muspostbossfadein);
-	else if (fastcmp(field,"musforcereset"))
+		break;
+	case mapheaderinfo_musforcereset:
 		lua_pushinteger(L, header->musforcereset);
-	else if (fastcmp(field,"forcecharacter"))
+		break;
+	case mapheaderinfo_forcecharacter:
 		lua_pushstring(L, header->forcecharacter);
-	else if (fastcmp(field,"weather"))
+		break;
+	case mapheaderinfo_weather:
 		lua_pushinteger(L, header->weather);
-	else if (fastcmp(field,"skynum"))
+		break;
+	case mapheaderinfo_skynum:
 		lua_pushinteger(L, header->skynum);
-	else if (fastcmp(field,"skybox_scalex"))
+		break;
+	case mapheaderinfo_skybox_scalex:
 		lua_pushinteger(L, header->skybox_scalex);
-	else if (fastcmp(field,"skybox_scaley"))
+		break;
+	case mapheaderinfo_skybox_scaley:
 		lua_pushinteger(L, header->skybox_scaley);
-	else if (fastcmp(field,"skybox_scalez"))
+		break;
+	case mapheaderinfo_skybox_scalez:
 		lua_pushinteger(L, header->skybox_scalez);
-	else if (fastcmp(field,"interscreen")) {
+		break;
+	case mapheaderinfo_interscreen:
 		for (i = 0; i < 8; i++)
 			if (!header->interscreen[i])
 				break;
 		lua_pushlstring(L, header->interscreen, i);
-	} else if (fastcmp(field,"runsoc"))
+		break;
+	case mapheaderinfo_runsoc:
 		lua_pushstring(L, header->runsoc);
-	else if (fastcmp(field,"scriptname"))
+		break;
+	case mapheaderinfo_scriptname:
 		lua_pushstring(L, header->scriptname);
-	else if (fastcmp(field,"precutscenenum"))
+		break;
+	case mapheaderinfo_precutscenenum:
 		lua_pushinteger(L, header->precutscenenum);
-	else if (fastcmp(field,"cutscenenum"))
+		break;
+	case mapheaderinfo_cutscenenum:
 		lua_pushinteger(L, header->cutscenenum);
-	else if (fastcmp(field,"countdown"))
+		break;
+	case mapheaderinfo_countdown:
 		lua_pushinteger(L, header->countdown);
-	else if (fastcmp(field,"palette"))
+		break;
+	case mapheaderinfo_palette:
 		lua_pushinteger(L, header->palette);
-	else if (fastcmp(field,"numlaps"))
+		break;
+	case mapheaderinfo_numlaps:
 		lua_pushinteger(L, header->numlaps);
-	else if (fastcmp(field,"unlockrequired"))
+		break;
+	case mapheaderinfo_unlockrequired:
 		lua_pushinteger(L, header->unlockrequired);
-	else if (fastcmp(field,"levelselect"))
+		break;
+	case mapheaderinfo_levelselect:
 		lua_pushinteger(L, header->levelselect);
-	else if (fastcmp(field,"bonustype"))
+		break;
+	case mapheaderinfo_bonustype:
 		lua_pushinteger(L, header->bonustype);
-	else if (fastcmp(field,"ltzzpatch"))
+		break;
+	case mapheaderinfo_ltzzpatch:
 		lua_pushstring(L, header->ltzzpatch);
-	else if (fastcmp(field,"ltzztext"))
+		break;
+	case mapheaderinfo_ltzztext:
 		lua_pushstring(L, header->ltzztext);
-	else if (fastcmp(field,"ltactdiamond"))
+		break;
+	case mapheaderinfo_ltactdiamond:
 		lua_pushstring(L, header->ltactdiamond);
-	else if (fastcmp(field,"maxbonuslives"))
+		break;
+	case mapheaderinfo_maxbonuslives:
 		lua_pushinteger(L, header->maxbonuslives);
-	else if (fastcmp(field,"levelflags"))
+		break;
+	case mapheaderinfo_levelflags:
 		lua_pushinteger(L, header->levelflags);
-	else if (fastcmp(field,"menuflags"))
+		break;
+	case mapheaderinfo_menuflags:
 		lua_pushinteger(L, header->menuflags);
-	else if (fastcmp(field,"selectheading"))
+		break;
+	case mapheaderinfo_selectheading:
 		lua_pushstring(L, header->selectheading);
-	else if (fastcmp(field,"startrings"))
+		break;
+	case mapheaderinfo_startrings:
 		lua_pushinteger(L, header->startrings);
-	else if (fastcmp(field, "sstimer"))
+		break;
+	case mapheaderinfo_sstimer:
 		lua_pushinteger(L, header->sstimer);
-	else if (fastcmp(field, "ssspheres"))
+		break;
+	case mapheaderinfo_ssspheres:
 		lua_pushinteger(L, header->ssspheres);
-	else if (fastcmp(field, "gravity"))
+		break;
+	case mapheaderinfo_gravity:
 		lua_pushfixed(L, header->gravity);
+		break;
 	// TODO add support for reading numGradedMares and grades
-	else {
+	default:
 		// Read custom vars now
 		// (note: don't include the "LUA." in your lua scripts!)
 		UINT8 j = 0;
-		for (;j < header->numCustomOptions && !fastcmp(field, header->customopts[j].option); ++j);
+		for (;j < header->numCustomOptions && !fastcmp(lua_tostring(L, 2), header->customopts[j].option); ++j);
 
 		if(j < header->numCustomOptions)
 			lua_pushstring(L, header->customopts[j].value);
@@ -2539,6 +2705,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	sector_fields_ref = Lua_CreateFieldTable(L, sector_opt);
+
 	luaL_newmetatable(L, META_SUBSECTOR);
 		lua_pushcfunction(L, subsector_get);
 		lua_setfield(L, -2, "__index");
@@ -2547,6 +2715,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	subsector_fields_ref = Lua_CreateFieldTable(L, subsector_opt);
+
 	luaL_newmetatable(L, META_LINE);
 		lua_pushcfunction(L, line_get);
 		lua_setfield(L, -2, "__index");
@@ -2555,6 +2725,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	line_fields_ref = Lua_CreateFieldTable(L, line_opt);
+
 	luaL_newmetatable(L, META_LINEARGS);
 		lua_pushcfunction(L, lineargs_get);
 		lua_setfield(L, -2, "__index");
@@ -2587,6 +2759,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	side_fields_ref = Lua_CreateFieldTable(L, side_opt);
+
 	luaL_newmetatable(L, META_VERTEX);
 		lua_pushcfunction(L, vertex_get);
 		lua_setfield(L, -2, "__index");
@@ -2595,6 +2769,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	vertex_fields_ref = Lua_CreateFieldTable(L, vertex_opt);
+
 	luaL_newmetatable(L, META_FFLOOR);
 		lua_pushcfunction(L, ffloor_get);
 		lua_setfield(L, -2, "__index");
@@ -2603,6 +2779,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L, 1);
 
+	ffloor_fields_ref = Lua_CreateFieldTable(L, ffloor_opt);
+
 #ifdef HAVE_LUA_SEGS
 	luaL_newmetatable(L, META_SEG);
 		lua_pushcfunction(L, seg_get);
@@ -2612,6 +2790,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	seg_fields_ref = Lua_CreateFieldTable(L, seg_opt);
+
 	luaL_newmetatable(L, META_NODE);
 		lua_pushcfunction(L, node_get);
 		lua_setfield(L, -2, "__index");
@@ -2620,6 +2800,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	node_fields_ref = Lua_CreateFieldTable(L, node_opt);
+
 	luaL_newmetatable(L, META_NODEBBOX);
 		//lua_pushcfunction(L, nodebbox_get);
 		//lua_setfield(L, -2, "__index");
@@ -2646,6 +2828,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L, 1);
 
+	slope_fields_ref = Lua_CreateFieldTable(L, slope_opt);
+
 	luaL_newmetatable(L, META_VECTOR2);
 		lua_pushcfunction(L, vector2_get);
 		lua_setfield(L, -2, "__index");
@@ -2664,6 +2848,8 @@ int LUA_MapLib(lua_State *L)
 		//lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	mapheaderinfo_fields_ref = Lua_CreateFieldTable(L, mapheaderinfo_opt);
+
 	LUA_PushTaggableObjectArray(L, "sectors",
 			lib_iterateSectors,
 			lib_getSector,
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 5c2b8b4d3cec0be91593807114346c77f454d879..09d244c9101bc7b562e4c082113b197be5abf618 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -179,10 +179,12 @@ static const char *const mobj_opt[] = {
 
 #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field])
 
+static int mobj_fields_ref = LUA_NOREF;
+
 static int mobj_get(lua_State *L)
 {
 	mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
-	enum mobj_e field = Lua_optoption(L, 2, NULL, mobj_opt);
+	enum mobj_e field = Lua_optoption(L, 2, -1, mobj_fields_ref);
 	lua_settop(L, 2);
 
 	if (!mo || !ISINLEVEL) {
@@ -467,7 +469,7 @@ static int mobj_get(lua_State *L)
 static int mobj_set(lua_State *L)
 {
 	mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
-	enum mobj_e field = Lua_optoption(L, 2, mobj_opt[0], mobj_opt);
+	enum mobj_e field = Lua_optoption(L, 2, mobj_valid, mobj_fields_ref);
 	lua_settop(L, 3);
 
 	INLEVEL
@@ -876,14 +878,55 @@ static int thingstringargs_len(lua_State *L)
 	return 1;
 }
 
+enum mapthing_e {
+	mapthing_valid = 0,
+	mapthing_x,
+	mapthing_y,
+	mapthing_angle,
+	mapthing_pitch,
+	mapthing_roll,
+	mapthing_type,
+	mapthing_options,
+	mapthing_scale,
+	mapthing_z,
+	mapthing_extrainfo,
+	mapthing_tag,
+	mapthing_taglist,
+	mapthing_args,
+	mapthing_stringargs,
+	mapthing_mobj,
+};
+
+const char *const mapthing_opt[] = {
+	"valid",
+	"x",
+	"y",
+	"angle",
+	"pitch",
+	"roll",
+	"type",
+	"options",
+	"scale",
+	"z",
+	"extrainfo",
+	"tag",
+	"taglist",
+	"args",
+	"stringargs",
+	"mobj",
+	NULL,
+};
+
+static int mapthing_fields_ref = LUA_NOREF;
+
 static int mapthing_get(lua_State *L)
 {
 	mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
-	const char *field = luaL_checkstring(L, 2);
-	lua_Integer number;
+	enum mapthing_e field = Lua_optoption(L, 2, -1, mapthing_fields_ref);
+	lua_settop(L, 2);
 
 	if (!mt) {
-		if (fastcmp(field,"valid")) {
+		if (field == mapthing_valid) {
 			lua_pushboolean(L, false);
 			return 1;
 		}
@@ -892,62 +935,71 @@ static int mapthing_get(lua_State *L)
 		return 0;
 	}
 
-	if (fastcmp(field,"valid")) {
-		lua_pushboolean(L, true);
-		return 1;
-	} else if(fastcmp(field,"x"))
-		number = mt->x;
-	else if(fastcmp(field,"y"))
-		number = mt->y;
-	else if(fastcmp(field,"angle"))
-		number = mt->angle;
-	else if(fastcmp(field,"pitch"))
-		number = mt->pitch;
-	else if(fastcmp(field,"roll"))
-		number = mt->roll;
-	else if(fastcmp(field,"type"))
-		number = mt->type;
-	else if(fastcmp(field,"options"))
-		number = mt->options;
-	else if(fastcmp(field,"scale"))
-		number = mt->scale;
-	else if(fastcmp(field,"z"))
-		number = mt->z;
-	else if(fastcmp(field,"extrainfo"))
-		number = mt->extrainfo;
-	else if(fastcmp(field,"tag"))
-		number = Tag_FGet(&mt->tags);
-	else if(fastcmp(field,"taglist"))
-	{
-		LUA_PushUserdata(L, &mt->tags, META_TAGLIST);
-		return 1;
-	}
-	else if(fastcmp(field,"args"))
-	{
-		LUA_PushUserdata(L, mt->args, META_THINGARGS);
-		return 1;
-	}
-	else if(fastcmp(field,"stringargs"))
+	switch (field)
 	{
-		LUA_PushUserdata(L, mt->stringargs, META_THINGSTRINGARGS);
-		return 1;
+		case mapthing_valid:
+			lua_pushboolean(L, true);
+			break;
+		case mapthing_x:
+			lua_pushinteger(L, mt->x);
+			break;
+		case mapthing_y:
+			lua_pushinteger(L, mt->y);
+			break;
+		case mapthing_angle:
+			lua_pushinteger(L, mt->angle);
+			break;
+		case mapthing_pitch:
+			lua_pushinteger(L, mt->pitch);
+			break;
+		case mapthing_roll:
+			lua_pushinteger(L, mt->roll);
+			break;
+		case mapthing_type:
+			lua_pushinteger(L, mt->type);
+			break;
+		case mapthing_options:
+			lua_pushinteger(L, mt->options);
+			break;
+		case mapthing_scale:
+			lua_pushinteger(L, mt->scale);
+			break;
+		case mapthing_z:
+			lua_pushinteger(L, mt->z);
+			break;
+		case mapthing_extrainfo:
+			lua_pushinteger(L, mt->extrainfo);
+			break;
+		case mapthing_tag:
+			lua_pushinteger(L, Tag_FGet(&mt->tags));
+			break;
+		case mapthing_taglist:
+			LUA_PushUserdata(L, &mt->tags, META_TAGLIST);
+			break;
+		case mapthing_args:
+			LUA_PushUserdata(L, mt->args, META_THINGARGS);
+			break;
+		case mapthing_stringargs:
+			LUA_PushUserdata(L, mt->stringargs, META_THINGSTRINGARGS);
+			break;
+		case mapthing_mobj:
+			LUA_PushUserdata(L, mt->mobj, META_MOBJ);
+			break;
+		default:
+			if (devparm)
+				return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
+			else
+				return 0;
 	}
-	else if(fastcmp(field,"mobj")) {
-		LUA_PushUserdata(L, mt->mobj, META_MOBJ);
-		return 1;
-	} else if (devparm)
-		return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
-	else
-		return 0;
 
-	lua_pushinteger(L, number);
 	return 1;
 }
 
 static int mapthing_set(lua_State *L)
 {
 	mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
-	const char *field = luaL_checkstring(L, 2);
+	enum mapthing_e field = Lua_optoption(L, 2, -1, mapthing_fields_ref);
+	lua_settop(L, 3);
 
 	if (!mt)
 		return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
@@ -957,39 +1009,52 @@ static int mapthing_set(lua_State *L)
 	if (hook_cmd_running)
 		return luaL_error(L, "Do not alter mapthing_t in CMD building code!");
 
-	if(fastcmp(field,"x"))
-		mt->x = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"y"))
-		mt->y = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"angle"))
-		mt->angle = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"pitch"))
-		mt->pitch = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"roll"))
-		mt->roll = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"type"))
-		mt->type = (UINT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"options"))
-		mt->options = (UINT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"scale"))
-		mt->scale = luaL_checkfixed(L, 3);
-	else if(fastcmp(field,"z"))
-		mt->z = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"extrainfo"))
+	switch (field)
 	{
-		INT32 extrainfo = luaL_checkinteger(L, 3);
-		if (extrainfo & ~15)
-			return luaL_error(L, "mapthing_t extrainfo set %d out of range (%d - %d)", extrainfo, 0, 15);
-		mt->extrainfo = (UINT8)extrainfo;
+		case mapthing_x:
+			mt->x = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_y:
+			mt->y = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_angle:
+			mt->angle = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_pitch:
+			mt->pitch = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_roll:
+			mt->roll = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_type:
+			mt->type = (UINT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_options:
+			mt->options = (UINT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_scale:
+			mt->scale = luaL_checkfixed(L, 3);
+			break;
+		case mapthing_z:
+			mt->z = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_extrainfo:
+			INT32 extrainfo = luaL_checkinteger(L, 3);
+			if (extrainfo & ~15)
+				return luaL_error(L, "mapthing_t extrainfo set %d out of range (%d - %d)", extrainfo, 0, 15);
+			mt->extrainfo = (UINT8)extrainfo;
+			break;
+		case mapthing_tag:
+			Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3));
+			break;
+		case mapthing_taglist:
+			return LUA_ErrSetDirectly(L, "mapthing_t", "taglist");
+		case mapthing_mobj:
+			mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
+			break;
+		default:
+			return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
 	}
-	else if (fastcmp(field,"tag"))
-		Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3));
-	else if (fastcmp(field,"taglist"))
-		return LUA_ErrSetDirectly(L, "mapthing_t", "taglist");
-	else if(fastcmp(field,"mobj"))
-		mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
-	else
-		return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
 
 	return 0;
 }
@@ -1051,6 +1116,8 @@ int LUA_MobjLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L,1);
 
+	mobj_fields_ref = Lua_CreateFieldTable(L, mobj_opt);
+
 	luaL_newmetatable(L, META_THINGARGS);
 		lua_pushcfunction(L, thingargs_get);
 		lua_setfield(L, -2, "__index");
@@ -1078,6 +1145,8 @@ int LUA_MobjLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L,1);
 
+	mapthing_fields_ref = Lua_CreateFieldTable(L, mapthing_opt);
+
 	LUA_PushTaggableObjectArray(L, "mapthings",
 			lib_iterateMapthings,
 			lib_getMapthing,
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index f7e14e78f96a89e490069da4fcadfffdfc59dd7c..1d06e081b114435fd6595d8c9896a5ba10660d81 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -80,322 +80,764 @@ static int lib_lenPlayer(lua_State *L)
 	return 1;
 }
 
+enum player_e
+{
+	player_valid,
+	player_name,
+	player_realmo,
+	player_mo,
+	player_cmd,
+	player_playerstate,
+	player_camerascale,
+	player_shieldscale,
+	player_viewz,
+	player_viewheight,
+	player_deltaviewheight,
+	player_bob,
+	player_viewrollangle,
+	player_aiming,
+	player_drawangle,
+	player_rings,
+	player_spheres,
+	player_pity,
+	player_currentweapon,
+	player_ringweapons,
+	player_ammoremoval,
+	player_ammoremovaltimer,
+	player_ammoremovalweapon,
+	player_powers,
+	player_pflags,
+	player_panim,
+	player_flashcount,
+	player_flashpal,
+	player_skincolor,
+	player_skin,
+	player_availabilities,
+	player_score,
+	player_dashspeed,
+	player_normalspeed,
+	player_runspeed,
+	player_thrustfactor,
+	player_accelstart,
+	player_acceleration,
+	player_charability,
+	player_charability2,
+	player_charflags,
+	player_thokitem,
+	player_spinitem,
+	player_revitem,
+	player_followitem,
+	player_followmobj,
+	player_actionspd,
+	player_mindash,
+	player_maxdash,
+	player_jumpfactor,
+	player_height,
+	player_spinheight,
+	player_lives,
+	player_continues,
+	player_xtralife,
+	player_gotcontinue,
+	player_speed,
+	player_secondjump,
+	player_fly1,
+	player_scoreadd,
+	player_glidetime,
+	player_climbing,
+	player_deadtimer,
+	player_exiting,
+	player_homing,
+	player_dashmode,
+	player_skidtime,
+	player_cmomx,
+	player_cmomy,
+	player_rmomx,
+	player_rmomy,
+	player_numboxes,
+	player_totalring,
+	player_realtime,
+	player_laps,
+	player_ctfteam,
+	player_gotflag,
+	player_weapondelay,
+	player_tossdelay,
+	player_starpostx,
+	player_starposty,
+	player_starpostz,
+	player_starpostnum,
+	player_starposttime,
+	player_starpostangle,
+	player_starpostscale,
+	player_angle_pos,
+	player_old_angle_pos,
+	player_axis1,
+	player_axis2,
+	player_bumpertime,
+	player_flyangle,
+	player_drilltimer,
+	player_linkcount,
+	player_linktimer,
+	player_anotherflyangle,
+	player_nightstime,
+	player_drillmeter,
+	player_drilldelay,
+	player_bonustime,
+	player_capsule,
+	player_drone,
+	player_oldscale,
+	player_mare,
+	player_marelap,
+	player_marebonuslap,
+	player_marebegunat,
+	player_startedtime,
+	player_finishedtime,
+	player_lapbegunat,
+	player_lapstartedtime,
+	player_finishedspheres,
+	player_finishedrings,
+	player_marescore,
+	player_lastmarescore,
+	player_totalmarescore,
+	player_lastmare,
+	player_lastmarelap,
+	player_lastmarebonuslap,
+	player_totalmarelap,
+	player_totalmarebonuslap,
+	player_maxlink,
+	player_texttimer,
+	player_textvar,
+	player_lastsidehit,
+	player_lastlinehit,
+	player_losstime,
+	player_timeshit,
+	player_onconveyor,
+	player_awayviewmobj,
+	player_awayviewtics,
+	player_awayviewaiming,
+	player_spectator,
+	player_outofcoop,
+	player_bot,
+	player_botleader,
+	player_lastbuttons,
+	player_blocked,
+	player_jointime,
+	player_quittime,
+#ifdef HWRENDER
+	player_fovadd,
+#endif
+};
+
+static const char *const player_opt[] = {
+	"valid",
+	"name",
+	"realmo",
+	"mo",
+	"cmd",
+	"playerstate",
+	"camerascale",
+	"shieldscale",
+	"viewz",
+	"viewheight",
+	"deltaviewheight",
+	"bob",
+	"viewrollangle",
+	"aiming",
+	"drawangle",
+	"rings",
+	"spheres",
+	"pity",
+	"currentweapon",
+	"ringweapons",
+	"ammoremoval",
+	"ammoremovaltimer",
+	"ammoremovalweapon",
+	"powers",
+	"pflags",
+	"panim",
+	"flashcount",
+	"flashpal",
+	"skincolor",
+	"skin",
+	"availabilities",
+	"score",
+	"dashspeed",
+	"normalspeed",
+	"runspeed",
+	"thrustfactor",
+	"accelstart",
+	"acceleration",
+	"charability",
+	"charability2",
+	"charflags",
+	"thokitem",
+	"spinitem",
+	"revitem",
+	"followitem",
+	"followmobj",
+	"actionspd",
+	"mindash",
+	"maxdash",
+	"jumpfactor",
+	"height",
+	"spinheight",
+	"lives",
+	"continues",
+	"xtralife",
+	"gotcontinue",
+	"speed",
+	"secondjump",
+	"fly1",
+	"scoreadd",
+	"glidetime",
+	"climbing",
+	"deadtimer",
+	"exiting",
+	"homing",
+	"dashmode",
+	"skidtime",
+	"cmomx",
+	"cmomy",
+	"rmomx",
+	"rmomy",
+	"numboxes",
+	"totalring",
+	"realtime",
+	"laps",
+	"ctfteam",
+	"gotflag",
+	"weapondelay",
+	"tossdelay",
+	"starpostx",
+	"starposty",
+	"starpostz",
+	"starpostnum",
+	"starposttime",
+	"starpostangle",
+	"starpostscale",
+	"angle_pos",
+	"old_angle_pos",
+	"axis1",
+	"axis2",
+	"bumpertime",
+	"flyangle",
+	"drilltimer",
+	"linkcount",
+	"linktimer",
+	"anotherflyangle",
+	"nightstime",
+	"drillmeter",
+	"drilldelay",
+	"bonustime",
+	"capsule",
+	"drone",
+	"oldscale",
+	"mare",
+	"marelap",
+	"marebonuslap",
+	"marebegunat",
+	"startedtime",
+	"finishedtime",
+	"lapbegunat",
+	"lapstartedtime",
+	"finishedspheres",
+	"finishedrings",
+	"marescore",
+	"lastmarescore",
+	"totalmarescore",
+	"lastmare",
+	"lastmarelap",
+	"lastmarebonuslap",
+	"totalmarelap",
+	"totalmarebonuslap",
+	"maxlink",
+	"texttimer",
+	"textvar",
+	"lastsidehit",
+	"lastlinehit",
+	"losstime",
+	"timeshit",
+	"onconveyor",
+	"awayviewmobj",
+	"awayviewtics",
+	"awayviewaiming",
+	"spectator",
+	"outofcoop",
+	"bot",
+	"botleader",
+	"lastbuttons",
+	"blocked",
+	"jointime",
+	"quittime",
+#ifdef HWRENDER
+	"fovadd",
+#endif
+	NULL,
+};
+
+static int player_fields_ref = LUA_NOREF;
+
 static int player_get(lua_State *L)
 {
 	player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
-	const char *field = luaL_checkstring(L, 2);
+	enum player_e field = Lua_optoption(L, 2, -1, player_fields_ref);
+	lua_settop(L, 2);
 
-	if (!plr) {
-		if (fastcmp(field,"valid")) {
+	if (!plr)
+	{
+		if (field == player_valid)
+		{
 			lua_pushboolean(L, false);
 			return 1;
 		}
 		return LUA_ErrInvalid(L, "player_t");
 	}
 
-	if (fastcmp(field,"valid"))
+	switch (field)
+	{
+	case player_valid:
 		lua_pushboolean(L, true);
-	else if (fastcmp(field,"name"))
+		break;
+	case player_name:
 		lua_pushstring(L, player_names[plr-players]);
-	else if (fastcmp(field,"realmo"))
+		break;
+	case player_realmo:
 		LUA_PushUserdata(L, plr->mo, META_MOBJ);
+		break;
 	// Kept for backward-compatibility
 	// Should be fixed to work like "realmo" later
-	else if (fastcmp(field,"mo"))
-	{
+	case player_mo:
 		if (plr->spectator)
 			lua_pushnil(L);
 		else
 			LUA_PushUserdata(L, plr->mo, META_MOBJ);
-	}
-	else if (fastcmp(field,"cmd"))
+		break;
+	case player_cmd:
 		LUA_PushUserdata(L, &plr->cmd, META_TICCMD);
-	else if (fastcmp(field,"playerstate"))
+		break;
+	case player_playerstate:
 		lua_pushinteger(L, plr->playerstate);
-	else if (fastcmp(field,"camerascale"))
+		break;
+	case player_camerascale:
 		lua_pushfixed(L, plr->camerascale);
-	else if (fastcmp(field,"shieldscale"))
+		break;
+	case player_shieldscale:
 		lua_pushfixed(L, plr->shieldscale);
-	else if (fastcmp(field,"viewz"))
+		break;
+	case player_viewz:
 		lua_pushfixed(L, plr->viewz);
-	else if (fastcmp(field,"viewheight"))
+		break;
+	case player_viewheight:
 		lua_pushfixed(L, plr->viewheight);
-	else if (fastcmp(field,"deltaviewheight"))
+		break;
+	case player_deltaviewheight:
 		lua_pushfixed(L, plr->deltaviewheight);
-	else if (fastcmp(field,"bob"))
+		break;
+	case player_bob:
 		lua_pushfixed(L, plr->bob);
-	else if (fastcmp(field,"viewrollangle"))
+		break;
+	case player_viewrollangle:
 		lua_pushangle(L, plr->viewrollangle);
-	else if (fastcmp(field,"aiming"))
+		break;
+	case player_aiming:
 		lua_pushangle(L, plr->aiming);
-	else if (fastcmp(field,"drawangle"))
+		break;
+	case player_drawangle:
 		lua_pushangle(L, plr->drawangle);
-	else if (fastcmp(field,"rings"))
+		break;
+	case player_rings:
 		lua_pushinteger(L, plr->rings);
-	else if (fastcmp(field,"spheres"))
+		break;
+	case player_spheres:
 		lua_pushinteger(L, plr->spheres);
-	else if (fastcmp(field,"pity"))
+		break;
+	case player_pity:
 		lua_pushinteger(L, plr->pity);
-	else if (fastcmp(field,"currentweapon"))
+		break;
+	case player_currentweapon:
 		lua_pushinteger(L, plr->currentweapon);
-	else if (fastcmp(field,"ringweapons"))
+		break;
+	case player_ringweapons:
 		lua_pushinteger(L, plr->ringweapons);
-	else if (fastcmp(field,"ammoremoval"))
+		break;
+	case player_ammoremoval:
 		lua_pushinteger(L, plr->ammoremoval);
-	else if (fastcmp(field,"ammoremovaltimer"))
+		break;
+	case player_ammoremovaltimer:
 		lua_pushinteger(L, plr->ammoremovaltimer);
-	else if (fastcmp(field,"ammoremovalweapon"))
+		break;
+	case player_ammoremovalweapon:
 		lua_pushinteger(L, plr->ammoremovalweapon);
-	else if (fastcmp(field,"powers"))
+		break;
+	case player_powers:
 		LUA_PushUserdata(L, plr->powers, META_POWERS);
-	else if (fastcmp(field,"pflags"))
+		break;
+	case player_pflags:
 		lua_pushinteger(L, plr->pflags);
-	else if (fastcmp(field,"panim"))
+		break;
+	case player_panim:
 		lua_pushinteger(L, plr->panim);
-	else if (fastcmp(field,"flashcount"))
+		break;
+	case player_flashcount:
 		lua_pushinteger(L, plr->flashcount);
-	else if (fastcmp(field,"flashpal"))
+		break;
+	case player_flashpal:
 		lua_pushinteger(L, plr->flashpal);
-	else if (fastcmp(field,"skincolor"))
+		break;
+	case player_skincolor:
 		lua_pushinteger(L, plr->skincolor);
-	else if (fastcmp(field,"skin"))
+		break;
+	case player_skin:
 		lua_pushinteger(L, plr->skin);
-	else if (fastcmp(field,"availabilities"))
+		break;
+	case player_availabilities:
 		lua_pushinteger(L, plr->availabilities);
-	else if (fastcmp(field,"score"))
+		break;
+	case player_score:
 		lua_pushinteger(L, plr->score);
-	else if (fastcmp(field,"dashspeed"))
+		break;
+	case player_dashspeed:
 		lua_pushfixed(L, plr->dashspeed);
-	else if (fastcmp(field,"normalspeed"))
+		break;
+	case player_normalspeed:
 		lua_pushfixed(L, plr->normalspeed);
-	else if (fastcmp(field,"runspeed"))
+		break;
+	case player_runspeed:
 		lua_pushfixed(L, plr->runspeed);
-	else if (fastcmp(field,"thrustfactor"))
+		break;
+	case player_thrustfactor:
 		lua_pushinteger(L, plr->thrustfactor);
-	else if (fastcmp(field,"accelstart"))
+		break;
+	case player_accelstart:
 		lua_pushinteger(L, plr->accelstart);
-	else if (fastcmp(field,"acceleration"))
+		break;
+	case player_acceleration:
 		lua_pushinteger(L, plr->acceleration);
-	else if (fastcmp(field,"charability"))
+		break;
+	case player_charability:
 		lua_pushinteger(L, plr->charability);
-	else if (fastcmp(field,"charability2"))
+		break;
+	case player_charability2:
 		lua_pushinteger(L, plr->charability2);
-	else if (fastcmp(field,"charflags"))
+		break;
+	case player_charflags:
 		lua_pushinteger(L, plr->charflags);
-	else if (fastcmp(field,"thokitem"))
+		break;
+	case player_thokitem:
 		lua_pushinteger(L, plr->thokitem);
-	else if (fastcmp(field,"spinitem"))
+		break;
+	case player_spinitem:
 		lua_pushinteger(L, plr->spinitem);
-	else if (fastcmp(field,"revitem"))
+		break;
+	case player_revitem:
 		lua_pushinteger(L, plr->revitem);
-	else if (fastcmp(field,"followitem"))
+		break;
+	case player_followitem:
 		lua_pushinteger(L, plr->followitem);
-	else if (fastcmp(field,"followmobj"))
+		break;
+	case player_followmobj:
 		LUA_PushUserdata(L, plr->followmobj, META_MOBJ);
-	else if (fastcmp(field,"actionspd"))
+		break;
+	case player_actionspd:
 		lua_pushfixed(L, plr->actionspd);
-	else if (fastcmp(field,"mindash"))
+		break;
+	case player_mindash:
 		lua_pushfixed(L, plr->mindash);
-	else if (fastcmp(field,"maxdash"))
+		break;
+	case player_maxdash:
 		lua_pushfixed(L, plr->maxdash);
-	else if (fastcmp(field,"jumpfactor"))
+		break;
+	case player_jumpfactor:
 		lua_pushfixed(L, plr->jumpfactor);
-	else if (fastcmp(field,"height"))
+		break;
+	case player_height:
 		lua_pushfixed(L, plr->height);
-	else if (fastcmp(field,"spinheight"))
+		break;
+	case player_spinheight:
 		lua_pushfixed(L, plr->spinheight);
-	else if (fastcmp(field,"lives"))
+		break;
+	case player_lives:
 		lua_pushinteger(L, plr->lives);
-	else if (fastcmp(field,"continues"))
+		break;
+	case player_continues:
 		lua_pushinteger(L, plr->continues);
-	else if (fastcmp(field,"xtralife"))
+		break;
+	case player_xtralife:
 		lua_pushinteger(L, plr->xtralife);
-	else if (fastcmp(field,"gotcontinue"))
+		break;
+	case player_gotcontinue:
 		lua_pushinteger(L, plr->gotcontinue);
-	else if (fastcmp(field,"speed"))
+		break;
+	case player_speed:
 		lua_pushfixed(L, plr->speed);
-	else if (fastcmp(field,"secondjump"))
+		break;
+	case player_secondjump:
 		lua_pushinteger(L, plr->secondjump);
-	else if (fastcmp(field,"fly1"))
+		break;
+	case player_fly1:
 		lua_pushinteger(L, plr->fly1);
-	else if (fastcmp(field,"scoreadd"))
+		break;
+	case player_scoreadd:
 		lua_pushinteger(L, plr->scoreadd);
-	else if (fastcmp(field,"glidetime"))
+		break;
+	case player_glidetime:
 		lua_pushinteger(L, plr->glidetime);
-	else if (fastcmp(field,"climbing"))
+		break;
+	case player_climbing:
 		lua_pushinteger(L, plr->climbing);
-	else if (fastcmp(field,"deadtimer"))
+		break;
+	case player_deadtimer:
 		lua_pushinteger(L, plr->deadtimer);
-	else if (fastcmp(field,"exiting"))
+		break;
+	case player_exiting:
 		lua_pushinteger(L, plr->exiting);
-	else if (fastcmp(field,"homing"))
+		break;
+	case player_homing:
 		lua_pushinteger(L, plr->homing);
-	else if (fastcmp(field,"dashmode"))
+		break;
+	case player_dashmode:
 		lua_pushinteger(L, plr->dashmode);
-	else if (fastcmp(field,"skidtime"))
+		break;
+	case player_skidtime:
 		lua_pushinteger(L, plr->skidtime);
-	else if (fastcmp(field,"cmomx"))
+		break;
+	case player_cmomx:
 		lua_pushfixed(L, plr->cmomx);
-	else if (fastcmp(field,"cmomy"))
+		break;
+	case player_cmomy:
 		lua_pushfixed(L, plr->cmomy);
-	else if (fastcmp(field,"rmomx"))
+		break;
+	case player_rmomx:
 		lua_pushfixed(L, plr->rmomx);
-	else if (fastcmp(field,"rmomy"))
+		break;
+	case player_rmomy:
 		lua_pushfixed(L, plr->rmomy);
-	else if (fastcmp(field,"numboxes"))
+		break;
+	case player_numboxes:
 		lua_pushinteger(L, plr->numboxes);
-	else if (fastcmp(field,"totalring"))
+		break;
+	case player_totalring:
 		lua_pushinteger(L, plr->totalring);
-	else if (fastcmp(field,"realtime"))
+		break;
+	case player_realtime:
 		lua_pushinteger(L, plr->realtime);
-	else if (fastcmp(field,"laps"))
+		break;
+	case player_laps:
 		lua_pushinteger(L, plr->laps);
-	else if (fastcmp(field,"ctfteam"))
+		break;
+	case player_ctfteam:
 		lua_pushinteger(L, plr->ctfteam);
-	else if (fastcmp(field,"gotflag"))
+		break;
+	case player_gotflag:
 		lua_pushinteger(L, plr->gotflag);
-	else if (fastcmp(field,"weapondelay"))
+		break;
+	case player_weapondelay:
 		lua_pushinteger(L, plr->weapondelay);
-	else if (fastcmp(field,"tossdelay"))
+		break;
+	case player_tossdelay:
 		lua_pushinteger(L, plr->tossdelay);
-	else if (fastcmp(field,"starpostx"))
+		break;
+	case player_starpostx:
 		lua_pushinteger(L, plr->starpostx);
-	else if (fastcmp(field,"starposty"))
+		break;
+	case player_starposty:
 		lua_pushinteger(L, plr->starposty);
-	else if (fastcmp(field,"starpostz"))
+		break;
+	case player_starpostz:
 		lua_pushinteger(L, plr->starpostz);
-	else if (fastcmp(field,"starpostnum"))
+		break;
+	case player_starpostnum:
 		lua_pushinteger(L, plr->starpostnum);
-	else if (fastcmp(field,"starposttime"))
+		break;
+	case player_starposttime:
 		lua_pushinteger(L, plr->starposttime);
-	else if (fastcmp(field,"starpostangle"))
+		break;
+	case player_starpostangle:
 		lua_pushangle(L, plr->starpostangle);
-	else if (fastcmp(field,"starpostscale"))
+		break;
+	case player_starpostscale:
 		lua_pushfixed(L, plr->starpostscale);
-	else if (fastcmp(field,"angle_pos"))
+		break;
+	case player_angle_pos:
 		lua_pushangle(L, plr->angle_pos);
-	else if (fastcmp(field,"old_angle_pos"))
+		break;
+	case player_old_angle_pos:
 		lua_pushangle(L, plr->old_angle_pos);
-	else if (fastcmp(field,"axis1"))
+		break;
+	case player_axis1:
 		LUA_PushUserdata(L, plr->axis1, META_MOBJ);
-	else if (fastcmp(field,"axis2"))
+		break;
+	case player_axis2:
 		LUA_PushUserdata(L, plr->axis2, META_MOBJ);
-	else if (fastcmp(field,"bumpertime"))
+		break;
+	case player_bumpertime:
 		lua_pushinteger(L, plr->bumpertime);
-	else if (fastcmp(field,"flyangle"))
+		break;
+	case player_flyangle:
 		lua_pushinteger(L, plr->flyangle);
-	else if (fastcmp(field,"drilltimer"))
+		break;
+	case player_drilltimer:
 		lua_pushinteger(L, plr->drilltimer);
-	else if (fastcmp(field,"linkcount"))
+		break;
+	case player_linkcount:
 		lua_pushinteger(L, plr->linkcount);
-	else if (fastcmp(field,"linktimer"))
+		break;
+	case player_linktimer:
 		lua_pushinteger(L, plr->linktimer);
-	else if (fastcmp(field,"anotherflyangle"))
+		break;
+	case player_anotherflyangle:
 		lua_pushinteger(L, plr->anotherflyangle);
-	else if (fastcmp(field,"nightstime"))
+		break;
+	case player_nightstime:
 		lua_pushinteger(L, plr->nightstime);
-	else if (fastcmp(field,"drillmeter"))
+		break;
+	case player_drillmeter:
 		lua_pushinteger(L, plr->drillmeter);
-	else if (fastcmp(field,"drilldelay"))
+		break;
+	case player_drilldelay:
 		lua_pushinteger(L, plr->drilldelay);
-	else if (fastcmp(field,"bonustime"))
+		break;
+	case player_bonustime:
 		lua_pushboolean(L, plr->bonustime);
-	else if (fastcmp(field,"capsule"))
+		break;
+	case player_capsule:
 		LUA_PushUserdata(L, plr->capsule, META_MOBJ);
-	else if (fastcmp(field,"drone"))
+		break;
+	case player_drone:
 		LUA_PushUserdata(L, plr->drone, META_MOBJ);
-	else if (fastcmp(field,"oldscale"))
+		break;
+	case player_oldscale:
 		lua_pushfixed(L, plr->oldscale);
-	else if (fastcmp(field,"mare"))
+		break;
+	case player_mare:
 		lua_pushinteger(L, plr->mare);
-	else if (fastcmp(field,"marelap"))
+		break;
+	case player_marelap:
 		lua_pushinteger(L, plr->marelap);
-	else if (fastcmp(field,"marebonuslap"))
+		break;
+	case player_marebonuslap:
 		lua_pushinteger(L, plr->marebonuslap);
-	else if (fastcmp(field,"marebegunat"))
+		break;
+	case player_marebegunat:
 		lua_pushinteger(L, plr->marebegunat);
-	else if (fastcmp(field,"startedtime"))
+		break;
+	case player_startedtime:
 		lua_pushinteger(L, plr->startedtime);
-	else if (fastcmp(field,"finishedtime"))
+		break;
+	case player_finishedtime:
 		lua_pushinteger(L, plr->finishedtime);
-	else if (fastcmp(field,"lapbegunat"))
+		break;
+	case player_lapbegunat:
 		lua_pushinteger(L, plr->lapbegunat);
-	else if (fastcmp(field,"lapstartedtime"))
+		break;
+	case player_lapstartedtime:
 		lua_pushinteger(L, plr->lapstartedtime);
-	else if (fastcmp(field,"finishedspheres"))
+		break;
+	case player_finishedspheres:
 		lua_pushinteger(L, plr->finishedspheres);
-	else if (fastcmp(field,"finishedrings"))
+		break;
+	case player_finishedrings:
 		lua_pushinteger(L, plr->finishedrings);
-	else if (fastcmp(field,"marescore"))
+		break;
+	case player_marescore:
 		lua_pushinteger(L, plr->marescore);
-	else if (fastcmp(field,"lastmarescore"))
+		break;
+	case player_lastmarescore:
 		lua_pushinteger(L, plr->lastmarescore);
-	else if (fastcmp(field,"totalmarescore"))
+		break;
+	case player_totalmarescore:
 		lua_pushinteger(L, plr->totalmarescore);
-	else if (fastcmp(field,"lastmare"))
+		break;
+	case player_lastmare:
 		lua_pushinteger(L, plr->lastmare);
-	else if (fastcmp(field,"lastmarelap"))
+		break;
+	case player_lastmarelap:
 		lua_pushinteger(L, plr->lastmarelap);
-	else if (fastcmp(field,"lastmarebonuslap"))
+		break;
+	case player_lastmarebonuslap:
 		lua_pushinteger(L, plr->lastmarebonuslap);
-	else if (fastcmp(field,"totalmarelap"))
+		break;
+	case player_totalmarelap:
 		lua_pushinteger(L, plr->totalmarelap);
-	else if (fastcmp(field,"totalmarebonuslap"))
+		break;
+	case player_totalmarebonuslap:
 		lua_pushinteger(L, plr->totalmarebonuslap);
-	else if (fastcmp(field,"maxlink"))
+		break;
+	case player_maxlink:
 		lua_pushinteger(L, plr->maxlink);
-	else if (fastcmp(field,"texttimer"))
+		break;
+	case player_texttimer:
 		lua_pushinteger(L, plr->texttimer);
-	else if (fastcmp(field,"textvar"))
+		break;
+	case player_textvar:
 		lua_pushinteger(L, plr->textvar);
-	else if (fastcmp(field,"lastsidehit"))
+		break;
+	case player_lastsidehit:
 		lua_pushinteger(L, plr->lastsidehit);
-	else if (fastcmp(field,"lastlinehit"))
+		break;
+	case player_lastlinehit:
 		lua_pushinteger(L, plr->lastlinehit);
-	else if (fastcmp(field,"losstime"))
+		break;
+	case player_losstime:
 		lua_pushinteger(L, plr->losstime);
-	else if (fastcmp(field,"timeshit"))
+		break;
+	case player_timeshit:
 		lua_pushinteger(L, plr->timeshit);
-	else if (fastcmp(field,"onconveyor"))
+		break;
+	case player_onconveyor:
 		lua_pushinteger(L, plr->onconveyor);
-	else if (fastcmp(field,"awayviewmobj"))
+		break;
+	case player_awayviewmobj:
 		LUA_PushUserdata(L, plr->awayviewmobj, META_MOBJ);
-	else if (fastcmp(field,"awayviewtics"))
+		break;
+	case player_awayviewtics:
 		lua_pushinteger(L, plr->awayviewtics);
-	else if (fastcmp(field,"awayviewaiming"))
+		break;
+	case player_awayviewaiming:
 		lua_pushangle(L, plr->awayviewaiming);
-	else if (fastcmp(field,"spectator"))
+		break;
+	case player_spectator:
 		lua_pushboolean(L, plr->spectator);
-	else if (fastcmp(field,"outofcoop"))
+		break;
+	case player_outofcoop:
 		lua_pushboolean(L, plr->outofcoop);
-	else if (fastcmp(field,"bot"))
+		break;
+	case player_bot:
 		lua_pushinteger(L, plr->bot);
-	else if (fastcmp(field,"botleader"))
+		break;
+	case player_botleader:
 		LUA_PushUserdata(L, plr->botleader, META_PLAYER);
-	else if (fastcmp(field,"lastbuttons"))
+		break;
+	case player_lastbuttons:
 		lua_pushinteger(L, plr->lastbuttons);
-	else if (fastcmp(field,"blocked"))
+		break;
+	case player_blocked:
 		lua_pushboolean(L, plr->blocked);
-	else if (fastcmp(field,"jointime"))
+		break;
+	case player_jointime:
 		lua_pushinteger(L, plr->jointime);
-	else if (fastcmp(field,"quittime"))
+		break;
+	case player_quittime:
 		lua_pushinteger(L, plr->quittime);
+		break;
 #ifdef HWRENDER
-	else if (fastcmp(field,"fovadd"))
+	case player_fovadd:
 		lua_pushfixed(L, plr->fovadd);
+		break;
 #endif
-	else {
+	default:
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
 		I_Assert(lua_istable(L, -1));
 		lua_pushlightuserdata(L, plr);
 		lua_rawget(L, -2);
 		if (!lua_istable(L, -1)) { // no extra values table
-			CONS_Debug(DBG_LUA, M_GetText("'%s' has no extvars table or field named '%s'; returning nil.\n"), "player_t", field);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no extvars table or field named '%s'; returning nil.\n"), "player_t", lua_tostring(L, 2));
 			return 0;
 		}
-		lua_getfield(L, -1, field);
+		lua_pushvalue(L, 2); // field name
+		lua_gettable(L, -2);
 		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"), "player_t", field);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "player_t", lua_tostring(L, 2));
+		break;
 	}
 
 	return 1;
@@ -405,7 +847,7 @@ static int player_get(lua_State *L)
 static int player_set(lua_State *L)
 {
 	player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
-	const char *field = luaL_checkstring(L, 2);
+	enum player_e field = Lua_optoption(L, 2, player_cmd, player_fields_ref);
 	if (!plr)
 		return LUA_ErrInvalid(L, "player_t");
 
@@ -414,337 +856,473 @@ static int player_set(lua_State *L)
 	if (hook_cmd_running)
 		return luaL_error(L, "Do not alter player_t in CMD building code!");
 
-	if (fastcmp(field,"mo") || fastcmp(field,"realmo")) {
+	switch (field)
+	{
+	case player_mo:
+	case player_realmo:
+	{
 		mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		plr->mo->player = NULL; // remove player pointer from old mobj
 		(newmo->player = plr)->mo = newmo; // set player pointer for new mobj, and set new mobj as the player's mobj
+		break;
 	}
-	else if (fastcmp(field,"cmd"))
+	case player_cmd:
 		return NOSET;
-	else if (fastcmp(field,"playerstate"))
+	case player_playerstate:
 		plr->playerstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"camerascale"))
+		break;
+	case player_camerascale:
 		plr->camerascale = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"shieldscale"))
+		break;
+	case player_shieldscale:
 		plr->shieldscale = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"viewz"))
+		break;
+	case player_viewz:
 		plr->viewz = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"viewheight"))
+		break;
+	case player_viewheight:
 		plr->viewheight = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"deltaviewheight"))
+		break;
+	case player_deltaviewheight:
 		plr->deltaviewheight = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"bob"))
+		break;
+	case player_bob:
 		plr->bob = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"viewrollangle"))
+		break;
+	case player_viewrollangle:
 		plr->viewrollangle = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"aiming")) {
+		break;
+	case player_aiming:
+	{
 		plr->aiming = luaL_checkangle(L, 3);
 		if (plr == &players[consoleplayer])
 			localaiming = plr->aiming;
 		else if (plr == &players[secondarydisplayplayer])
 			localaiming2 = plr->aiming;
+		break;
 	}
-	else if (fastcmp(field,"drawangle"))
+	case player_drawangle:
 		plr->drawangle = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"rings"))
+		break;
+	case player_rings:
 		plr->rings = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"spheres"))
+		break;
+	case player_spheres:
 		plr->spheres = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"pity"))
+		break;
+	case player_pity:
 		plr->pity = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"currentweapon"))
+		break;
+	case player_currentweapon:
 		plr->currentweapon = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ringweapons"))
+		break;
+	case player_ringweapons:
 		plr->ringweapons = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ammoremoval"))
+		break;
+	case player_ammoremoval:
 		plr->ammoremoval = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ammoremovaltimer"))
+		break;
+	case player_ammoremovaltimer:
 		plr->ammoremovaltimer = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ammoremovalweapon"))
+		break;
+	case player_ammoremovalweapon:
 		plr->ammoremovalweapon = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"powers"))
+		break;
+	case player_powers:
 		return NOSET;
-	else if (fastcmp(field,"pflags"))
+	case player_pflags:
 		plr->pflags = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"panim"))
+		break;
+	case player_panim:
 		plr->panim = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"flashcount"))
+		break;
+	case player_flashcount:
 		plr->flashcount = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"flashpal"))
+		break;
+	case player_flashpal:
 		plr->flashpal = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"skincolor"))
+		break;
+	case player_skincolor:
 	{
 		UINT16 newcolor = (UINT16)luaL_checkinteger(L,3);
 		if (newcolor >= numskincolors)
 			return luaL_error(L, "player.skincolor %d out of range (0 - %d).", newcolor, numskincolors-1);
 		plr->skincolor = newcolor;
+		break;
 	}
-	else if (fastcmp(field,"skin"))
+	case player_skin:
 		return NOSET;
-	else if (fastcmp(field,"availabilities"))
+	case player_availabilities:
 		return NOSET;
-	else if (fastcmp(field,"score"))
+	case player_score:
 		plr->score = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"dashspeed"))
+		break;
+	case player_dashspeed:
 		plr->dashspeed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"normalspeed"))
+		break;
+	case player_normalspeed:
 		plr->normalspeed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"runspeed"))
+		break;
+	case player_runspeed:
 		plr->runspeed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"thrustfactor"))
+		break;
+	case player_thrustfactor:
 		plr->thrustfactor = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"accelstart"))
+		break;
+	case player_accelstart:
 		plr->accelstart = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"acceleration"))
+		break;
+	case player_acceleration:
 		plr->acceleration = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"charability"))
+		break;
+	case player_charability:
 		plr->charability = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"charability2"))
+		break;
+	case player_charability2:
 		plr->charability2 = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"charflags"))
+		break;
+	case player_charflags:
 		plr->charflags = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"thokitem"))
+		break;
+	case player_thokitem:
 		plr->thokitem = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"spinitem"))
+		break;
+	case player_spinitem:
 		plr->spinitem = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"revitem"))
+		break;
+	case player_revitem:
 		plr->revitem = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"followitem"))
+		break;
+	case player_followitem:
 		plr->followitem = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"followmobj"))
+		break;
+	case player_followmobj:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->followmobj, mo);
+		break;
 	}
-	else if (fastcmp(field,"actionspd"))
+	case player_actionspd:
 		plr->actionspd = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"mindash"))
+		break;
+	case player_mindash:
 		plr->mindash = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"maxdash"))
+		break;
+	case player_maxdash:
 		plr->maxdash = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"jumpfactor"))
+		break;
+	case player_jumpfactor:
 		plr->jumpfactor = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"height"))
+		break;
+	case player_height:
 		plr->height = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"spinheight"))
+		break;
+	case player_spinheight:
 		plr->spinheight = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"lives"))
+		break;
+	case player_lives:
 		plr->lives = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"continues"))
+		break;
+	case player_continues:
 		plr->continues = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"xtralife"))
+		break;
+	case player_xtralife:
 		plr->xtralife = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"gotcontinue"))
+		break;
+	case player_gotcontinue:
 		plr->gotcontinue = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"speed"))
+		break;
+	case player_speed:
 		plr->speed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"secondjump"))
+		break;
+	case player_secondjump:
 		plr->secondjump = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"fly1"))
+		break;
+	case player_fly1:
 		plr->fly1 = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"scoreadd"))
+		break;
+	case player_scoreadd:
 		plr->scoreadd = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"glidetime"))
+		break;
+	case player_glidetime:
 		plr->glidetime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"climbing"))
+		break;
+	case player_climbing:
 		plr->climbing = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"deadtimer"))
+		break;
+	case player_deadtimer:
 		plr->deadtimer = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"exiting"))
+		break;
+	case player_exiting:
 		plr->exiting = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"homing"))
+		break;
+	case player_homing:
 		plr->homing = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"dashmode"))
+		break;
+	case player_dashmode:
 		plr->dashmode = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"skidtime"))
+		break;
+	case player_skidtime:
 		plr->skidtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"cmomx"))
+		break;
+	case player_cmomx:
 		plr->cmomx = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"cmomy"))
+		break;
+	case player_cmomy:
 		plr->cmomy = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"rmomx"))
+		break;
+	case player_rmomx:
 		plr->rmomx = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"rmomy"))
+		break;
+	case player_rmomy:
 		plr->rmomy = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"numboxes"))
+		break;
+	case player_numboxes:
 		plr->numboxes = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"totalring"))
+		break;
+	case player_totalring:
 		plr->totalring = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"realtime"))
+		break;
+	case player_realtime:
 		plr->realtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"laps"))
+		break;
+	case player_laps:
 		plr->laps = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ctfteam"))
+		break;
+	case player_ctfteam:
 		plr->ctfteam = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"gotflag"))
+		break;
+	case player_gotflag:
 		plr->gotflag = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"weapondelay"))
+		break;
+	case player_weapondelay:
 		plr->weapondelay = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"tossdelay"))
+		break;
+	case player_tossdelay:
 		plr->tossdelay = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starpostx"))
+		break;
+	case player_starpostx:
 		plr->starpostx = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starposty"))
+		break;
+	case player_starposty:
 		plr->starposty = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starpostz"))
+		break;
+	case player_starpostz:
 		plr->starpostz = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starpostnum"))
+		break;
+	case player_starpostnum:
 		plr->starpostnum = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starposttime"))
+		break;
+	case player_starposttime:
 		plr->starposttime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starpostangle"))
+		break;
+	case player_starpostangle:
 		plr->starpostangle = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"starpostscale"))
+		break;
+	case player_starpostscale:
 		plr->starpostscale = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"angle_pos"))
+		break;
+	case player_angle_pos:
 		plr->angle_pos = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"old_angle_pos"))
+		break;
+	case player_old_angle_pos:
 		plr->old_angle_pos = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"axis1"))
+		break;
+	case player_axis1:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->axis1, mo);
+		break;
 	}
-	else if (fastcmp(field,"axis2"))
+	case player_axis2:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->axis2, mo);
+		break;
 	}
-	else if (fastcmp(field,"bumpertime"))
+	case player_bumpertime:
 		plr->bumpertime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"flyangle"))
+		break;
+	case player_flyangle:
 		plr->flyangle = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"drilltimer"))
+		break;
+	case player_drilltimer:
 		plr->drilltimer = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"linkcount"))
+		break;
+	case player_linkcount:
 		plr->linkcount = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"linktimer"))
+		break;
+	case player_linktimer:
 		plr->linktimer = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"anotherflyangle"))
+		break;
+	case player_anotherflyangle:
 		plr->anotherflyangle = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"nightstime"))
+		break;
+	case player_nightstime:
 		plr->nightstime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"drillmeter"))
+		break;
+	case player_drillmeter:
 		plr->drillmeter = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"drilldelay"))
+		break;
+	case player_drilldelay:
 		plr->drilldelay = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"bonustime"))
+		break;
+	case player_bonustime:
 		plr->bonustime = luaL_checkboolean(L, 3);
-	else if (fastcmp(field,"capsule"))
+		break;
+	case player_capsule:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->capsule, mo);
+		break;
 	}
-	else if (fastcmp(field,"drone"))
+	case player_drone:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->drone, mo);
+		break;
 	}
-	else if (fastcmp(field,"oldscale"))
+	case player_oldscale:
 		plr->oldscale = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"mare"))
+		break;
+	case player_mare:
 		plr->mare = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"marelap"))
+		break;
+	case player_marelap:
 		plr->marelap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"marebonuslap"))
+		break;
+	case player_marebonuslap:
 		plr->marebonuslap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"marebegunat"))
+		break;
+	case player_marebegunat:
 		plr->marebegunat = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"startedtime"))
+		break;
+	case player_startedtime:
 		plr->startedtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"finishedtime"))
+		break;
+	case player_finishedtime:
 		plr->finishedtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lapbegunat"))
+		break;
+	case player_lapbegunat:
 		plr->lapbegunat = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lapstartedtime"))
+		break;
+	case player_lapstartedtime:
 		plr->lapstartedtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"finishedspheres"))
+		break;
+	case player_finishedspheres:
 		plr->finishedspheres = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"finishedrings"))
+		break;
+	case player_finishedrings:
 		plr->finishedrings = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"marescore"))
+		break;
+	case player_marescore:
 		plr->marescore = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastmarescore"))
+		break;
+	case player_lastmarescore:
 		plr->lastmarescore = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"totalmarescore"))
+		break;
+	case player_totalmarescore:
 		plr->totalmarescore = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastmare"))
+		break;
+	case player_lastmare:
 		plr->lastmare = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastmarelap"))
+		break;
+	case player_lastmarelap:
 		plr->lastmarelap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastmarebonuslap"))
+		break;
+	case player_lastmarebonuslap:
 		plr->lastmarebonuslap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"totalmarelap"))
+		break;
+	case player_totalmarelap:
 		plr->totalmarelap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"totalmarebonuslap"))
+		break;
+	case player_totalmarebonuslap:
 		plr->totalmarebonuslap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"maxlink"))
+		break;
+	case player_maxlink:
 		plr->maxlink = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"texttimer"))
+		break;
+	case player_texttimer:
 		plr->texttimer = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"textvar"))
+		break;
+	case player_textvar:
 		plr->textvar = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastsidehit"))
+		break;
+	case player_lastsidehit:
 		plr->lastsidehit = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastlinehit"))
+		break;
+	case player_lastlinehit:
 		plr->lastlinehit = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"losstime"))
+		break;
+	case player_losstime:
 		plr->losstime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"timeshit"))
+		break;
+	case player_timeshit:
 		plr->timeshit = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"onconveyor"))
+		break;
+	case player_onconveyor:
 		plr->onconveyor = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"awayviewmobj"))
+		break;
+	case player_awayviewmobj:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->awayviewmobj, mo);
+		break;
 	}
-	else if (fastcmp(field,"awayviewtics"))
-	{
+	case player_awayviewtics:
 		plr->awayviewtics = (INT32)luaL_checkinteger(L, 3);
 		if (plr->awayviewtics && !plr->awayviewmobj) // awayviewtics must ALWAYS have an awayviewmobj set!!
 			P_SetTarget(&plr->awayviewmobj, plr->mo); // but since the script might set awayviewmobj immediately AFTER setting awayviewtics, use player mobj as filler for now.
-	}
-	else if (fastcmp(field,"awayviewaiming"))
+		break;
+	case player_awayviewaiming:
 		plr->awayviewaiming = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"spectator"))
+		break;
+	case player_spectator:
 		plr->spectator = lua_toboolean(L, 3);
-	else if (fastcmp(field,"outofcoop"))
+		break;
+	case player_outofcoop:
 		plr->outofcoop = lua_toboolean(L, 3);
-	else if (fastcmp(field,"bot"))
+		break;
+	case player_bot:
 		return NOSET;
-	else if (fastcmp(field,"botleader"))
+	case player_botleader:
 	{
 		player_t *player = NULL;
 		if (!lua_isnil(L, 3))
 			player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER));
 		plr->botleader = player;
+		break;
 	}
-	else if (fastcmp(field,"lastbuttons"))
+	case player_lastbuttons:
 		plr->lastbuttons = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"blocked"))
+		break;
+	case player_blocked:
 		plr->blocked = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"jointime"))
+		break;
+	case player_jointime:
 		plr->jointime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"quittime"))
+		break;
+	case player_quittime:
 		plr->quittime = (tic_t)luaL_checkinteger(L, 3);
+		break;
 #ifdef HWRENDER
-	else if (fastcmp(field,"fovadd"))
+	case player_fovadd:
 		plr->fovadd = luaL_checkfixed(L, 3);
+		break;
 #endif
-	else {
+	default:
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
 		I_Assert(lua_istable(L, -1));
 		lua_pushlightuserdata(L, plr);
@@ -752,15 +1330,17 @@ static int player_set(lua_State *L)
 		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"), "player_t", field);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "player_t", lua_tostring(L, 2));
 			lua_newtable(L);
 			lua_pushlightuserdata(L, plr);
 			lua_pushvalue(L, -2); // ext value table
 			lua_rawset(L, -4); // LREG_EXTVARS table
 		}
+		lua_pushvalue(L, 2); // key
 		lua_pushvalue(L, 3); // value to store
-		lua_setfield(L, -2, field);
+		lua_settable(L, -3);
 		lua_pop(L, 2);
+		break;
 	}
 
 	return 0;
@@ -814,27 +1394,58 @@ static int power_len(lua_State *L)
 #define NOFIELD luaL_error(L, LUA_QL("ticcmd_t") " has no field named " LUA_QS, field)
 #define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field " LUA_QS " should not be set directly.", field)
 
+enum ticcmd_e
+{
+	ticcmd_forwardmove,
+	ticcmd_sidemove,
+	ticcmd_angleturn,
+	ticcmd_aiming,
+	ticcmd_buttons,
+	ticcmd_latency,
+};
+
+static const char *const ticcmd_opt[] = {
+	"forwardmove",
+	"sidemove",
+	"angleturn",
+	"aiming",
+	"buttons",
+	"latency",
+	NULL,
+};
+
+static int ticcmd_fields_ref = LUA_NOREF;
+
 static int ticcmd_get(lua_State *L)
 {
 	ticcmd_t *cmd = *((ticcmd_t **)luaL_checkudata(L, 1, META_TICCMD));
-	const char *field = luaL_checkstring(L, 2);
+	enum ticcmd_e field = Lua_optoption(L, 2, -1, ticcmd_fields_ref);
 	if (!cmd)
 		return LUA_ErrInvalid(L, "player_t");
 
-	if (fastcmp(field,"forwardmove"))
+	switch (field)
+	{
+	case ticcmd_forwardmove:
 		lua_pushinteger(L, cmd->forwardmove);
-	else if (fastcmp(field,"sidemove"))
+		break;
+	case ticcmd_sidemove:
 		lua_pushinteger(L, cmd->sidemove);
-	else if (fastcmp(field,"angleturn"))
+		break;
+	case ticcmd_angleturn:
 		lua_pushinteger(L, cmd->angleturn);
-	else if (fastcmp(field,"aiming"))
+		break;
+	case ticcmd_aiming:
 		lua_pushinteger(L, cmd->aiming);
-	else if (fastcmp(field,"buttons"))
+		break;
+	case ticcmd_buttons:
 		lua_pushinteger(L, cmd->buttons);
-	else if (fastcmp(field,"latency"))
+		break;
+	case ticcmd_latency:
 		lua_pushinteger(L, cmd->latency);
-	else
+		break;
+	default:
 		return NOFIELD;
+	}
 
 	return 1;
 }
@@ -842,27 +1453,35 @@ static int ticcmd_get(lua_State *L)
 static int ticcmd_set(lua_State *L)
 {
 	ticcmd_t *cmd = *((ticcmd_t **)luaL_checkudata(L, 1, META_TICCMD));
-	const char *field = luaL_checkstring(L, 2);
+	enum ticcmd_e field = Lua_optoption(L, 2, -1, ticcmd_fields_ref);
 	if (!cmd)
 		return LUA_ErrInvalid(L, "ticcmd_t");
 
 	if (hud_running)
 		return luaL_error(L, "Do not alter player_t in HUD rendering code!");
 
-	if (fastcmp(field,"forwardmove"))
+	switch (field)
+	{
+	case ticcmd_forwardmove:
 		cmd->forwardmove = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"sidemove"))
+		break;
+	case ticcmd_sidemove:
 		cmd->sidemove = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"angleturn"))
+		break;
+	case ticcmd_angleturn:
 		cmd->angleturn = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"aiming"))
+		break;
+	case ticcmd_aiming:
 		cmd->aiming = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"buttons"))
+		break;
+	case ticcmd_buttons:
 		cmd->buttons = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"latency"))
+		break;
+	case ticcmd_latency:
 		return NOSET;
-	else
+	default:
 		return NOFIELD;
+	}
 
 	return 0;
 }
@@ -883,6 +1502,8 @@ int LUA_PlayerLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L,1);
 
+	player_fields_ref = Lua_CreateFieldTable(L, player_opt);
+
 	luaL_newmetatable(L, META_POWERS);
 		lua_pushcfunction(L, power_get);
 		lua_setfield(L, -2, "__index");
@@ -902,6 +1523,8 @@ int LUA_PlayerLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L,1);
 
+	ticcmd_fields_ref = Lua_CreateFieldTable(L, ticcmd_opt);
+
 	lua_newuserdata(L, 0);
 		lua_createtable(L, 0, 2);
 			lua_pushcfunction(L, lib_getPlayer);
diff --git a/src/lua_script.c b/src/lua_script.c
index 9cb146c1286fad44add644a2b79c67d8ffe226d4..6893265d5754995ce2ef75a778b895f69c075480 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -1729,17 +1729,39 @@ void LUA_UnArchive(void)
 }
 
 // For mobj_t, player_t, etc. to take custom variables.
-int Lua_optoption(lua_State *L, int narg,
-	const char *def, const char *const lst[])
+int Lua_optoption(lua_State *L, int narg, int def, int list_ref)
 {
-	const char *name = (def) ? luaL_optstring(L, narg, def) :  luaL_checkstring(L, narg);
-	int i;
-	for (i=0; lst[i]; i++)
-		if (fastcmp(lst[i], name))
-			return i;
+	if (lua_isnoneornil(L, narg))
+		return def;
+
+	I_Assert(lua_checkstack(L, 2));
+	luaL_checkstring(L, narg);
+
+	lua_rawgeti(L, LUA_REGISTRYINDEX, list_ref);
+	I_Assert(lua_istable(L, -1));
+	lua_pushvalue(L, narg);
+	lua_rawget(L, -2);
+
+	if (lua_isnumber(L, -1))
+		return lua_tointeger(L, -1);
 	return -1;
 }
 
+int Lua_CreateFieldTable(lua_State *L, const char *const lst[])
+{
+	int i;
+
+	lua_newtable(L);
+	for (i = 0; lst[i] != NULL; i++)
+	{
+		lua_pushstring(L, lst[i]);
+		lua_pushinteger(L, i);
+		lua_settable(L, -3);
+	}
+
+	return luaL_ref(L, LUA_REGISTRYINDEX);
+}
+
 void LUA_PushTaggableObjectArray
 (		lua_State *L,
 		const char *field,
diff --git a/src/lua_script.h b/src/lua_script.h
index fe04e5e608fdeca20690799e5df2aa1c3b145db6..d0b06a719e32a254ba56c5f860252aba15484da0 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -57,8 +57,8 @@ int LUA_PushGlobals(lua_State *L, const char *word);
 int LUA_CheckGlobals(lua_State *L, const char *word);
 void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c
 void LUA_CVarChanged(void *cvar); // lua_consolelib.c
-int Lua_optoption(lua_State *L, int narg,
-	const char *def, const char *const lst[]);
+int Lua_optoption(lua_State *L, int narg, int def, int list_ref);
+int Lua_CreateFieldTable(lua_State *L, const char *const lst[]);
 void LUA_HookNetArchive(lua_CFunction archFunc);
 
 void LUA_PushTaggableObjectArray
diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index 5c21b04c3a31ea4d29d0d73049dbd54d5ab1dc04..b7890a6c71ecdd1356715a7df95d73b4ab573ea5 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -55,6 +55,7 @@ enum skin {
 	skin_soundsid,
 	skin_sprites
 };
+
 static const char *const skin_opt[] = {
 	"valid",
 	"name",
@@ -95,10 +96,12 @@ static const char *const skin_opt[] = {
 
 #define UNIMPLEMENTED luaL_error(L, LUA_QL("skin_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", skin_opt[field])
 
+static int skin_fields_ref = LUA_NOREF;
+
 static int skin_get(lua_State *L)
 {
 	skin_t *skin = *((skin_t **)luaL_checkudata(L, 1, META_SKIN));
-	enum skin field = luaL_checkoption(L, 2, NULL, skin_opt);
+	enum skin field = Lua_optoption(L, 2, -1, skin_fields_ref);
 
 	// skins are always valid, only added, never removed
 	I_Assert(skin != NULL);
@@ -376,6 +379,8 @@ int LUA_SkinLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L,1);
 
+	skin_fields_ref = Lua_CreateFieldTable(L, skin_opt);
+
 	luaL_newmetatable(L, META_SOUNDSID);
 		lua_pushcfunction(L, soundsid_get);
 		lua_setfield(L, -2, "__index");