From ce3a15d0396c56d561880ab0a554fb607797aefa Mon Sep 17 00:00:00 2001
From: TimePath <andrew.hardaker1995@gmail.com>
Date: Sun, 18 Oct 2015 16:28:48 +1100
Subject: [PATCH] Gametypes: invert control

---
 qcsrc/common/mapinfo.qc                       |  5 ++
 qcsrc/common/mutators/base.qh                 |  4 +-
 qcsrc/server/mutators/all.qc                  |  6 +-
 qcsrc/server/mutators/all.qh                  |  3 +
 .../mutators/mutator/gamemode_assault.qc      | 57 ++++++-------
 qcsrc/server/mutators/mutator/gamemode_ca.qc  | 56 +++++++------
 qcsrc/server/mutators/mutator/gamemode_ctf.qc | 61 +++++++-------
 qcsrc/server/mutators/mutator/gamemode_cts.qc | 61 +++++++-------
 .../mutators/mutator/gamemode_deathmatch.qc   | 21 +++--
 .../mutators/mutator/gamemode_domination.qc   | 63 ++++++++-------
 .../mutators/mutator/gamemode_freezetag.qc    | 69 ++++++++--------
 .../mutators/mutator/gamemode_invasion.qc     | 81 ++++++++++---------
 .../mutators/mutator/gamemode_keepaway.qc     | 52 ++++++------
 .../mutators/mutator/gamemode_keyhunt.qc      | 67 +++++++--------
 qcsrc/server/mutators/mutator/gamemode_lms.qc | 56 +++++++------
 .../mutators/mutator/gamemode_onslaught.qc    | 61 +++++++-------
 .../server/mutators/mutator/gamemode_race.qc  | 57 ++++++-------
 qcsrc/server/mutators/mutator/gamemode_tdm.qc | 70 +++++++++-------
 qcsrc/server/race.qh                          |  2 +
 qcsrc/server/teamplay.qh                      |  2 +
 20 files changed, 461 insertions(+), 393 deletions(-)

diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc
index 46c2edc9e3..2faf4b7ec9 100644
--- a/qcsrc/common/mapinfo.qc
+++ b/qcsrc/common/mapinfo.qc
@@ -640,10 +640,12 @@ void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType)
 			case "timelimit":
 			{
 				cvar_set("timelimit", v);
+				break;
 			}
 			case "leadlimit":
 			{
 				cvar_set("leadlimit", v);
+				break;
 			}
 			case "pointlimit":
 			case "fraglimit":
@@ -652,15 +654,18 @@ void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType)
 			case "caplimit":
 			{
 				fraglimit_normal = v;
+				break;
 			}
 			case "teampointlimit":
 			case "teamlaplimit":
 			{
 				fraglimit_teams = v;
+				break;
 			}
 			default:
 			{
 			    handled = false;
+			    break;
 			}
 		}
 		FOREACH(Gametypes, true, LAMBDA(handled |= it.m_parse_mapinfo(k, v)));
diff --git a/qcsrc/common/mutators/base.qh b/qcsrc/common/mutators/base.qh
index 07620bd445..ce98cd508e 100644
--- a/qcsrc/common/mutators/base.qh
+++ b/qcsrc/common/mutators/base.qh
@@ -144,12 +144,12 @@ typedef bool(int) mutatorfunc_t;
 
 CLASS(Mutator, Object)
     ATTRIB(Mutator, m_id, int, 0)
-    ATTRIB(Mutator, mutatorname, string, string_null)
+    ATTRIB(Mutator, m_name, string, string_null)
     ATTRIB(Mutator, mutatorfunc, mutatorfunc_t, func_null)
     ATTRIB(Mutator, mutatorcheck, bool(), func_null)
     CONSTRUCTOR(Mutator, string _name, mutatorfunc_t func) {
         CONSTRUCT(Mutator);
-        this.mutatorname = _name;
+        this.m_name = _name;
         this.mutatorfunc = func;
     }
 ENDCLASS(Mutator)
diff --git a/qcsrc/server/mutators/all.qc b/qcsrc/server/mutators/all.qc
index 7ad3726299..0e73189074 100644
--- a/qcsrc/server/mutators/all.qc
+++ b/qcsrc/server/mutators/all.qc
@@ -74,8 +74,10 @@
 
 #include "all.qh"
 
-#include "mutator.qh"
-#include "gamemode.qh"
+STATIC_INIT_LATE(Gametype) {
+    string s = GetGametype();
+    FOREACH(Mutators, it.m_name == s, LAMBDA(Mutator_Add(it); break));
+}
 
 #define IMPLEMENTATION
 #include "all.inc"
diff --git a/qcsrc/server/mutators/all.qh b/qcsrc/server/mutators/all.qh
index ad4a5b9f5e..e586ec74e9 100644
--- a/qcsrc/server/mutators/all.qh
+++ b/qcsrc/server/mutators/all.qh
@@ -1,3 +1,6 @@
+#include "mutator.qh"
+#include "gamemode.qh"
+
 #ifndef SERVER_MUTATORS_H
 #define SERVER_MUTATORS_H
 
diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qc b/qcsrc/server/mutators/mutator/gamemode_assault.qc
index 52fb40fea5..fdc2662659 100644
--- a/qcsrc/server/mutators/mutator/gamemode_assault.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_assault.qc
@@ -1,6 +1,36 @@
 #ifndef GAMEMODE_ASSAULT_H
 #define GAMEMODE_ASSAULT_H
 
+void assault_ScoreRules();
+
+REGISTER_MUTATOR(as, false)
+{
+	ActivateTeamplay();
+	have_team_spawns = -1; // request team spawns
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		assault_ScoreRules();
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back assault_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 // sprites
 .entity assault_decreaser;
 .entity assault_sprite;
@@ -641,31 +671,4 @@ void assault_ScoreRules()
 	ScoreRules_basics_end();
 }
 
-REGISTER_MUTATOR(as, g_assault)
-{
-	ActivateTeamplay();
-	have_team_spawns = -1; // request team spawns
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		assault_ScoreRules();
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back assault_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_ca.qc b/qcsrc/server/mutators/mutator/gamemode_ca.qc
index 20330b7579..88e56bb9d2 100644
--- a/qcsrc/server/mutators/mutator/gamemode_ca.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_ca.qc
@@ -1,19 +1,46 @@
 #ifndef GAMEMODE_CA_H
 #define GAMEMODE_CA_H
 
+int autocvar_g_ca_point_limit;
+int autocvar_g_ca_point_leadlimit;
+bool autocvar_g_ca_team_spawns;
+
+void ca_Initialize();
+
+REGISTER_MUTATOR(ca, false)
+{
+	ActivateTeamplay();
+	SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, -1, -1);
+
+	if (autocvar_g_ca_team_spawns)
+		have_team_spawns = -1; // request team spawns
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		ca_Initialize();
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 // should be removed in the future, as other code should not have to care
 .float caplayer; // 0.5 if scheduled to join the next round
 #endif
 
 #ifdef IMPLEMENTATION
 float autocvar_g_ca_damage2score_multiplier;
-int autocvar_g_ca_point_leadlimit;
-int autocvar_g_ca_point_limit;
 float autocvar_g_ca_round_timelimit;
 bool autocvar_g_ca_spectate_enemies;
 int autocvar_g_ca_teams;
 int autocvar_g_ca_teams_override;
-bool autocvar_g_ca_team_spawns;
 float autocvar_g_ca_warmup;
 
 float ca_teams;
@@ -503,27 +530,4 @@ void ca_Initialize()
 	EliminatedPlayers_Init(ca_isEliminated);
 }
 
-REGISTER_MUTATOR(ca, g_ca)
-{
-	ActivateTeamplay();
-	SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, -1, -1);
-
-	if(autocvar_g_ca_team_spawns)
-		have_team_spawns = -1; // request team spawns
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		ca_Initialize();
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_ctf.qc b/qcsrc/server/mutators/mutator/gamemode_ctf.qc
index e2eb898eff..73e992c95b 100644
--- a/qcsrc/server/mutators/mutator/gamemode_ctf.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_ctf.qc
@@ -1,6 +1,39 @@
 #ifndef GAMEMODE_CTF_H
 #define GAMEMODE_CTF_H
 
+#ifndef CSQC
+void ctf_Initialize();
+
+REGISTER_MUTATOR(ctf, false)
+{
+	ActivateTeamplay();
+	SetLimits(autocvar_capturelimit_override, -1, autocvar_captureleadlimit_override, -1);
+	have_team_spawns = -1; // request team spawns
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		ctf_Initialize();
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back ctf_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+#endif
+
 #ifdef SVQC
 // used in cheats.qc
 void ctf_RespawnFlag(entity flag);
@@ -2741,32 +2774,4 @@ void ctf_Initialize()
 	InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE);
 }
 
-REGISTER_MUTATOR(ctf, g_ctf)
-{
-	ActivateTeamplay();
-	SetLimits(autocvar_capturelimit_override, -1, autocvar_captureleadlimit_override, -1);
-	have_team_spawns = -1; // request team spawns
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		ctf_Initialize();
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back ctf_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_cts.qc b/qcsrc/server/mutators/mutator/gamemode_cts.qc
index 1414fe0655..064031153c 100644
--- a/qcsrc/server/mutators/mutator/gamemode_cts.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_cts.qc
@@ -1,6 +1,39 @@
+#include "../../race.qh"
+
 #ifndef GAMEMODE_CTS_H
 #define GAMEMODE_CTS_H
 
+void cts_Initialize();
+
+REGISTER_MUTATOR(cts, false)
+{
+	g_race_qualifying = true;
+	independent_players = 1;
+	SetLimits(0, 0, 0, -1);
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		cts_Initialize();
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back cts_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 // scores
 const float ST_CTS_LAPS = 1;
 const float SP_CTS_LAPS = 4;
@@ -414,32 +447,4 @@ void cts_Initialize()
 	cts_ScoreRules();
 }
 
-REGISTER_MUTATOR(cts, g_cts)
-{
-	g_race_qualifying = 1;
-	independent_players = 1;
-	SetLimits(0, 0, 0, -1);
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		cts_Initialize();
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back cts_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc
index ac7ea21b26..5ebd7c6b6e 100644
--- a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc
@@ -1,15 +1,11 @@
-#ifdef IMPLEMENTATION
-MUTATOR_HOOKFUNCTION(dm, Scores_CountFragsRemaining)
-{
-	// announce remaining frags
-	return true;
-}
+#ifndef GAMEMODE_DEATHMATCH_H
+#define GAMEMODE_DEATHMATCH_H
 
-REGISTER_MUTATOR(dm, IS_GAMETYPE(DEATHMATCH))
+REGISTER_MUTATOR(dm, false)
 {
 	MUTATOR_ONADD
 	{
-		if(time > 1) // game loads at time 1
+		if (time > 1) // game loads at time 1
 			error("This is a game type and it cannot be added at runtime.");
 	}
 
@@ -28,4 +24,13 @@ REGISTER_MUTATOR(dm, IS_GAMETYPE(DEATHMATCH))
 
 	return 0;
 }
+
+#endif
+
+#ifdef IMPLEMENTATION
+MUTATOR_HOOKFUNCTION(dm, Scores_CountFragsRemaining)
+{
+	// announce remaining frags
+	return true;
+}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qc b/qcsrc/server/mutators/mutator/gamemode_domination.qc
index 75d0d7dc3c..5195177dfc 100644
--- a/qcsrc/server/mutators/mutator/gamemode_domination.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_domination.qc
@@ -1,6 +1,39 @@
 #ifndef GAMEMODE_DOMINATION_H
 #define GAMEMODE_DOMINATION_H
 
+#define autocvar_g_domination_point_limit cvar("g_domination_point_limit")
+bool autocvar_g_domination_roundbased;
+int autocvar_g_domination_roundbased_point_limit;
+int autocvar_g_domination_point_leadlimit;
+
+void dom_Initialize();
+
+REGISTER_MUTATOR(dom, false)
+{
+	int fraglimit_override = autocvar_g_domination_point_limit;
+	if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit)
+		fraglimit_override = autocvar_g_domination_roundbased_point_limit;
+
+	ActivateTeamplay();
+	SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, -1, -1);
+	have_team_spawns = -1; // request team spawns
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		dom_Initialize();
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 // score rule declarations
 const float ST_DOM_TICKS = 1;
 const float SP_DOM_TICKS = 4;
@@ -40,12 +73,8 @@ int autocvar_g_domination_default_teams;
 bool autocvar_g_domination_disable_frags;
 int autocvar_g_domination_point_amt;
 bool autocvar_g_domination_point_fullbright;
-int autocvar_g_domination_point_leadlimit;
-bool autocvar_g_domination_roundbased;
-int autocvar_g_domination_roundbased_point_limit;
 float autocvar_g_domination_round_timelimit;
 float autocvar_g_domination_warmup;
-#define autocvar_g_domination_point_limit cvar("g_domination_point_limit")
 float autocvar_g_domination_point_rate;
 int autocvar_g_domination_teams_override;
 
@@ -695,30 +724,4 @@ void dom_Initialize()
 	InitializeEntity(world, dom_DelayedInit, INITPRIO_GAMETYPE);
 }
 
-
-REGISTER_MUTATOR(dom, IS_GAMETYPE(DOMINATION))
-{
-	int fraglimit_override = autocvar_g_domination_point_limit;
-	if(autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit)
-		fraglimit_override = autocvar_g_domination_roundbased_point_limit;
-
-	ActivateTeamplay();
-	SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, -1, -1);
-	have_team_spawns = -1; // request team spawns
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		dom_Initialize();
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc
index 912df1575d..c392c4be58 100644
--- a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc
@@ -1,6 +1,42 @@
 #ifndef GAMEMODE_FREEZETAG_H
 #define GAMEMODE_FREEZETAG_H
 
+int autocvar_g_freezetag_point_limit;
+int autocvar_g_freezetag_point_leadlimit;
+bool autocvar_g_freezetag_team_spawns;
+void freezetag_Initialize();
+
+REGISTER_MUTATOR(ft, false)
+{
+	ActivateTeamplay();
+	SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, -1, -1);
+
+	if (autocvar_g_freezetag_team_spawns)
+		have_team_spawns = -1; // request team spawns
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		freezetag_Initialize();
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back freezetag_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 .float freezetag_frozen_time;
 .float freezetag_frozen_timeout;
 const float ICE_MAX_ALPHA = 1;
@@ -15,15 +51,12 @@ float freezetag_teams;
 float autocvar_g_freezetag_frozen_maxtime;
 bool autocvar_g_freezetag_revive_nade;
 float autocvar_g_freezetag_revive_nade_health;
-int autocvar_g_freezetag_point_leadlimit;
-int autocvar_g_freezetag_point_limit;
 float autocvar_g_freezetag_revive_extra_size;
 float autocvar_g_freezetag_revive_speed;
 float autocvar_g_freezetag_revive_clearspeed;
 float autocvar_g_freezetag_round_timelimit;
 int autocvar_g_freezetag_teams;
 int autocvar_g_freezetag_teams_override;
-bool autocvar_g_freezetag_team_spawns;
 float autocvar_g_freezetag_warmup;
 
 const float SP_FREEZETAG_REVIVALS = 4;
@@ -621,34 +654,4 @@ void freezetag_Initialize()
 	EliminatedPlayers_Init(freezetag_isEliminated);
 }
 
-REGISTER_MUTATOR(ft, g_freezetag)
-{
-	ActivateTeamplay();
-	SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, -1, -1);
-
-	if(autocvar_g_freezetag_team_spawns)
-		have_team_spawns = -1; // request team spawns
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		freezetag_Initialize();
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back freezetag_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_invasion.qc b/qcsrc/server/mutators/mutator/gamemode_invasion.qc
index 4dc5ed63ab..f52afb9e2f 100644
--- a/qcsrc/server/mutators/mutator/gamemode_invasion.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_invasion.qc
@@ -1,6 +1,48 @@
 #ifndef GAMEMODE_INVASION_H
 #define GAMEMODE_INVASION_H
 
+#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit")
+int autocvar_g_invasion_teams;
+bool autocvar_g_invasion_team_spawns;
+bool g_invasion;
+void invasion_Initialize();
+
+REGISTER_MUTATOR(inv, false)
+{
+	SetLimits(autocvar_g_invasion_point_limit, -1, -1, -1);
+	if (autocvar_g_invasion_teams >= 2)
+	{
+		ActivateTeamplay();
+		if (autocvar_g_invasion_team_spawns)
+			have_team_spawns = -1; // request team spawns
+	}
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		g_invasion = true;
+		invasion_Initialize();
+
+		cvar_settemp("g_monsters", "1");
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back invasion_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 float inv_numspawned;
 float inv_maxspawned;
 float inv_roundcnt;
@@ -24,13 +66,9 @@ const float ST_INV_KILLS = 1;
 
 #include "../../teamplay.qh"
 
-bool g_invasion;
 
 float autocvar_g_invasion_round_timelimit;
-int autocvar_g_invasion_teams;
-bool autocvar_g_invasion_team_spawns;
 float autocvar_g_invasion_spawnpoint_spawn_delay;
-#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit")
 float autocvar_g_invasion_warmup;
 int autocvar_g_invasion_monster_count;
 bool autocvar_g_invasion_zombies_only;
@@ -487,39 +525,4 @@ void invasion_Initialize()
 	InitializeEntity(world, invasion_DelayedInit, INITPRIO_GAMETYPE);
 }
 
-REGISTER_MUTATOR(inv, IS_GAMETYPE(INVASION))
-{
-	SetLimits(autocvar_g_invasion_point_limit, -1, -1, -1);
-	if(autocvar_g_invasion_teams >= 2)
-	{
-		ActivateTeamplay();
-		if(autocvar_g_invasion_team_spawns)
-			have_team_spawns = -1; // request team spawns
-	}
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		g_invasion = true;
-		invasion_Initialize();
-
-		cvar_settemp("g_monsters", "1");
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back invasion_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc b/qcsrc/server/mutators/mutator/gamemode_keepaway.qc
index 855b766a9d..8961e6bf00 100644
--- a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_keepaway.qc
@@ -1,6 +1,33 @@
 #ifndef GAMEMODE_KEEPAWAY_H
 #define GAMEMODE_KEEPAWAY_H
 
+void ka_Initialize();
+
+REGISTER_MUTATOR(ka, false)
+{
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		ka_Initialize();
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back ka_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 
 entity ka_ball;
 
@@ -472,29 +499,4 @@ void ka_Initialize() // run at the start of a match, initiates game mode
 	ka_SpawnBall();
 }
 
-
-REGISTER_MUTATOR(ka, IS_GAMETYPE(KEEPAWAY))
-{
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		ka_Initialize();
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back ka_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc
index 3b86b66418..5982cf2085 100644
--- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc
@@ -1,6 +1,41 @@
 #ifndef GAMEMODE_KEYHUNT_H
 #define GAMEMODE_KEYHUNT_H
 
+#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit")
+int autocvar_g_keyhunt_point_leadlimit;
+bool autocvar_g_keyhunt_team_spawns;
+void kh_Initialize();
+
+REGISTER_MUTATOR(kh, false)
+{
+	ActivateTeamplay();
+	SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, -1, -1);
+	if (autocvar_g_keyhunt_team_spawns)
+		have_team_spawns = -1; // request team spawns
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		kh_Initialize();
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back kh_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 #define FOR_EACH_KH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext )
 
 // ALL OF THESE should be removed in the future, as other code should not have to care
@@ -37,9 +72,6 @@ int autocvar_g_balance_keyhunt_score_destroyed_ownfactor;
 int autocvar_g_balance_keyhunt_score_push;
 float autocvar_g_balance_keyhunt_throwvelocity;
 
-int autocvar_g_keyhunt_point_leadlimit;
-bool autocvar_g_keyhunt_team_spawns;
-#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit")
 int autocvar_g_keyhunt_teams;
 int autocvar_g_keyhunt_teams_override;
 
@@ -1367,33 +1399,4 @@ MUTATOR_HOOKFUNCTION(kh, reset_map_global)
 	return false;
 }
 
-REGISTER_MUTATOR(kh, IS_GAMETYPE(KEYHUNT))
-{
-	ActivateTeamplay();
-	SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, -1, -1);
-	if(autocvar_g_keyhunt_team_spawns)
-		have_team_spawns = -1; // request team spawns
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		kh_Initialize();
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back kh_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_lms.qc b/qcsrc/server/mutators/mutator/gamemode_lms.qc
index 30789a520c..efab45e3fa 100644
--- a/qcsrc/server/mutators/mutator/gamemode_lms.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_lms.qc
@@ -1,6 +1,36 @@
 #ifndef GAMEMODE_LMS_H
 #define GAMEMODE_LMS_H
 
+#define autocvar_g_lms_lives_override cvar("g_lms_lives_override")
+void lms_Initialize();
+
+REGISTER_MUTATOR(lms, false)
+{
+	SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, -1, -1);
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		lms_Initialize();
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back lms_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 // scoreboard stuff
 const float SP_LMS_LIVES = 4;
 const float SP_LMS_RANK = 5;
@@ -20,7 +50,6 @@ float LMS_NewPlayerLives();
 int autocvar_g_lms_extra_lives;
 bool autocvar_g_lms_join_anytime;
 int autocvar_g_lms_last_join;
-#define autocvar_g_lms_lives_override cvar("g_lms_lives_override")
 bool autocvar_g_lms_regenerate;
 
 // main functions
@@ -284,30 +313,5 @@ void lms_Initialize()
 	lms_ScoreRules();
 }
 
-REGISTER_MUTATOR(lms, IS_GAMETYPE(LMS))
-{
-	SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, -1, -1);
 
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		lms_Initialize();
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back lms_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_onslaught.qc b/qcsrc/server/mutators/mutator/gamemode_onslaught.qc
index d01b48ca49..4cda650826 100644
--- a/qcsrc/server/mutators/mutator/gamemode_onslaught.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_onslaught.qc
@@ -1,6 +1,38 @@
 #ifndef GAMEMODE_ONSLAUGHT_H
 #define GAMEMODE_ONSLAUGHT_H
 
+float autocvar_g_onslaught_point_limit;
+void ons_Initialize();
+
+REGISTER_MUTATOR(ons, false)
+{
+	ActivateTeamplay();
+	SetLimits(autocvar_g_onslaught_point_limit, -1, -1, -1);
+	have_team_spawns = -1; // request team spawns
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		ons_Initialize();
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back ons_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return false;
+}
+
 #ifdef SVQC
 
 .entity ons_toucher; // player who touched the control point
@@ -120,7 +152,6 @@ float autocvar_g_onslaught_gen_health;
 float autocvar_g_onslaught_shield_force = 100;
 float autocvar_g_onslaught_allow_vehicle_touch;
 float autocvar_g_onslaught_round_timelimit;
-float autocvar_g_onslaught_point_limit;
 float autocvar_g_onslaught_warmup;
 float autocvar_g_onslaught_teleport_radius;
 float autocvar_g_onslaught_spawn_choose;
@@ -2284,32 +2315,4 @@ void ons_Initialize()
 	InitializeEntity(world, ons_DelayedInit, INITPRIO_GAMETYPE);
 }
 
-REGISTER_MUTATOR(ons, IS_GAMETYPE(ONSLAUGHT))
-{
-	ActivateTeamplay();
-	SetLimits(autocvar_g_onslaught_point_limit, -1, -1, -1);
-	have_team_spawns = -1; // request team spawns
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		ons_Initialize();
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back ons_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return false;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_race.qc b/qcsrc/server/mutators/mutator/gamemode_race.qc
index 4b9201d459..cc250dfc73 100644
--- a/qcsrc/server/mutators/mutator/gamemode_race.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_race.qc
@@ -1,7 +1,36 @@
 #ifndef GAMEMODE_RACE_H
 #define GAMEMODE_RACE_H
 
-float g_race_qualifying;
+void rc_SetLimits();
+void race_Initialize();
+
+REGISTER_MUTATOR(rc, false)
+{
+	rc_SetLimits();
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		race_Initialize();
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back race_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
 float race_teams;
 
 // scores
@@ -437,30 +466,4 @@ void rc_SetLimits()
 	SetLimits(fraglimit_override, leadlimit_override, timelimit_override, qualifying_override);
 }
 
-REGISTER_MUTATOR(rc, g_race)
-{
-	rc_SetLimits();
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		race_Initialize();
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back race_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/mutators/mutator/gamemode_tdm.qc b/qcsrc/server/mutators/mutator/gamemode_tdm.qc
index aaa3d51631..49ec0acc9f 100644
--- a/qcsrc/server/mutators/mutator/gamemode_tdm.qc
+++ b/qcsrc/server/mutators/mutator/gamemode_tdm.qc
@@ -1,7 +1,44 @@
-#ifdef IMPLEMENTATION
-bool autocvar_g_tdm_team_spawns;
+#ifndef GAMEMODE_TDM_H
+#define GAMEMODE_TDM_H
+
 int autocvar_g_tdm_point_limit;
 int autocvar_g_tdm_point_leadlimit;
+bool autocvar_g_tdm_team_spawns;
+void tdm_DelayedInit();
+
+REGISTER_MUTATOR(tdm, false)
+{
+	ActivateTeamplay();
+	SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, -1, -1);
+	if (autocvar_g_tdm_team_spawns)
+		have_team_spawns = -1; // request team spawns
+
+	MUTATOR_ONADD
+	{
+		if (time > 1) // game loads at time 1
+			error("This is a game type and it cannot be added at runtime.");
+		InitializeEntity(world, tdm_DelayedInit, INITPRIO_GAMETYPE);
+	}
+
+	MUTATOR_ONROLLBACK_OR_REMOVE
+	{
+		// we actually cannot roll back tdm_Initialize here
+		// BUT: we don't need to! If this gets called, adding always
+		// succeeds.
+	}
+
+	MUTATOR_ONREMOVE
+	{
+		LOG_INFO("This is a game type and it cannot be removed at runtime.");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif
+
+#ifdef IMPLEMENTATION
 int autocvar_g_tdm_teams;
 int autocvar_g_tdm_teams_override;
 
@@ -59,33 +96,4 @@ MUTATOR_HOOKFUNCTION(tdm, Scores_CountFragsRemaining)
 	return true;
 }
 
-REGISTER_MUTATOR(tdm, g_tdm)
-{
-	ActivateTeamplay();
-	SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, -1, -1);
-	if(autocvar_g_tdm_team_spawns)
-		have_team_spawns = -1; // request team spawns
-
-	MUTATOR_ONADD
-	{
-		if(time > 1) // game loads at time 1
-			error("This is a game type and it cannot be added at runtime.");
-		InitializeEntity(world, tdm_DelayedInit, INITPRIO_GAMETYPE);
-	}
-
-	MUTATOR_ONROLLBACK_OR_REMOVE
-	{
-		// we actually cannot roll back tdm_Initialize here
-		// BUT: we don't need to! If this gets called, adding always
-		// succeeds.
-	}
-
-	MUTATOR_ONREMOVE
-	{
-		LOG_INFO("This is a game type and it cannot be removed at runtime.");
-		return -1;
-	}
-
-	return 0;
-}
 #endif
diff --git a/qcsrc/server/race.qh b/qcsrc/server/race.qh
index 5e9f0cf85c..03e8a54aba 100644
--- a/qcsrc/server/race.qh
+++ b/qcsrc/server/race.qh
@@ -1,6 +1,8 @@
 #ifndef RACE_H
 #define RACE_H
 
+bool g_race_qualifying;
+
 float speedaward_lastsent;
 float speedaward_lastupdate;
 
diff --git a/qcsrc/server/teamplay.qh b/qcsrc/server/teamplay.qh
index 5664d261da..b934993235 100644
--- a/qcsrc/server/teamplay.qh
+++ b/qcsrc/server/teamplay.qh
@@ -21,6 +21,8 @@ void default_delayedinit();
 
 void ActivateTeamplay();
 
+void SetLimits(int fraglimit_override, int leadlimit_override, float timelimit_override, float qualifying_override);
+
 void InitGameplayMode();
 
 string GetClientVersionMessage();
-- 
2.39.5