diff --git a/src/dehacked.c b/src/dehacked.c
index 719476543..cdfbc5e22 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -64,6 +64,7 @@ memset(used_spr,0,sizeof(UINT8) * ((NUMSPRITEFREESLOTS / 8) + 1));\
 static mobjtype_t get_mobjtype(const char *word);
 static statenum_t get_state(const char *word);
 static spritenum_t get_sprite(const char *word);
+static playersprite_t get_sprite2(const char *word);
 static sfxenum_t get_sfx(const char *word);
 #ifdef MUSICSLOT_COMPATIBILITY
 static UINT16 get_mus(const char *word, UINT8 dehacked_mode);
@@ -769,6 +770,49 @@ static void readspritelight(MYFILE *f, INT32 num)
 }
 #endif // HWRENDER
 
+static void readsprite2(MYFILE *f, INT32 num)
+{
+	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
+	char *word, *word2;
+	char *tmp;
+
+	do
+	{
+		if (myfgets(s, MAXLINELEN, f))
+		{
+			if (s[0] == '\n')
+				break;
+
+			tmp = strchr(s, '#');
+			if (tmp)
+				*tmp = '\0';
+			if (s == tmp)
+				continue; // Skip comment lines, but don't break.
+
+			word = strtok(s, " ");
+			if (word)
+				strupr(word);
+			else
+				break;
+
+			word2 = strtok(NULL, " = ");
+			if (word2)
+				strupr(word2);
+			else
+				break;
+			if (word2[strlen(word2)-1] == '\n')
+				word2[strlen(word2)-1] = '\0';
+
+			if (fastcmp(word, "DEFAULT"))
+				spr2defaults[num] = get_number(word2);
+			else
+				deh_warning("Sprite2 %s: unknown word '%s'", spr2names[num], word);
+		}
+	} while (!myfeof(f)); // finish when the line is empty
+
+	Z_Free(s);
+}
+
 static const struct {
 	const char *name;
 	const UINT16 flag;
@@ -3020,9 +3064,21 @@ static void DEH_LoadDehackedFile(MYFILE *f)
 						ignorelines(f);
 					}
 				}
+				else if (fastcmp(word, "SPRITE2"))
+				{
+					if (i == 0 && word2[0] != '0') // If word2 isn't a number
+						i = get_sprite2(word2); // find a sprite by name
+					if (i < (INT32)free_spr2 && i >= (INT32)SPR2_FIRSTFREESLOT)
+						readsprite2(f, i);
+					else
+					{
+						deh_warning("Sprite2 number %d out of range (%d - %d)", i, SPR2_FIRSTFREESLOT, free_spr2-1);
+						ignorelines(f);
+					}
+				}
+#ifdef HWRENDER
 				else if (fastcmp(word, "LIGHT"))
 				{
-#ifdef HWRENDER
 					// TODO: Read lights by name
 					if (i > 0 && i < NUMLIGHTS)
 						readlight(f, i);
@@ -3031,22 +3087,20 @@ static void DEH_LoadDehackedFile(MYFILE *f)
 						deh_warning("Light number %d out of range (1 - %d)", i, NUMLIGHTS-1);
 						ignorelines(f);
 					}
-#endif
 				}
 				else if (fastcmp(word, "SPRITE"))
 				{
-#ifdef HWRENDER
 					if (i == 0 && word2[0] != '0') // If word2 isn't a number
 						i = get_sprite(word2); // find a sprite by name
-					if (i < NUMSPRITES && i >= 0)
+					if (i < NUMSPRITES && i > 0)
 						readspritelight(f, i);
 					else
 					{
 						deh_warning("Sprite number %d out of range (0 - %d)", i, NUMSPRITES-1);
 						ignorelines(f);
 					}
-#endif
 				}
+#endif
 				else if (fastcmp(word, "LEVEL"))
 				{
 					// Support using the actual map name,
@@ -7290,6 +7344,20 @@ static spritenum_t get_sprite(const char *word)
 	return SPR_NULL;
 }
 
+static playersprite_t get_sprite2(const char *word)
+{ // Returns the value of SPR2_ enumerations
+	playersprite_t i;
+	if (*word >= '0' && *word <= '9')
+		return atoi(word);
+	if (fastncmp("SPR2_",word,5))
+		word += 5; // take off the SPR2_
+	for (i = 0; i < NUMPLAYERSPRITES; i++)
+		if (!spr2names[i][4] && memcmp(word,spr2names[i],4)==0)
+			return i;
+	deh_warning("Couldn't find sprite named 'SPR2_%s'",word);
+	return SPR2_STND;
+}
+
 static sfxenum_t get_sfx(const char *word)
 { // Returns the value of SFX_ enumerations
 	sfxenum_t i;
@@ -7735,7 +7803,7 @@ static inline int lib_freeslot(lua_State *L)
 		else if (fastcmp(type, "SPR2"))
 		{
 			// Search if we already have an SPR2 by that name...
-			enum playersprite i;
+			playersprite_t i;
 			for (i = SPR2_FIRSTFREESLOT; i < free_spr2; i++)
 				if (memcmp(spr2names[i],word,4) == 0)
 					break;
diff --git a/src/info.c b/src/info.c
index 2d6c9a3d1..dc774bb9c 100644
--- a/src/info.c
+++ b/src/info.c
@@ -481,7 +481,88 @@ char spr2names[NUMPLAYERSPRITES][5] =
 	"SIGN",
 	"LIFE"
 };
-enum playersprite free_spr2 = SPR2_FIRSTFREESLOT;
+playersprite_t free_spr2 = SPR2_FIRSTFREESLOT;
+
+playersprite_t spr2defaults[NUMPLAYERSPRITES] = {
+	0, // SPR2_STND,
+	0, // SPR2_WAIT,
+	0, // SPR2_WALK,
+	SPR2_WALK, // SPR2_RUN ,
+	SPR2_FRUN, // SPR2_DASH,
+	0, // SPR2_PAIN,
+	SPR2_PAIN, // SPR2_STUN,
+	0, // SPR2_DEAD,
+	SPR2_DEAD, // SPR2_DRWN,
+	0, // SPR2_ROLL,
+	SPR2_SPNG, // SPR2_GASP,
+	0, // SPR2_JUMP, (conditional)
+	SPR2_FALL, // SPR2_SPNG,
+	SPR2_WALK, // SPR2_FALL,
+	0, // SPR2_EDGE,
+	SPR2_FALL, // SPR2_RIDE,
+
+	SPR2_ROLL, // SPR2_SPIN,
+
+	SPR2_SPNG, // SPR2_FLY ,
+	SPR2_FLY , // SPR2_SWIM,
+	0, // SPR2_TIRE, (conditional)
+
+	SPR2_FLY , // SPR2_GLID,
+	SPR2_CLMB, // SPR2_CLNG,
+	SPR2_ROLL, // SPR2_CLMB,
+
+	SPR2_WALK, // SPR2_FLT ,
+	SPR2_RUN , // SPR2_FRUN,
+
+	SPR2_FALL, // SPR2_BNCE,
+	SPR2_ROLL, // SPR2_BLND,
+
+	0, // SPR2_FIRE,
+
+	SPR2_ROLL, // SPR2_TWIN,
+
+	SPR2_TWIN, // SPR2_MLEE,
+	0, // SPR2_MLEL,
+
+	0, // SPR2_TRNS,
+
+	0, // SPR2_NSTD,
+	0, // SPR2_NFLT,
+	0, // SPR2_NSTN,
+	SPR2_NSTN, // SPR2_NPUL,
+	0, // SPR2_NATK,
+
+	0, // SPR2_NGT0, (should never be referenced)
+	SPR2_NGT0, // SPR2_NGT1,
+	SPR2_NGT1, // SPR2_NGT2,
+	SPR2_NGT2, // SPR2_NGT3,
+	SPR2_NGT3, // SPR2_NGT4,
+	SPR2_NGT4, // SPR2_NGT5,
+	SPR2_NGT5, // SPR2_NGT6,
+	SPR2_NGT0, // SPR2_NGT7,
+	SPR2_NGT7, // SPR2_NGT8,
+	SPR2_NGT8, // SPR2_NGT9,
+	SPR2_NGT9, // SPR2_NGTA,
+	SPR2_NGTA, // SPR2_NGTB,
+	SPR2_NGTB, // SPR2_NGTC,
+
+	SPR2_NGT0, // SPR2_DRL0,
+	SPR2_NGT1, // SPR2_DRL1,
+	SPR2_NGT2, // SPR2_DRL2,
+	SPR2_NGT3, // SPR2_DRL3,
+	SPR2_NGT4, // SPR2_DRL4,
+	SPR2_NGT5, // SPR2_DRL5,
+	SPR2_NGT6, // SPR2_DRL6,
+	SPR2_NGT7, // SPR2_DRL7,
+	SPR2_NGT8, // SPR2_DRL8,
+	SPR2_NGT9, // SPR2_DRL9,
+	SPR2_NGTA, // SPR2_DRLA,
+	SPR2_NGTB, // SPR2_DRLB,
+	SPR2_NGTC, // SPR2_DRLC,
+
+	0, // SPR2_SIGN,
+	0, // SPR2_LIFE
+};
 
 // Doesn't work with g++, needs actionf_p1 (don't modify this comment)
 state_t states[NUMSTATES] =
diff --git a/src/info.h b/src/info.h
index cd79b12a9..4ae5fcf63 100644
--- a/src/info.h
+++ b/src/info.h
@@ -604,7 +604,7 @@ typedef enum sprite
 // Make sure to be conscious of FF_FRAMEMASK and the fact sprite2 is stored as a UINT8 whenever you change this table.
 // Currently, FF_FRAMEMASK is 0xff, or 255 - but the second half is used by FF_SPR2SUPER, so the limitation is 0x7f.
 // Since this is zero-based, there can be at most 128 different SPR2_'s without changing that.
-enum playersprite
+typedef enum playersprite
 {
 	SPR2_STND = 0,
 	SPR2_WAIT,
@@ -690,7 +690,7 @@ enum playersprite
 	SPR2_FIRSTFREESLOT,
 	SPR2_LASTFREESLOT = 0x7f,
 	NUMPLAYERSPRITES
-};
+} playersprite_t;
 
 typedef enum state
 {
@@ -3193,8 +3193,9 @@ typedef struct
 extern state_t states[NUMSTATES];
 extern char sprnames[NUMSPRITES + 1][5];
 extern char spr2names[NUMPLAYERSPRITES][5];
+extern playersprite_t spr2defaults[NUMPLAYERSPRITES];
 extern state_t *astate;
-extern enum playersprite free_spr2;
+extern playersprite_t free_spr2;
 
 typedef enum mobj_type
 {
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index be1455415..b88a9712e 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -462,6 +462,8 @@ static int lib_pSpawnLockOn(lua_State *L)
 		return LUA_ErrInvalid(L, "mobj_t");
 	if (!player)
 		return LUA_ErrInvalid(L, "player_t");
+	if (state >= NUMSTATES)
+		return luaL_error(L, "state %d out of range (0 - %d)", state, NUMSTATES-1);
 	if (P_IsLocalPlayer(player)) // Only display it on your own view.
 	{
 		mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 9361abe94..c3803f7e2 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -127,6 +127,74 @@ static int lib_getSpr2name(lua_State *L)
 	return 0;
 }
 
+static int lib_getSpr2default(lua_State *L)
+{
+	UINT32 i;
+
+	lua_remove(L, 1); // don't care about spr2defaults[] dummy userdata.
+
+	if (lua_isnumber(L, 1))
+		i = lua_tonumber(L, 1);
+	else if (lua_isstring(L, 1))
+	{
+		const char *name = lua_tostring(L, 1);
+		for (i = 0; i < free_spr2; i++)
+			if (fastcmp(name, spr2names[i]))
+				break;
+	}
+	else
+		return luaL_error(L, "spr2defaults[] invalid index");
+
+	if (i >= free_spr2)
+		return 0;
+
+	lua_pushinteger(L, spr2defaults[i]);
+	return 1;
+}
+
+static int lib_setSpr2default(lua_State *L)
+{
+	UINT32 i;
+	UINT8 j = 0;
+
+	lua_remove(L, 1); // don't care about spr2defaults[] dummy userdata.
+
+	if (lua_isnumber(L, 1))
+		i = lua_tonumber(L, 1);
+	else if (lua_isstring(L, 1))
+	{
+		const char *name = lua_tostring(L, 1);
+		for (i = 0; i < free_spr2; i++)
+			if (fastcmp(name, spr2names[i]))
+				break;
+			if (i == free_spr2)
+				return luaL_error(L, "spr2defaults[] invalid index");
+	}
+	else
+		return luaL_error(L, "spr2defaults[] invalid index");
+
+	if (i < SPR2_FIRSTFREESLOT || i >= free_spr2)
+		return luaL_error(L, "spr2defaults[] index %d out of range (%d - %d)", i, SPR2_FIRSTFREESLOT, free_spr2-1);
+
+	if (lua_isnumber(L, 2))
+		j = lua_tonumber(L, 2);
+	else if (lua_isstring(L, 2))
+	{
+		const char *name = lua_tostring(L, 2);
+		for (j = 0; j < free_spr2; j++)
+			if (fastcmp(name, spr2names[j]))
+				break;
+			if (j == free_spr2)
+				return luaL_error(L, "spr2defaults[] invalid index");
+	}
+
+	if (j >= free_spr2)
+		j = 0; // return luaL_error(L, "spr2defaults[] set %d out of range (%d - %d)", j, 0, free_spr2-1);
+
+	spr2defaults[i] = j;
+	return 0;
+}
+
 static int lib_spr2namelen(lua_State *L)
 {
 	lua_pushinteger(L, free_spr2);
@@ -984,6 +1052,19 @@ int LUA_InfoLib(lua_State *L)
 		lua_setmetatable(L, -2);
 	lua_setglobal(L, "spr2names");
 
+	lua_newuserdata(L, 0);
+		lua_createtable(L, 0, 2);
+			lua_pushcfunction(L, lib_getSpr2default);
+			lua_setfield(L, -2, "__index");
+
+			lua_pushcfunction(L, lib_setSpr2default);
+			lua_setfield(L, -2, "__newindex");
+
+			lua_pushcfunction(L, lib_spr2namelen);
+			lua_setfield(L, -2, "__len");
+		lua_setmetatable(L, -2);
+	lua_setglobal(L, "spr2defaults");
+
 	lua_newuserdata(L, 0);
 		lua_createtable(L, 0, 2);
 			lua_pushcfunction(L, lib_getState);
diff --git a/src/r_things.c b/src/r_things.c
index b2437d4ac..ebec4a14a 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -2431,13 +2431,14 @@ CV_PossibleValue_t skin_cons_t[MAXSKINS+1];
 
 UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player)
 {
-	UINT8 super = (spr2 & FF_SPR2SUPER);
+	UINT8 super = (spr2 & FF_SPR2SUPER), i = 0;
 
 	if (!skin)
 		return 0;
 
 	while (!(skin->sprites[spr2].numframes)
-		&& spr2 != SPR2_STND)
+		&& spr2 != SPR2_STND
+		&& ++i != 32) // recursion limiter
 	{
 		if (spr2 & FF_SPR2SUPER)
 		{
@@ -2447,83 +2448,18 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player)
 
 		switch(spr2)
 		{
-		case SPR2_RUN:
-			spr2 = SPR2_WALK;
-			break;
-		case SPR2_STUN:
-			spr2 = SPR2_PAIN;
-			break;
-		case SPR2_DRWN:
-			spr2 = SPR2_DEAD;
-			break;
-		case SPR2_SPIN:
-			spr2 = SPR2_ROLL;
-			break;
-		case SPR2_GASP:
-			spr2 = SPR2_SPNG;
-			break;
+
+		// Normal special cases.
 		case SPR2_JUMP:
 			spr2 = ((player
 					? player->charflags
 					: skin->flags)
 					& SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL;
 			break;
-		case SPR2_SPNG: // spring
-			spr2 = SPR2_FALL;
-			break;
-		case SPR2_FALL:
-			spr2 = SPR2_WALK;
-			break;
-		case SPR2_RIDE:
-			spr2 = SPR2_FALL;
-			break;
-
-		case SPR2_FLY :
-			spr2 = SPR2_SPNG;
-			break;
-		case SPR2_SWIM:
-			spr2 = SPR2_FLY ;
-			break;
 		case SPR2_TIRE:
 			spr2 = (player && player->charability == CA_SWIM) ? SPR2_SWIM : SPR2_FLY;
 			break;
 
-		case SPR2_GLID:
-			spr2 = SPR2_FLY;
-			break;
-		case SPR2_CLMB:
-			spr2 = SPR2_ROLL;
-			break;
-		case SPR2_CLNG:
-			spr2 = SPR2_CLMB;
-			break;
-
-		case SPR2_FLT :
-			spr2 = SPR2_WALK;
-			break;
-		case SPR2_FRUN:
-			spr2 = SPR2_RUN ;
-			break;
-
-		case SPR2_DASH:
-			spr2 = SPR2_FRUN;
-			break;
-
-		case SPR2_BNCE:
-			spr2 = SPR2_FALL;
-			break;
-		case SPR2_BLND:
-			spr2 = SPR2_ROLL;
-			break;
-
-		case SPR2_TWIN:
-			spr2 = SPR2_ROLL;
-			break;
-
-		case SPR2_MLEE:
-			spr2 = SPR2_TWIN;
-			break;
-
 		// NiGHTS sprites.
 		case SPR2_NSTD:
 			spr2 = SPR2_STND;
@@ -2535,72 +2471,16 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player)
 			break;
 		case SPR2_NSTN:
 			spr2 = SPR2_STUN;
-			break;
-		case SPR2_NPUL:
-			spr2 = SPR2_NSTN;
+			super = FF_SPR2SUPER;
 			break;
 		case SPR2_NATK:
 			spr2 = SPR2_ROLL;
 			super = FF_SPR2SUPER;
 			break;
-		/*case SPR2_NGT0:
-			spr2 = SPR2_NFLT;
-			break;*/
-		case SPR2_NGT1:
-		case SPR2_NGT7:
-		case SPR2_DRL0:
-			spr2 = SPR2_NGT0;
-			break;
-		case SPR2_NGT2:
-		case SPR2_DRL1:
-			spr2 = SPR2_NGT1;
-			break;
-		case SPR2_NGT3:
-		case SPR2_DRL2:
-			spr2 = SPR2_NGT2;
-			break;
-		case SPR2_NGT4:
-		case SPR2_DRL3:
-			spr2 = SPR2_NGT3;
-			break;
-		case SPR2_NGT5:
-		case SPR2_DRL4:
-			spr2 = SPR2_NGT4;
-			break;
-		case SPR2_NGT6:
-		case SPR2_DRL5:
-			spr2 = SPR2_NGT5;
-			break;
-		case SPR2_DRL6:
-			spr2 = SPR2_NGT6;
-			break;
-		case SPR2_NGT8:
-		case SPR2_DRL7:
-			spr2 = SPR2_NGT7;
-			break;
-		case SPR2_NGT9:
-		case SPR2_DRL8:
-			spr2 = SPR2_NGT8;
-			break;
-		case SPR2_NGTA:
-		case SPR2_DRL9:
-			spr2 = SPR2_NGT9;
-			break;
-		case SPR2_NGTB:
-		case SPR2_DRLA:
-			spr2 = SPR2_NGTA;
-			break;
-		case SPR2_NGTC:
-		case SPR2_DRLB:
-			spr2 = SPR2_NGTB;
-			break;
-		case SPR2_DRLC:
-			spr2 = SPR2_NGTC;
-			break;
 
-		// Dunno? Just go to standing then.
+		// Use the handy list, that's what it's there for!
 		default:
-			spr2 = SPR2_STND;
+			spr2 = spr2defaults[spr2];
 			break;
 		}