diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index a2f56880be3c4647d3478b2b71625e923bdc374d..6bae1494f5f6ae140adc33b0a072cc70f754a25a 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -438,6 +438,7 @@ sectortypes
 	160 = "Special Stage Time/Spheres Parameters <deprecated>";
 	176 = "Custom Global Gravity <deprecated>";
 	1280 = "Speed Pad";
+	1536 = "Flip Gravity on Jump";
 	4096 = "Star Post Activator";
 	8192 = "Exit/Special Stage Pit/Return Flag";
 	12288 = "CTF Red Team Base";
@@ -496,6 +497,7 @@ gen_sectortypes
 	{
 		0 = "Normal";
 		1280 = "Speed Pad";
+		1536 = "Flip Gravity on Jump";
 	}
 
 	fourth
@@ -576,6 +578,7 @@ linedeftypes
 			title = "Per-Sector Gravity";
 			prefix = "(1)";
 			flags64text = "[6] Flip in reverse gravity";
+			flags8192text = "[13] Reverse while inside";
 		}
 
 		5
@@ -2062,6 +2065,30 @@ linedeftypes
 			prefix = "(342)";
 		}
 
+		343
+		{
+			title = "Gravity Check - Continuous";
+			flags2text = "[1] Check temporary reverse gravity";
+			flags64text = "[6] Check for reverse gravity";
+			prefix = "(343)";
+		}
+
+		344
+		{
+			title = "Gravity Check - Each Time";
+			flags2text = "[1] Check temporary reverse gravity";
+			flags64text = "[6] Check for reverse gravity";
+			prefix = "(344)";
+		}
+
+		345
+		{
+			title = "Gravity Check - Once";
+			flags2text = "[1] Check temporary reverse gravity";
+			flags64text = "[6] Check for reverse gravity";
+			prefix = "(345)";
+		}
+
 		399
 		{
 			title = "Level Load";
@@ -2315,6 +2342,7 @@ linedeftypes
 			title = "Enable/Disable Gravity Flip";
 			prefix = "(433)";
 			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] Invert current gravity";
 			flags64text = "[6] Return to normal";
 		}
 
diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index 1dec9ab88116330f9a1bc77b751d3f90dea8abbc..99c3056590dbc65135fc3c75c33b68267838674b 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -779,6 +779,21 @@ doom
 			title = "NiGHTS Mare - Once";
 			prefix = "(342)";
 		}
+		343
+		{
+			title = "Gravity Check - Continuous";
+			prefix = "(343)";
+		}
+		344
+		{
+			title = "Gravity Check - Each Time";
+			prefix = "(344)";
+		}
+		345
+		{
+			title = "Gravity Check - Once";
+			prefix = "(345)";
+		}
 		399
 		{
 			title = "Level Load";
@@ -3581,6 +3596,29 @@ udmf
 			}
 		}
 
+		343
+		{
+			title = "Gravity Check";
+			prefix = "(343)";
+			arg0
+			{
+				title = "Trigger type";
+				type = 11;
+				enum = "triggertype";
+			}
+			arg1
+			{
+				title = "Gravity";
+				type = 11;
+				enum
+				{
+					0 = "Normal gravity";
+					1 = "Reverse gravity";
+					2 = "Reverse gravity (no MF2_OBJECTFLIP)";
+				}
+			}
+		}
+
 		399
 		{
 			title = "Level Load";
@@ -3918,6 +3956,16 @@ udmf
 					2 = "Remove";
 				}
 			}
+			arg3
+			{
+				title = "Override gravity?";
+				type = 11;
+				enum
+				{
+					0 = "No";
+					1 = "Yes";
+				}
+			}
 			stringarg0
 			{
 				title = "Gravity value";
@@ -4157,7 +4205,7 @@ udmf
 			prefix = "(433)";
 			arg0
 			{
-				title = "Gravity";
+				title = "Set gravity";
 				type = 11;
 				enum
 				{
@@ -4165,6 +4213,16 @@ udmf
 					1 = "Normal";
 				}
 			}
+			arg1
+			{
+				title = "Invert current gravity";
+				type = 11;
+				enum
+				{
+					0 = "No";
+					1 = "Yes";
+				}
+			}
 		}
 
 		434
diff --git a/extras/conf/udb/Includes/SRB222_sectors.cfg b/extras/conf/udb/Includes/SRB222_sectors.cfg
index f9df297e76d06a3ed122156379066d3b38136c84..5b3ad4155c176b229eee741b9a1744121b68d4ae 100644
--- a/extras/conf/udb/Includes/SRB222_sectors.cfg
+++ b/extras/conf/udb/Includes/SRB222_sectors.cfg
@@ -28,6 +28,7 @@ sectortypes
 	160 = "Special Stage Time/Spheres Parameters <deprecated>";
 	176 = "Custom Global Gravity <deprecated>";
 	1280 = "Speed Pad";
+	1536 = "Flip Gravity on Jump";
 	4096 = "Star Post Activator";
 	8192 = "Exit/Special Stage Pit/Return Flag";
 	12288 = "CTF Red Team Base";
@@ -84,6 +85,7 @@ gen_sectortypes
 	{
 		0 = "Normal";
 		1280 = "Speed Pad";
+		1536 = "Flip Gravity on Jump";
 	}
 
 	fourth
@@ -102,4 +104,4 @@ gen_sectortypes
 		45056 = "Rope Hang";
 		49152 = "Intangible to the Camera";
 	}
-}
\ No newline at end of file
+}
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 5af9669ca3ab14672da929a900fcb6c448386fec..29adb478abfffd58a5d564769382c6290c6acefd 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1301,6 +1301,17 @@ static int lib_pInQuicksand(lua_State *L)
 	return 1;
 }
 
+static int lib_pInJumpFlipSector(lua_State *L)
+{
+	mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+	//HUDSAFE
+	INLEVEL
+	if (!mo)
+		return LUA_ErrInvalid(L, "mobj_t");
+	lua_pushboolean(L, P_InJumpFlipSector(mo));
+	return 1;
+}
+
 static int lib_pSetObjectMomZ(lua_State *L)
 {
 	mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@@ -4044,6 +4055,7 @@ static luaL_Reg lib[] = {
 	{"P_IsObjectOnGround",lib_pIsObjectOnGround},
 	{"P_InSpaceSector",lib_pInSpaceSector},
 	{"P_InQuicksand",lib_pInQuicksand},
+	{"P_InJumpFlipSector",lib_pInJumpFlipSector},
 	{"P_SetObjectMomZ",lib_pSetObjectMomZ},
 	{"P_PlayJingle",lib_pPlayJingle},
 	{"P_PlayJingleMusic",lib_pPlayJingleMusic},
diff --git a/src/p_inter.c b/src/p_inter.c
index c230ce178ad5b388ca123cde1352e8771b80f8c5..dd3e0f9c27fc41657841c22e24d20391d9e94b74 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -3883,7 +3883,10 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
 				P_SetObjectMomZ(mo, ns, true);
 		}
 		if (player->mo->eflags & MFE_VERTICALFLIP)
+		{
 			mo->momz *= -1;
+			mo->flags2 |= MF2_OBJECTFLIP;
+		}
 	}
 
 	player->losstime += 10*TICRATE;
@@ -4107,6 +4110,8 @@ void P_PlayerWeaponPanelOrAmmoBurst(player_t *player)
 		P_SetObjectMomZ(mo, 4*FRACUNIT, false); \
 		if (i & 1) \
 			P_SetObjectMomZ(mo, 4*FRACUNIT, true); \
+		if (player->mo->eflags & MFE_VERTICALFLIP) \
+			mo->flags2 |= MF2_OBJECTFLIP; \
 		++i; \
 	} \
 	else if (player->powers[power] > 0) \
@@ -4126,6 +4131,8 @@ void P_PlayerWeaponPanelOrAmmoBurst(player_t *player)
 		P_SetObjectMomZ(mo, 3*FRACUNIT, false); \
 		if (i & 1) \
 			P_SetObjectMomZ(mo, 3*FRACUNIT, true); \
+		if (player->mo->eflags & MFE_VERTICALFLIP) \
+			mo->flags2 |= MF2_OBJECTFLIP; \
 		player->powers[power] = 0; \
 		++i; \
 	}
@@ -4266,7 +4273,10 @@ void P_PlayerEmeraldBurst(player_t *player, boolean toss)
 			P_SetObjectMomZ(mo, 3*FRACUNIT, false);
 
 			if (player->mo->eflags & MFE_VERTICALFLIP)
+			{
 				mo->momz = -mo->momz;
+				mo->flags2 |= MF2_OBJECTFLIP;
+			}
 
 			if (toss)
 				player->tossdelay = 2*TICRATE;
@@ -4295,7 +4305,10 @@ void P_PlayerFlagBurst(player_t *player, boolean toss)
 	flag = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, type);
 
 	if (player->mo->eflags & MFE_VERTICALFLIP)
+	{
 		flag->z += player->mo->height - flag->height;
+		flag->flags2 |= MF2_OBJECTFLIP;
+	}
 
 	if (toss)
 		P_InstaThrust(flag, player->mo->angle, FixedMul(6*FRACUNIT, player->mo->scale));
diff --git a/src/p_local.h b/src/p_local.h
index 49af03f3666a29ec703beb7dc4a0ae8fa673b297..2b3020997da387a624efcf5e5c9d46ded7384930 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -151,6 +151,7 @@ boolean P_IsObjectInGoop(mobj_t *mo);
 boolean P_IsObjectOnGround(mobj_t *mo);
 boolean P_InSpaceSector(mobj_t *mo);
 boolean P_InQuicksand(mobj_t *mo);
+boolean P_InJumpFlipSector(mobj_t *mo);
 boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff);
 
 void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative);
diff --git a/src/p_map.c b/src/p_map.c
index 117d49ea491e05310d4c358f7708d8d2b9473d4c..275403e2e125ec1d04f4ccbdec1b0e4880d59728 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2153,7 +2153,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 
 			if (rover->fofflags & FOF_QUICKSAND)
 			{
-				if (thing->z < topheight && bottomheight < thingtop)
+				if (!(thing->eflags & MFE_VERTICALFLIP) && thing->z < topheight && bottomheight < thingtop)
 				{
 					if (tmfloorz < thing->z) {
 						tmfloorz = thing->z;
@@ -2161,6 +2161,14 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 						tmfloorslope = NULL;
 					}
 				}
+				else if (thing->eflags & MFE_VERTICALFLIP && thing->z < topheight && bottomheight < thingtop)
+				{
+					if (tmceilingz > thingtop) {
+						tmceilingz = thingtop;
+						tmceilingrover = rover;
+						tmceilingslope = NULL;
+					}
+				}
 				// Quicksand blocks never change heights otherwise.
 				continue;
 			}
@@ -5119,8 +5127,8 @@ fixed_t P_CeilingzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height)
 			{
 				if (thingtop > bottomheight && topheight > z)
 				{
-					if (ceilingz > z)
-						ceilingz = z;
+					if (ceilingz > thingtop)
+						ceilingz = thingtop;
 				}
 				continue;
 			}
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 965e8e57e5710ea5429fe23704a7693555794978..e658d192c6bb50452433c48653e46dc4d6575656 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1432,7 +1432,7 @@ static void P_PlayerFlip(mobj_t *mo)
 fixed_t P_GetMobjGravity(mobj_t *mo)
 {
 	fixed_t gravityadd = 0;
-	boolean no3dfloorgrav = true; // Custom gravity
+	sector_t *gravsector = NULL; // Custom gravity
 	boolean goopgravity = false;
 	boolean wasflip;
 
@@ -1440,14 +1440,11 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
 	I_Assert(!P_MobjWasRemoved(mo));
 
 	wasflip = (mo->eflags & MFE_VERTICALFLIP) != 0;
-
-	if (mo->type != MT_SPINFIRE)
-		mo->eflags &= ~MFE_VERTICALFLIP;
+	mo->eflags &= ~MFE_VERTICALFLIP;
 
 	if (mo->subsector->sector->ffloors) // Check for 3D floor gravity too.
 	{
 		ffloor_t *rover;
-		fixed_t gravfactor;
 
 		for (rover = mo->subsector->sector->ffloors; rover; rover = rover->next)
 		{
@@ -1457,27 +1454,24 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
 			if ((rover->fofflags & (FOF_SWIMMABLE|FOF_GOOWATER)) == (FOF_SWIMMABLE|FOF_GOOWATER))
 				goopgravity = true;
 
-			gravfactor = P_GetSectorGravityFactor(rover->master->frontsector);
-
-			if (gravfactor == FRACUNIT)
+			if (P_GetSectorGravityFactor(rover->master->frontsector) == FRACUNIT)
 				continue;
 
-			gravityadd = -FixedMul(gravity, gravfactor);
-
-			if ((rover->master->frontsector->flags & MSF_GRAVITYFLIP) && gravityadd > 0)
-				mo->eflags |= MFE_VERTICALFLIP;
-
-			no3dfloorgrav = false;
+			gravsector = rover->master->frontsector;
 			break;
 		}
 	}
 
-	if (no3dfloorgrav)
-	{
-		gravityadd = -FixedMul(gravity, P_GetSectorGravityFactor(mo->subsector->sector));
+	if (!gravsector) // If there is no 3D floor gravity, check sector's gravity
+		gravsector = mo->subsector->sector;
 
-		if ((mo->subsector->sector->flags & MSF_GRAVITYFLIP) && gravityadd > 0)
-			mo->eflags |= MFE_VERTICALFLIP;
+	gravityadd = -FixedMul(gravity, P_GetSectorGravityFactor(gravsector));
+
+	if ((gravsector->flags & MSF_GRAVITYFLIP) && gravityadd > 0)
+	{
+		if (gravsector->specialflags & SSF_GRAVITYOVERRIDE)
+			mo->flags2 &= ~MF2_OBJECTFLIP;
+		mo->eflags |= MFE_VERTICALFLIP;
 	}
 
 	// Less gravity underwater.
@@ -1515,36 +1509,6 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
 		{
 			switch (mo->type)
 			{
-				case MT_FLINGRING:
-				case MT_FLINGCOIN:
-				case MT_FLINGBLUESPHERE:
-				case MT_FLINGNIGHTSCHIP:
-				case MT_FLINGEMERALD:
-				case MT_BOUNCERING:
-				case MT_RAILRING:
-				case MT_INFINITYRING:
-				case MT_AUTOMATICRING:
-				case MT_EXPLOSIONRING:
-				case MT_SCATTERRING:
-				case MT_GRENADERING:
-				case MT_BOUNCEPICKUP:
-				case MT_RAILPICKUP:
-				case MT_AUTOPICKUP:
-				case MT_EXPLODEPICKUP:
-				case MT_SCATTERPICKUP:
-				case MT_GRENADEPICKUP:
-				case MT_REDFLAG:
-				case MT_BLUEFLAG:
-					if (mo->target)
-					{
-						// Flung items copy the gravity of their tosser.
-						if ((mo->target->eflags & MFE_VERTICALFLIP) && !(mo->eflags & MFE_VERTICALFLIP))
-						{
-							gravityadd = -gravityadd;
-							mo->eflags |= MFE_VERTICALFLIP;
-						}
-					}
-					break;
 				case MT_WATERDROP:
 				case MT_CYBRAKDEMON:
 					gravityadd >>= 1;
@@ -2193,15 +2157,20 @@ void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype)
 				case 2: // scenery does things differently for some reason
 					if (mo->z < topheight && bottomheight < thingtop)
 					{
-						mo->floorz = mo->z;
+						if (!(mo->eflags & MFE_VERTICALFLIP))
+							mo->floorz = mo->z;
+						else if (mo->eflags & MFE_VERTICALFLIP)
+							mo->ceilingz = thingtop;
 						continue;
 					}
 					break;
 				default:
 					if (mo->z < topheight && bottomheight < thingtop)
 					{
-						if (mo->floorz < mo->z)
+						if (!(mo->eflags & MFE_VERTICALFLIP) && mo->floorz < mo->z)
 							mo->floorz = mo->z;
+						else if (mo->eflags & MFE_VERTICALFLIP && mo->ceilingz > thingtop)
+							mo->ceilingz = thingtop;
 					}
 					continue; // This is so you can jump/spring up through quicksand from below.
 			}
diff --git a/src/p_setup.c b/src/p_setup.c
index ed8c5c2e9daa8cf3b308c36b9caebd72744c5c5a..deb308da22ea5bd45837f8848baba8e1c9e41d67 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1727,6 +1727,10 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char
 		sectors[i].specialflags |= SSF_FINISHLINE;
 	else if (fastcmp(param, "ropehang") && fastcmp("true", val))
 		sectors[i].specialflags |= SSF_ROPEHANG;
+	else if (fastcmp(param, "jumpflip") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_JUMPFLIP;
+	else if (fastcmp(param, "gravityoverride") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_GRAVITYOVERRIDE;
 	else if (fastcmp(param, "friction"))
 		sectors[i].friction = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "gravity"))
@@ -2580,6 +2584,10 @@ static void P_WriteTextmap(void)
 			fprintf(f, "finishline = true;\n");
 		if (wsectors[i].specialflags & SSF_ROPEHANG)
 			fprintf(f, "ropehang = true;\n");
+		if (wsectors[i].specialflags & SSF_JUMPFLIP)
+			fprintf(f, "jumpflip = true;\n");
+		if (wsectors[i].specialflags & SSF_GRAVITYOVERRIDE)
+			fprintf(f, "gravityoverride = true;\n");
 		if (wsectors[i].friction != ORIG_FRICTION)
 			fprintf(f, "friction = %f;\n", FIXED_TO_FLOAT(wsectors[i].friction));
 		if (wsectors[i].gravity != FRACUNIT)
@@ -4964,6 +4972,21 @@ static void P_ConvertBinaryLinedefTypes(void)
 				lines[i].args[2] = TMC_EQUAL;
 			lines[i].special = 340;
 			break;
+		case 343: //Gravity check - continuous
+		case 344: //Gravity check - each time
+		case 345: //Gravity check - once
+			if (lines[i].special == 345)
+				lines[i].args[0] = TMT_ONCE;
+			else if (lines[i].special == 344)
+				lines[i].args[0] = (lines[i].flags & ML_BOUNCY) ? TMT_EACHTIMEENTERANDEXIT : TMT_EACHTIMEENTER;
+			else
+				lines[i].args[0] = TMT_CONTINUOUS;
+			if (lines[i].flags & ML_BLOCKMONSTERS)
+				lines[i].args[1] = TMG_TEMPREVERSE;
+			else if (lines[i].flags & ML_NOCLIMB)
+				lines[i].args[1] = TMG_REVERSE;
+			lines[i].special = 343;
+			break;
 		case 400: //Set tagged sector's floor height/texture
 		case 401: //Set tagged sector's ceiling height/texture
 			lines[i].args[0] = tag;
@@ -5243,6 +5266,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			break;
 		case 433: //Enable/disable gravity flip
 			lines[i].args[0] = !!(lines[i].flags & ML_NOCLIMB);
+			lines[i].args[1] = !!(lines[i].flags & ML_SKEWTD);
 			break;
 		case 434: //Award power-up
 			if (sides[lines[i].sidenum[0]].text)
@@ -6015,6 +6039,9 @@ static void P_ConvertBinarySectorTypes(void)
 			case 5: //Speed pad
 				sectors[i].specialflags |= SSF_SPEEDPAD;
 				break;
+			case 6: //Gravity flip on jump (think VVVVVV)
+				sectors[i].specialflags |= SSF_JUMPFLIP;
+				break;
 			default:
 				break;
 		}
diff --git a/src/p_spec.c b/src/p_spec.c
index a1e2e3c5d2e81c326eb970a2e9f270ec32143b7d..82337d2f6b07aa5d664363a234695243601d2883 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1863,6 +1863,12 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			if (!P_CheckPlayerMare(triggerline))
 				return false;
 			break;
+		case 343: // gravity check
+			if (triggerline->args[1] == TMG_TEMPREVERSE && (!(actor->flags2 & MF2_OBJECTFLIP) != !(actor->player->powers[pw_gravityboots])))
+				return false;
+			if ((triggerline->args[1] == TMG_NORMAL) != !(actor->eflags & MFE_VERTICALFLIP))
+				return false;
+			break;
 		default:
 			break;
 	}
@@ -1900,7 +1906,8 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			|| specialtype == 319 // Unlockable
 			|| specialtype == 331 // Player skin
 			|| specialtype == 334 // Object dye
-			|| specialtype == 337) // Emerald check
+			|| specialtype == 337 // Emerald check
+			|| specialtype == 343) // Gravity check
 			&& triggerline->args[0] == TMT_ONCE)
 			triggerline->special = 0;
 	}
@@ -1950,7 +1957,8 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller)
 			|| lines[masterline].special == 319 // Unlockable trigger
 			|| lines[masterline].special == 331 // Player skin
 			|| lines[masterline].special == 334 // Object dye
-			|| lines[masterline].special == 337) // Emerald check
+			|| lines[masterline].special == 337 // Emerald check
+			|| lines[masterline].special == 343) // Gravity check
 			&& lines[masterline].args[0] > TMT_EACHTIMEMASK)
 			continue;
 
@@ -2774,7 +2782,9 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			break;
 
 		case 433: // Flip/flop gravity. Works on pushables, too!
-			if (line->args[0])
+			if (line->args[1])
+				mo->flags2 ^= MF2_OBJECTFLIP;
+			else if (line->args[0])
 				mo->flags2 &= ~MF2_OBJECTFLIP;
 			else
 				mo->flags2 |= MF2_OBJECTFLIP;
@@ -3827,6 +3837,9 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					sectors[secnum].flags |= MSF_GRAVITYFLIP;
 				else if (line->args[2] == TMF_REMOVE)
 					sectors[secnum].flags &= ~MSF_GRAVITYFLIP;
+
+				if (line->args[3])
+					sectors[secnum].specialflags |= SSF_GRAVITYOVERRIDE;
 			}
 		}
 		break;
@@ -6307,6 +6320,9 @@ void P_SpawnSpecials(boolean fromnetsave)
 					else
 						sectors[s].flags &= ~MSF_GRAVITYFLIP;
 
+					if (lines[i].flags & ML_EFFECT6)
+						sectors[s].specialflags |= SSF_GRAVITYOVERRIDE;
+
 					CheckForReverseGravity |= (sectors[s].flags & MSF_GRAVITYFLIP);
 				}
 				break;
@@ -6924,6 +6940,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 			case 331: // Player skin
 			case 334: // Object dye
 			case 337: // Emerald check
+			case 343: // Gravity check
 				if (lines[i].args[0] > TMT_EACHTIMEMASK)
 					P_AddEachTimeThinker(&lines[i], lines[i].args[0] == TMT_EACHTIMEENTERANDEXIT);
 				break;
diff --git a/src/p_spec.h b/src/p_spec.h
index 69c629c4783ad3680e13f25a79b75b053b9f4f07..cd97efa1a7486b5c0afb1b3ae0294fac69c1cb6b 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -254,6 +254,13 @@ typedef enum
 	TMC_GTE   = 2,
 } textmapcomparison_t;
 
+typedef enum
+{
+	TMG_NORMAL  = 0,
+	TMG_REVERSE = 1,
+	TMG_TEMPREVERSE = 2,
+} textmapgravity_t;
+
 typedef enum
 {
 	TMNP_FASTEST   = 0,
diff --git a/src/p_user.c b/src/p_user.c
index 87809255df9e3180eb86818e183caa1ebafa66b5..ed9da035cd4f640c00abbb2a2f045959b5f07797 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2494,6 +2494,41 @@ boolean P_InQuicksand(mobj_t *mo) // Returns true if you are in quicksand
 	return false; // No sand here, Captain!
 }
 
+boolean P_InJumpFlipSector(mobj_t *mo) // Returns true if you are in a jumpflip sector
+{
+	sector_t *sector = mo->subsector->sector;
+	fixed_t topheight, bottomheight;
+
+	if (sector->specialflags & SSF_JUMPFLIP)
+		return true;
+
+	if (sector->ffloors)
+	{
+		ffloor_t *rover;
+
+		for (rover = sector->ffloors; rover; rover = rover->next)
+		{
+			if (!(rover->fofflags & FOF_EXISTS))
+				continue;
+
+			if (!(rover->master->frontsector->specialflags & SSF_JUMPFLIP))
+				continue;
+			topheight    = P_GetFFloorTopZAt   (rover, mo->x, mo->y);
+			bottomheight = P_GetFFloorBottomZAt(rover, mo->x, mo->y);
+
+			if (mo->z + (mo->height/2) > topheight)
+				continue;
+
+			if (mo->z + (mo->height/2) < bottomheight)
+				continue;
+
+			return true;
+		}
+	}
+
+	return false; // No gravity jumping here, Captain Viridian!
+}
+
 static boolean P_PlayerCanBust(player_t *player, ffloor_t *rover)
 {
 	if (!(rover->fofflags & FOF_EXISTS))
@@ -2789,12 +2824,13 @@ static void P_CheckQuicksand(player_t *player)
 	fixed_t sinkspeed;
 	fixed_t topheight, bottomheight;
 
-	if (!(player->mo->subsector->sector->ffloors && player->mo->momz <= 0))
+	if (!(player->mo->subsector->sector->ffloors && P_MobjFlip(player->mo)*player->mo->momz <= 0))
 		return;
 
 	for (rover = player->mo->subsector->sector->ffloors; rover; rover = rover->next)
 	{
-		if (!(rover->fofflags & FOF_EXISTS)) continue;
+		if (!(rover->fofflags & FOF_EXISTS))
+			continue;
 
 		if (!(rover->fofflags & FOF_QUICKSAND))
 			continue;
@@ -4459,6 +4495,12 @@ void P_DoJump(player_t *player, boolean soundandstate)
 	if (player->charflags & SF_NOJUMPDAMAGE)
 		player->pflags &= ~PF_SPINNING;
 
+	if (P_InJumpFlipSector(player->mo)) // Flip gravity on jump?
+	{
+		player->mo->flags2 ^= MF2_OBJECTFLIP;
+		S_StartSound(player->mo, sfx_s3k73); // Play gravity flip sound
+	}
+
 	if (soundandstate)
 	{
 		if (!player->spectator)
@@ -7642,11 +7684,9 @@ void P_ElementalFire(player_t *player, boolean cropcircle)
 	else
 		ground = player->mo->floorz;
 
-	if (cropcircle)
-		ground += P_MobjFlip(player->mo);
-
 	if (cropcircle)
 	{
+		ground += P_MobjFlip(player->mo);
 #define numangles 8
 #define limitangle (180/numangles)
 		travelangle = player->mo->angle + P_RandomRange(-limitangle, limitangle)*ANG1;
@@ -7659,7 +7699,8 @@ void P_ElementalFire(player_t *player, boolean cropcircle)
 			flame->fuse = TICRATE*7; // takes about an extra second to hit the ground
 			flame->destscale = player->mo->scale;
 			P_SetScale(flame, player->mo->scale);
-			flame->flags2 = (flame->flags2 & ~MF2_OBJECTFLIP)|(player->mo->flags2 & MF2_OBJECTFLIP);
+			if (!(player->mo->flags2 & MF2_OBJECTFLIP) != !(player->powers[pw_gravityboots])) // take gravity boots into account
+				flame->flags2 |= MF2_OBJECTFLIP;
 			flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
 			P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale));
 			P_SetObjectMomZ(flame, 3*FRACUNIT, false);
@@ -7694,6 +7735,8 @@ void P_ElementalFire(player_t *player, boolean cropcircle)
 			flame->fuse = TICRATE*6;
 			flame->destscale = player->mo->scale;
 			P_SetScale(flame, player->mo->scale);
+			if (!(player->mo->flags2 & MF2_OBJECTFLIP) != !(player->powers[pw_gravityboots])) // take gravity boots into account
+				flame->flags2 |= MF2_OBJECTFLIP;
 			flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
 			if (!(gametyperules & GTR_FRIENDLY))
 			{
diff --git a/src/r_defs.h b/src/r_defs.h
index 6c9c91ab13125492652cc3ac3bb77bc4a5e671cc..dbede806e241819c11ece32009d2a724f9aa60e5 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -355,6 +355,8 @@ typedef enum
 	SSF_ZOOMTUBEEND = 1<<16,
 	SSF_FINISHLINE = 1<<17,
 	SSF_ROPEHANG = 1<<18,
+	SSF_JUMPFLIP = 1<<19,
+	SSF_GRAVITYOVERRIDE = 1<<20, // combine with MSF_GRAVITYFLIP
 } sectorspecialflags_t;
 
 typedef enum