diff --git a/src/dehacked.c b/src/dehacked.c
index 0662074ba4badb82ba2aef5e0fd993ce34d675f2..df692391c4b0185f51400ec8aacab38b6bb4253c 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -9585,7 +9585,9 @@ struct {
 	{"RF_VERTICALFLIP",RF_VERTICALFLIP},
 	{"RF_ABSOLUTEOFFSETS",RF_ABSOLUTEOFFSETS},
 	{"RF_FLIPOFFSETS",RF_FLIPOFFSETS},
+	{"RF_SPLATMASK",RF_SLOPESPLAT},
 	{"RF_SLOPESPLAT",RF_SLOPESPLAT},
+	{"RF_OBJECTSLOPESPLAT",RF_OBJECTSLOPESPLAT},
 	{"RF_NOSPLATBILLBOARD",RF_NOSPLATBILLBOARD},
 	{"RF_NOSPLATROLLANGLE",RF_NOSPLATROLLANGLE},
 	{"RF_BLENDMASK",RF_BLENDMASK},
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 3157371081deb9b10989821198b1361a769e73a2..948045df336a6f295301de797c0868b8b96bf1ee 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -3942,6 +3942,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 	{
 		F2DCoord verts[4];
 		F2DCoord rotated[4];
+
 		angle_t angle;
 		float ca, sa;
 		float w, h;
@@ -3950,12 +3951,14 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 		float leftoffset, topoffset;
 		float scale = spr->scale;
 		float zoffset = (P_MobjFlip(spr->mobj) * 0.05f);
+		pslope_t *splatslope = NULL;
 		INT32 i;
 
-		if (spr->renderflags & RF_SHADOWEFFECTS)
+		renderflags_t renderflags = spr->renderflags;
+		if (renderflags & RF_SHADOWEFFECTS)
 			scale *= spr->shadowscale;
 
-		if (spr->rotateflags & SRF_3D || spr->renderflags & RF_NOSPLATBILLBOARD)
+		if (spr->rotateflags & SRF_3D || renderflags & RF_NOSPLATBILLBOARD)
 			angle = spr->mobj->angle;
 		else
 			angle = viewangle;
@@ -4015,13 +4018,24 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 			wallVerts[i].z = rotated[i].y + FIXED_TO_FLOAT(spr->mobj->y);
 		}
 
-		if (spr->renderflags & RF_SLOPESPLAT && spr->mobj->standingslope)
+		if (renderflags & (RF_SLOPESPLAT | RF_OBJECTSLOPESPLAT))
 		{
-			pslope_t *slope = spr->mobj->standingslope;
+			pslope_t *standingslope = spr->mobj->standingslope; // The slope that the object is standing on.
+
+			// The slope that was defined for the sprite.
+			if (renderflags & RF_SLOPESPLAT)
+				splatslope = spr->mobj->floorspriteslope;
 
+			if (standingslope && (renderflags & RF_OBJECTSLOPESPLAT))
+				splatslope = standingslope;
+		}
+
+		// Set vertical position
+		if (splatslope)
+		{
 			for (i = 0; i < 4; i++)
 			{
-				fixed_t slopez = P_GetSlopeZAt(slope, FLOAT_TO_FIXED(wallVerts[i].x), FLOAT_TO_FIXED(wallVerts[i].z));
+				fixed_t slopez = P_GetSlopeZAt(splatslope, FLOAT_TO_FIXED(wallVerts[i].x), FLOAT_TO_FIXED(wallVerts[i].z));
 				wallVerts[i].y = FIXED_TO_FLOAT(slopez) + zoffset;
 			}
 		}
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index a544f151cd9ebf83b24b499d44b2c3f3c63e3b21..75b0c2f89372d04ef60b7c3e7b6e6c5e412401b2 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -43,6 +43,7 @@ enum mobj_e {
 	mobj_spriteyscale,
 	mobj_spritexoffset,
 	mobj_spriteyoffset,
+	mobj_floorspriteslope,
 	mobj_touching_sectorlist,
 	mobj_subsector,
 	mobj_floorz,
@@ -117,6 +118,7 @@ static const char *const mobj_opt[] = {
 	"spriteyscale",
 	"spritexoffset",
 	"spriteyoffset",
+	"floorspriteslope",
 	"touching_sectorlist",
 	"subsector",
 	"floorz",
@@ -249,6 +251,9 @@ static int mobj_get(lua_State *L)
 	case mobj_spriteyoffset:
 		lua_pushfixed(L, mo->spriteyoffset);
 		break;
+	case mobj_floorspriteslope:
+		LUA_PushUserdata(L, mo->floorspriteslope, META_SLOPE);
+		break;
 	case mobj_touching_sectorlist:
 		return UNIMPLEMENTED;
 	case mobj_subsector:
@@ -529,6 +534,8 @@ static int mobj_set(lua_State *L)
 	case mobj_spriteyoffset:
 		mo->spriteyoffset = luaL_checkfixed(L, 3);
 		break;
+	case mobj_floorspriteslope:
+		return NOSET;
 	case mobj_touching_sectorlist:
 		return UNIMPLEMENTED;
 	case mobj_subsector:
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 2260ace51132ebbb86958fb07a3457400348772c..6d36ee30332faa4187f74358ae7139521c67b154 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -10473,6 +10473,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 	// Sprite rendering
 	mobj->spritexscale = mobj->spriteyscale = mobj->scale;
 	mobj->spritexoffset = mobj->spriteyoffset = 0;
+	mobj->floorspriteslope = Z_Calloc(sizeof(pslope_t), PU_LEVEL, NULL);
+	mobj->floorspriteslope->normal.z = FRACUNIT;
 
 	// set subsector and/or block links
 	P_SetThingPosition(mobj);
@@ -10935,6 +10937,10 @@ void P_RemoveMobj(mobj_t *mobj)
 	mobj->state = NULL;
 	mobj->player = NULL;
 
+	if (mobj->floorspriteslope)
+		Z_Free(mobj->floorspriteslope);
+	mobj->floorspriteslope = NULL;
+
 	// stop any playing sound
 	S_StopSound(mobj);
 
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 0b688a9ad72305227bd04a90db9935a3ba3e4bdc..d26a38897bd067e9475362dfaa12b8c79b9f37af 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -289,6 +289,7 @@ typedef struct mobj_s
 	UINT32 renderflags; // render flags
 	fixed_t spritexscale, spriteyscale;
 	fixed_t spritexoffset, spriteyoffset;
+	struct pslope_s *floorspriteslope; // The slope that the floorsprite is rotated by
 
 	struct msecnode_s *touching_sectorlist; // a linked list of sectors where this object appears
 
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 3a9663326669db8e9b4797feb3c60adfd9777d1d..ae8181693fafa6e2b203ad770eb75b3a09aa2b13 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1393,12 +1393,13 @@ typedef enum
 	MD2_COLORIZED    = 1<<12,
 	MD2_MIRRORED     = 1<<13,
 	MD2_ROLLANGLE    = 1<<14,
-	MD2_SPRITEXSCALE = 1<<15,
-	MD2_SPRITEYSCALE = 1<<16,
-	MD2_SPRITEXOFFSET = 1<<17,
-	MD2_SPRITEYOFFSET = 1<<18,
-	MD2_SHADOWSCALE   = 1<<19,
-	MD2_RENDERFLAGS   = 1<<20,
+	MD2_SHADOWSCALE  = 1<<15,
+	MD2_RENDERFLAGS  = 1<<16,
+	MD2_SPRITEXSCALE = 1<<17,
+	MD2_SPRITEYSCALE = 1<<18,
+	MD2_SPRITEXOFFSET = 1<<19,
+	MD2_SPRITEYOFFSET = 1<<20,
+	MD2_FLOORSPRITESLOPE = 1<<21,
 } mobj_diff2_t;
 
 typedef enum
@@ -1609,18 +1610,27 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		diff2 |= MD2_MIRRORED;
 	if (mobj->rollangle)
 		diff2 |= MD2_ROLLANGLE;
+	if (mobj->shadowscale)
+		diff2 |= MD2_SHADOWSCALE;
+	if (mobj->renderflags)
+		diff2 |= MD2_RENDERFLAGS;
 	if (mobj->spritexscale != FRACUNIT)
 		diff2 |= MD2_SPRITEXSCALE;
 	if (mobj->spriteyscale != FRACUNIT)
 		diff2 |= MD2_SPRITEYSCALE;
 	if (mobj->spritexoffset)
 		diff2 |= MD2_SPRITEXOFFSET;
-	if (mobj->spriteyoffset)
-		diff2 |= MD2_SPRITEYOFFSET;
-	if (mobj->shadowscale)
-		diff2 |= MD2_SHADOWSCALE;
-	if (mobj->renderflags)
-		diff2 |= MD2_RENDERFLAGS;
+
+	{
+		pslope_t *slope = mobj->floorspriteslope;
+		if (slope->zangle || slope->zdelta || slope->xydirection
+		|| slope->o.x || slope->o.y || slope->o.z
+		|| slope->d.x || slope->d.y
+		|| slope->normal.x || slope->normal.y
+		|| (slope->normal.z != FRACUNIT))
+			diff2 |= MD2_FLOORSPRITESLOPE;
+	}
+
 	if (diff2 != 0)
 		diff |= MD_MORE;
 
@@ -1761,6 +1771,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		WRITEUINT8(save_p, mobj->mirrored);
 	if (diff2 & MD2_ROLLANGLE)
 		WRITEANGLE(save_p, mobj->rollangle);
+	if (diff2 & MD2_SHADOWSCALE)
+		WRITEFIXED(save_p, mobj->shadowscale);
+	if (diff2 & MD2_RENDERFLAGS)
+		WRITEUINT32(save_p, mobj->renderflags);
 	if (diff2 & MD2_SPRITEXSCALE)
 		WRITEFIXED(save_p, mobj->spritexscale);
 	if (diff2 & MD2_SPRITEYSCALE)
@@ -1769,10 +1783,25 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		WRITEFIXED(save_p, mobj->spritexoffset);
 	if (diff2 & MD2_SPRITEYOFFSET)
 		WRITEFIXED(save_p, mobj->spriteyoffset);
-	if (diff2 & MD2_SHADOWSCALE)
-		WRITEFIXED(save_p, mobj->shadowscale);
-	if (diff2 & MD2_RENDERFLAGS)
-		WRITEUINT32(save_p, mobj->renderflags);
+	if (diff2 & MD2_FLOORSPRITESLOPE)
+	{
+		pslope_t *slope = mobj->floorspriteslope;
+
+		WRITEFIXED(save_p, slope->zdelta);
+		WRITEANGLE(save_p, slope->zangle);
+		WRITEANGLE(save_p, slope->xydirection);
+
+		WRITEFIXED(save_p, slope->o.x);
+		WRITEFIXED(save_p, slope->o.y);
+		WRITEFIXED(save_p, slope->o.z);
+
+		WRITEFIXED(save_p, slope->d.x);
+		WRITEFIXED(save_p, slope->d.y);
+
+		WRITEFIXED(save_p, slope->normal.x);
+		WRITEFIXED(save_p, slope->normal.y);
+		WRITEFIXED(save_p, slope->normal.z);
+	}
 
 	WRITEUINT32(save_p, mobj->mobjnum);
 }
@@ -2780,6 +2809,10 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		mobj->mirrored = READUINT8(save_p);
 	if (diff2 & MD2_ROLLANGLE)
 		mobj->rollangle = READANGLE(save_p);
+	if (diff2 & MD2_SHADOWSCALE)
+		mobj->shadowscale = READFIXED(save_p);
+	if (diff2 & MD2_RENDERFLAGS)
+		mobj->renderflags = READUINT32(save_p);
 	if (diff2 & MD2_SPRITEXSCALE)
 		mobj->spritexscale = READFIXED(save_p);
 	if (diff2 & MD2_SPRITEYSCALE)
@@ -2788,10 +2821,25 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		mobj->spritexoffset = READFIXED(save_p);
 	if (diff2 & MD2_SPRITEYOFFSET)
 		mobj->spriteyoffset = READFIXED(save_p);
-	if (diff2 & MD2_SHADOWSCALE)
-		mobj->shadowscale = READFIXED(save_p);
-	if (diff2 & MD2_RENDERFLAGS)
-		mobj->renderflags = READUINT32(save_p);
+	if (diff2 & MD2_FLOORSPRITESLOPE)
+	{
+		pslope_t *slope = mobj->floorspriteslope;
+
+		slope->zdelta = READFIXED(save_p);
+		slope->zangle = READANGLE(save_p);
+		slope->xydirection = READANGLE(save_p);
+
+		slope->o.x = READFIXED(save_p);
+		slope->o.y = READFIXED(save_p);
+		slope->o.z = READFIXED(save_p);
+
+		slope->d.x = READFIXED(save_p);
+		slope->d.y = READFIXED(save_p);
+
+		slope->normal.x = READFIXED(save_p);
+		slope->normal.y = READFIXED(save_p);
+		slope->normal.z = READFIXED(save_p);
+	}
 
 	if (diff & MD_REDFLAG)
 	{
diff --git a/src/r_defs.h b/src/r_defs.h
index 7b7c5d7f055da69dbd8a68256fae179d43c8c73e..4423a42665c3eca3ea8c734b96dcb50dff976d0e 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -720,9 +720,11 @@ typedef enum
 	RF_ABSOLUTEOFFSETS  = 0x0004,   // Sprite uses the object's offsets absolutely, instead of relatively
 	RF_FLIPOFFSETS      = 0x0008,   // Relative object offsets are flipped with the sprite
 
-	RF_SLOPESPLAT       = 0x0010,   // Rotate floor sprites by the object's standing slope
-	RF_NOSPLATBILLBOARD = 0x0020,   // Don't billboard floor sprites (faces forward from the view angle)
-	RF_NOSPLATROLLANGLE = 0x0040,   // Don't rotate floor sprites by the object's rollangle (uses rotated patches instead)
+	RF_SPLATMASK        = 0x00F0,   // --Floor sprite flags
+	RF_SLOPESPLAT       = 0x0010,   // Rotate floor sprites by a slope
+	RF_OBJECTSLOPESPLAT = 0x0020,   // Rotate floor sprites by the object's standing slope
+	RF_NOSPLATBILLBOARD = 0x0040,   // Don't billboard floor sprites (faces forward from the view angle)
+	RF_NOSPLATROLLANGLE = 0x0080,   // Don't rotate floor sprites by the object's rollangle (uses rotated patches instead)
 
 	RF_BLENDMASK        = 0x0F00,   // --Blending modes
 	RF_FULLBRIGHT       = 0x0100,   // Sprite is drawn at full brightness
diff --git a/src/r_things.c b/src/r_things.c
index a0b6239a78d2a2717b87f53449e87043f99be514..e8c00b86b2c3cbd42f65771f6604babe483935e3 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -2797,21 +2797,26 @@ static void R_DrawVisSplat(vissprite_t *spr)
 #ifdef FLOORSPLATS
 	floorsplat_t splat;
 	fixed_t tr_x, tr_y, rot_x, rot_y, rot_z;
+
 	vector3_t *v3d;
 	vector2_t v2d[4];
+	vector2_t rotated[4];
+
 	fixed_t x, y;
 	fixed_t w, h;
-	angle_t splatangle, angle;
+	angle_t angle, splatangle;
 	fixed_t ca, sa;
 	fixed_t xscale, yscale;
 	fixed_t xoffset, yoffset;
 	fixed_t leftoffset, topoffset;
+	pslope_t *slope = NULL;
+	INT32 i;
+
 	boolean hflip = (spr->xiscale < 0);
 	boolean vflip = (spr->cut & SC_VFLIP);
 	UINT8 flipflags = 0;
-	vector2_t rotated[4];
-	pslope_t *slope = NULL;
-	INT32 i;
+
+	renderflags_t renderflags = spr->renderflags;
 
 	if (hflip)
 		flipflags |= PICFLAGS_XFLIP;
@@ -2831,7 +2836,7 @@ static void R_DrawVisSplat(vissprite_t *spr)
 	if (spr->mobj->skin && ((skin_t *)spr->mobj->skin)->flags & SF_HIRES)
 		splat.scale = FixedMul(splat.scale, ((skin_t *)spr->mobj->skin)->highresscale);
 
-	if (spr->rotateflags & SRF_3D || spr->renderflags & RF_NOSPLATBILLBOARD)
+	if (spr->rotateflags & SRF_3D || renderflags & RF_NOSPLATBILLBOARD)
 		splatangle = spr->mobj->angle;
 	else
 		splatangle = viewangle;
@@ -2895,9 +2900,18 @@ static void R_DrawVisSplat(vissprite_t *spr)
 		rotated[i].y = FixedMul(splat.verts[i].x, sa) + FixedMul(splat.verts[i].y, ca);
 	}
 
-	if (spr->renderflags & RF_SLOPESPLAT)
+	if (renderflags & (RF_SLOPESPLAT | RF_OBJECTSLOPESPLAT))
 	{
-		slope = spr->mobj->standingslope;
+		pslope_t *standingslope = spr->mobj->standingslope; // The slope that the object is standing on.
+
+		// The slope that was defined for the sprite.
+		if (renderflags & RF_SLOPESPLAT)
+			slope = spr->mobj->floorspriteslope;
+
+		if (standingslope && (renderflags & RF_OBJECTSLOPESPLAT))
+			slope = standingslope;
+
+		// Set splat as tilted
 		splat.tilted = (slope != NULL);
 	}