diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 2f986085a0a38765fd7192f18c91d3cde96bccc3..80ca623ca5ac5902fffd7d1c03a44c3a1ab15a26 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -5493,7 +5493,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	spriteinfo_t *sprinfo;
 	size_t lumpoff;
 	unsigned rot;
-	UINT8 flip;
+	UINT16 flip;
 	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
 
 	angle_t ang;
@@ -5579,12 +5579,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 		flip = sprframe->flip; // Will only be 0x00 or 0xFF
 
 		if (papersprite && ang < ANGLE_180)
-		{
-			if (flip)
-				flip = 0;
-			else
-				flip = 255;
-		}
+			flip ^= 0xFFFF;
 	}
 	else
 	{
@@ -5593,6 +5588,11 @@ static void HWR_ProjectSprite(mobj_t *thing)
 			rot = 6; // F7 slot
 		else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left
 			rot = 2; // F3 slot
+		else if (sprframe->rotate & SRF_3DGE) // 16-angle mode
+		{
+			rot = (ang+ANGLE_180+ANGLE_11hh)>>28;
+			rot = ((rot & 1)<<3)|(rot>>1);
+		}
 		else // Normal behaviour
 			rot = (ang+ANGLE_202h)>>29;
 
@@ -5601,12 +5601,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 		flip = sprframe->flip & (1<<rot);
 
 		if (papersprite && ang < ANGLE_180)
-		{
-			if (flip)
-				flip = 0;
-			else
-				flip = 1<<rot;
-		}
+			flip ^= (1<<rot);
 	}
 
 	if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES)
@@ -5621,7 +5616,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	if (thing->rollangle)
 	{
 		rollangle = R_GetRollAngle(thing->rollangle);
-		if (!sprframe->rotsprite.cached[rot])
+		if (!(sprframe->rotsprite.cached & (1<<rot)))
 			R_CacheRotSprite(thing->sprite, (thing->frame & FF_FRAMEMASK), sprinfo, sprframe, rot, flip);
 		rotsprite = sprframe->rotsprite.patch[rot][rollangle];
 		if (rotsprite != NULL)
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 58af64359e00abfbd309565aade645280f68a3b2..f451944e3d95f6c6b40c990f6c120e6ee196c965 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -468,11 +468,11 @@ static int libd_getSpritePatch(lua_State *L)
 
 	// convert WAD editor angle numbers (1-8) to internal angle numbers (0-7)
 	// keep 0 the same since we'll make it default to angle 1 (which is internally 0)
-	// in case somebody didn't know that angle 0 really just maps all 8 angles to the same patch
+	// in case somebody didn't know that angle 0 really just maps all 8/16 angles to the same patch
 	if (angle != 0)
 		angle--;
 
-	if (angle >= 8) // out of range?
+	if (angle >= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range?
 		return 0;
 
 	// push both the patch and it's "flip" value
@@ -563,11 +563,11 @@ static int libd_getSprite2Patch(lua_State *L)
 
 	// convert WAD editor angle numbers (1-8) to internal angle numbers (0-7)
 	// keep 0 the same since we'll make it default to angle 1 (which is internally 0)
-	// in case somebody didn't know that angle 0 really just maps all 8 angles to the same patch
+	// in case somebody didn't know that angle 0 really just maps all 8/16 angles to the same patch
 	if (angle != 0)
 		angle--;
 
-	if (angle >= 8) // out of range?
+	if (angle >= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range?
 		return 0;
 
 	// push both the patch and it's "flip" value
diff --git a/src/r_data.c b/src/r_data.c
index d35777664645d99a7d557dc222a52793b3213c31..db18a8833ab8eaa186605593ab62fee5cb7fa56a 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -2710,14 +2710,29 @@ void R_PrecacheLevel(void)
 		for (j = 0; j < sprites[i].numframes; j++)
 		{
 			sf = &sprites[i].spriteframes[j];
-			for (k = 0; k < 8; k++)
+#define cacheang(a) {\
+		lump = sf->lumppat[a];\
+		if (devparm)\
+			spritememory += W_LumpLength(lump);\
+		W_CachePatchNum(lump, PU_PATCH);\
+	}
+			// see R_InitSprites for more about lumppat,lumpid
+			switch (sf->rotate)
 			{
-				// see R_InitSprites for more about lumppat,lumpid
-				lump = sf->lumppat[k];
-				if (devparm)
-					spritememory += W_LumpLength(lump);
-				W_CachePatchNum(lump, PU_PATCH);
+				case SRF_SINGLE:
+					cacheang(0);
+					break;
+				case SRF_2D:
+					cacheang(2);
+					cacheang(6);
+					break;
+				default:
+					k = (sf->rotate & SRF_3DGE ? 16 : 8);
+					while (k--)
+						cacheang(k);
+					break;
 			}
+#undef cacheang
 		}
 	}
 	free(spritepresent);
diff --git a/src/r_defs.h b/src/r_defs.h
index 176f33c4f9a22adf72e2bf414ae7df79b428a695..52b9bea951d9ad0c914157cf8850c968794dafd1 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -727,8 +727,8 @@ typedef struct
 #ifdef ROTSPRITE
 typedef struct
 {
-	patch_t *patch[8][ROTANGLES];
-	boolean cached[8];
+	patch_t *patch[16][ROTANGLES];
+	UINT16 cached;
 } rotsprite_t;
 #endif/*ROTSPRITE*/
 
@@ -736,9 +736,11 @@ typedef enum
 {
 	SRF_SINGLE      = 0,   // 0-angle for all rotations
 	SRF_3D          = 1,   // Angles 1-8
-	SRF_LEFT        = 2,   // Left side uses single patch
-	SRF_RIGHT       = 4,   // Right side uses single patch
-	SRF_2D          = SRF_LEFT|SRF_RIGHT, // 6
+	SRF_3DGE        = 2,   // 3DGE, ZDoom and Doom Legacy all have 16-angle support. Why not us?
+	SRF_3DMASK      = SRF_3D|SRF_3DGE, // 3
+	SRF_LEFT        = 4,   // Left side uses single patch
+	SRF_RIGHT       = 8,   // Right side uses single patch
+	SRF_2D          = SRF_LEFT|SRF_RIGHT, // 12
 	SRF_NONE        = 0xff // Initial value
 } spriterotateflags_t;     // SRF's up!
 
@@ -764,7 +766,7 @@ typedef struct patchinfo_s patchinfo_t;
 // Sprites are patches with a special naming convention so they can be
 //  recognized by R_InitSprites.
 // The base name is NNNNFx or NNNNFxFx, with x indicating the rotation,
-//  x = 0, 1-8, L/R
+//  x = 0, 1-8, 9+A-G, L/R
 // The sprite and frame specified by a thing_t is range checked at run time.
 // A sprite is a patch_t that is assumed to represent a three dimensional
 //  object and may have multiple rotations predrawn.
@@ -781,12 +783,12 @@ typedef struct
 	//  name eight times.
 	UINT8 rotate; // see spriterotateflags_t above
 
-	// Lump to use for view angles 0-7.
-	lumpnum_t lumppat[8]; // lump number 16 : 16 wad : lump
-	size_t lumpid[8]; // id in the spriteoffset, spritewidth, etc. tables
+	// Lump to use for view angles 0-7/15.
+	lumpnum_t lumppat[16]; // lump number 16 : 16 wad : lump
+	size_t lumpid[16]; // id in the spriteoffset, spritewidth, etc. tables
 
-	// Flip bits (1 = flip) to use for view angles 0-7.
-	UINT8 flip;
+	// Flip bits (1 = flip) to use for view angles 0-7/15.
+	UINT16 flip;
 
 #ifdef ROTSPRITE
 	rotsprite_t rotsprite;
diff --git a/src/r_patch.c b/src/r_patch.c
index 13414aae40960f12b9d849335362d97a39e1f83b..b4127f825e0a97d5164d5e3f1a009354259559d3 100644
--- a/src/r_patch.c
+++ b/src/r_patch.c
@@ -1205,7 +1205,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 #define ROTSPRITE_XCENTER (newwidth / 2)
 #define ROTSPRITE_YCENTER (newheight / 2)
 
-	if (!sprframe->rotsprite.cached[rot])
+	if (!(sprframe->rotsprite.cached & (1<<rot)))
 	{
 		INT32 dx, dy;
 		INT32 px, py;
@@ -1375,7 +1375,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 		}
 
 		// This rotation is cached now
-		sprframe->rotsprite.cached[rot] = true;
+		sprframe->rotsprite.cached |= (1<<rot);
 
 		// free image data
 		Z_Free(patch);
@@ -1399,9 +1399,9 @@ void R_FreeSingleRotSprite(spritedef_t *spritedef)
 	for (frame = 0; frame < spritedef->numframes; frame++)
 	{
 		spriteframe_t *sprframe = &spritedef->spriteframes[frame];
-		for (rot = 0; rot < 8; rot++)
+		for (rot = 0; rot < 16; rot++)
 		{
-			if (sprframe->rotsprite.cached[rot])
+			if (sprframe->rotsprite.cached & (1<<rot))
 			{
 				for (ang = 0; ang < ROTANGLES; ang++)
 				{
@@ -1432,7 +1432,7 @@ void R_FreeSingleRotSprite(spritedef_t *spritedef)
 						Z_Free(rotsprite);
 					}
 				}
-				sprframe->rotsprite.cached[rot] = false;
+				sprframe->rotsprite.cached &= ~(1<<rot);
 			}
 		}
 	}
diff --git a/src/r_things.c b/src/r_things.c
index 8458d983570ccde2efef319330a130a94388c5e0..d80d74d788674efaf56388394b0867800f6f8f10 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -105,24 +105,21 @@ static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
                                 UINT8 rotation,
                                 UINT8 flipped)
 {
-	char cn = R_Frame2Char(frame); // for debugging
+	char cn = R_Frame2Char(frame), cr = R_Rotation2Char(rotation); // for debugging
 
 	INT32 r, ang;
 	lumpnum_t lumppat = wad;
 	lumppat <<= 16;
 	lumppat += lump;
 
-	if (frame >= 64 || !(R_ValidSpriteAngle(rotation)))
-		I_Error("R_InstallSpriteLump: Bad frame characters in lump %s", W_CheckNameForNum(lumppat));
-
 	if (maxframe ==(size_t)-1 || frame > maxframe)
 		maxframe = frame;
 
 	// rotsprite
 #ifdef ROTSPRITE
-	for (r = 0; r < 8; r++)
+	sprtemp[frame].rotsprite.cached = 0;
+	for (r = 0; r < 16; r++)
 	{
-		sprtemp[frame].rotsprite.cached[r] = false;
 		for (ang = 0; ang < ROTANGLES; ang++)
 			sprtemp[frame].rotsprite.patch[r][ang] = NULL;
 	}
@@ -133,16 +130,16 @@ static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
 		// the lump should be used for all rotations
 		if (sprtemp[frame].rotate == SRF_SINGLE)
 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has multiple rot = 0 lump\n", spritename, cn);
-		else if (sprtemp[frame].rotate != SRF_NONE) // Let's bundle 1-8 and L/R rotations into one debug message.
+		else if (sprtemp[frame].rotate != SRF_NONE) // Let's bundle 1-8/16 and L/R rotations into one debug message.
 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has rotations and a rot = 0 lump\n", spritename, cn);
 
 		sprtemp[frame].rotate = SRF_SINGLE;
-		for (r = 0; r < 8; r++)
+		for (r = 0; r < 16; r++)
 		{
 			sprtemp[frame].lumppat[r] = lumppat;
 			sprtemp[frame].lumpid[r] = lumpid;
 		}
-		sprtemp[frame].flip = flipped ? 0xFF : 0; // 11111111 in binary
+		sprtemp[frame].flip = flipped ? 0xFFFF : 0; // 1111111111111111 in binary
 		return;
 	}
 
@@ -151,54 +148,67 @@ static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
 		UINT8 rightfactor = ((rotation == ROT_R) ? 4 : 0);
 
 		// the lump should be used for half of all rotations
-		if (sprtemp[frame].rotate == SRF_SINGLE)
+		if (sprtemp[frame].rotate == SRF_NONE)
+			sprtemp[frame].rotate = SRF_SINGLE;
+		else if (sprtemp[frame].rotate == SRF_SINGLE)
 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has L/R rotations and a rot = 0 lump\n", spritename, cn);
 		else if (sprtemp[frame].rotate == SRF_3D)
 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has both L/R and 1-8 rotations\n", spritename, cn);
+		else if (sprtemp[frame].rotate == SRF_3DGE)
+			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has both L/R and 1-G rotations\n", spritename, cn);
 		else if ((sprtemp[frame].rotate & SRF_LEFT) && (rotation == ROT_L))
 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has multiple L rotations\n", spritename, cn);
 		else if ((sprtemp[frame].rotate & SRF_RIGHT) && (rotation == ROT_R))
 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has multiple R rotations\n", spritename, cn);
 
-		if (sprtemp[frame].rotate == SRF_NONE)
-			sprtemp[frame].rotate = SRF_SINGLE;
-
 		sprtemp[frame].rotate |= ((rotation == ROT_R) ? SRF_RIGHT : SRF_LEFT);
-		if (sprtemp[frame].rotate == (SRF_3D|SRF_2D))
-			sprtemp[frame].rotate = SRF_2D; // SRF_3D|SRF_2D being enabled at the same time doesn't HURT in the current sprite angle implementation, but it DOES mean more to check in some of the helper functions. Let's not allow this scenario to happen.
+		if ((sprtemp[frame].rotate & SRF_2D) == SRF_2D)
+			sprtemp[frame].rotate &= ~SRF_3DMASK; // SRF_3D|SRF_2D being enabled at the same time doesn't HURT in the current sprite angle implementation, but it DOES mean more to check in some of the helper functions. Let's not allow this scenario to happen.
 
-		for (r = 0; r < 4; r++) // Thanks to R_PrecacheLevel, we can't leave sprtemp[*].lumppat[*] == LUMPERROR... so we load into the front/back angle too.
+		// load into every relevant angle, including the front one
+		for (r = 0; r < 4; r++)
 		{
 			sprtemp[frame].lumppat[r + rightfactor] = lumppat;
 			sprtemp[frame].lumpid[r + rightfactor] = lumpid;
+			sprtemp[frame].lumppat[r + rightfactor + 8] = lumppat;
+			sprtemp[frame].lumpid[r + rightfactor + 8] = lumpid;
+
 		}
 
 		if (flipped)
-			sprtemp[frame].flip |= (0x0F<<rightfactor); // 00001111 or 11110000 in binary, depending on rotation being ROT_L or ROT_R
+			sprtemp[frame].flip |= (0x0F0F<<rightfactor); // 0000111100001111 or 1111000011110000 in binary, depending on rotation being ROT_L or ROT_R
 		else
-			sprtemp[frame].flip &= ~(0x0F<<rightfactor); // ditto
+			sprtemp[frame].flip &= ~(0x0F0F<<rightfactor); // ditto
 
 		return;
 	}
 
-	// the lump is only used for one rotation
-	if (sprtemp[frame].rotate == SRF_SINGLE)
-		CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has 1-8 rotations and a rot = 0 lump\n", spritename, cn);
-	else if ((sprtemp[frame].rotate != SRF_3D) && (sprtemp[frame].rotate != SRF_NONE))
-		CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has both L/R and 1-8 rotations\n", spritename, cn);
+	if (sprtemp[frame].rotate == SRF_NONE)
+		sprtemp[frame].rotate = SRF_SINGLE;
+	else if (sprtemp[frame].rotate == SRF_SINGLE)
+		CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has 1-8/G rotations and a rot = 0 lump\n", spritename, cn);
+	else if (sprtemp[frame].rotate & SRF_2D)
+		CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has both L/R and 1-8/G rotations\n", spritename, cn);
 
 	// make 0 based
 	rotation--;
 
-	if (rotation == 0 || rotation == 4) // Front or back...
-		sprtemp[frame].rotate = SRF_3D; // Prevent L and R changeover
-	else if (rotation > 3) // Right side
-		sprtemp[frame].rotate = (SRF_3D | (sprtemp[frame].rotate & SRF_LEFT)); // Continue allowing L frame changeover
-	else // if (rotation <= 3) // Left side
-		sprtemp[frame].rotate = (SRF_3D | (sprtemp[frame].rotate & SRF_RIGHT)); // Continue allowing R frame changeover
+	{
+		// SRF_3D|SRF_3DGE being enabled at the same time doesn't HURT in the current sprite angle implementation, but it DOES mean more to check in some of the helper functions. Let's not allow this scenario to happen.
+		UINT8 threedrot = (rotation > 7) ? SRF_3DGE : (sprtemp[frame].rotate & SRF_3DMASK);
+		if (!threedrot)
+			threedrot = SRF_3D;
+
+		if (rotation == 0 || rotation == 4) // Front or back...
+			sprtemp[frame].rotate = threedrot; // Prevent L and R changeover
+		else if ((rotation & 7) > 3) // Right side
+			sprtemp[frame].rotate = (threedrot | (sprtemp[frame].rotate & SRF_LEFT)); // Continue allowing L frame changeover
+		else // if ((rotation & 7) <= 3) // Left side
+			sprtemp[frame].rotate = (threedrot | (sprtemp[frame].rotate & SRF_RIGHT)); // Continue allowing R frame changeover
+	}
 
 	if (sprtemp[frame].lumppat[rotation] != LUMPERROR)
-		CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s: %c%c has two lumps mapped to it\n", spritename, cn, '1'+rotation);
+		CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s: %c%c has two lumps mapped to it\n", spritename, cn, cr);
 
 	// lumppat & lumpid are the same for original Doom, but different
 	// when using sprites in pwad : the lumppat points the new graphics
@@ -259,9 +269,9 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef,
 		if (memcmp(lumpinfo[l].name,sprname,4)==0)
 		{
 			frame = R_Char2Frame(lumpinfo[l].name[4]);
-			rotation = (UINT8)(lumpinfo[l].name[5] - '0');
+			rotation = R_Char2Rotation(lumpinfo[l].name[5]);
 
-			if (frame >= 64 || !(R_ValidSpriteAngle(rotation))) // Give an actual NAME error -_-...
+			if (frame >= 64 || rotation == 255) // Give an actual NAME error -_-...
 			{
 				CONS_Alert(CONS_WARNING, M_GetText("Bad sprite name: %s\n"), W_CheckNameForNumPwad(wadnum,l));
 				continue;
@@ -308,7 +318,13 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef,
 			if (lumpinfo[l].name[6])
 			{
 				frame = R_Char2Frame(lumpinfo[l].name[6]);
-				rotation = (UINT8)(lumpinfo[l].name[7] - '0');
+				rotation = R_Char2Rotation(lumpinfo[l].name[7]);
+
+				if (frame >= 64 || rotation == 255) // Give an actual NAME error -_-...
+				{
+					CONS_Alert(CONS_WARNING, M_GetText("Bad sprite name: %s\n"), W_CheckNameForNumPwad(wadnum,l));
+					continue;
+				}
 				R_InstallSpriteLump(wadnum, l, numspritelumps, frame, rotation, 1);
 			}
 
@@ -369,18 +385,19 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef,
 			case SRF_2D: // both Left and Right rotations
 			// we test to see whether the left and right slots are present
 			if ((sprtemp[frame].lumppat[2] == LUMPERROR) || (sprtemp[frame].lumppat[6] == LUMPERROR))
-				I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations",
+				I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations (L-R mode)",
 				sprname, R_Frame2Char(frame));
 			break;
 
 			default:
-			// must have all 8 frames
-			for (rotation = 0; rotation < 8; rotation++)
+			// must have all 8/16 frames
+				rotation = ((sprtemp[frame].rotate & SRF_3DGE) ? 16 : 8);
+				while (rotation--)
 				// we test the patch lump, or the id lump whatever
 				// if it was not loaded the two are LUMPERROR
 				if (sprtemp[frame].lumppat[rotation] == LUMPERROR)
-					I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations",
-					        sprname, R_Frame2Char(frame));
+					I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations (1-%c mode)",
+					        sprname, R_Frame2Char(frame), ((sprtemp[frame].rotate & SRF_3DGE) ? 'G' : '8'));
 			break;
 		}
 	}
@@ -1094,7 +1111,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	size_t lump;
 
 	size_t rot;
-	UINT8 flip;
+	UINT16 flip;
 	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
 
 	INT32 lindex;
@@ -1224,7 +1241,7 @@ static void R_ProjectSprite(mobj_t *thing)
 		// use single rotation for all views
 		rot = 0;                        //Fab: for vis->patch below
 		lump = sprframe->lumpid[0];     //Fab: see note above
-		flip = sprframe->flip; // Will only be 0x00 or 0xFF
+		flip = sprframe->flip; 			// Will only be 0 or 0xFFFF
 	}
 	else
 	{
@@ -1235,6 +1252,11 @@ static void R_ProjectSprite(mobj_t *thing)
 			rot = 6; // F7 slot
 		else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left
 			rot = 2; // F3 slot
+		else if (sprframe->rotate & SRF_3DGE) // 16-angle mode
+		{
+			rot = (ang+ANGLE_180+ANGLE_11hh)>>28;
+			rot = ((rot & 1)<<3)|(rot>>1);
+		}
 		else // Normal behaviour
 			rot = (ang+ANGLE_202h)>>29;
 
@@ -1257,7 +1279,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	if (thing->rollangle)
 	{
 		rollangle = R_GetRollAngle(thing->rollangle);
-		if (!sprframe->rotsprite.cached[rot])
+		if (!(sprframe->rotsprite.cached & (1<<rot)))
 			R_CacheRotSprite(thing->sprite, (thing->frame & FF_FRAMEMASK), sprinfo, sprframe, rot, flip);
 		rotsprite = sprframe->rotsprite.patch[rot][rollangle];
 		if (rotsprite != NULL)
diff --git a/src/r_things.h b/src/r_things.h
index 8e4a543c36067d1101068a86a5d33e779c4cd8cf..b4ef66a62be7a3a628341d89260ffef60eecb638 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -20,10 +20,6 @@
 #include "r_portal.h"
 #include "r_defs.h"
 
-// "Left" and "Right" character symbols for additional rotation functionality
-#define ROT_L ('L' - '0')
-#define ROT_R ('R' - '0')
-
 // number of sprite lumps for spritewidth,offset,topoffset lookup tables
 // Fab: this is a hack : should allocate the lookup tables per sprite
 #define MAXVISSPRITES 2048 // added 2-2-98 was 128
@@ -270,7 +266,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE UINT8 R_Char2Frame(char cn)
 	if (cn == '+') return '\\' - 'A'; // PK3 can't use backslash, so use + instead
 	return cn - 'A';
 #else
-	if (cn >= 'A' && cn <= 'Z') return cn - 'A';
+	if (cn >= 'A' && cn <= 'Z') return (cn - 'A');
 	if (cn >= '0' && cn <= '9') return (cn - '0') + 26;
 	if (cn >= 'a' && cn <= 'z') return (cn - 'a') + 36;
 	if (cn == '!') return 62;
@@ -279,9 +275,26 @@ FUNCMATH FUNCINLINE static ATTRINLINE UINT8 R_Char2Frame(char cn)
 #endif
 }
 
-FUNCMATH FUNCINLINE static ATTRINLINE boolean R_ValidSpriteAngle(UINT8 rotation)
+// "Left" and "Right" character symbols for additional rotation functionality
+#define ROT_L 17
+#define ROT_R 18
+
+FUNCMATH FUNCINLINE static ATTRINLINE char R_Rotation2Char(UINT8 rot)
 {
-	return ((rotation <= 8) || (rotation == ROT_L) || (rotation == ROT_R));
+	if (rot <=     9) return '0' + rot;
+	if (rot <=    16) return 'A' + (rot - 10);
+	if (rot == ROT_L) return 'L';
+	if (rot == ROT_R) return 'R';
+	return '\xFF';
+}
+
+FUNCMATH FUNCINLINE static ATTRINLINE UINT8 R_Char2Rotation(char cn)
+{
+	if (cn >= '0' && cn <= '9') return (cn - '0');
+	if (cn >= 'A' && cn <= 'G') return (cn - 'A') + 10;
+	if (cn == 'L') return ROT_L;
+	if (cn == 'R') return ROT_R;
+	return 255;
 }
 
 #endif //__R_THINGS__