diff --git a/src/doomdata.h b/src/doomdata.h
index d9bfb43b14fd78a26b48fda27dea6fdb58d98f40..7708d0bf5d35109593b234e74c8204e572fcb4a5 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -198,7 +198,7 @@ typedef struct
 typedef struct
 {
 	INT16 x, y;
-	INT16 angle;
+	INT16 angle, pitch, roll;
 	UINT16 type;
 	UINT16 options;
 	INT16 z;
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 8d2aad91ebf9528781487abb834bbc64c179ccb0..81a6cd5683bad471bde8292c01d6911f057f2f79 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -31,6 +31,8 @@ enum mobj_e {
 	mobj_snext,
 	mobj_sprev,
 	mobj_angle,
+	mobj_pitch,
+	mobj_roll,
 	mobj_rollangle,
 	mobj_sprite,
 	mobj_frame,
@@ -97,6 +99,8 @@ static const char *const mobj_opt[] = {
 	"snext",
 	"sprev",
 	"angle",
+	"pitch",
+	"roll",
 	"rollangle",
 	"sprite",
 	"frame",
@@ -198,6 +202,12 @@ static int mobj_get(lua_State *L)
 	case mobj_angle:
 		lua_pushangle(L, mo->angle);
 		break;
+	case mobj_pitch:
+		lua_pushangle(L, mo->pitch);
+		break;
+	case mobj_roll:
+		lua_pushangle(L, mo->roll);
+		break;
 	case mobj_rollangle:
 		lua_pushangle(L, mo->rollangle);
 		break;
@@ -453,6 +463,11 @@ static int mobj_set(lua_State *L)
 			localangle = mo->angle;
 		else if (mo->player == &players[secondarydisplayplayer])
 			localangle2 = mo->angle;
+	case mobj_pitch:
+		mo->pitch = luaL_checkangle(L, 3);
+		break;
+	case mobj_roll:
+		mo->roll = luaL_checkangle(L, 3);
 		break;
 	case mobj_rollangle:
 		mo->rollangle = luaL_checkangle(L, 3);
diff --git a/src/p_mobj.c b/src/p_mobj.c
index aaea9d49b9b22128350ec3c7567534caf49432dc..65308438bc8566f5802675cbb612e8b8971a4a80 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -13083,6 +13083,9 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y,
 	if (doangle)
 		mobj->angle = FixedAngle(mthing->angle << FRACBITS);
 
+	mobj->pitch = FixedAngle(mthing->pitch << FRACBITS);
+	mobj->roll = FixedAngle(mthing->roll << FRACBITS);
+
 	mthing->mobj = mobj;
 
 	// ignore MTF_ flags and return early
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 5deb288e42c8969a336ab119d37f88c3fb9a9876..c434bb29f79f85c7bc2e2e9bd35d2ac6f8513226 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -278,7 +278,7 @@ typedef struct mobj_s
 	struct mobj_s **sprev; // killough 8/11/98: change to ptr-to-ptr
 
 	// More drawing info: to determine current sprite.
-	angle_t angle;  // orientation
+	angle_t angle, pitch, roll; // orientation
 	angle_t rollangle;
 	spritenum_t sprite; // used to find patch_t and flip value
 	UINT32 frame; // frame number, plus bits see p_pspr.h
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 34392886d8ae9d5f135c67c6152c173ee2d23959..51d4b75d9d272ace578437a52d9b6bcde2d4b755 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1451,7 +1451,9 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 
 		if ((mobj->x != mobj->spawnpoint->x << FRACBITS) ||
 			(mobj->y != mobj->spawnpoint->y << FRACBITS) ||
-			(mobj->angle != FixedAngle(mobj->spawnpoint->angle*FRACUNIT)))
+			(mobj->angle != FixedAngle(mobj->spawnpoint->angle*FRACUNIT)) ||
+			(mobj->pitch != FixedAngle(mobj->spawnpoint->pitch*FRACUNIT)) ||
+			(mobj->roll != FixedAngle(mobj->spawnpoint->roll*FRACUNIT)) )
 			diff |= MD_POS;
 
 		if (mobj->info->doomednum != mobj->spawnpoint->type)
@@ -1633,6 +1635,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		WRITEFIXED(save_p, mobj->x);
 		WRITEFIXED(save_p, mobj->y);
 		WRITEANGLE(save_p, mobj->angle);
+		WRITEANGLE(save_p, mobj->pitch);
+		WRITEANGLE(save_p, mobj->roll);
 	}
 	if (diff & MD_MOM)
 	{
@@ -2649,12 +2653,16 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		mobj->x = READFIXED(save_p);
 		mobj->y = READFIXED(save_p);
 		mobj->angle = READANGLE(save_p);
+		mobj->pitch = READANGLE(save_p);
+		mobj->roll = READANGLE(save_p);
 	}
 	else
 	{
 		mobj->x = mobj->spawnpoint->x << FRACBITS;
 		mobj->y = mobj->spawnpoint->y << FRACBITS;
 		mobj->angle = FixedAngle(mobj->spawnpoint->angle*FRACUNIT);
+		mobj->pitch = FixedAngle(mobj->spawnpoint->pitch*FRACUNIT);
+		mobj->roll = FixedAngle(mobj->spawnpoint->roll*FRACUNIT);
 	}
 	if (diff & MD_MOM)
 	{
diff --git a/src/p_setup.c b/src/p_setup.c
index 9014062bbbdece30a0cd9c42ddacbe42d3a9f94e..1c5119bdd02494d0b5149b0d1a36183f93751047 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1282,6 +1282,7 @@ static void P_LoadThings(UINT8 *data)
 		mt->options = READUINT16(data);
 		mt->extrainfo = (UINT8)(mt->type >> 12);
 		mt->tag = 0;
+		mt->pitch = mt->roll = 0;
 
 		mt->type &= 4095;
 
@@ -1568,6 +1569,10 @@ static void ParseTextmapThingParameter(UINT32 i, char *param, char *val)
 		mapthings[i].z = atol(val);
 	else if (fastcmp(param, "angle"))
 		mapthings[i].angle = atol(val);
+	else if (fastcmp(param, "pitch"))
+		mapthings[i].pitch = atol(val);
+	else if (fastcmp(param, "roll"))
+		mapthings[i].roll = atol(val);
 	else if (fastcmp(param, "type"))
 		mapthings[i].type = atol(val);