diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 8c754e32f9d79067dab66f686bb598f24cb5607a..b5ebad6d6be39f84b1157f56fb925b48b938c53b 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1156,7 +1156,7 @@ UINT8 CanChangeSkin(INT32 playernum)
 	// Server has skin change restrictions.
 	if (cv_restrictskinchange.value)
 	{
-		if (gametype == GT_COOP)
+		if (gametyperules & GTR_FRIENDLY)
 			return true;
 
 		// Can change skin during initial countdown.
@@ -1747,7 +1747,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
 	}
 	CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d ultmode=%d resetplayers=%d delay=%d skipprecutscene=%d\n",
 	           mapnum, newgametype, pultmode, resetplayers, delay, skipprecutscene);
-	if ((netgame || multiplayer) && !((gametype == newgametype) && (newgametype == GT_COOP)))
+	if ((netgame || multiplayer) && !((gametype == newgametype) && (gametypedefaultrules[newgametype] & GTR_CAMPAIGN)))
 		FLS = false;
 
 	if (delay != 2)
@@ -1989,7 +1989,7 @@ static void Command_Map_f(void)
 			fromlevelselect =
 				( netgame || multiplayer ) &&
 				newgametype == gametype    &&
-				newgametype == GT_COOP;
+				gametypedefaultrules[newgametype] & GTR_CAMPAIGN;
 		}
 	}
 
@@ -2779,7 +2779,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
 			players[playernum].spectator = false;
 
 			//If joining after hidetime in normal tag, default to being IT.
-			if (gametype == GT_TAG && (leveltime > (hidetime * TICRATE)))
+			if (((gametyperules & (GTR_TAG|GTR_HIDEFROZEN)) == GTR_TAG) && (leveltime > (hidetime * TICRATE)))
 			{
 				NetPacket.packet.newteam = 1; //minor hack, causes the "is it" message to be printed later.
 				players[playernum].pflags |= PF_TAGIT; //make the player IT.
@@ -3629,7 +3629,7 @@ static void PointLimit_OnChange(void)
 static void NumLaps_OnChange(void)
 {
 	// Just don't be verbose
-	if (gametype == GT_RACE)
+	if ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE)
 		CONS_Printf(M_GetText("Number of laps set to %d\n"), cv_numlaps.value);
 }
 
@@ -3743,7 +3743,7 @@ static void ExitMove_OnChange(void)
 {
 	UINT8 i;
 
-	if (!(netgame || multiplayer) || gametype != GT_COOP)
+	if (!(netgame || multiplayer) || !(gametyperules & GTR_FRIENDLY))
 		return;
 
 	if (cv_exitmove.value)
@@ -4620,7 +4620,7 @@ static void Command_ShowTime_f(void)
 
 static void BaseNumLaps_OnChange(void)
 {
-	if (gametype == GT_RACE)
+	if ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE)
 	{
 		if (cv_basenumlaps.value)
 			CONS_Printf(M_GetText("Number of laps will be changed to map defaults next round.\n"));
diff --git a/src/g_game.c b/src/g_game.c
index 73d21ad54e87f85b24b6aa933542bb436bebd460..a927f8ef22747e36f47f6646875d8ecfcab70f07 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2067,7 +2067,7 @@ boolean G_Responder(event_t *ev)
 					 && players[displayplayer].ctfteam != players[consoleplayer].ctfteam)
 						continue;
 				}
-				else if (gametype == GT_HIDEANDSEEK)
+				else if (gametyperules & GTR_HIDEFROZEN)
 				{
 					if (players[consoleplayer].pflags & PF_TAGIT)
 						continue;
@@ -2618,7 +2618,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 		S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
 	}
 
-	if (gametype == GT_COOP)
+	if (gametyperules & GTR_EMERALDHUNT)
 		P_FindEmerald(); // scan for emeralds to hunt for
 
 	// If NiGHTS, find lowest mare to start with.
@@ -2786,6 +2786,26 @@ mapthing_t *G_FindCoopStart(INT32 playernum)
 	return NULL;
 }
 
+// Find a Co-op start, or fallback into other types of starts.
+static inline mapthing_t *G_FindCoopStartOrFallback(INT32 playernum)
+{
+	mapthing_t *spawnpoint = NULL;
+	if (!(spawnpoint = G_FindCoopStart(playernum)) // find a Co-op start
+	&& !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start
+		spawnpoint = G_FindCTFStart(playernum); // fallback
+	return spawnpoint;
+}
+
+// Find a Match start, or fallback into other types of starts.
+static inline mapthing_t *G_FindMatchStartOrFallback(INT32 playernum)
+{
+	mapthing_t *spawnpoint = NULL;
+	if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start
+	&& !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start
+		spawnpoint = G_FindCoopStart(playernum); // fallback
+	return spawnpoint;
+}
+
 mapthing_t *G_FindMapStart(INT32 playernum)
 {
 	mapthing_t *spawnpoint;
@@ -2793,9 +2813,22 @@ mapthing_t *G_FindMapStart(INT32 playernum)
 	if (!playeringame[playernum])
 		return NULL;
 
+	// -- Spectators --
+	// Order in platform gametypes: Coop->DM->CTF
+	// And, with deathmatch starts: DM->CTF->Coop
+	if (players[playernum].spectator)
+	{
+		// In platform gametypes, spawn in Co-op starts first
+		// Overriden by GTR_DEATHMATCHSTARTS.
+		if (G_PlatformGametype() && !(gametyperules & GTR_DEATHMATCHSTARTS))
+			spawnpoint = G_FindCoopStartOrFallback(playernum);
+		else
+			spawnpoint = G_FindMatchStartOrFallback(playernum);
+	}
+
 	// -- CTF --
 	// Order: CTF->DM->Coop
-	if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam)
+	else if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam)
 	{
 		if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start
 		&& !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start
@@ -2804,21 +2837,13 @@ mapthing_t *G_FindMapStart(INT32 playernum)
 
 	// -- DM/Tag/CTF-spectator/etc --
 	// Order: DM->CTF->Coop
-	else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT))
-	{
-		if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start
-		&& !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start
-			spawnpoint = G_FindCoopStart(playernum); // fallback
-	}
+	else if (G_TagGametype() ? (!(players[playernum].pflags & PF_TAGIT)) : (gametyperules & GTR_DEATHMATCHSTARTS))
+		spawnpoint = G_FindMatchStartOrFallback(playernum);
 
 	// -- Other game modes --
 	// Order: Coop->DM->CTF
 	else
-	{
-		if (!(spawnpoint = G_FindCoopStart(playernum)) // find a Co-op start
-		&& !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start
-			spawnpoint = G_FindCTFStart(playernum); // fallback
-	}
+		spawnpoint = G_FindCoopStartOrFallback(playernum);
 
 	//No spawns found. ANYWHERE.
 	if (!spawnpoint)
@@ -2904,7 +2929,7 @@ void G_DoReborn(INT32 playernum)
 		return;
 	}
 
-	if (countdowntimeup || (!(netgame || multiplayer) && gametype == GT_COOP))
+	if (countdowntimeup || (!(netgame || multiplayer) && (gametyperules & GTR_CAMPAIGN)))
 		resetlevel = true;
 	else if ((G_GametypeUsesCoopLives() || G_GametypeUsesCoopStarposts()) && (netgame || multiplayer) && !G_IsSpecialStage(gamemap))
 	{
@@ -3109,7 +3134,7 @@ void G_AddPlayer(INT32 playernum)
 
 	p->height = mobjinfo[MT_PLAYER].height;
 
-	if (G_GametypeUsesLives() || ((netgame || multiplayer) && gametype == GT_COOP))
+	if (G_GametypeUsesLives() || ((netgame || multiplayer) && (gametyperules & GTR_FRIENDLY)))
 		p->lives = cv_startinglives.value;
 
 	if ((countplayers && !notexiting) || G_IsSpecialStage(gamemap))
@@ -3158,7 +3183,7 @@ void G_ExitLevel(void)
 				CV_SetValue(&cv_teamscramble, cv_scrambleonchange.value);
 		}
 
-		if (!(gametyperules & GTR_CAMPAIGN))
+		if (!(gametyperules & (GTR_FRIENDLY|GTR_CAMPAIGN)))
 			CONS_Printf(M_GetText("The round has ended.\n"));
 
 		// Remove CEcho text on round end.
@@ -3224,7 +3249,7 @@ UINT32 gametypedefaultrules[NUMGAMETYPES] =
 	// Tag
 	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
 	// Hide and Seek
-	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
+	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_HIDEFROZEN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
 
 	// CTF
 	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
@@ -3569,6 +3594,16 @@ boolean G_PlatformGametype(void)
 	return (!(gametyperules & GTR_RINGSLINGER));
 }
 
+//
+// G_CoopGametype
+//
+// Returns true if a gametype is a Co-op gametype.
+//
+boolean G_CoopGametype(void)
+{
+	return ((gametyperules & (GTR_FRIENDLY|GTR_CAMPAIGN)) == (GTR_FRIENDLY|GTR_CAMPAIGN));
+}
+
 //
 // G_TagGametype
 //
@@ -3835,7 +3870,10 @@ static void G_DoCompleted(void)
 		}
 	}
 
-	if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed))
+	// If the current gametype has no intermission screen set, then don't start it.
+	Y_DetermineIntermissionType();
+
+	if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed) || (intertype == int_none))
 	{
 		G_UpdateVisited();
 		G_AfterIntermission();
@@ -3890,7 +3928,7 @@ static void G_DoWorldDone(void)
 {
 	if (server)
 	{
-		if (gametype == GT_COOP)
+		if (gametyperules & GTR_CAMPAIGN)
 			// don't reset player between maps
 			D_MapChange(nextmap+1, gametype, ultimatemode, false, 0, false, false);
 		else
diff --git a/src/g_game.h b/src/g_game.h
index 21fa682b7c544b106b2213289fbfced2f497d7f0..fecfa4096fd37d331000ba734c70e8e47ddf5195 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -191,6 +191,7 @@ boolean G_GametypeHasTeams(void);
 boolean G_GametypeHasSpectators(void);
 boolean G_RingSlingerGametype(void);
 boolean G_PlatformGametype(void);
+boolean G_CoopGametype(void);
 boolean G_TagGametype(void);
 boolean G_CompetitionGametype(void);
 boolean G_EnoughPlayersFinished(void);
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 861c2f382f05d6f869ad6859f6cd561673fb0695..d8ea7c7a33b7655e9c1375dfb17f646df53bbf32 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -61,9 +61,6 @@ typedef struct
 // equivalent of the software renderer's vissprites
 typedef struct gr_vissprite_s
 {
-	// Doubly linked list
-	struct gr_vissprite_s *prev;
-	struct gr_vissprite_s *next;
 	float x1, x2;
 	float tz, ty;
 	//lumpnum_t patchlumpnum;
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 12f03ab94859538ede0a4690a330f144675900f3..455985ddfba7d92c36118e356a8323a335fce673 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -4179,96 +4179,45 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr)
 // --------------------------------------------------------------------------
 // Sort vissprites by distance
 // --------------------------------------------------------------------------
-static gr_vissprite_t gr_vsprsortedhead;
+gr_vissprite_t* gr_vsprorder[MAXVISSPRITES];
 
-static void HWR_SortVisSprites(void)
+// Note: For more correct transparency the transparent sprites would need to be
+// sorted and drawn together with transparent surfaces.
+static int CompareVisSprites(const void *p1, const void *p2)
 {
-	UINT32 i;
-	gr_vissprite_t *ds, *dsprev, *dsnext, *dsfirst;
-	gr_vissprite_t *best = NULL;
-	gr_vissprite_t unsorted;
-	float bestdist = 0.0f;
-	INT32 bestdispoffset = 0;
-
-	if (!gr_visspritecount)
-		return;
-
-	dsfirst = HWR_GetVisSprite(0);
-
-	for (i = 0, dsnext = dsfirst, ds = NULL; i < gr_visspritecount; i++)
-	{
-		dsprev = ds;
-		ds = dsnext;
-		if (i < gr_visspritecount - 1) dsnext = HWR_GetVisSprite(i + 1);
-
-		ds->next = dsnext;
-		ds->prev = dsprev;
-	}
-
-	// Fix first and last. ds still points to the last one after the loop
-	dsfirst->prev = &unsorted;
-	unsorted.next = dsfirst;
-	if (ds)
-		ds->next = &unsorted;
-	unsorted.prev = ds;
-
-	// pull the vissprites out by scale
-	gr_vsprsortedhead.next = gr_vsprsortedhead.prev = &gr_vsprsortedhead;
-	for (i = 0; i < gr_visspritecount; i++)
-	{
-		best = NULL;
-		for (ds = unsorted.next; ds != &unsorted; ds = ds->next)
-		{
-			if (!best || ds->tz > bestdist)
-			{
-				bestdist = ds->tz;
-				bestdispoffset = ds->dispoffset;
-				best = ds;
-			}
-			// order visprites of same scale by dispoffset, smallest first
-			else if (fabsf(ds->tz - bestdist) < 1.0E-36f && ds->dispoffset < bestdispoffset)
-			{
-				bestdispoffset = ds->dispoffset;
-				best = ds;
-			}
-		}
-		if (best)
-		{
-			best->next->prev = best->prev;
-			best->prev->next = best->next;
-			best->next = &gr_vsprsortedhead;
-			best->prev = gr_vsprsortedhead.prev;
-		}
-		gr_vsprsortedhead.prev->next = best;
-		gr_vsprsortedhead.prev = best;
-	}
-
+	gr_vissprite_t* spr1 = *(gr_vissprite_t*const*)p1;
+	gr_vissprite_t* spr2 = *(gr_vissprite_t*const*)p2;
+	int idiff;
+	float fdiff;
+	
+	// Make transparent sprites last. Comment from the previous sort implementation:
 	// Sryder:	Oh boy, while it's nice having ALL the sprites sorted properly, it fails when we bring MD2's into the
 	//			mix and they want to be translucent. So let's place all the translucent sprites and MD2's AFTER
 	//			everything else, but still ordered of course, the depth buffer can handle the opaque ones plenty fine.
 	//			We just need to move all translucent ones to the end in order
 	// TODO:	Fully sort all sprites and MD2s with walls and floors, this part will be unnecessary after that
-	best = gr_vsprsortedhead.next;
+	int transparency1 = (spr1->mobj->flags2 & MF2_SHADOW) || (spr1->mobj->frame & FF_TRANSMASK);
+	int transparency2 = (spr2->mobj->flags2 & MF2_SHADOW) || (spr2->mobj->frame & FF_TRANSMASK);
+	idiff = transparency1 - transparency2;
+	if (idiff != 0) return idiff;
+
+	fdiff = spr2->tz - spr1->tz; // this order seems correct when checking with apitrace. Back to front.
+	if (fabsf(fdiff) < 1.0E-36f)
+		return spr1->dispoffset - spr2->dispoffset; // smallest dispoffset first if sprites are at (almost) same location.
+	else if (fdiff > 0)
+		return 1;
+	else
+		return -1;
+}
+
+static void HWR_SortVisSprites(void)
+{
+	UINT32 i;
 	for (i = 0; i < gr_visspritecount; i++)
 	{
-		if ((best->mobj->flags2 & MF2_SHADOW) || (best->mobj->frame & FF_TRANSMASK))
-		{
-			if (best == gr_vsprsortedhead.next)
-			{
-				gr_vsprsortedhead.next = best->next;
-			}
-			best->prev->next = best->next;
-			best->next->prev = best->prev;
-			best->prev = gr_vsprsortedhead.prev;
-			gr_vsprsortedhead.prev->next = best;
-			gr_vsprsortedhead.prev = best;
-			ds = best;
-			best = best->next;
-			ds->next = &gr_vsprsortedhead;
-		}
-		else
-			best = best->next;
+		gr_vsprorder[i] = HWR_GetVisSprite(i);
 	}
+	qsort(gr_vsprorder, gr_visspritecount, sizeof(gr_vissprite_t*), CompareVisSprites);
 }
 
 // A drawnode is something that points to a 3D floor, 3D side, or masked
@@ -4575,52 +4524,45 @@ static void HWR_CreateDrawNodes(void)
 // added the stransform so they can be switched as drawing happenes so MD2s and sprites are sorted correctly with each other
 static void HWR_DrawSprites(void)
 {
-	if (gr_visspritecount > 0)
+	UINT32 i;
+	HWD.pfnSetSpecialState(HWD_SET_MODEL_LIGHTING, cv_grmodellighting.value);
+	for (i = 0; i < gr_visspritecount; i++)
 	{
-		gr_vissprite_t *spr;
-		HWD.pfnSetSpecialState(HWD_SET_MODEL_LIGHTING, cv_grmodellighting.value);
-
-		// draw all vissprites back to front
-		for (spr = gr_vsprsortedhead.next;
-		     spr != &gr_vsprsortedhead;
-		     spr = spr->next)
-		{
+		gr_vissprite_t *spr = gr_vsprorder[i];
 #ifdef HWPRECIP
-			if (spr->precip)
-				HWR_DrawPrecipitationSprite(spr);
-			else
+		if (spr->precip)
+			HWR_DrawPrecipitationSprite(spr);
+		else
 #endif
+		{
+			if (spr->mobj && spr->mobj->shadowscale && cv_shadow.value)
 			{
-				if (spr->mobj && spr->mobj->shadowscale && cv_shadow.value)
-				{
-					HWR_DrawDropShadow(spr->mobj, spr->mobj->shadowscale);
-				}
+				HWR_DrawDropShadow(spr->mobj, spr->mobj->shadowscale);
+			}
 
-				if (spr->mobj && spr->mobj->skin && spr->mobj->sprite == SPR_PLAY)
+			if (spr->mobj && spr->mobj->skin && spr->mobj->sprite == SPR_PLAY)
+			{
+				if (!cv_grmodels.value || md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound || md2_playermodels[(skin_t*)spr->mobj->skin-skins].scale < 0.0f)
+					HWR_DrawSprite(spr);
+				else
 				{
-					if (!cv_grmodels.value || md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound || md2_playermodels[(skin_t*)spr->mobj->skin-skins].scale < 0.0f)
+					if (!HWR_DrawModel(spr))
 						HWR_DrawSprite(spr);
-					else
-					{
-						if (!HWR_DrawModel(spr))
-							HWR_DrawSprite(spr);
-					}
 				}
+			}
+			else
+			{
+				if (!cv_grmodels.value || md2_models[spr->mobj->sprite].notfound || md2_models[spr->mobj->sprite].scale < 0.0f)
+					HWR_DrawSprite(spr);
 				else
 				{
-					if (!cv_grmodels.value || md2_models[spr->mobj->sprite].notfound || md2_models[spr->mobj->sprite].scale < 0.0f)
+					if (!HWR_DrawModel(spr))
 						HWR_DrawSprite(spr);
-					else
-					{
-						if (!HWR_DrawModel(spr))
-							HWR_DrawSprite(spr);
-					}
 				}
 			}
 		}
-
-		HWD.pfnSetSpecialState(HWD_SET_MODEL_LIGHTING, 0);
 	}
+	HWD.pfnSetSpecialState(HWD_SET_MODEL_LIGHTING, 0);
 }
 
 // --------------------------------------------------------------------------
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 6f2319a50cc4eb83e5aaccd77f7c9f2d81313b9c..7f84e9875b7c0b1f490b259cf4fe9c68445a9ee0 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2102,7 +2102,7 @@ void HU_Drawer(void)
 		{
 			if (LUA_HudEnabled(hud_rankings))
 				HU_DrawRankings();
-			if (gametype == GT_COOP)
+			if (gametyperules & GTR_CAMPAIGN)
 				HU_DrawNetplayCoopOverlay();
 		}
 		else
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index d460f8cf715c9667d74ed3595050d0d518c2dd41..db70a11a23489941a7190835bd57890438e8d6b8 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -3103,6 +3103,14 @@ static int lib_gPlatformGametype(lua_State *L)
 	return 1;
 }
 
+static int lib_gCoopGametype(lua_State *L)
+{
+	//HUDSAFE
+	INLEVEL
+	lua_pushboolean(L, G_CoopGametype());
+	return 1;
+}
+
 static int lib_gTagGametype(lua_State *L)
 {
 	//HUDSAFE
@@ -3386,6 +3394,7 @@ static luaL_Reg lib[] = {
 	{"G_GametypeHasSpectators",lib_gGametypeHasSpectators},
 	{"G_RingSlingerGametype",lib_gRingSlingerGametype},
 	{"G_PlatformGametype",lib_gPlatformGametype},
+	{"G_CoopGametype",lib_gCoopGametype},
 	{"G_TagGametype",lib_gTagGametype},
 	{"G_CompetitionGametype",lib_gCompetitionGametype},
 	{"G_TicsToHours",lib_gTicsToHours},
diff --git a/src/p_enemy.c b/src/p_enemy.c
index a9c1602549b1e30796977c58b7c067d0e3b8c03f..a38496d99e90be03559e53545cfc3207c1c89445 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -4950,7 +4950,7 @@ void A_ThrownRing(mobj_t *actor)
 				continue;
 
 			// Don't home in on teammates.
-			if ((gametyperules & GTR_TEAMFLAGS)
+			if ((gametyperules & GTR_TEAMS)
 				&& actor->target->player->ctfteam == player->ctfteam)
 				continue;
 		}
@@ -6523,7 +6523,7 @@ void A_OldRingExplode(mobj_t *actor) {
 
 		if (changecolor)
 		{
-			if (!(gametyperules & GTR_TEAMFLAGS))
+			if (!(gametyperules & GTR_TEAMS))
 				mo->color = actor->target->color; //copy color
 			else if (actor->target->player->ctfteam == 2)
 				mo->color = skincolor_bluering;
@@ -6539,7 +6539,7 @@ void A_OldRingExplode(mobj_t *actor) {
 
 	if (changecolor)
 	{
-		if (!(gametyperules & GTR_TEAMFLAGS))
+		if (!(gametyperules & GTR_TEAMS))
 			mo->color = actor->target->color; //copy color
 		else if (actor->target->player->ctfteam == 2)
 			mo->color = skincolor_bluering;
@@ -6554,7 +6554,7 @@ void A_OldRingExplode(mobj_t *actor) {
 
 	if (changecolor)
 	{
-		if (!(gametyperules & GTR_TEAMFLAGS))
+		if (!(gametyperules & GTR_TEAMS))
 			mo->color = actor->target->color; //copy color
 		else if (actor->target->player->ctfteam == 2)
 			mo->color = skincolor_bluering;
diff --git a/src/p_inter.c b/src/p_inter.c
index 052ecc0da78e741ecaf487b3f96bc76c7e61332d..b01d40c004ecc7a31c0729b4746b51021796dede 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2278,7 +2278,7 @@ void P_CheckSurvivors(void)
 	if (!taggers) //If there are no taggers, pick a survivor at random to be it.
 	{
 		// Exception for hide and seek. If a round has started and the IT player leaves, end the round.
-		if (gametype == GT_HIDEANDSEEK && (leveltime >= (hidetime * TICRATE)))
+		if ((gametyperules & GTR_HIDEFROZEN) && (leveltime >= (hidetime * TICRATE)))
 		{
 			CONS_Printf(M_GetText("The IT player has left the game.\n"));
 			if (server)
@@ -2516,7 +2516,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 			}
 			P_RestoreMusic(target->player);
 
-			if (gametype != GT_COOP)
+			if (!G_CoopGametype())
 			{
 				HU_SetCEchoFlags(0);
 				HU_SetCEchoDuration(5);
@@ -2594,7 +2594,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 			// allow them to try again, rather than sitting the whole thing out.
 			if (leveltime >= hidetime * TICRATE)
 			{
-				if (gametype == GT_TAG)//suiciding in survivor makes you IT.
+				if (!(gametyperules & GTR_HIDEFROZEN))//suiciding in survivor makes you IT.
 				{
 					target->player->pflags |= PF_TAGIT;
 					CONS_Printf(M_GetText("%s is now IT!\n"), player_names[target->player-players]); // Tell everyone who is it!
@@ -3088,7 +3088,7 @@ static boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, IN
 		P_AddPlayerScore(source->player, 100); //award points to tagger.
 		P_HitDeathMessages(player, inflictor, source, 0);
 
-		if (gametype == GT_TAG) //survivor
+		if (!(gametyperules & GTR_HIDEFROZEN)) //survivor
 		{
 			player->pflags |= PF_TAGIT; //in survivor, the player becomes IT and helps hunt down the survivors.
 			CONS_Printf(M_GetText("%s is now IT!\n"), player_names[player-players]); // Tell everyone who is it!
@@ -3149,7 +3149,7 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou
 		// In COOP/RACE, you can't hurt other players unless cv_friendlyfire is on
 		if (!(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)) && (gametyperules & GTR_FRIENDLY))
 		{
-			if (gametype == GT_COOP && inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) // co-op only
+			if ((gametyperules & GTR_FRIENDLY) && inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) // co-op only
 			{
 				if (player->revitem != MT_LHRT && player->spinitem != MT_LHRT && player->thokitem != MT_LHRT) // Healers do not get to heal other healers.
 				{
@@ -3248,7 +3248,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
 	}
 
 	// If the player was super, tell them he/she ain't so super nomore.
-	if (gametype != GT_COOP && player->powers[pw_super])
+	if (!G_CoopGametype() && player->powers[pw_super])
 	{
 		S_StartSound(NULL, sfx_s3k66); //let all players hear it.
 		HU_SetCEchoFlags(0);
@@ -3610,7 +3610,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 				if (source == target)
 					return false; // Don't hit yourself with your own paraloop, baka
 				if (source && source->player && !(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE))
-				&& (gametype == GT_COOP
+				&& ((gametyperules & GTR_FRIENDLY)
 				|| (G_GametypeHasTeams() && player->ctfteam == source->player->ctfteam)))
 					return false; // Don't run eachother over in special stages and team games and such
 			}
diff --git a/src/p_map.c b/src/p_map.c
index 00b8761c883d36e49371a7cd65eea4f15760d16f..18d6a18530535a4e838c16e41de5940b7f0a9e60 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -1588,7 +1588,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	}
 
 	// Force solid players in hide and seek to avoid corner stacking.
-	if (cv_tailspickup.value && gametype != GT_HIDEANDSEEK)
+	if (cv_tailspickup.value && !(gametyperules & GTR_HIDEFROZEN))
 	{
 		if (tmthing->player && thing->player)
 		{
diff --git a/src/p_mobj.c b/src/p_mobj.c
index f690ad89465d9b47026226ba98880e7b6db2e170..48d67d69c50eb7db6ff8b3f02c16f2472272c897 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11309,7 +11309,7 @@ void P_SpawnPlayer(INT32 playernum)
 	if (!G_GametypeHasSpectators())
 	{
 		p->spectator = p->outofcoop =
-		(((multiplayer || netgame) && gametype == GT_COOP) // only question status in coop
+		(((multiplayer || netgame) && G_CoopGametype()) // only question status in coop
 		&& ((leveltime > 0
 		&& ((G_IsSpecialStage(gamemap)) // late join special stage
 		|| (cv_coopstarposts.value == 2 && (p->jointime < 1 || p->outofcoop)))) // late join or die in new coop
@@ -11768,7 +11768,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
 	case MT_EMERALD5:
 	case MT_EMERALD6:
 	case MT_EMERALD7:
-		if (gametype != GT_COOP) // Don't place emeralds in non-coop modes
+		if (!G_CoopGametype()) // Don't place emeralds in non-coop modes
 			return false;
 
 		if (metalrecording)
@@ -11788,7 +11788,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
 		runemeraldmanager = true;
 		break;
 	case MT_ROSY:
-		if (!(gametype == GT_COOP || (mthing->options & MTF_EXTRA)))
+		if (!(G_CoopGametype() || (mthing->options & MTF_EXTRA)))
 			return false; // she doesn't hang out here
 
 		if (!mariomode && !(netgame || multiplayer) && players[consoleplayer].skin == 3)
@@ -11903,7 +11903,7 @@ static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i)
 			}
 		}
 		// Set powerup boxes to user settings for other netplay modes
-		else if (gametype != GT_COOP)
+		else if (!G_CoopGametype())
 		{
 			switch (cv_matchboxes.value)
 			{
diff --git a/src/p_setup.c b/src/p_setup.c
index ffb68b1b04f0713a50eb99a6f810ee6f6ad1ac20..5027bab0c6cdb93bdd5d4381fec15d150e6bdc5f 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3474,7 +3474,7 @@ static void P_InitGametype(void)
 
 	if (G_TagGametype())
 		P_InitTagGametype();
-	else if (gametype == GT_RACE && server)
+	else if (((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE) && server)
 		CV_StealthSetValue(&cv_numlaps,
 		(cv_basenumlaps.value)
 			? cv_basenumlaps.value
diff --git a/src/p_spec.c b/src/p_spec.c
index 7708f2f1efe196c4b4f22308d9dd1f6da3abfe95..499efd899d507ff8967d31297ca4ad445e05d146 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -4078,7 +4078,7 @@ void P_SetupSignExit(player_t *player)
 
 		if (!numfound
 			&& !(player->mo->target && player->mo->target->type == MT_SIGN)
-			&& !(gametype == GT_COOP && (netgame || multiplayer) && cv_exitmove.value))
+			&& !((gametyperules & GTR_FRIENDLY) && (netgame || multiplayer) && cv_exitmove.value))
 				P_SetTarget(&player->mo->target, thing);
 
 		if (thing->state != &states[thing->info->spawnstate])
@@ -4109,7 +4109,7 @@ void P_SetupSignExit(player_t *player)
 
 		if (!numfound
 			&& !(player->mo->target && player->mo->target->type == MT_SIGN)
-			&& !(gametype == GT_COOP && (netgame || multiplayer) && cv_exitmove.value))
+			&& !((gametyperules & GTR_FRIENDLY) && (netgame || multiplayer) && cv_exitmove.value))
 				P_SetTarget(&player->mo->target, thing);
 
 		if (thing->state != &states[thing->info->spawnstate])
@@ -4465,7 +4465,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
 					continue;
 				if (players[i].bot)
 					continue;
-				if (gametype == GT_COOP && players[i].lives <= 0)
+				if (G_CoopGametype() && players[i].lives <= 0)
 					continue;
 				if (roversector)
 				{
@@ -4691,7 +4691,7 @@ DoneSection2:
 				// FOF custom exits not to work.
 				lineindex = P_FindSpecialLineFromTag(2, sector->tag, -1);
 
-				if (gametype == GT_COOP && lineindex != -1) // Custom exit!
+				if (G_CoopGametype() && lineindex != -1) // Custom exit!
 				{
 					// Special goodies with the block monsters flag depending on emeralds collected
 					if ((lines[lineindex].flags & ML_BLOCKMONSTERS) && ALL7EMERALDS(emeralds))
@@ -4925,7 +4925,7 @@ DoneSection2:
 			break;
 
 		case 10: // Finish Line
-			if (gametype == GT_RACE && !player->exiting)
+			if (((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE) && !player->exiting)
 			{
 				if (player->starpostnum == numstarposts) // Must have touched all the starposts
 				{
@@ -6240,7 +6240,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 		switch(GETSECSPECIAL(sector->special, 4))
 		{
 			case 10: // Circuit finish line
-				if (gametype == GT_RACE)
+				if ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE)
 					circuitmap = true;
 				break;
 		}
diff --git a/src/p_tick.c b/src/p_tick.c
index 1f1331856fd494c153bcf780d379843ae60f2f21..f84ae96c0b5d860dfe26447c0403676536453fec 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -692,7 +692,7 @@ void P_Ticker(boolean run)
 
 	if (run)
 	{
-		if (countdowntimer && G_PlatformGametype() && (gametype == GT_COOP || leveltime >= 4*TICRATE) && !stoppedclock && --countdowntimer <= 0)
+		if (countdowntimer && G_PlatformGametype() && ((gametyperules & GTR_CAMPAIGN) || leveltime >= 4*TICRATE) && !stoppedclock && --countdowntimer <= 0)
 		{
 			countdowntimer = 0;
 			countdowntimeup = true;
diff --git a/src/p_user.c b/src/p_user.c
index f21da1564c5dd59235a90eef6f549ab8027207f8..aa51414725b6c6abc663ea24767f265c3219d318 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -1051,7 +1051,8 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
 
 	// Point penalty for hitting a hazard during tag.
 	// Discourages players from intentionally hurting themselves to avoid being tagged.
-	if (gametype == GT_TAG && (!(player->pflags & PF_GAMETYPEOVER) && !(player->pflags & PF_TAGIT)))
+	if (((gametyperules & (GTR_TAG|GTR_HIDEFROZEN)) == GTR_TAG)
+	&& (!(player->pflags & PF_GAMETYPEOVER) && !(player->pflags & PF_TAGIT)))
 	{
 		if (player->score >= 50)
 			player->score -= 50;
@@ -1351,7 +1352,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
 		player->powers[pw_sneakers] = 0;
 	}
 
-	if (gametype != GT_COOP)
+	if (!G_CoopGametype())
 	{
 		HU_SetCEchoFlags(0);
 		HU_SetCEchoDuration(5);
@@ -1418,7 +1419,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
 			}
 		}
 
-		if (gametype == GT_COOP)
+		if (G_CoopGametype())
 			return;
 	}
 
@@ -1437,7 +1438,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
 	}
 
 	// In team match, all awarded points are incremented to the team's running score.
-	if (gametype == GT_TEAMMATCH)
+	if ((gametyperules & (GTR_TEAMS|GTR_TEAMFLAGS)) == GTR_TEAMS)
 	{
 		if (player->ctfteam == 1)
 			redscore += amount;
@@ -1471,7 +1472,7 @@ void P_StealPlayerScore(player_t *player, UINT32 amount)
 	if (stolen > 0)
 	{
 		// In team match, all stolen points are removed from the enemy team's running score.
-		if (gametype == GT_TEAMMATCH)
+		if ((gametyperules & (GTR_TEAMS|GTR_TEAMFLAGS)) == GTR_TEAMS)
 		{
 			if (player->ctfteam == 1)
 				bluescore -= amount;
@@ -3621,7 +3622,7 @@ static boolean PIT_CheckSolidsTeeter(mobj_t *thing)
 	if (thing == teeterer)
 		return true;
 
-	if (thing->player && cv_tailspickup.value && gametype != GT_HIDEANDSEEK)
+	if (thing->player && cv_tailspickup.value && !(gametyperules & GTR_HIDEFROZEN))
 		return true;
 
 	blockdist = teeterer->radius + thing->radius;
@@ -4233,7 +4234,7 @@ static void P_DoSuperStuff(player_t *player)
 				G_GhostAddColor(GHC_NORMAL);
 			}
 
-			if (gametype != GT_COOP)
+			if (!G_CoopGametype())
 			{
 				HU_SetCEchoFlags(0);
 				HU_SetCEchoDuration(5);
@@ -4283,14 +4284,14 @@ static void P_DoSuperStuff(player_t *player)
 				G_GhostAddColor(GHC_NORMAL);
 			}
 
-			if (gametype != GT_COOP)
+			if (!G_CoopGametype())
 				player->powers[pw_flashing] = flashingtics-1;
 
 			if (player->mo->sprite2 & FF_SPR2SUPER)
 				P_SetPlayerMobjState(player->mo, player->mo->state-states);
 
 			// Inform the netgame that the champion has fallen in the heat of battle.
-			if (gametype != GT_COOP)
+			if (!G_CoopGametype())
 			{
 				S_StartSound(NULL, sfx_s3k66); //let all players hear it.
 				HU_SetCEchoFlags(0);
@@ -7905,7 +7906,7 @@ static void P_MovePlayer(player_t *player)
 			if (player->pflags & PF_TAGIT)
 				forcestasis = true;
 		}
-		else if (gametype == GT_HIDEANDSEEK)
+		else if (gametyperules & GTR_HIDEFROZEN)
 		{
 			if (!(player->pflags & PF_TAGIT))
 			{
@@ -9433,7 +9434,7 @@ static void P_DeathThink(player_t *player)
 	}
 
 	if ((cv_cooplives.value != 1)
-	&& (gametype == GT_COOP)
+	&& (G_GametypeUsesCoopLives())
 	&& (netgame || multiplayer)
 	&& (player->lives <= 0))
 	{
@@ -9515,17 +9516,17 @@ static void P_DeathThink(player_t *player)
 					countdown2 = 1*TICRATE;
 			}
 		}
-		//else if (gametype == GT_COOP) -- moved to G_DoReborn
+		//else if (G_CoopGametype()) -- moved to G_DoReborn
 	}
 
-	if (gametype == GT_COOP && (multiplayer || netgame) && (player->lives <= 0) && (player->deadtimer >= 8*TICRATE || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE))))
+	if (G_CoopGametype() && (multiplayer || netgame) && (player->lives <= 0) && (player->deadtimer >= 8*TICRATE || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE))))
 	{
 		//player->spectator = true;
 		player->outofcoop = true;
 		player->playerstate = PST_REBORN;
 	}
 
-	if ((gametyperules & GTR_RACE) || (gametype == GT_COOP && (multiplayer || netgame)))
+	if ((gametyperules & GTR_RACE) || (G_CoopGametype() && (multiplayer || netgame)))
 	{
 		// Keep time rolling in race mode
 		if (!(countdown2 && !countdown) && !player->exiting && !(player->pflags & PF_GAMETYPEOVER) && !stoppedclock)
@@ -9542,7 +9543,7 @@ static void P_DeathThink(player_t *player)
 		}
 
 		// Return to level music
-		if (gametype != GT_COOP && player->lives <= 0 && player->deadtimer == gameovertics)
+		if (!G_CoopGametype() && player->lives <= 0 && player->deadtimer == gameovertics)
 			P_RestoreMultiMusic(player);
 	}
 
@@ -9719,7 +9720,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 	if (player->exiting)
 	{
 		if (mo->target && mo->target->type == MT_SIGN && mo->target->spawnpoint
-		&& !(gametype == GT_COOP && (netgame || multiplayer) && cv_exitmove.value)
+		&& !((gametyperules & GTR_FRIENDLY) && (netgame || multiplayer) && cv_exitmove.value)
 		&& !(twodlevel || (mo->flags2 & MF2_TWOD)))
 			sign = mo->target;
 		else if ((player->powers[pw_carry] == CR_NIGHTSMODE)
@@ -10388,7 +10389,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 
 boolean P_SpectatorJoinGame(player_t *player)
 {
-	if (gametype != GT_COOP && !cv_allowteamchange.value)
+	if (!G_CoopGametype() && !cv_allowteamchange.value)
 	{
 		if (P_IsLocalPlayer(player))
 			CONS_Printf(M_GetText("Server does not allow team change.\n"));
@@ -10470,7 +10471,7 @@ boolean P_SpectatorJoinGame(player_t *player)
 			player->spectator = player->outofcoop = false;
 			player->playerstate = PST_REBORN;
 
-			if (gametype == GT_TAG)
+			if ((gametyperules & (GTR_TAG|GTR_HIDEFROZEN)) == GTR_TAG)
 			{
 				//Make joining players "it" after hidetime.
 				if (leveltime > (hidetime * TICRATE))
@@ -10491,7 +10492,7 @@ boolean P_SpectatorJoinGame(player_t *player)
 				displayplayer = consoleplayer;
 			}
 
-			if (gametype != GT_COOP)
+			if (!G_CoopGametype())
 				CONS_Printf(M_GetText("%s entered the game.\n"), player_names[player-players]);
 			return true; // no more player->mo, cannot continue.
 		}
@@ -11518,7 +11519,7 @@ void P_PlayerThink(player_t *player)
 	// so we can fade music
 	if (!exitfadestarted &&
 		player->exiting > 0 && player->exiting <= 1*TICRATE &&
-		(!multiplayer || gametype == GT_COOP ? !mapheaderinfo[gamemap-1]->musinterfadeout : true) &&
+		(!multiplayer || G_CoopGametype() ? !mapheaderinfo[gamemap-1]->musinterfadeout : true) &&
 			// don't fade if we're fading during intermission. follows Y_StartIntermission intertype = int_coop
 		((gametyperules & GTR_RACE) ? countdown2 == 0 : true) && // don't fade on timeout
 		player->lives > 0 && // don't fade on game over (competition)
@@ -11591,7 +11592,7 @@ void P_PlayerThink(player_t *player)
 
 	if (player->pflags & PF_FINISHED)
 	{
-		if ((gametype == GT_COOP && cv_exitmove.value) && !G_EnoughPlayersFinished())
+		if (((gametyperules & GTR_FRIENDLY) && cv_exitmove.value) && !G_EnoughPlayersFinished())
 			player->exiting = 0;
 		else
 			P_DoPlayerExit(player);
@@ -11625,10 +11626,10 @@ void P_PlayerThink(player_t *player)
 	// Make sure spectators always have a score and ring count of 0.
 	if (player->spectator)
 	{
-		if (gametype != GT_COOP)
+		if (!(gametyperules & GTR_CAMPAIGN))
 			player->score = 0;
 	}
-	else if ((netgame || multiplayer) && player->lives <= 0 && gametype != GT_COOP)
+	else if ((netgame || multiplayer) && player->lives <= 0 && !G_CoopGametype())
 	{
 		// Outside of Co-Op, replenish a user's lives if they are depleted.
 		// of course, this is just a cheap hack, meh...
@@ -12492,7 +12493,7 @@ void P_PlayerAfterThink(player_t *player)
 					player->mo->momz = tails->momz;
 				}
 
-				if (gametype == GT_COOP && (!tails->player || tails->player->bot != 1))
+				if (G_CoopGametype() && (!tails->player || tails->player->bot != 1))
 				{
 					player->mo->angle = tails->angle;
 
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 621acb4894e100fc06117c5c72071a9fc778cc20..3ab9a5eabaa949fba14d69d4ef48677f34095393 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2215,7 +2215,7 @@ static void ST_drawTextHUD(void)
 	if (F_GetPromptHideHud(y))
 		return;
 
-	if (stplyr->spectator && (gametype != GT_COOP || stplyr->playerstate == PST_LIVE))
+	if (stplyr->spectator && (!G_CoopGametype() || stplyr->playerstate == PST_LIVE))
 		textHUDdraw(M_GetText("\x86""Spectator mode:"))
 
 	if (circuitmap)
@@ -2226,7 +2226,7 @@ static void ST_drawTextHUD(void)
 			textHUDdraw(va("Lap:""\x82 %u/%d", stplyr->laps+1, cv_numlaps.value))
 	}
 
-	if (gametype != GT_COOP && (stplyr->exiting || (G_GametypeUsesLives() && stplyr->lives <= 0 && countdown != 1)))
+	if (!G_CoopGametype() && (stplyr->exiting || (G_GametypeUsesLives() && stplyr->lives <= 0 && countdown != 1)))
 	{
 		if (!splitscreen && !donef12)
 		{
@@ -2243,7 +2243,7 @@ static void ST_drawTextHUD(void)
 		else
 			textHUDdraw(M_GetText("\x82""JUMP:""\x80 Respawn"))
 	}
-	else if (stplyr->spectator && (gametype != GT_COOP || stplyr->playerstate == PST_LIVE))
+	else if (stplyr->spectator && (!G_CoopGametype() || stplyr->playerstate == PST_LIVE))
 	{
 		if (!splitscreen && !donef12)
 		{
@@ -2290,7 +2290,7 @@ static void ST_drawTextHUD(void)
 			textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game"))
 	}
 
-	if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && (stplyr->exiting || (stplyr->pflags & PF_FINISHED)))
+	if (G_CoopGametype() && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && (stplyr->exiting || (stplyr->pflags & PF_FINISHED)))
 	{
 		UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value);
 		if (numneeded)
@@ -2339,20 +2339,19 @@ static void ST_drawTextHUD(void)
 					textHUDdraw(M_GetText("\x82""You are blindfolded!"))
 				textHUDdraw(M_GetText("Waiting for players to hide..."))
 			}
-			else if (gametype == GT_HIDEANDSEEK)
+			else if (gametyperules & GTR_HIDEFROZEN)
 				textHUDdraw(M_GetText("Hide before time runs out!"))
 			else
 				textHUDdraw(M_GetText("Flee before you are hunted!"))
 		}
-		else if (gametype == GT_HIDEANDSEEK && !(stplyr->pflags & PF_TAGIT))
+		else if ((gametyperules & GTR_HIDEFROZEN) && !(stplyr->pflags & PF_TAGIT))
 		{
 			if (!splitscreen && !donef12)
 			{
 				textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
 				donef12 = true;
 			}
-			if (gametyperules & GTR_HIDEFROZEN)
-				textHUDdraw(M_GetText("You cannot move while hiding."))
+			textHUDdraw(M_GetText("You cannot move while hiding."))
 		}
 	}
 
@@ -2628,11 +2627,11 @@ static void ST_overlayDrawer(void)
 	}
 
 	// GAME OVER hud
-	if ((gametype == GT_COOP)
+	if (G_GametypeUsesCoopLives()
 		&& (netgame || multiplayer)
 		&& (cv_cooplives.value == 0))
 	;
-	else if ((G_GametypeUsesLives() || gametype == GT_RACE) && stplyr->lives <= 0 && !(hu_showscores && (netgame || multiplayer)))
+	else if ((G_GametypeUsesLives() || ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE)) && stplyr->lives <= 0 && !(hu_showscores && (netgame || multiplayer)))
 	{
 		INT32 i = MAXPLAYERS;
 		INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1));
diff --git a/src/y_inter.c b/src/y_inter.c
index c32182518bfc15519cb5386503afb93660620a20..cd197c5cfd589b99b1d20dd7556c300f28be4aba 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -227,8 +227,7 @@ static void Y_IntermissionTokenDrawer(void)
 //
 // Y_ConsiderScreenBuffer
 //
-// Can we copy the current screen
-// to a buffer?
+// Can we copy the current screen to a buffer?
 //
 void Y_ConsiderScreenBuffer(void)
 {
@@ -254,9 +253,7 @@ void Y_ConsiderScreenBuffer(void)
 //
 // Y_RescaleScreenBuffer
 //
-// Write the rescaled source picture,
-// to the destination picture that
-// has the current screen's resolutions.
+// Write the rescaled source picture, to the destination picture that has the current screen's resolutions.
 //
 static void Y_RescaleScreenBuffer(void)
 {
@@ -326,13 +323,6 @@ void Y_IntermissionDrawer(void)
 	if (rendermode == render_none)
 		return;
 
-	if (intertype == int_none)
-	{
-		LUAh_IntermissionHUD();
-		return;
-	}
-
-	if (!usebuffer)
 	// Lactozilla: Renderer switching
 	if (needpatchrecache)
 	{
@@ -373,11 +363,11 @@ void Y_IntermissionDrawer(void)
 		{
 			if (widebgpatch && rendermode == render_soft && vid.width / vid.dupx == 400)
 				V_DrawScaledPatch(0, 0, V_SNAPTOLEFT, widebgpatch);
-			else
+			else if (bgpatch)
 				V_DrawScaledPatch(0, 0, 0, bgpatch);
 		}
 	}
-	else
+	else if (bgtile)
 		V_DrawPatchFill(bgtile);
 
 	LUAh_IntermissionHUD();
@@ -1186,6 +1176,34 @@ void Y_Ticker(void)
 	}
 }
 
+//
+// Y_DetermineIntermissionType
+//
+// Determines the intermission type from the current gametype.
+//
+void Y_DetermineIntermissionType(void)
+{
+	// set to int_none initially
+	intertype = int_none;
+
+	if (intermissiontypes[gametype] != int_none)
+		intertype = intermissiontypes[gametype];
+	else if (gametype == GT_COOP)
+		intertype = (G_IsSpecialStage(gamemap)) ? int_spec : int_coop;
+	else if (gametype == GT_TEAMMATCH)
+		intertype = int_teammatch;
+	else if (gametype == GT_MATCH
+	 || gametype == GT_TAG
+	 || gametype == GT_HIDEANDSEEK)
+		intertype = int_match;
+	else if (gametype == GT_RACE)
+		intertype = int_race;
+	else if (gametype == GT_COMPETITION)
+		intertype = int_comp;
+	else if (gametype == GT_CTF)
+		intertype = int_ctf;
+}
+
 //
 // Y_StartIntermission
 //
@@ -1207,12 +1225,11 @@ void Y_StartIntermission(void)
 	if (!multiplayer)
 	{
 		timer = 0;
-
 		intertype = (G_IsSpecialStage(gamemap)) ? int_spec : int_coop;
 	}
 	else
 	{
-		if (cv_inttime.value == 0 && gametype == GT_COOP)
+		if (cv_inttime.value == 0 && ((intertype == int_coop) || (intertype == int_spec)))
 			timer = 0;
 		else
 		{
@@ -1221,23 +1238,6 @@ void Y_StartIntermission(void)
 			if (!timer)
 				timer = 1;
 		}
-
-		if (intermissiontypes[gametype] != int_none)
-			intertype = intermissiontypes[gametype];
-		else if (gametype == GT_COOP)
-			intertype = (G_IsSpecialStage(gamemap)) ? int_spec : int_coop;
-		else if (gametype == GT_TEAMMATCH)
-			intertype = int_teammatch;
-		else if (gametype == GT_MATCH
-		 || gametype == GT_TAG
-		 || gametype == GT_HIDEANDSEEK)
-			intertype = int_match;
-		else if (gametype == GT_RACE)
-			intertype = int_race;
-		else if (gametype == GT_COMPETITION)
-			intertype = int_comp;
-		else if (gametype == GT_CTF)
-			intertype = int_ctf;
 	}
 
 	// We couldn't display the intermission even if we wanted to.
diff --git a/src/y_inter.h b/src/y_inter.h
index 855844fcccfd4ebfed5b6789d66140d182ea04b1..859144b1d4ad71f7a98a797327537a213e0970b2 100644
--- a/src/y_inter.h
+++ b/src/y_inter.h
@@ -13,11 +13,15 @@ extern boolean usebuffer;
 
 void Y_IntermissionDrawer(void);
 void Y_Ticker(void);
+
 void Y_StartIntermission(void);
 void Y_EndIntermission(void);
+
 void Y_ConsiderScreenBuffer(void);
 void Y_CleanupScreenBuffer(void);
 
+void Y_DetermineIntermissionType(void);
+
 typedef enum
 {
 	int_none,