diff --git a/src/deh_soc.c b/src/deh_soc.c
index 5ec9816d635f6b566408b8c1c5ae5e35b7222a46..646ec874436027ece44577446a820086f9d28f9c 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -1130,8 +1130,7 @@ void readsprite2(MYFILE *f, INT32 num)
 	Z_Free(s);
 }
 
-// copypasted from readPlayer :]
-void readgametype(MYFILE *f, char *gtname)
+void readgametype(MYFILE *f, INT32 num)
 {
 	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
 	char *word;
@@ -1139,21 +1138,15 @@ void readgametype(MYFILE *f, char *gtname)
 	char *tmp;
 	INT32 i, j;
 
-	INT16 newgtidx = 0;
-	UINT32 newgtrules = 0;
-	UINT32 newgttol = 0;
-	INT32 newgtpointlimit = 0;
-	INT32 newgttimelimit = 0;
-	UINT8 newgtleftcolor = 0;
-	UINT8 newgtrightcolor = 0;
-	INT16 newgtrankingstype = -1;
-	int newgtinttype = 0;
+	char *gtname = gametypes[num].name;
+	char gtconst[32];
 	char gtdescription[441];
-	char gtconst[MAXLINELEN];
 
-	// Empty strings.
-	gtdescription[0] = '\0';
-	gtconst[0] = '\0';
+	UINT8 newgtleftcolor, newgtrightcolor;
+	boolean has_desc_colors[2] = { false, false };
+
+	memset(gtconst, 0, sizeof(gtconst));
+	memset(gtdescription, 0, sizeof(gtconst));
 
 	do
 	{
@@ -1212,8 +1205,8 @@ void readgametype(MYFILE *f, char *gtname)
 			if (word2)
 			{
 				if (!word2lwr)
-					word2lwr = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
-				strcpy(word2lwr, word2);
+					word2lwr = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
+				strlcpy(word2lwr, word2, MAXLINELEN);
 				strupr(word2);
 			}
 			else
@@ -1221,13 +1214,21 @@ void readgametype(MYFILE *f, char *gtname)
 
 			if (word2[strlen(word2)-1] == '\n')
 				word2[strlen(word2)-1] = '\0';
+			if (word2lwr[strlen(word2lwr)-1] == '\n')
+				word2lwr[strlen(word2lwr)-1] = '\0';
 			i = atoi(word2);
 
+			// Name
+			if (fastcmp(word, "NAME"))
+			{
+				Z_Free(gametypes[num].name);
+				gametypes[num].name = Z_StrDup(word2lwr);
+			}
 			// Game type rules
-			if (fastcmp(word, "RULES"))
+			else if (fastcmp(word, "RULES"))
 			{
 				// GTR_
-				newgtrules = (UINT32)get_number(word2);
+				gametypes[num].rules = (UINT32)get_number(word2);
 			}
 			// Identifier
 			else if (fastcmp(word, "IDENTIFIER"))
@@ -1237,33 +1238,60 @@ void readgametype(MYFILE *f, char *gtname)
 			}
 			// Point and time limits
 			else if (fastcmp(word, "DEFAULTPOINTLIMIT"))
-				newgtpointlimit = (INT32)i;
+				gametypes[num].pointlimit = (INT32)i;
 			else if (fastcmp(word, "DEFAULTTIMELIMIT"))
-				newgttimelimit = (INT32)i;
+				gametypes[num].timelimit = (INT32)i;
 			// Level platter
 			else if (fastcmp(word, "HEADERCOLOR") || fastcmp(word, "HEADERCOLOUR"))
-				newgtleftcolor = newgtrightcolor = (UINT8)get_number(word2);
+			{
+				INT32 color = (INT32)i;
+				if (color >= 0 && color <= 255)
+				{
+					newgtleftcolor = newgtrightcolor = (UINT8)color;
+					has_desc_colors[0] = has_desc_colors[1] = true;
+				}
+				else
+					deh_warning("readgametype %d: Level platter header color %d out of range (0 - 255)", num, i);
+			}
 			else if (fastcmp(word, "HEADERLEFTCOLOR") || fastcmp(word, "HEADERLEFTCOLOUR"))
-				newgtleftcolor = (UINT8)get_number(word2);
+			{
+				INT32 color = (INT32)i;
+				if (color >= 0 && color <= 255)
+				{
+					newgtleftcolor = (UINT8)color;
+					has_desc_colors[0] = true;
+				}
+				else
+					deh_warning("readgametype %d: Level platter header left color %d out of range (0 - 255)", num, i);
+			}
 			else if (fastcmp(word, "HEADERRIGHTCOLOR") || fastcmp(word, "HEADERRIGHTCOLOUR"))
-				newgtrightcolor = (UINT8)get_number(word2);
+			{
+				INT32 color = (INT32)i;
+				if (color >= 0 && color < 255)
+				{
+					newgtrightcolor = (UINT8)color;
+					has_desc_colors[1] = true;
+				}
+				else
+					deh_warning("readgametype %d: Level platter header right color %d out of range (0 - 255)", num, i);
+			}
 			// Rankings type
 			else if (fastcmp(word, "RANKINGTYPE"))
 			{
 				// Case insensitive
-				newgtrankingstype = (int)get_number(word2);
+				gametypes[num].rankings_type = (int)get_number(word2);
 			}
 			// Intermission type
 			else if (fastcmp(word, "INTERMISSIONTYPE"))
 			{
 				// Case sensitive
-				newgtinttype = (int)get_number(word2lwr);
+				gametypes[num].intermission_type = (int)get_number(word2lwr);
 			}
 			// Type of level
 			else if (fastcmp(word, "TYPEOFLEVEL"))
 			{
 				if (i) // it's just a number
-					newgttol = (UINT32)i;
+					gametypes[num].typeoflevel = (UINT32)i;
 				else
 				{
 					UINT32 tol = 0;
@@ -1273,28 +1301,28 @@ void readgametype(MYFILE *f, char *gtname)
 							if (fasticmp(tmp, TYPEOFLEVEL[i].name))
 								break;
 						if (!TYPEOFLEVEL[i].name)
-							deh_warning("readgametype %s: unknown typeoflevel flag %s\n", gtname, tmp);
+							deh_warning("readgametype %d: unknown typeoflevel flag %s\n", num, tmp);
 						tol |= TYPEOFLEVEL[i].flag;
 					} while((tmp = strtok(NULL,",")) != NULL);
-					newgttol = tol;
+					gametypes[num].typeoflevel = tol;
 				}
 			}
-			// The SOC probably provided gametype rules as words,
-			// instead of using the RULES keyword.
-			// Like for example "NOSPECTATORSPAWN = TRUE".
-			// This is completely valid, and looks better anyway.
+			// This SOC probably provided gametype rules as words, instead of using the RULES keyword.
+			// (For example, "NOSPECTATORSPAWN = TRUE")
 			else
 			{
 				UINT32 wordgt = 0;
 				for (j = 0; GAMETYPERULE_LIST[j]; j++)
 					if (fastcmp(word, GAMETYPERULE_LIST[j])) {
 						wordgt |= (1<<j);
-						if (i || word2[0] == 'T' || word2[0] == 'Y')
-							newgtrules |= wordgt;
+						if (word2[0] == 'T' || word2[0] == 'Y')
+							gametypes[num].rules |= wordgt;
+						else if (word2[0] == 'F' || word2[0] == 'N')
+							gametypes[num].rules &= ~wordgt;
 						break;
 					}
 				if (!wordgt)
-					deh_warning("readgametype %s: unknown word '%s'", gtname, word);
+					deh_warning("readgametype %d: unknown word '%s'", num, word);
 			}
 		}
 	} while (!myfeof(f)); // finish when the line is empty
@@ -1304,38 +1332,26 @@ void readgametype(MYFILE *f, char *gtname)
 	if (word2lwr)
 		Z_Free(word2lwr);
 
-	// Ran out of gametype slots
-	if (gametypecount == NUMGAMETYPEFREESLOTS)
-	{
-		CONS_Alert(CONS_WARNING, "Ran out of free gametype slots!\n");
-		return;
-	}
-
-	// Add the new gametype
-	newgtidx = G_AddGametype(newgtrules);
-	G_AddGametypeTOL(newgtidx, newgttol);
-	G_SetGametypeDescription(newgtidx, gtdescription, newgtleftcolor, newgtrightcolor);
-
-	// Not covered by G_AddGametype alone.
-	if (newgtrankingstype == -1)
-		newgtrankingstype = newgtidx;
-	gametypes[newgtidx].rankings_type = newgtrankingstype;
-	gametypes[newgtidx].intermission_type = newgtinttype;
-	gametypes[newgtidx].pointlimit = newgtpointlimit;
-	gametypes[newgtidx].timelimit = newgttimelimit;
-
-	// Write the new gametype name.
-	gametypes[newgtidx].name = Z_StrDup(gtname);
+	if (gtdescription[0])
+		G_SetGametypeDescription(num, gtdescription);
+	if (has_desc_colors[0])
+		G_SetGametypeDescriptionLeftColor(num, newgtleftcolor);
+	if (has_desc_colors[1])
+		G_SetGametypeDescriptionRightColor(num, newgtrightcolor);
 
 	// Write the constant name.
-	if (gtconst[0] == '\0')
-		strncpy(gtconst, gtname, MAXLINELEN);
-	G_AddGametypeConstant(newgtidx, gtconst);
+	if (gametypes[num].constant_name == NULL)
+	{
+		if (gtconst[0] == '\0')
+			G_AddGametypeConstant(num, gtname);
+		else
+			G_AddGametypeConstant(num, gtconst);
+	}
+	else if (gtconst[0] != '\0')
+		G_AddGametypeConstant(num, gtconst);
 
 	// Update gametype_cons_t accordingly.
 	G_UpdateGametypeSelections();
-
-	CONS_Printf("Added gametype %s\n", gametypes[newgtidx].name);
 }
 
 void readlevelheader(MYFILE *f, INT32 num)
@@ -4151,6 +4167,22 @@ skincolornum_t get_skincolor(const char *word)
 	return SKINCOLOR_GREEN;
 }
 
+INT16 get_gametype(const char *word)
+{
+	INT16 i;
+	if (*word >= '0' && *word <= '9')
+		return atoi(word);
+	if (fastncmp("GT_",word,3))
+		word += 3; // take off the GT_
+	for (i = 0; i < gametypecount; i++)
+	{
+		if (fastcmp(word, gametypes[i].constant_name + 3))
+			return i;
+	}
+	deh_warning("Couldn't find gametype named 'GT_%s'",word);
+	return GT_COOP;
+}
+
 spritenum_t get_sprite(const char *word)
 { // Returns the value of SPR_ enumerations
 	spritenum_t i;
diff --git a/src/deh_soc.h b/src/deh_soc.h
index 029390133761c597c88cefb679cc847f8ce5e520..a060e72e3eec67419808bfd77aea5af595a7991d 100644
--- a/src/deh_soc.h
+++ b/src/deh_soc.h
@@ -54,9 +54,9 @@ playersprite_t get_sprite2(const char *word);
 sfxenum_t get_sfx(const char *word);
 hudnum_t get_huditem(const char *word);
 menutype_t get_menutype(const char *word);
-//INT16 get_gametype(const char *word);
-//powertype_t get_power(const char *word);
 skincolornum_t get_skincolor(const char *word);
+INT16 get_gametype(const char *word);
+UINT8 get_team(const char *word);
 
 void readwipes(MYFILE *f);
 void readmaincfg(MYFILE *f);
@@ -71,7 +71,8 @@ void readmenu(MYFILE *f, INT32 num);
 void readtextprompt(MYFILE *f, INT32 num);
 void readcutscene(MYFILE *f, INT32 num);
 void readlevelheader(MYFILE *f, INT32 num);
-void readgametype(MYFILE *f, char *gtname);
+void readgametype(MYFILE *f, INT32 num);
+void readteam(MYFILE *f, INT32 num);
 void readsprite2(MYFILE *f, INT32 num);
 void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2);
 #ifdef HWRENDER
diff --git a/src/dehacked.c b/src/dehacked.c
index fd2a701715e17ce7dab0b6c829147113844972fe..51f960ac30b6b89d677b00d5c117a8b942365059 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -172,9 +172,9 @@ static void ignorelines(MYFILE *f)
 static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 {
 	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
-	char textline[MAXLINELEN];
 	char *word;
 	char *word2;
+	char word2lwr[MAXLINELEN];
 	INT32 i;
 
 	if (!deh_loaded)
@@ -189,7 +189,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 	while (!myfeof(f))
 	{
 		myfgets(s, MAXLINELEN, f);
-		memcpy(textline, s, MAXLINELEN);
 		if (s[0] == '\n' || s[0] == '#')
 			continue;
 
@@ -216,10 +215,14 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 				continue;
 			}
 			word2 = strtok(NULL, " ");
+			word2lwr[0] = '\0';
 			if (word2) {
+				strcpy(word2lwr, word2);
 				strupr(word2);
 				if (word2[strlen(word2) - 1] == '\n')
 					word2[strlen(word2) - 1] = '\0';
+				if (word2lwr[strlen(word2lwr) - 1] == '\n')
+					word2lwr[strlen(word2lwr) - 1] = '\0';
 				i = atoi(word2);
 			}
 			else
@@ -381,33 +384,49 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 				}
 				else if (fastcmp(word, "GAMETYPE"))
 				{
-					// Get the gametype name from textline
-					// instead of word2, so that gametype names
-					// aren't allcaps
-					INT32 c;
-					for (c = 0; c < MAXLINELEN; c++)
+					char *gtname = NULL;
+					if (word2lwr[0])
 					{
-						if (textline[c] == '\0')
-							break;
-						if (textline[c] == ' ')
+						gtname = word2lwr;
+
+						for (size_t j = 0; j < strlen(gtname); j++)
 						{
-							char *gtname = (textline+c+1);
-							if (gtname)
+							if (gtname[j] == '\0')
+								break;
+							if (gtname[j] < 32 || gtname[j] == '\n')
 							{
-								// remove funny characters
-								INT32 j;
-								for (j = 0; j < (MAXLINELEN - c); j++)
-								{
-									if (gtname[j] == '\0')
-										break;
-									if (gtname[j] < 32)
-										gtname[j] = '\0';
-								}
-								readgametype(f, gtname);
+								gtname[j] = '\0';
+								break;
 							}
-							break;
 						}
 					}
+
+					if (!gtname || !strlen(gtname))
+					{
+						deh_warning("Invalid gametype name");
+						ignorelines(f);
+					}
+					else
+					{
+						INT32 gametype_id = G_GetGametypeByName(gtname);
+						if (gametype_id == -1)
+						{
+							if (!strncmp(gtname, "GT_", 3))
+								gametype_id = get_gametype(gtname + 3);
+							else if (gametypecount != NUMGAMETYPEFREESLOTS)
+							{
+								gametype_id = G_AddGametype();
+								CONS_Printf("Added gametype %s\n", gtname);
+							}
+							else
+								deh_warning("Ran out of free gametype slots");
+						}
+
+						if (gametype_id != -1)
+							readgametype(f, gametype_id);
+						else
+							ignorelines(f);
+					}
 				}
 				else if (fastcmp(word, "CUTSCENE"))
 				{
diff --git a/src/g_game.c b/src/g_game.c
index 3bd7c3db67150785b58f3bf57243e23c1c82249b..d870208c3822d3e6423a03256ec9ebb271cb80a5 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3520,16 +3520,17 @@ void G_SetGametype(INT16 gtype)
 //
 // Adds a gametype. Returns the new gametype number.
 //
-INT16 G_AddGametype(UINT32 rules)
+INT16 G_AddGametype(void)
 {
 	INT16 newgtype = gametypecount;
 	gametypecount++;
 
 	gametypes[newgtype].name = Z_StrDup("???");
-	gametypes[newgtype].rules = rules;
+	gametypes[newgtype].rules = 0;
 
-	// Update gametype_cons_t accordingly.
-	G_UpdateGametypeSelections();
+	G_SetGametypeDescription(newgtype, "???");
+	G_SetGametypeDescriptionLeftColor(newgtype, 54);
+	G_SetGametypeDescriptionRightColor(newgtype, 54);
 
 	return newgtype;
 }
@@ -3567,20 +3568,32 @@ void G_UpdateGametypeSelections(void)
 	}
 	gametype_cons_t[NUMGAMETYPES].value = 0;
 	gametype_cons_t[NUMGAMETYPES].strvalue = NULL;
+
+	cv_newgametype.defaultvalue = gametype_cons_t[0].strvalue;
+
+	if (cv_newgametype.string && G_GetGametypeByName(cv_newgametype.string) == -1)
+	{
+		cv_newgametype.value = 0;
+		cv_newgametype.string = gametype_cons_t[0].strvalue;
+	}
 }
 
-//
-// G_SetGametypeDescription
-//
-// Set a description for the specified gametype.
-// (Level platter)
-//
-void G_SetGametypeDescription(INT16 gtype, char *descriptiontext, UINT8 leftcolor, UINT8 rightcolor)
+void G_SetGametypeDescription(INT16 gtype, const char *descriptiontext)
+{
+	if (descriptiontext)
+		strlcpy(gametypedesc[gtype].notes, descriptiontext, sizeof gametypedesc[gtype].notes);
+	else
+		memset(gametypedesc[gtype].notes, 0, sizeof gametypedesc[gtype].notes);
+}
+
+void G_SetGametypeDescriptionLeftColor(INT16 gtype, UINT8 color)
 {
-	if (descriptiontext != NULL)
-		strncpy(gametypedesc[gtype].notes, descriptiontext, 441);
-	gametypedesc[gtype].col[0] = leftcolor;
-	gametypedesc[gtype].col[1] = rightcolor;
+	gametypedesc[gtype].col[0] = color;
+}
+
+void G_SetGametypeDescriptionRightColor(INT16 gtype, UINT8 color)
+{
+	gametypedesc[gtype].col[1] = color;
 }
 
 tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = {
@@ -3629,16 +3642,6 @@ void G_AddTOL(UINT32 newtol, const char *tolname)
 	TYPEOFLEVEL[i].flag = newtol;
 }
 
-//
-// G_AddGametypeTOL
-//
-// Assigns a type of level to a gametype.
-//
-void G_AddGametypeTOL(INT16 gtype, UINT32 newtol)
-{
-	gametypes[gtype].typeoflevel = newtol;
-}
-
 //
 // G_GetGametypeByName
 //
diff --git a/src/g_game.h b/src/g_game.h
index 97e38d88a131de729f2c9d3c2d280dae2d0b1d31..7b2e6620bb62778bf3c007044220ffb6b3243fbb 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -193,12 +193,14 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives);
 
 void G_SetGametype(INT16 gametype);
 void G_InitGametypes(void);
-INT16 G_AddGametype(UINT32 rules);
+INT16 G_AddGametype(void);
 void G_AddGametypeConstant(INT16 gtype, const char *newgtconst);
 void G_UpdateGametypeSelections(void);
 void G_AddTOL(UINT32 newtol, const char *tolname);
-void G_AddGametypeTOL(INT16 gtype, UINT32 newtol);
-void G_SetGametypeDescription(INT16 gtype, char *descriptiontext, UINT8 leftcolor, UINT8 rightcolor);
+
+void G_SetGametypeDescription(INT16 gtype, const char *descriptiontext);
+void G_SetGametypeDescriptionLeftColor(INT16 gtype, UINT8 color);
+void G_SetGametypeDescriptionRightColor(INT16 gtype, UINT8 color);
 
 INT32 G_GetGametypeByName(const char *gametypestr);
 boolean G_IsSpecialStage(INT32 mapnum);
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 5fcd0f9b22bfe63d94f13e726e1f7784e5199ad0..e4aae50a8aa65b0ff6833661dc57367d45183999 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -3553,15 +3553,14 @@ static int lib_gAddGametype(lua_State *L)
 
 	char *gtname = NULL;
 	char *gtconst = NULL;
-	const char *gtdescription = NULL;
-	INT16 newgtidx = 0;
+	char *gtdescription = NULL;
 	UINT32 newgtrules = 0;
 	UINT32 newgttol = 0;
 	INT32 newgtpointlimit = 0;
 	INT32 newgttimelimit = 0;
-	UINT8 newgtleftcolor = 0;
-	UINT8 newgtrightcolor = 0;
-	INT16 newgtrankingstype = -1;
+	UINT8 newgtleftcolor = 54;
+	UINT8 newgtrightcolor = 54;
+	INT16 newgtrankingstype = RANKINGS_DEFAULT;
 	int newgtinttype = 0;
 
 	luaL_checktype(L, 1, LUA_TTABLE);
@@ -3651,36 +3650,47 @@ static int lib_gAddGametype(lua_State *L)
 	// Set defaults
 	if (gtname == NULL)
 		gtname = Z_StrDup("Unnamed gametype");
-	if (gtdescription == NULL)
-		gtdescription = Z_StrDup("???");
+
+	if (G_GetGametypeByName(gtname) != -1)
+	{
+		luaL_error(L, "gametype %s already exists", gtname);
+		Z_Free(gtname);
+		Z_Free(gtconst);
+		Z_Free(gtdescription);
+		return 0;
+	}
 
 	// Add the new gametype
-	newgtidx = G_AddGametype(newgtrules);
-	G_AddGametypeTOL(newgtidx, newgttol);
-	G_SetGametypeDescription(newgtidx, NULL, newgtleftcolor, newgtrightcolor);
-	strncpy(gametypedesc[newgtidx].notes, gtdescription, 441);
-
-	// Not covered by G_AddGametype alone.
-	if (newgtrankingstype == -1)
-		newgtrankingstype = newgtidx;
-	gametypes[newgtidx].rankings_type = newgtrankingstype;
-	gametypes[newgtidx].intermission_type = newgtinttype;
-	gametypes[newgtidx].pointlimit = newgtpointlimit;
-	gametypes[newgtidx].timelimit = newgttimelimit;
-
-	// Write the new gametype name.
-	gametypes[newgtidx].name = gtname;
-
-	// Write the constant name.
-	if (gtconst == NULL)
-		gtconst = gtname;
-	G_AddGametypeConstant(newgtidx, gtconst);
-
-	// Update gametype_cons_t accordingly.
+	INT16 gtype = G_AddGametype();
+
+	gametype_t *gt = &gametypes[gtype];
+
+	gt->name = gtname;
+
+	if (gtconst)
+		G_AddGametypeConstant(gtype, gtconst);
+	else
+		G_AddGametypeConstant(gtype, gtname);
+
+	if (gtdescription)
+		G_SetGametypeDescription(gtype, gtdescription);
+	G_SetGametypeDescriptionLeftColor(gtype, newgtleftcolor);
+	G_SetGametypeDescriptionRightColor(gtype, newgtrightcolor);
+
+	gt->rules = newgtrules;
+	gt->typeoflevel = newgttol;
+	gt->rankings_type = newgtrankingstype;
+	gt->intermission_type = newgtinttype;
+	gt->pointlimit = newgtpointlimit;
+	gt->timelimit = newgttimelimit;
+
 	G_UpdateGametypeSelections();
 
-	// done
-	CONS_Printf("Added gametype %s\n", gametypes[newgtidx].name);
+	CONS_Printf("Added gametype %s\n", gt->name);
+
+	Z_Free(gtconst);
+	Z_Free(gtdescription);
+
 	return 0;
 }
 
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index bb453fd613ea8af3fb13bcee384bab4f97e54a43..40c20576f9084d7ff44e4a7d48d62a0fd1e3028a 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -2280,7 +2280,12 @@ static void Command_Teamchange_f(void)
 	if (COM_Argc() <= 1)
 	{
 		if (G_GametypeHasTeams())
-			CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "red, blue or spectator");
+		{
+			if (G_GametypeHasSpectators())
+				CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "red, blue or spectator");
+			else
+				CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "red or blue");
+		}
 		else if (G_GametypeHasSpectators())
 			CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "spectator or playing");
 		else
@@ -2294,7 +2299,7 @@ static void Command_Teamchange_f(void)
 			NetPacket.packet.newteam = 1;
 		else if (!strcasecmp(COM_Argv(1), "blue") || !strcasecmp(COM_Argv(1), "2"))
 			NetPacket.packet.newteam = 2;
-		else if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
+		else if (G_GametypeHasSpectators() && (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0")))
 			NetPacket.packet.newteam = 0;
 		else
 			error = true;
@@ -2317,7 +2322,12 @@ static void Command_Teamchange_f(void)
 	if (error)
 	{
 		if (G_GametypeHasTeams())
-			CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "red, blue or spectator");
+		{
+			if (G_GametypeHasSpectators())
+				CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "red, blue or spectator");
+			else
+				CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "red or blue");
+		}
 		else if (G_GametypeHasSpectators())
 			CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "spectator or playing");
 		return;
@@ -2376,9 +2386,14 @@ static void Command_Teamchange2_f(void)
 	if (COM_Argc() <= 1)
 	{
 		if (G_GametypeHasTeams())
-			CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "red, blue or spectator");
+		{
+			if (G_GametypeHasSpectators())
+				CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "red, blue or spectator");
+			else
+				CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "red or blue");
+		}
 		else if (G_GametypeHasSpectators())
-			CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "spectator or playing");
+			CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "spectator or playing");
 		else
 			CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
 		return;
@@ -2390,7 +2405,7 @@ static void Command_Teamchange2_f(void)
 			NetPacket.packet.newteam = 1;
 		else if (!strcasecmp(COM_Argv(1), "blue") || !strcasecmp(COM_Argv(1), "2"))
 			NetPacket.packet.newteam = 2;
-		else if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
+		else if (G_GametypeHasSpectators() && (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0")))
 			NetPacket.packet.newteam = 0;
 		else
 			error = true;
@@ -2414,7 +2429,12 @@ static void Command_Teamchange2_f(void)
 	if (error)
 	{
 		if (G_GametypeHasTeams())
-			CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "red, blue or spectator");
+		{
+			if (G_GametypeHasSpectators())
+				CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "red, blue or spectator");
+			else
+				CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "red or blue");
+		}
 		else if (G_GametypeHasSpectators())
 			CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "spectator or playing");
 		return;
@@ -2481,7 +2501,12 @@ static void Command_ServerTeamChange_f(void)
 		if (G_TagGametype())
 			CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator");
 		else if (G_GametypeHasTeams())
-			CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
+		{
+			if (G_GametypeHasSpectators())
+				CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
+			else
+				CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red or blue");
+		}
 		else if (G_GametypeHasSpectators())
 			CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
 		else
@@ -2508,7 +2533,7 @@ static void Command_ServerTeamChange_f(void)
 			NetPacket.packet.newteam = 1;
 		else if (!strcasecmp(COM_Argv(2), "blue") || !strcasecmp(COM_Argv(2), "2"))
 			NetPacket.packet.newteam = 2;
-		else if (!strcasecmp(COM_Argv(2), "spectator") || !strcasecmp(COM_Argv(2), "0"))
+		else if (G_GametypeHasSpectators() && (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0")))
 			NetPacket.packet.newteam = 0;
 		else
 			error = true;
@@ -2533,7 +2558,12 @@ static void Command_ServerTeamChange_f(void)
 		if (G_TagGametype())
 			CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator");
 		else if (G_GametypeHasTeams())
-			CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
+		{
+			if (G_GametypeHasSpectators())
+				CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
+			else
+				CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red or blue");
+		}
 		else if (G_GametypeHasSpectators())
 			CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
 		return;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 7a5aaf42497c422f9d5d745aee99dafff011ae6f..eb5c7a0af1e8324c3b192e0dc00f8f0fb875c892 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11607,7 +11607,12 @@ void P_SpawnPlayer(INT32 playernum)
 	{
 		// Fix stupid non spectator spectators.
 		if (!p->spectator && !p->ctfteam)
-			p->spectator = true;
+		{
+			if (G_GametypeHasSpectators())
+				p->spectator = true;
+			else
+				p->ctfteam = 1;
+		}
 	}
 
 	if ((netgame || multiplayer) && ((gametyperules & GTR_SPAWNINVUL) || leveltime) && !p->spectator && !(maptol & TOL_NIGHTS))