diff --git a/src/d_player.h b/src/d_player.h
index f2fdda0500ad09e1efa88736985f087c6428e5a8..d3f84d0e6d62b071dc9013f4d763eb98ae5966f6 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -47,6 +47,7 @@ typedef enum
 	SF_DASHMODE         = 1<<11, // Sonic Advance 2 style top speed increase?
 	SF_FASTEDGE         = 1<<12, // Faster edge teeter?
 	SF_MULTIABILITY     = 1<<13, // Revenge of Final Demo.
+	SF_NONIGHTSROTATION = 1<<14, // Disable sprite rotation for NiGHTS
 	// free up to and including 1<<31
 } skinflags_t;
 
diff --git a/src/dehacked.c b/src/dehacked.c
index ac68c8f2883a8386836ea8d94d0aefe718a4aeaa..aba0b33d00a640c48558a2e138fe97603881918c 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -9108,6 +9108,7 @@ struct {
 	{"SF_DASHMODE",SF_DASHMODE},
 	{"SF_FASTEDGE",SF_FASTEDGE},
 	{"SF_MULTIABILITY",SF_MULTIABILITY},
+	{"SF_NONIGHTSROTATION",SF_NONIGHTSROTATION},
 
 	// Character abilities!
 	// Primary
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index db8b85194bf0c2d9af774cdd7520dde9afd6f0e3..6f300cd91493bc9a5a92f130fa213b40480fc036 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -297,6 +297,9 @@ static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, in
 					case LUA_TNUMBER:
 						value = lua_tonumber(L, stk+2);
 						break;
+					case LUA_TBOOLEAN:
+						value = (UINT8)lua_toboolean(L, stk+2);
+						break;
 					default:
 						TYPEERROR("pivot value", LUA_TNUMBER, lua_type(L, stk+2))
 				}
@@ -308,9 +311,8 @@ static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, in
 				else if (ikey == 3 || (key && fastcmp(key, "rotaxis")))
 					pivot[idx].rotaxis = (UINT8)value;
 				else if (ikey == -1 && (key != NULL))
-					FIELDERROR("pivot key", va("x, y or roll axis expected, got %s", key));
+					FIELDERROR("pivot key", va("invalid option %s", key));
 				ok = 1;
-				//info->available = true; // the pivot for this frame is available
 				lua_pop(L, 1);
 			}
 			break;
@@ -330,7 +332,7 @@ static int PopPivotTable(spriteinfo_t *info, lua_State *L, int stk)
 	// stk = 0 has the pivot table
 	// stk = 1 has the frame key
 	// stk = 2 has the frame table
-	// stk = 3 has either "x" or "y" or "rotaxis" or a number as key
+	// stk = 3 has either a string or a number as key
 	// stk = 4 has the value for the key mentioned above
 	while (lua_next(L, stk))
 	{
diff --git a/src/p_setup.c b/src/p_setup.c
index c55dcec5e5bed970a29edde3208494f8c1e32263..547a29b7b400381ac1b62801a6275b709c9f4f01 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3504,6 +3504,20 @@ boolean P_AddWadFile(const char *wadfilename)
 	if (!mapsadded)
 		CONS_Printf(M_GetText("No maps added\n"));
 
+#ifdef ROTSPRITE
+	for (i = 0; i < NUMSPRITES; i++)
+		R_FreeRotSprite(&sprites[i]);
+	for (i = 0; i < MAXSKINS; i++)
+	{
+		spritedef_t *skinsprites = skins[i].sprites;
+		for (j = 0; j < NUMPLAYERSPRITES*2; j++)
+		{
+			R_FreeRotSprite(skinsprites);
+			skinsprites++;
+		}
+	}
+#endif
+
 	R_LoadSpriteInfoLumps(wadnum, numlumps);
 
 #ifdef HWRENDER
diff --git a/src/p_user.c b/src/p_user.c
index 216ca770b55eba9be3c226f5d602159859ecb63e..0ad7b8ac5204abd6d5de73bccbfbb9892e178696 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -6748,6 +6748,17 @@ static void P_DoNiGHTSCapsule(player_t *player)
 			P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
 	}
 
+#ifdef ROTSPRITE
+	if (!(player->charflags & SF_NONIGHTSROTATION))
+	{
+		if ((player->mo->state == &states[S_PLAY_NIGHTS_PULL])
+		&& (player->mo->sprite2 == SPR2_NPUL))
+			player->mo->rollangle -= ANG30;
+		else
+			player->mo->rollangle = 0;
+	}
+#endif
+
 	if (G_IsSpecialStage(gamemap))
 	{ // In special stages, share rings. Everyone gives up theirs to the capsule player always, because we can't have any individualism here!
 		for (i = 0; i < MAXPLAYERS; i++)
@@ -7491,46 +7502,51 @@ static void P_NiGHTSMovement(player_t *player)
 		flystate = (P_IsObjectOnGround(player->mo)) ? S_PLAY_NIGHTS_STAND : S_PLAY_NIGHTS_FLOAT;
 	else
 	{
-#ifndef ROTSPRITE
-		visangle = ((player->anotherflyangle + 7) % 360)/15;
-		if (visangle > 18) // Over 270 degrees.
-			visangle = 30 - visangle;
-		else if (visangle > 12) // Over 180 degrees.
-			visangle -= 6;
-		else if (visangle > 6) // Over 90 degrees.
-			visangle = 12 - visangle;
-
-		if (player->mo->eflags & MFE_VERTICALFLIP && visangle) // S_PLAY_NIGHTS_FLY0 stays the same, even in reverse gravity
+		if (player->charflags & SF_NONIGHTSROTATION)
 		{
-			if (visangle > 6)
-				visangle -= 6; // shift to S_PLAY_NIGHTS_FLY1-6
-			else
-				visangle += 6; // shift to S_PLAY_NIGHTS_FLY7-C
-		}
+			visangle = ((player->anotherflyangle + 7) % 360)/15;
+			if (visangle > 18) // Over 270 degrees.
+				visangle = 30 - visangle;
+			else if (visangle > 12) // Over 180 degrees.
+				visangle -= 6;
+			else if (visangle > 6) // Over 90 degrees.
+				visangle = 12 - visangle;
 
-		flystate = S_PLAY_NIGHTS_FLY0 + (visangle*2); // S_PLAY_NIGHTS_FLY0-C - the *2 is to skip over drill states
-
-		if (player->pflags & PF_DRILLING)
-			flystate++; // shift to S_PLAY_NIGHTS_DRILL0-C
-#else
-		angle_t a = R_PointToAngle(player->mo->x, player->mo->y) - player->mo->angle;
-		visangle = (player->flyangle % 360);
+			if (player->mo->eflags & MFE_VERTICALFLIP && visangle) // S_PLAY_NIGHTS_FLY0 stays the same, even in reverse gravity
+			{
+				if (visangle > 6)
+					visangle -= 6; // shift to S_PLAY_NIGHTS_FLY1-6
+				else
+					visangle += 6; // shift to S_PLAY_NIGHTS_FLY7-C
+			}
 
-		flystate = S_PLAY_NIGHTS_FLY0;
-		if (player->pflags & PF_DRILLING)
-			flystate++; // shift to S_PLAY_NIGHTS_DRILL0-C
+			flystate = S_PLAY_NIGHTS_FLY0 + (visangle*2); // S_PLAY_NIGHTS_FLY0-C - the *2 is to skip over drill states
 
-		if (player->flyangle >= 90 && player->flyangle <= 270)
-		{
-			if (player->flyangle == 270 && (a < ANGLE_180))
-				;
-			else if (player->flyangle == 90 && (a < ANGLE_180))
-				;
-			else
-				visangle += 180;
+			if (player->pflags & PF_DRILLING)
+				flystate++; // shift to S_PLAY_NIGHTS_DRILL0-C
 		}
+#ifdef ROTSPRITE
+		else
+		{
+			angle_t a = R_PointToAngle(player->mo->x, player->mo->y) - player->mo->angle;
+			visangle = (player->flyangle % 360);
+
+			flystate = S_PLAY_NIGHTS_FLY0;
+			if (player->pflags & PF_DRILLING)
+				flystate++; // shift to S_PLAY_NIGHTS_DRILL0-C
 
-		rollangle = FixedAngle(visangle*FRACUNIT);
+			if (player->flyangle >= 90 && player->flyangle <= 270)
+			{
+				if (player->flyangle == 270 && (a < ANGLE_180))
+					;
+				else if (player->flyangle == 90 && (a < ANGLE_180))
+					;
+				else
+					visangle += 180;
+			}
+
+			rollangle = FixedAngle(visangle<<FRACBITS);
+		}
 #endif
 	}
 
@@ -7538,7 +7554,10 @@ static void P_NiGHTSMovement(player_t *player)
 		P_SetPlayerMobjState(player->mo, flystate);
 
 #ifdef ROTSPRITE
-	player->mo->rollangle = rollangle;
+	if (player->charflags & SF_NONIGHTSROTATION)
+		player->mo->rollangle = 0;
+	else
+		player->mo->rollangle = rollangle;
 #endif
 
 	if (player == &players[consoleplayer])
diff --git a/src/r_patch.c b/src/r_patch.c
index 28a9d2732d2e8d364e6cdb26fad73aaffa6a4192..65c7332491adc9b2d8ace7ee61614d61ebd37df9 100644
--- a/src/r_patch.c
+++ b/src/r_patch.c
@@ -1107,7 +1107,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 	patch_t *newpatch;
 	UINT16 *rawsrc, *rawdst;
 	size_t size, size2;
-	INT32 bflip = ((flip != 0x00) ? -1 : 1);
+	INT32 bflip = (flip != 0x00);
 
 #define SPRITE_XCENTER (patch->leftoffset)
 #define SPRITE_YCENTER (height / 2)
@@ -1155,7 +1155,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 		for (i = 0; i < size; i++)
 			rawsrc[i] = 0xFF00;
 
-		R_PatchToFlat_16bpp(patch, rawsrc, (flip != 0x00));
+		R_PatchToFlat_16bpp(patch, rawsrc, bflip);
 
 		// Don't cache angle = 0
 		for (angle = 1; angle < ROTANGLES; angle++)
@@ -1252,7 +1252,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 
 			// make patch
 			newpatch = R_FlatToPatch_16bpp(rawdst, newwidth, newheight, &size);
-			newpatch->leftoffset = (newpatch->width / 2) - ((SPRITE_XCENTER - patch->leftoffset) * bflip);
+			newpatch->leftoffset = (newpatch->width / 2) - ((SPRITE_XCENTER - patch->leftoffset) * (bflip ? -1 : 1));
 			newpatch->topoffset = (newpatch->height / 2) - (SPRITE_YCENTER - patch->topoffset);
 			newpatch->leftoffset += ((width / 2) - px);
 			newpatch->topoffset += (SPRITE_YCENTER - py);
@@ -1291,4 +1291,56 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 #undef ROTSPRITE_XCENTER
 #undef ROTSPRITE_YCENTER
 }
+
+//
+// R_FreeRotSprite
+//
+// Free sprite rotation data from memory.
+//
+void R_FreeRotSprite(spritedef_t *spritedef)
+{
+	UINT8 frame;
+	INT32 rot, ang;
+
+	for (frame = 0; frame < spritedef->numframes; frame++)
+	{
+		spriteframe_t *sprframe = &spritedef->spriteframes[frame];
+		for (rot = 0; rot < 8; rot++)
+		{
+			if (sprframe->rotsprite.cached[rot])
+			{
+				for (ang = 0; ang < ROTANGLES; ang++)
+				{
+					patch_t *rotsprite = sprframe->rotsprite.patch[rot][ang];
+					if (rotsprite)
+					{
+#ifdef HWRENDER
+						if (rendermode == render_opengl)
+						{
+							GLPatch_t *grPatch = (GLPatch_t *)rotsprite;
+							if (grPatch->rawpatch)
+							{
+								Z_Free(grPatch->rawpatch);
+								grPatch->rawpatch = NULL;
+							}
+							if (grPatch->mipmap)
+							{
+								if (grPatch->mipmap->grInfo.data)
+								{
+									Z_Free(grPatch->mipmap->grInfo.data);
+									grPatch->mipmap->grInfo.data = NULL;
+								}
+								Z_Free(grPatch->mipmap);
+								grPatch->mipmap = NULL;
+							}
+						}
+#endif
+						Z_Free(rotsprite);
+					}
+				}
+				sprframe->rotsprite.cached[rot] = false;
+			}
+		}
+	}
+}
 #endif
diff --git a/src/r_patch.h b/src/r_patch.h
index 7fbdeb6e1314947dbb82d0fb00d49a4f72f274dc..fe2bba34b0cee7532a2f6b0518ac104dcc5aa2d5 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -67,6 +67,7 @@ void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum);
 // rotsprite
 #ifdef ROTSPRITE
 void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip);
+void R_FreeRotSprite(spritedef_t *spritedef);
 extern fixed_t cosang2rad[ROTANGLES];
 extern fixed_t sinang2rad[ROTANGLES];
 #endif
diff --git a/src/r_things.c b/src/r_things.c
index 3d2f2a846101e036a268f5ae251aed49a170217e..e0dd804a79ab0ddbf2306b5586ccab40d3e518df 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -214,55 +214,6 @@ static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
 		sprtemp[frame].flip &= ~(1<<rotation);
 }
 
-#ifdef ROTSPRITE
-static void R_FreeRotSprite(spritedef_t *spritedef)
-{
-	UINT8 frame;
-	INT32 rot, ang;
-
-	for (frame = 0; frame < spritedef->numframes; frame++)
-	{
-		spriteframe_t *sprframe = &spritedef->spriteframes[frame];
-		for (rot = 0; rot < 8; rot++)
-		{
-			if (sprframe->rotsprite.cached[rot])
-			{
-				for (ang = 0; ang < ROTANGLES; ang++)
-				{
-					patch_t *rotsprite = sprframe->rotsprite.patch[rot][ang];
-					if (rotsprite)
-					{
-#ifdef HWRENDER
-						if (rendermode == render_opengl)
-						{
-							GLPatch_t *grPatch = (GLPatch_t *)rotsprite;
-							if (grPatch->rawpatch)
-							{
-								Z_Free(grPatch->rawpatch);
-								grPatch->rawpatch = NULL;
-							}
-							if (grPatch->mipmap)
-							{
-								if (grPatch->mipmap->grInfo.data)
-								{
-									Z_Free(grPatch->mipmap->grInfo.data);
-									grPatch->mipmap->grInfo.data = NULL;
-								}
-								Z_Free(grPatch->mipmap);
-								grPatch->mipmap = NULL;
-							}
-						}
-#endif
-						Z_Free(rotsprite);
-					}
-				}
-				sprframe->rotsprite.cached[rot] = false;
-			}
-		}
-	}
-}
-#endif
-
 // Install a single sprite, given its identifying name (4 chars)
 //
 // (originally part of R_AddSpriteDefs)