diff --git a/src/dehacked.c b/src/dehacked.c
index 254f94ade0abe409d3219e7dba6c6ead24da7e21..e60bcae5d84596b3cb571a2b072dbb542d043073 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -8919,6 +8919,8 @@ static const char *const GAMETYPERULE_LIST[] = {
 	"PITYSHIELD",
 	"DEATHPENALTY",
 	"NOSPECTATORSPAWN",
+	"SPECIALSTAGES",
+	"EMERALDTOKENS",
 	"EMERALDHUNT",
 	"SPAWNENEMIES",
 	"ALLOWEXIT",
diff --git a/src/doomstat.h b/src/doomstat.h
index 459bfc88fd4408dea891ae2263eda97a5572ad0b..b79edb93ced782c02a0d1af8b494b5b600e93193 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -411,14 +411,16 @@ enum GameTypeRules
 	GTR_PITYSHIELD       = 1<<14, // Award pity shield
 	GTR_DEATHPENALTY     = 1<<15, // Death score penalty
 	GTR_NOSPECTATORSPAWN = 1<<16, // For use with GTR_SPECTATORS - spawn in the map instead of with the spectators
-	GTR_EMERALDHUNT      = 1<<17, // Emerald Hunt
-	GTR_SPAWNENEMIES     = 1<<18, // Spawn enemies
-	GTR_ALLOWEXIT        = 1<<19, // Allow exit sectors
-	GTR_ROUNDENDMESSAGE  = 1<<20, // Prints "The round has ended." into the console
-	GTR_NOTITLECARD      = 1<<21, // Don't show the title card
-	GTR_POINTLIMIT       = 1<<22, // Ringslinger point limit
-	GTR_TIMELIMIT        = 1<<23, // Ringslinger time limit
-	GTR_OVERTIME         = 1<<24, // Allow overtime
+	GTR_SPECIALSTAGES    = 1<<17, // Allow special stages
+	GTR_EMERALDTOKENS    = 1<<18, // Spawn emerald tokens
+	GTR_EMERALDHUNT      = 1<<19, // Emerald Hunt
+	GTR_SPAWNENEMIES     = 1<<20, // Spawn enemies
+	GTR_ALLOWEXIT        = 1<<21, // Allow exit sectors
+	GTR_ROUNDENDMESSAGE  = 1<<22, // Prints "The round has ended." into the console
+	GTR_NOTITLECARD      = 1<<23, // Don't show the title card
+	GTR_POINTLIMIT       = 1<<24, // Ringslinger point limit
+	GTR_TIMELIMIT        = 1<<25, // Ringslinger time limit
+	GTR_OVERTIME         = 1<<26, // Allow overtime
 };
 
 // String names for gametypes
diff --git a/src/g_game.c b/src/g_game.c
index 2675930a45796755f7d428dfcbe089a0139aa77e..74fd82be617a28e9dd0971f0e0236cfe28c6f728 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3088,9 +3088,9 @@ const char *Gametype_ConstantNames[NUMGAMETYPES] =
 UINT32 gametypedefaultrules[NUMGAMETYPES] =
 {
 	// Co-op
-	GTR_PLATFORM|GTR_LIVES|GTR_CHASECAM|GTR_EMERALDHUNT|GTR_SPAWNENEMIES|GTR_ALLOWEXIT,
+	GTR_PLATFORM|GTR_LIVES|GTR_CHASECAM|GTR_EMERALDHUNT|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES,
 	// Competition
-	GTR_PLATFORM|GTR_LIVES|GTR_RACE|GTR_CHASECAM|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_ROUNDENDMESSAGE,
+	GTR_PLATFORM|GTR_LIVES|GTR_RACE|GTR_CHASECAM|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_ROUNDENDMESSAGE|GTR_EMERALDTOKENS,
 	// Race
 	GTR_PLATFORM|GTR_RACE|GTR_CHASECAM|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_ROUNDENDMESSAGE,
 
@@ -3530,7 +3530,7 @@ static void G_DoCompleted(void)
 	if (nextmap >= 1100-1 && nextmap <= 1102-1 && (gametyperules & GTR_RACE))
 		nextmap = (INT16)(spstage_start-1);
 
-	if ((gottoken = (gametype == GT_COOP && token)))
+	if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token)))
 	{
 		token--;
 
diff --git a/src/p_inter.c b/src/p_inter.c
index 900435a99ec54c281ef05064e1e1e5fe63024f44..08e8a62a125c34a30fce0a225a5abf32dfcd25d9 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -625,7 +625,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 			P_AddPlayerScore(player, 1000);
 
-			if (gametype != GT_COOP || modeattacking) // score only?
+			if (!(gametyperules & GTR_SPECIALSTAGES) || modeattacking) // score only?
 			{
 				S_StartSound(toucher, sfx_chchng);
 				break;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 8c5497e265ba787bb60192af9234fef5253fca3e..c85d2566cd2093a3e79cba0205775c20dae56940 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11811,7 +11811,7 @@ You should think about modifying the deathmatch starts to take full advantage of
 			return; // no doubles
 	}
 
-	if (i == MT_TOKEN && ((gametype != GT_COOP && gametype != GT_COMPETITION) || tokenbits == 30 || tokenlist & (1 << tokenbits++)))
+	if (i == MT_TOKEN && (!(gametyperules & GTR_EMERALDTOKENS) || tokenbits == 30 || tokenlist & (1 << tokenbits++)))
 		return; // you already got this token, or there are too many, or the gametype's not right
 
 	if (i == MT_EMBLEM && (netgame || multiplayer || (modifiedgame && !savemoddata))) // No cheating!!