diff --git a/src/d_main.c b/src/d_main.c
index 22a6762554663a182f473efc2c7354ef824feee6..24c70843a3bd03d383651d7423914a7fd4d3f0e5 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1737,14 +1737,15 @@ void D_SRB2Main(void)
 			// Prevent warping to nonexistent levels
 			if (W_CheckNumForName(G_BuildMapName(pstartmap)) == LUMPERROR)
 				I_Error("Could not warp to %s (map not found)\n", G_BuildMapName(pstartmap));
-			// Prevent warping to locked levels
-			// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
-			// running a dedicated server and joining it yourself, but that's better than making dedicated server's
-			// lives hell.
-			else if (!dedicated && M_MapLocked(pstartmap, serverGamedata))
-				I_Error("You need to unlock this level before you can warp to it!\n");
 			else
 			{
+				if (M_CampaignWarpIsCheat(gametype, pstartmap, serverGamedata))
+				{
+					// If you're warping via command line, you know what you're doing.
+					// No need to I_Error over this.
+					G_SetUsedCheats(false);
+				}
+
 				D_MapChange(pstartmap, gametype, ultimatemode, true, 0, false, false);
 			}
 		}
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 77a271825e6abad6fa18cb3d29c19d19094a8018..6df79c7bd7d7d130b4b16f24df0a29a8f77be7b4 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1902,8 +1902,8 @@ static void Command_Map_f(void)
 	size_t option_gametype;
 	const char *gametypename;
 	boolean newresetplayers;
-
-	boolean wouldSetCheats;
+	boolean prevent_cheat;
+	boolean set_cheated;
 
 	INT32 newmapnum;
 
@@ -1924,21 +1924,33 @@ static void Command_Map_f(void)
 	option_gametype =   COM_CheckPartialParm("-g");
 	newresetplayers = ! COM_CheckParm("-noresetplayers");
 
-	wouldSetCheats =
-		!( netgame || multiplayer ) &&
-		!( usedCheats );
+	prevent_cheat = !( usedCheats ) && !( option_force || cv_debug );
 
-	if (wouldSetCheats && !option_force)
+	if (!( netgame || multiplayer ))
 	{
-		/* May want to be more descriptive? */
-		CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
-		return;
+		if (prevent_cheat)
+		{
+			/* May want to be more descriptive? */
+			CONS_Printf(M_GetText("Cheats must be enabled to level change in single player.\n"));
+			return;
+		}
+		else
+		{
+			set_cheated = true;
+		}
 	}
 
-	if (!newresetplayers && !cv_debug)
+	if (!newresetplayers)
 	{
-		CONS_Printf(M_GetText("DEVMODE must be enabled.\n"));
-		return;
+		if (prevent_cheat)
+		{
+			CONS_Printf(M_GetText("Cheats must be enabled to use -noresetplayers.\n"));
+			return;
+		}
+		else
+		{
+			set_cheated = true;
+		}
 	}
 
 	if (option_gametype)
@@ -1946,7 +1958,7 @@ static void Command_Map_f(void)
 		if (!multiplayer)
 		{
 			CONS_Printf(M_GetText(
-						"You can't switch gametypes in single player!\n"));
+				"You can't switch gametypes in single player!\n"));
 			return;
 		}
 		else if (COM_Argc() < option_gametype + 2)/* no argument after? */
@@ -1959,7 +1971,9 @@ static void Command_Map_f(void)
 	}
 
 	if (!( first_option = COM_FirstOption() ))
+	{
 		first_option = COM_Argc();
+	}
 
 	if (first_option < 2)
 	{
@@ -1982,11 +1996,6 @@ static void Command_Map_f(void)
 		return;
 	}
 
-	if (wouldSetCheats && option_force)
-	{
-		G_SetUsedCheats(false);
-	}
-
 	// new gametype value
 	// use current one by default
 	if (option_gametype)
@@ -2028,15 +2037,13 @@ static void Command_Map_f(void)
 	}
 
 	// don't use a gametype the map doesn't support
-	if (cv_debug || option_force || cv_skipmapcheck.value)
-		fromlevelselect = false; // The player wants us to trek on anyway.  Do so.
 	// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
-	else
+	if (!(
+			mapheaderinfo[newmapnum-1] &&
+			mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)
+	))
 	{
-		if (!(
-					mapheaderinfo[newmapnum-1] &&
-					mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)
-		))
+		if (prevent_cheat && !cv_skipmapcheck.value)
 		{
 			CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum),
 				(multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player"));
@@ -2046,23 +2053,33 @@ static void Command_Map_f(void)
 		}
 		else
 		{
-			fromlevelselect =
-				( netgame || multiplayer ) &&
-				newgametype == gametype    &&
-				gametypedefaultrules[newgametype] & GTR_CAMPAIGN;
+			// The player wants us to trek on anyway.  Do so.
+			fromlevelselect = false;
+			set_cheated = ((gametypedefaultrules[newgametype] & GTR_CAMPAIGN) == GTR_CAMPAIGN);
 		}
 	}
+	else
+	{
+		fromlevelselect =
+			( netgame || multiplayer ) &&
+			newgametype == gametype    &&
+			(gametypedefaultrules[newgametype] & GTR_CAMPAIGN);
+	}
 
 	// Prevent warping to locked levels
-	// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
-	// running a dedicated server and joining it yourself, but that's better than making dedicated server's
-	// lives hell.
-	if (!dedicated && M_MapLocked(newmapnum, serverGamedata))
+	if (M_CampaignWarpIsCheat(newgametype, newmapnum, serverGamedata))
 	{
-		CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
-		Z_Free(realmapname);
-		Z_Free(mapname);
-		return;
+		if (prevent_cheat)
+		{
+			CONS_Alert(CONS_NOTICE, M_GetText("Cheats must be enabled to warp to a locked level!\n"));
+			Z_Free(realmapname);
+			Z_Free(mapname);
+			return;
+		}
+		else
+		{
+			set_cheated = true;
+		}
 	}
 
 	// Ultimate Mode only in SP via menu
@@ -2079,6 +2096,11 @@ static void Command_Map_f(void)
 	}
 	tutorialmode = false; // warping takes us out of tutorial mode
 
+	if (set_cheated && !usedCheats)
+	{
+		G_SetUsedCheats(false);
+	}
+
 	D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect);
 
 	Z_Free(realmapname);
@@ -4555,25 +4577,37 @@ static void Command_Mapmd5_f(void)
 		CONS_Printf(M_GetText("You must be in a level to use this.\n"));
 }
 
+void D_SendExitLevel(boolean cheat)
+{
+	UINT8 buf[8];
+	UINT8 *buf_p = buf;
+
+	WRITEUINT8(buf_p, cheat);
+
+	SendNetXCmd(XD_EXITLEVEL, &buf, buf_p - buf);
+}
+
 static void Command_ExitLevel_f(void)
 {
-	if (!(netgame || (multiplayer && gametype != GT_COOP)) && !cv_debug)
-		CONS_Printf(M_GetText("This only works in a netgame.\n"));
-	else if (!(server || (IsPlayerAdmin(consoleplayer))))
+	if (!(server || (IsPlayerAdmin(consoleplayer))))
 		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
 	else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demoplayback)
 		CONS_Printf(M_GetText("You must be in a level to use this.\n"));
 	else
-		SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+		D_SendExitLevel(true);
 }
 
 static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
 {
-	(void)cp;
+	boolean cheat = false;
+
+	cheat = (boolean)READUINT8(*cp);
 
 	// Ignore duplicate XD_EXITLEVEL commands.
 	if (gameaction == ga_completed)
+	{
 		return;
+	}
 
 	if (playernum != serverplayer && !IsPlayerAdmin(playernum))
 	{
@@ -4583,6 +4617,11 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
 		return;
 	}
 
+	if (G_CoopGametype() && cheat)
+	{
+		G_SetUsedCheats(false);
+	}
+
 	G_ExitLevel();
 }
 
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 26bf4d5c6bfbcfe9e481b35b84f38c44360ec3fa..8bbc801d0ef700868482fd982346dff5296c5e14 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -201,6 +201,7 @@ void D_SendPlayerConfig(void);
 void Command_ExitGame_f(void);
 void Command_Retry_f(void);
 void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
+void D_SendExitLevel(boolean cheat);
 void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pultmode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect);
 boolean IsPlayerAdmin(INT32 playernum);
 void SetAdminPlayer(INT32 playernum);
diff --git a/src/f_finale.c b/src/f_finale.c
index d03795dc437c69af5f12177755f3e48af643fcad..162e87d2945cd1e778bd3ce46e3d34eb40091e35 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1644,7 +1644,7 @@ void F_GameEvaluationTicker(void)
 		sparklloop = 0;
 	}
 
-	if (finalecount == 5*TICRATE)
+	if (G_CoopGametype() && !stagefailed && finalecount == 5*TICRATE)
 	{
 		serverGamedata->timesBeaten++;
 		clientGamedata->timesBeaten++;
diff --git a/src/g_game.c b/src/g_game.c
index 04b1ffe4da0809c30e656a13ff70611febceb8ec..24a771372b1642e7353dad7a57dec4e8478b3a2a 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2137,7 +2137,7 @@ boolean G_Responder(event_t *ev)
 			if (! netgame)
 				F_StartGameEvaluation();
 			else if (server || IsPlayerAdmin(consoleplayer))
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 			return true;
 		}
 	}
@@ -3861,7 +3861,7 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
 	for (ix = 0; ix < NUMMAPS; ix++)
 		if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags
 		 && ix != pprevmap // Don't pick the same map.
-		 && (dedicated || !M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps.
+		 && (!M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps.
 		)
 			okmaps[numokmaps++] = ix;
 
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 2e3bb9c683677be05181fef6f4010d37446c8325..63a866606fe575a46e0af5168f57c072f4f8d87f 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -1487,7 +1487,6 @@ void LUA_SetHudHook(int hook, huddrawlist_h list)
 			break;
 
 		case HUD_HOOK(intermission):
-			lua_pushboolean(gL, intertype == int_spec &&
-					stagefailed);
+			lua_pushboolean(gL, stagefailed);
 	}
 }
diff --git a/src/m_cond.c b/src/m_cond.c
index 13a483ea227d0271ee8a70aa0b852f0e53aea101..3dfb1dceb2c9531d7362a2024ba512d28baf6c8a 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -467,6 +467,15 @@ UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data)
 
 UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data)
 {
+	if (dedicated)
+	{
+		// If you're in a dedicated server, every level is unlocked.
+		// Yes, technically this means you can view any level by
+		// running a dedicated server and joining it yourself, but
+		// that's better than making dedicated server's lives hell.
+		return false;
+	}
+
 	if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0)
 	{
 		return false;
@@ -480,6 +489,48 @@ UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data)
 	return false;
 }
 
+UINT8 M_CampaignWarpIsCheat(INT32 gt, INT32 mapnum, gamedata_t *data)
+{
+	if (M_MapLocked(mapnum, data) == true)
+	{
+		// Warping to locked maps is definitely always a cheat
+		return true;
+	}
+
+	if ((gametypedefaultrules[gt] & GTR_CAMPAIGN) == 0)
+	{
+		// Not a campaign, do whatever you want.
+		return false;
+	}
+
+	if (G_IsSpecialStage(mapnum))
+	{
+		// Warping to special stages is a cheat
+		return true;
+	}
+
+	if (mapheaderinfo[mapnum-1]->menuflags & LF2_HIDEINMENU)
+	{
+		// You're never allowed to warp to this level.
+		return true;
+	}
+
+	if (mapheaderinfo[mapnum-1]->menuflags & LF2_NOVISITNEEDED)
+	{
+		// You're always allowed to warp to this level.
+		return false;
+	}
+
+	if (mapnum == spstage_start)
+	{
+		// Warping to the first level is never a cheat
+		return false;
+	}
+
+	// It's only a cheat if you've never been there.
+	return (!(data->mapvisited[mapnum]));
+}
+
 INT32 M_CountEmblems(gamedata_t *data)
 {
 	INT32 found = 0, i;
diff --git a/src/m_cond.h b/src/m_cond.h
index 2be8d564be46f0c6bba94a427555fc2a50c08d05..2491a384c02aa5f34ba47933eba689811ac599d0 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -252,6 +252,7 @@ void M_SilentUpdateSkinAvailabilites(void);
 UINT8 M_AnySecretUnlocked(gamedata_t *data);
 UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data);
 UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data);
+UINT8 M_CampaignWarpIsCheat(INT32 gt, INT32 mapnum, gamedata_t *data);
 INT32 M_CountEmblems(gamedata_t *data);
 
 // Emblem shit
diff --git a/src/p_inter.c b/src/p_inter.c
index 4d22ba343893cc1245df77da80f9a973c201ab49..74ca6972fb5c8600ae9fd4361d01d4466d148e09 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2235,7 +2235,7 @@ void P_CheckTimeLimit(void)
 		}
 
 		if (server)
-			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+			D_SendExitLevel(false);
 	}
 
 	//Optional tie-breaker for Match/CTF
@@ -2298,11 +2298,11 @@ void P_CheckTimeLimit(void)
 			}
 		}
 		if (server)
-			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+			D_SendExitLevel(false);
 	}
 
 	if (server)
-		SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+		D_SendExitLevel(false);
 }
 
 /** Checks if a player's score is over the pointlimit and the round should end.
@@ -2331,7 +2331,7 @@ void P_CheckPointLimit(void)
 		if ((UINT32)cv_pointlimit.value <= redscore || (UINT32)cv_pointlimit.value <= bluescore)
 		{
 			if (server)
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 		}
 	}
 	else
@@ -2344,7 +2344,7 @@ void P_CheckPointLimit(void)
 			if ((UINT32)cv_pointlimit.value <= players[i].score)
 			{
 				if (server)
-					SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+					D_SendExitLevel(false);
 				return;
 			}
 		}
@@ -2388,7 +2388,7 @@ void P_CheckSurvivors(void)
 		{
 			CONS_Printf(M_GetText("The IT player has left the game.\n"));
 			if (server)
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 
 			return;
 		}
@@ -2408,7 +2408,7 @@ void P_CheckSurvivors(void)
 			{
 				CONS_Printf(M_GetText("All players have been tagged!\n"));
 				if (server)
-					SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+					D_SendExitLevel(false);
 			}
 
 			return;
@@ -2420,7 +2420,7 @@ void P_CheckSurvivors(void)
 		{
 			CONS_Printf(M_GetText("There are no players able to become IT.\n"));
 			if (server)
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 		}
 
 		return;
@@ -2432,7 +2432,7 @@ void P_CheckSurvivors(void)
 	{
 		CONS_Printf(M_GetText("All players have been tagged!\n"));
 		if (server)
-			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+			D_SendExitLevel(false);
 	}
 }
 
diff --git a/src/p_setup.c b/src/p_setup.c
index 0c8ea656ec9d24c469a355d91f1418debc4c4701..13187bb41cdd5f8b1d4a7ffeae7d62107c73a7de 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -8238,7 +8238,7 @@ static boolean P_LoadAddon(UINT16 numlumps)
 	{
 		CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
 		if (server)
-			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+			D_SendExitLevel(false);
 	}
 
 	return true;
diff --git a/src/p_user.c b/src/p_user.c
index cf481829879b1395d45c0eef7d95578c686010b5..84d0c0abf0789314322bc7869a25949ad54d94b5 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11758,7 +11758,7 @@ void P_PlayerThink(player_t *player)
 			if (!total || ((4*exiting)/total) >= numneeded)
 			{
 				if (server)
-					SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+					D_SendExitLevel(false);
 			}
 			else
 				player->exiting = 3;
@@ -11766,7 +11766,7 @@ void P_PlayerThink(player_t *player)
 		else
 		{
 			if (server)
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 		}
 	}
 
diff --git a/src/y_inter.c b/src/y_inter.c
index 5e071171f33670ac1ce747bfa6d880247e215567..796ced8b8954b705c7870d9ae39c1c8374b51435 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -998,8 +998,7 @@ void Y_Ticker(void)
 	if (paused || P_AutoPause())
 		return;
 
-	LUA_HookBool(intertype == int_spec && stagefailed,
-			HOOK(IntermissionThinker));
+	LUA_HookBool(stagefailed, HOOK(IntermissionThinker));
 
 	intertic++;