diff --git a/src/deh_lua.c b/src/deh_lua.c
index 3571212f7f84fbe61e4858473226b37392b3bb32..a08477b26d95584405c383670768823115fc6fc6 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -133,19 +133,16 @@ static inline int lib_freeslot(lua_State *L)
 		}
 		else if (fastcmp(type, "TEAM"))
 		{
-			UINT8 i;
-			for (i = 0; i < MAXTEAMS; i++)
-				if (!teamnames[i]) {
-					CONS_Printf("Team TEAM_%s allocated.\n",word);
-					teamnames[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
-					strcpy(teamnames[i],word);
-					lua_pushinteger(L, i);
-					numteams++;
-					r++;
-					break;
-				}
-			if (i == MAXTEAMS)
+			if (numteams == MAXTEAMS)
 				CONS_Alert(CONS_WARNING, "Ran out of free team slots!\n");
+			UINT8 i = numteams;
+			CONS_Printf("Team TEAM_%s allocated.\n",word);
+			teamnames[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
+			strcpy(teamnames[i],word);
+			lua_pushinteger(L, i);
+			numteams++;
+			r++;
+			break;
 		}
 		else if (fastcmp(type, "SPR2"))
 		{
@@ -579,10 +576,8 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
 	}
 	else if (fastncmp("TEAM_",word,5)) {
 		p = word+5;
-		for (i = 0; i < MAXTEAMS; i++)
+		for (i = 0; i < numteams; i++)
 		{
-			if (!teamnames[i])
-				break;
 			if (fastcmp(p, teamnames[i])) {
 				CacheAndPushConstant(L, word, i);
 				return 1;
diff --git a/src/deh_soc.c b/src/deh_soc.c
index 0130b9a7a47c140bfc004b6cea459b88fe67854e..ad47c535ddd9421e477b24d3a5004f66eb4bc590 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -475,13 +475,12 @@ void readfreeslots(MYFILE *f)
 			}
 			else if (fastcmp(type, "TEAM"))
 			{
-				for (i = 0; i < MAXTEAMS; i++)
-					if (!teamnames[i]) {
-						teamnames[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
-						strcpy(teamnames[i],word);
-						numteams++;
-						break;
-					}
+				if (numteams < MAXTEAMS)
+				{
+					teamnames[numteams] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
+					strcpy(teamnames[numteams],word);
+					numteams++;
+				}
 			}
 			else if (fastcmp(type, "SPR2"))
 			{
@@ -1152,8 +1151,9 @@ void readgametype(MYFILE *f, INT32 num)
 	char gtconst[MAXLINELEN];
 	char gtdescription[441];
 
-	UINT8 teamcount = 0;
+	UINT8 teamcount = gametypes[num].teams.num;
 	UINT8 teamlist[MAXTEAMS];
+	boolean has_teams = false;
 
 	UINT8 newgtleftcolor, newgtrightcolor;
 	boolean has_desc_colors[2] = { false, false };
@@ -1329,26 +1329,20 @@ void readgametype(MYFILE *f, INT32 num)
 				do {
 					if (teamcount == MAXTEAMS)
 					{
-						deh_warning("readgametype %s: too many teams\n", gtname);
+						deh_warning("readgametype %d: too many teams\n", num);
 						break;
 					}
-					UINT8 team_id = TEAM_NONE;
-					for (i = 1; i < MAXTEAMS; i++)
-					{
-						if (!teamnames[i])
-							break;
-						if (fasticmp(tmp, teamnames[i]))
-						{
-							team_id = i;
-							break;
-						}
-					}
-					if (team_id == TEAM_NONE)
-						deh_warning("readgametype %s: unknown team %s\n", gtname, tmp);
-					else
+					char *tmp2 = Z_StrDup(tmp);
+					strupr(tmp2);
+					UINT8 team_id = get_team(tmp2);
+					if (team_id != TEAM_NONE)
 					{
 						teamlist[teamcount++] = team_id;
+						has_teams = true;
 					}
+					else
+						deh_warning("readgametype %d: unknown team %s\n", num, tmp);
+					Z_Free(tmp2);
 				} while((tmp = strtok(NULL,",")) != NULL);
 			}
 			// This SOC probably provided gametype rules as words, instead of using the RULES keyword.
@@ -1384,9 +1378,12 @@ void readgametype(MYFILE *f, INT32 num)
 		G_SetGametypeDescriptionRightColor(num, newgtrightcolor);
 
 	// Copy the teams
-	gametypes[num].teams.num = teamcount;
-	if (teamcount)
-		memcpy(gametypes[num].teams.list, teamlist, sizeof(teamlist[0]) * teamcount);
+	if (has_teams)
+	{
+		gametypes[num].teams.num = teamcount;
+		if (teamcount)
+			memcpy(gametypes[num].teams.list, teamlist, sizeof(teamlist[0]) * teamcount);
+	}
 
 	// Write the constant name.
 	if (gametypes[num].constant_name == NULL)
@@ -1403,6 +1400,133 @@ void readgametype(MYFILE *f, INT32 num)
 	G_UpdateGametypeSelections();
 }
 
+void readteam(MYFILE *f, INT32 num)
+{
+	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
+	char *word;
+	char *word2, *word2lwr = NULL;
+	INT32 i;
+
+	team_t *team = &teams[num];
+
+	do
+	{
+		if (myfgets(s, MAXLINELEN, f))
+		{
+			if (s[0] == '\n')
+				break;
+
+			word = strtok(s, " ");
+			if (word)
+				strupr(word);
+			else
+				break;
+
+			word2 = strtok(NULL, " = ");
+			if (word2)
+			{
+				if (!word2lwr)
+					word2lwr = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
+				strcpy(word2lwr, word2);
+				strupr(word2);
+			}
+			else
+				break;
+
+			if (word2[strlen(word2)-1] == '\n')
+				word2[strlen(word2)-1] = '\0';
+			i = atoi(word2);
+
+			if (fastcmp(word, "NAME"))
+			{
+				Z_Free(team->name);
+				team->name = Z_StrDup(word2lwr);
+			}
+			else if (fastcmp(word, "FLAGNAME"))
+			{
+				Z_Free(team->flag_name);
+				team->flag_name = Z_StrDup(word2lwr);
+			}
+			else if (fastcmp(word, "FLAG"))
+			{
+				team->flag = (UINT16)get_number(word2);
+			}
+			else if (fastcmp(word, "FLAGTYPE"))
+			{
+				if (i == 0 && word2[0] != '0') // If word2 isn't a number
+					i = get_mobjtype(word2); // find a thing by name
+				if (i < NUMMOBJTYPES && i > 0)
+					team->flag_mobj_type = i;
+				else
+				{
+					deh_warning("readteam %d: Thing %d out of range (1 - %d)", num, i, NUMMOBJTYPES-1);
+				}
+			}
+			else if (fastcmp(word, "COLOR"))
+			{
+				if (i == 0 && word2[0] != '0') // If word2 isn't a number
+					i = get_skincolor(word2); // find a skincolor by name
+				if (i && i < numskincolors)
+					team->color = i;
+				else
+				{
+					deh_warning("readteam %d: Color %d out of range (1 - %d)", num, i, numskincolors-1);
+				}
+			}
+			else if (fastcmp(word, "WEAPONCOLOR"))
+			{
+				if (i == 0 && word2[0] != '0') // If word2 isn't a number
+					i = get_skincolor(word2); // find a skincolor by name
+				if (i && i < numskincolors)
+					team->weapon_color = i;
+				else
+				{
+					deh_warning("readteam %d: Weapon color %d out of range (1 - %d)", num, i, numskincolors-1);
+				}
+			}
+			else if (fastcmp(word, "MISSILECOLOR"))
+			{
+				if (i == 0 && word2[0] != '0') // If word2 isn't a number
+					i = get_skincolor(word2); // find a skincolor by name
+				if (i && i < numskincolors)
+					team->missile_color = i;
+				else
+				{
+					deh_warning("readteam %d: Missile color %d out of range (1 - %d)", num, i, numskincolors-1);
+				}
+			}
+			else if (fastcmp(word, "ICON"))
+			{
+				G_SetTeamIcon(num, TEAM_ICON, word2lwr);
+			}
+			else if (fastcmp(word, "FLAGICON"))
+			{
+				G_SetTeamIcon(num, TEAM_ICON_FLAG, word2lwr);
+			}
+			else if (fastcmp(word, "GOTFLAGICON"))
+			{
+				G_SetTeamIcon(num, TEAM_ICON_GOT_FLAG, word2lwr);
+			}
+			else if (fastcmp(word, "MISSINGFLAGICON"))
+			{
+				G_SetTeamIcon(num, TEAM_ICON_MISSING_FLAG, word2lwr);
+			}
+			else
+			{
+				deh_warning("readteam %d: unknown word '%s'", num, word);
+			}
+		}
+	} while (!myfeof(f)); // finish when the line is empty
+
+	// Free strings.
+	Z_Free(s);
+	if (word2lwr)
+		Z_Free(word2lwr);
+
+	ST_LoadTeamIcons();
+	G_UpdateTeamSelection();
+}
+
 void readlevelheader(MYFILE *f, INT32 num)
 {
 	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@@ -4232,6 +4356,22 @@ INT16 get_gametype(const char *word)
 	return GT_COOP;
 }
 
+UINT8 get_team(const char *word)
+{
+	UINT8 i;
+	if (*word >= '0' && *word <= '9')
+		return atoi(word);
+	if (fastncmp("TEAM_",word,5))
+		word += 5; // take off the TEAM_
+	for (i = 0; i < numteams; i++)
+	{
+		if (fastcmp(word, teamnames[i]))
+			return i;
+	}
+	deh_warning("Couldn't find team named 'TEAM_%s'",word);
+	return TEAM_NONE;
+}
+
 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 dabf8e0a91c06139f2cf607783d224c48947e6af..a060e72e3eec67419808bfd77aea5af595a7991d 100644
--- a/src/deh_soc.h
+++ b/src/deh_soc.h
@@ -56,6 +56,7 @@ hudnum_t get_huditem(const char *word);
 menutype_t get_menutype(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,6 +72,7 @@ void readtextprompt(MYFILE *f, INT32 num);
 void readcutscene(MYFILE *f, INT32 num);
 void readlevelheader(MYFILE *f, INT32 num);
 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 51f960ac30b6b89d677b00d5c117a8b942365059..2c00ff133067899d86599f3edffcdbb152824d95 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -428,6 +428,18 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 							ignorelines(f);
 					}
 				}
+				else if (fastcmp(word, "TEAM"))
+				{
+					if (i == 0 && word2[0] != '0') // If word2 isn't a number
+						i = get_team(word2); // find a team by name
+					if (i >= 0 && i < MAXTEAMS)
+						readteam(f, i);
+					else
+					{
+						deh_warning("Team %d out of range (0 - %d)", i, MAXTEAMS);
+						ignorelines(f);
+					}
+				}
 				else if (fastcmp(word, "CUTSCENE"))
 				{
 					if (i > 0 && i < 129)
diff --git a/src/g_game.c b/src/g_game.c
index ad7866c8ae29494ffa23571daeed70a14767efd9..5f070711128ce8c770d19ceaf1148086e692b7d2 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3592,11 +3592,23 @@ void G_UpdateTeamSelection(void)
 		i++;
 	}
 
+	// If no selections were added, somehow, we add at least two fallbacks.
+	if (i == 0)
+	{
+		dummyteam_cons_t[i].value = 0;
+		dummyteam_cons_t[i].strvalue = "Spectator";
+		i++;
+
+		dummyteam_cons_t[i].value = 1;
+		dummyteam_cons_t[i].strvalue = "Playing";
+		i++;
+	}
+
 	dummyteam_cons_t[i].value = 0;
 	dummyteam_cons_t[i].strvalue = NULL;
 
 	cv_dummyteam.defaultvalue = dummyteam_cons_t[0].strvalue;
-	cv_dummyteam.value = 0;
+	cv_dummyteam.value = dummyteam_cons_t[0].value;
 	cv_dummyteam.string = cv_dummyteam.defaultvalue;
 }
 
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index bf694425d2888a00b48594b39521c150b9591d8d..0658387752ca37211da606d48ae88cdee8209215 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -2314,7 +2314,7 @@ static void Command_Teamchange_f(void)
 			if (M_StringOnlyHasDigits(COM_Argv(1)))
 			{
 				newteam = atoi(COM_Argv(1));
-				if (newteam >= teamsingame)
+				if (newteam == TEAM_NONE || newteam >= teamsingame)
 					newteam = MAXTEAMS;
 			}
 			else
@@ -2430,7 +2430,7 @@ static void Command_Teamchange2_f(void)
 			if (M_StringOnlyHasDigits(COM_Argv(1)))
 			{
 				newteam = atoi(COM_Argv(1));
-				if (newteam >= teamsingame)
+				if (newteam == TEAM_NONE || newteam >= teamsingame)
 					newteam = MAXTEAMS;
 			}
 			else
@@ -2567,7 +2567,7 @@ static void Command_ServerTeamChange_f(void)
 			if (M_StringOnlyHasDigits(COM_Argv(1)))
 			{
 				newteam = atoi(COM_Argv(1));
-				if (newteam >= teamsingame)
+				if (newteam == TEAM_NONE || newteam >= teamsingame)
 					newteam = MAXTEAMS;
 			}
 			else