diff --git a/src/g_game.c b/src/g_game.c
index 0e03c006c7e6fe27c9b0720a529a82c5aa83fcf3..2822e8d62a63a5fc0e15aff2bc1792d4ba485586 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3947,7 +3947,7 @@ UINT8 G_GetTeamFromTeamFlag(UINT32 flag)
 {
 	for (UINT16 i = 1; i < teamsingame; i++)
 	{
-		UINT32 otherflag = 1 << (i - 1);
+		UINT32 otherflag = teams[G_GetTeam(i)].flag;
 		if (flag == otherflag)
 			return i;
 	}
@@ -3961,7 +3961,7 @@ UINT8 G_GetTeamListFromTeamFlags(UINT8 *teamlist, UINT32 flags)
 
 	for (UINT16 i = 1; i < teamsingame; i++)
 	{
-		UINT32 otherflag = 1 << (i - 1);
+		UINT32 otherflag = teams[G_GetTeam(i)].flag;
 		if ((flags & otherflag) != 0)
 		{
 			teamlist[count++] = i;
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index c8b237bf6b89dd26111e93afa5ecbb869514db1c..2fedd37232f0828e2fc3edfe4ed09081cdf452d4 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -2340,6 +2340,19 @@ static int lib_pPlayerTouchingSectorSpecialFlag(lua_State *L)
 	return 1;
 }
 
+static int lib_pMobjTouchingTeamBase(lua_State *L)
+{
+	mobj_t *mo = *((mobj_t**)luaL_checkudata(L, 1, META_MOBJ));
+	INT32 team = (INT32)luaL_checkinteger(L, 2);
+	INLEVEL
+	if (!mo)
+		return LUA_ErrInvalid(L, "mobj_t");
+	if (team <= 0 || team >= teamsingame)
+		luaL_error(L, "team index %d out of range (1 - %d)", team, teamsingame-1);
+	LUA_PushUserdata(L, P_MobjTouchingTeamBase(mo, team), META_SECTOR);
+	return 1;
+}
+
 static int lib_pFindLowestFloorSurrounding(lua_State *L)
 {
 	sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
@@ -2481,11 +2494,29 @@ static int lib_pFadeLight(lua_State *L)
 static int lib_pIsFlagAtBase(lua_State *L)
 {
 	mobjtype_t flag = luaL_checkinteger(L, 1);
-	//HUDSAFE
 	INLEVEL
 	if (flag >= NUMMOBJTYPES)
 		return luaL_error(L, "mobj type %d out of range (0 - %d)", flag, NUMMOBJTYPES-1);
-	lua_pushboolean(L, P_IsFlagAtBase(flag));
+	for (UINT8 i = 1; i < teamsingame; i++)
+	{
+		UINT8 team = G_GetTeam(i);
+		if (teams[team].flag_mobj_type == flag)
+		{
+			lua_pushboolean(L, P_TeamHasFlagAtBase(team));
+			return 1;
+		}
+	}
+	lua_pushboolean(L, false);
+	return 1;
+}
+
+static int lib_pTeamHasFlagAtBase(lua_State *L)
+{
+	INT32 team = luaL_checkinteger(L, 1);
+	INLEVEL
+	if (team <= 0 || team >= teamsingame)
+		luaL_error(L, "team index %d out of range (1 - %d)", team, teamsingame-1);
+	lua_pushboolean(L, P_TeamHasFlagAtBase(team));
 	return 1;
 }
 
@@ -4281,6 +4312,7 @@ static luaL_Reg lib[] = {
 	{"P_MobjTouchingSectorSpecialFlag",lib_pMobjTouchingSectorSpecialFlag},
 	{"P_PlayerTouchingSectorSpecial",lib_pPlayerTouchingSectorSpecial},
 	{"P_PlayerTouchingSectorSpecialFlag",lib_pPlayerTouchingSectorSpecialFlag},
+	{"P_MobjTouchingTeamBase",lib_pMobjTouchingTeamBase},
 	{"P_FindLowestFloorSurrounding",lib_pFindLowestFloorSurrounding},
 	{"P_FindHighestFloorSurrounding",lib_pFindHighestFloorSurrounding},
 	{"P_FindNextHighestFloor",lib_pFindNextHighestFloor},
@@ -4293,6 +4325,7 @@ static luaL_Reg lib[] = {
 	{"P_SpawnLightningFlash",lib_pSpawnLightningFlash},
 	{"P_FadeLight",lib_pFadeLight},
 	{"P_IsFlagAtBase",lib_pIsFlagAtBase},
+	{"P_TeamHasFlagAtBase",lib_pTeamHasFlagAtBase},
 	{"P_SetupLevelSky",lib_pSetupLevelSky},
 	{"P_SetSkyboxMobj",lib_pSetSkyboxMobj},
 	{"P_StartQuake",lib_pStartQuake},
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 3d95cdb751f8ae349ed026591ede5cd55438b189..e4d6f48010452956f37d82e133078448ec698d89 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -60,6 +60,7 @@ enum sector_e {
 	sector_flags,
 	sector_specialflags,
 	sector_damagetype,
+	sector_teambase,
 	sector_triggertag,
 	sector_triggerer,
 	sector_friction,
@@ -98,6 +99,7 @@ static const char *const sector_opt[] = {
 	"flags",
 	"specialflags",
 	"damagetype",
+	"teambase",
 	"triggertag",
 	"triggerer",
 	"friction",
@@ -760,6 +762,9 @@ static int sector_get(lua_State *L)
 	case sector_damagetype: // damagetype
 		lua_pushinteger(L, (UINT8)sector->damagetype);
 		return 1;
+	case sector_teambase: // teambase
+		lua_pushinteger(L, (UINT8)sector->teambase);
+		return 1;
 	case sector_triggertag: // triggertag
 		lua_pushinteger(L, (INT16)sector->triggertag);
 		return 1;
@@ -893,6 +898,9 @@ static int sector_set(lua_State *L)
 	case sector_damagetype:
 		sector->damagetype = (UINT8)luaL_checkinteger(L, 3);
 		break;
+	case sector_teambase:
+		sector->teambase = (UINT8)luaL_checkinteger(L, 3);
+		break;
 	case sector_triggertag:
 		sector->triggertag = (INT16)luaL_checkinteger(L, 3);
 		break;
diff --git a/src/p_inter.c b/src/p_inter.c
index 8a04034545d8e35487358f852a28f13d150d256a..1fd66eaa22e1b8d265eac26ca8e1a9cbc6bb9651 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -566,7 +566,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 		if (special->extravalue1 > TEAM_NONE && special->extravalue1 < numteams)
 		{
 			UINT8 flagteam = special->extravalue1;
-			sectorspecialflags_t specialflag = flagteam == TEAM_RED ? SSF_REDTEAMBASE : SSF_BLUETEAMBASE;
 			const char *flagtext;
 			const char *flagcolor;
 			char plname[MAXPLAYERNAME+4];
@@ -588,7 +587,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					special->fuse = 1;
 					special->flags2 |= MF2_JUSTATTACKED;
 
-					if (!P_PlayerTouchingSectorSpecialFlag(player, specialflag))
+					if (!P_PlayerTouchingTeamBase(player, flagteam))
 						CONS_Printf(M_GetText("%s returned the %s%s%c to base.\n"), plname, flagcolor, flagtext, 0x80);
 				}
 				return;
@@ -4381,14 +4380,28 @@ void P_PlayerFlagBurst(player_t *player, boolean toss)
 	if (!player->gotflag)
 		return;
 
-	for (UINT8 i = 1; i < numteams; i++)
+	UINT8 totalcaptured = 0;
+	UINT8 teamscaptured[MAXTEAMS];
+
+	memset(teamscaptured, 0, sizeof(teamscaptured));
+
+	for (UINT8 i = 1; i < teamsingame; i++)
 	{
-		UINT8 team = G_GetTeam(i);
-		UINT32 flagflag = 1 << (team - 1);
-		if (!(player->gotflag & flagflag))
+		UINT8 otherteam = G_GetTeam(i);
+		UINT32 flagflag = teams[otherteam].flag;
+		if (!(player->gotflag & flagflag) || otherteam == player->ctfteam)
+			continue;
+		if (teams[otherteam].flag_mobj_type == 0)
 			continue;
 
-		mobj_t *flag = P_SpawnTeamFlag(team, player->mo->x, player->mo->y, player->mo->z);
+		teamscaptured[totalcaptured++] = otherteam;
+	}
+
+	for (UINT8 i = 0; i < totalcaptured; i++)
+	{
+		UINT8 otherteam = teamscaptured[i];
+
+		mobj_t *flag = P_SpawnTeamFlag(otherteam, player->mo->x, player->mo->y, player->mo->z);
 		if (flag == NULL)
 			continue;
 
@@ -4402,7 +4415,12 @@ void P_PlayerFlagBurst(player_t *player, boolean toss)
 			P_InstaThrust(flag, player->mo->angle, FixedMul(6*FRACUNIT, player->mo->scale));
 		else
 		{
-			angle_t fa = P_RandomByte()*FINEANGLES/256;
+			angle_t fa;
+			if (totalcaptured == 1)
+				fa = P_RandomByte()*FINEANGLES/256;
+			else
+				fa = ((255 / totalcaptured) * i) * FINEANGLES/256;
+
 			flag->momx = FixedMul(FINECOSINE(fa), FixedMul(6*FRACUNIT, player->mo->scale));
 			if (!(twodlevel || (player->mo->flags2 & MF2_TWOD)))
 				flag->momy = FixedMul(FINESINE(fa), FixedMul(6*FRACUNIT, player->mo->scale));
@@ -4412,37 +4430,34 @@ void P_PlayerFlagBurst(player_t *player, boolean toss)
 		if (player->mo->eflags & MFE_VERTICALFLIP)
 			flag->momz = -flag->momz;
 
-		flag->spawnpoint = flagpoints[team];
+		flag->spawnpoint = flagpoints[otherteam];
 
 		flag->fuse = cv_flagtime.value * TICRATE;
 		P_SetTarget(&flag->target, player->mo);
 
+		// Take out the flag from the player
+		player->gotflag &= ~teams[otherteam].flag;
+
 		// Flag text
-		{
-			char plname[MAXPLAYERNAME+4];
-			const char *flagtext;
-			const char *flagcolor;
+		char plname[MAXPLAYERNAME+4];
 
-			snprintf(plname, sizeof(plname), "%s%s%s",
-					 CTFTEAMCODE(player),
-					 player_names[player - players],
-					 CTFTEAMENDCODE(player));
+		snprintf(plname, sizeof(plname), "%s%s%s",
+				 CTFTEAMCODE(player),
+				 player_names[player - players],
+				 CTFTEAMENDCODE(player));
 
-			flagtext = G_GetTeamFlagName(team);
-			flagcolor = GetChatColorForSkincolor(G_GetTeamColor(team));
+		const char *flagtext = G_GetTeamFlagName(otherteam);
+		const char *flagcolor = GetChatColorForSkincolor(G_GetTeamColor(otherteam));
 
-			if (toss)
-				CONS_Printf(M_GetText("%s tossed the %s%s%c.\n"), plname, flagcolor, flagtext, 0x80);
-			else
-				CONS_Printf(M_GetText("%s dropped the %s%s%c.\n"), plname, flagcolor, flagtext, 0x80);
-		}
+		if (toss)
+			CONS_Printf(M_GetText("%s tossed the %s%s%c.\n"), plname, flagcolor, flagtext, 0x80);
+		else
+			CONS_Printf(M_GetText("%s dropped the %s%s%c.\n"), plname, flagcolor, flagtext, 0x80);
 
 		// Pointers set for displaying time value and for consistency restoration.
-		P_SetTarget(&flagmobjs[team], flag);
+		P_SetTarget(&flagmobjs[otherteam], flag);
 	}
 
-	player->gotflag = 0;
-
 	if (toss)
 		player->tossdelay = 2*TICRATE;
 }
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 0812f6c3a7b43dfb374c9db8753a389e10f21f7a..33e3aa4640c0edded1c6bcb1f2e80b5a7509f12e 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -867,6 +867,7 @@ static void P_NetUnArchiveWaypoints(void)
 #define SD_TRIGGERTAG 0x02
 #define SD_TRIGGERER 0x04
 #define SD_GRAVITY   0x08
+#define SD_TEAMBASE  0x10
 
 #define LD_FLAG     0x01
 #define LD_SPECIAL  0x02
@@ -1064,6 +1065,8 @@ static void ArchiveSectors(void)
 			diff4 |= SD_TRIGGERER;
 		if (ss->gravity != spawnss->gravity)
 			diff4 |= SD_GRAVITY;
+		if (ss->teambase != spawnss->teambase)
+			diff4 |= SD_TEAMBASE;
 
 		if (ss->ffloors && CheckFFloorDiff(ss))
 			diff |= SD_FFLOORS;
@@ -1145,6 +1148,8 @@ static void ArchiveSectors(void)
 				WRITEUINT8(save_p, ss->triggerer);
 			if (diff4 & SD_GRAVITY)
 				WRITEFIXED(save_p, ss->gravity);
+			if (diff4 & SD_TEAMBASE)
+				WRITEUINT8(save_p, ss->teambase);
 			if (diff & SD_FFLOORS)
 				ArchiveFFloors(ss);
 		}
@@ -1265,6 +1270,8 @@ static void UnArchiveSectors(void)
 			sectors[i].triggerer = READUINT8(save_p);
 		if (diff4 & SD_GRAVITY)
 			sectors[i].gravity = READFIXED(save_p);
+		if (diff4 & SD_TEAMBASE)
+			sectors[i].teambase = READUINT8(save_p);
 
 		if (diff & SD_FFLOORS)
 			UnArchiveFFloors(&sectors[i]);
diff --git a/src/p_setup.c b/src/p_setup.c
index 89b8ff7699181a74fc5521f2234ec8c079074253..92d22097ce19b58f0b468b14fd4c910763e1b806 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1801,9 +1801,28 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char
 	else if (fastcmp(param, "returnflag") && fastcmp("true", val))
 		sectors[i].specialflags |= SSF_RETURNFLAG;
 	else if (fastcmp(param, "redteambase") && fastcmp("true", val))
+	{
 		sectors[i].specialflags |= SSF_REDTEAMBASE;
+		sectors[i].teambase = G_GetTeam(TEAM_RED);
+	}
 	else if (fastcmp(param, "blueteambase") && fastcmp("true", val))
+	{
 		sectors[i].specialflags |= SSF_BLUETEAMBASE;
+		sectors[i].teambase = G_GetTeam(TEAM_BLUE);
+	}
+	else if (fastcmp(param, "teambase"))
+	{
+		sectors[i].teambase = TEAM_NONE;
+
+		for (UINT8 j = 0; j < numteams; j++)
+		{
+			if (fastcmp(val, teamnames[j]))
+			{
+				sectors[j].teambase = j;
+				break;
+			}
+		}
+	}
 	else if (fastcmp(param, "fan") && fastcmp("true", val))
 		sectors[i].specialflags |= SSF_FAN;
 	else if (fastcmp(param, "supertransform") && fastcmp("true", val))
@@ -2711,6 +2730,22 @@ static void P_WriteTextmap(void)
 			fprintf(f, "redteambase = true;\n");
 		if (wsectors[i].specialflags & SSF_BLUETEAMBASE)
 			fprintf(f, "blueteambase = true;\n");
+		if (wsectors[i].teambase != TEAM_NONE && (wsectors[i].specialflags & (SSF_REDTEAMBASE | SSF_BLUETEAMBASE)) == 0)
+		{
+			// We DON'T write the teambase if the sector has (SSF_REDTEAMBASE | SSF_BLUETEAMBASE) because
+			// these flags get the team bases for the current gametype's team 1 and team 2, rather than the
+			// actual teams TEAM_RED and TEAM_BLUE.
+			UINT8 team = wsectors[i].teambase;
+			if (team != TEAM_NONE && team < numteams)
+			{
+				char *teambase = Z_StrDup(teamnames[team]);
+				strlwr(teambase);
+				fprintf(f, "teambase = \"%s\";\n", teambase);
+				Z_Free(teambase);
+			}
+			else
+				fprintf(f, "teambase = \"%s\";\n", "unknown");
+		}
 		if (wsectors[i].specialflags & SSF_FAN)
 			fprintf(f, "fan = true;\n");
 		if (wsectors[i].specialflags & SSF_SUPERTRANSFORM)
@@ -6201,9 +6236,11 @@ static void P_ConvertBinarySectorTypes(void)
 				break;
 			case 3: //Red team base
 				sectors[i].specialflags |= SSF_REDTEAMBASE;
+				sectors[i].teambase = G_GetTeam(TEAM_RED);
 				break;
 			case 4: //Blue team base
 				sectors[i].specialflags |= SSF_BLUETEAMBASE;
+				sectors[i].teambase = G_GetTeam(TEAM_BLUE);
 				break;
 			case 5: //Fan sector
 				sectors[i].specialflags |= SSF_FAN;
diff --git a/src/p_spec.c b/src/p_spec.c
index c1703f8fe5ac38d3e0dca4cda77aa69ccd17588e..ea345b1b9b0ef0f149f7bdc7c5bc060523991bd6 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -3941,15 +3941,18 @@ void P_SetupSignExit(player_t *player)
 }
 
 //
-// P_IsFlagAtBase
+// P_TeamHasFlagAtBase
 //
-// Checks to see if a flag is at its base.
+// Checks to see if a team has its flag at its base.
 //
-boolean P_IsFlagAtBase(mobjtype_t flag)
+boolean P_TeamHasFlagAtBase(UINT8 team)
 {
+	if (team == TEAM_NONE || team >= teamsingame)
+		return false;
+
 	thinker_t *think;
 	mobj_t *mo;
-	sectorspecialflags_t specialflag = (flag == teams[TEAM_RED].flag_mobj_type) ? SSF_REDTEAMBASE : SSF_BLUETEAMBASE;
+	mobjtype_t flag = teams[team].flag_mobj_type;
 
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
@@ -3961,7 +3964,7 @@ boolean P_IsFlagAtBase(mobjtype_t flag)
 		if (mo->type != flag)
 			continue;
 
-		if (mo->subsector->sector->specialflags & specialflag)
+		if (mo->subsector->sector->teambase == team)
 			return true;
 		else if (mo->subsector->sector->ffloors) // Check the 3D floors
 		{
@@ -3972,7 +3975,7 @@ boolean P_IsFlagAtBase(mobjtype_t flag)
 				if (!(rover->fofflags & FOF_EXISTS))
 					continue;
 
-				if (!(rover->master->frontsector->specialflags & specialflag))
+				if (rover->master->frontsector->teambase != team)
 					continue;
 
 				if (!(mo->z <= P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector)
@@ -4238,6 +4241,97 @@ sector_t *P_MobjTouchingSectorSpecialFlag(mobj_t *mo, sectorspecialflags_t flag)
 	return NULL;
 }
 
+static sector_t *P_MobjTouching3DFloorTeamBase(mobj_t *mo, sector_t *sector, UINT8 team)
+{
+	ffloor_t *rover;
+
+	for (rover = sector->ffloors; rover; rover = rover->next)
+	{
+		if (rover->master->frontsector->teambase != team)
+			continue;
+
+		if (!(rover->fofflags & FOF_EXISTS))
+			continue;
+
+		if (!P_IsMobjTouching3DFloor(mo, rover, sector))
+			continue;
+
+		// This FOF has the special we're looking for, but are we allowed to touch it?
+		if (sector == mo->subsector->sector
+			|| (rover->master->frontsector->flags & MSF_TRIGGERSPECIAL_TOUCH))
+			return rover->master->frontsector;
+	}
+
+	return NULL;
+}
+
+static sector_t *P_MobjTouchingPolyobjTeamBase(mobj_t *mo, UINT8 team)
+{
+	polyobj_t *po;
+	sector_t *polysec;
+	boolean touching = false;
+	boolean inside = false;
+
+	for (po = mo->subsector->polyList; po; po = (polyobj_t *)(po->link.next))
+	{
+		if (po->flags & POF_NOSPECIALS)
+			continue;
+
+		polysec = po->lines[0]->backsector;
+
+		if (polysec->teambase != team)
+			continue;
+
+		touching = (polysec->flags & MSF_TRIGGERSPECIAL_TOUCH) && P_MobjTouchingPolyobj(po, mo);
+		inside = P_MobjInsidePolyobj(po, mo);
+
+		if (!(inside || touching))
+			continue;
+
+		if (!P_IsMobjTouchingPolyobj(mo, po, polysec))
+			continue;
+
+		return polysec;
+	}
+
+	return NULL;
+}
+
+sector_t *P_MobjTouchingTeamBase(mobj_t *mo, UINT8 team)
+{
+	msecnode_t *node;
+	sector_t *result;
+
+	result = P_MobjTouching3DFloorTeamBase(mo, mo->subsector->sector, team);
+	if (result)
+		return result;
+
+	result = P_MobjTouchingPolyobjTeamBase(mo, team);
+	if (result)
+		return result;
+
+	if (mo->subsector->sector->teambase == team)
+		return mo->subsector->sector;
+
+	for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
+	{
+		if (node->m_sector == mo->subsector->sector) // Don't duplicate
+			continue;
+
+		result = P_MobjTouching3DFloorTeamBase(mo, node->m_sector, team);
+		if (result)
+			return result;
+
+		if (!(node->m_sector->flags & MSF_TRIGGERSPECIAL_TOUCH))
+			continue;
+
+		if (node->m_sector->teambase == team)
+			return node->m_sector;
+	}
+
+	return NULL;
+}
+
 //
 // P_PlayerTouchingSectorSpecial
 //
@@ -4265,6 +4359,14 @@ sector_t *P_PlayerTouchingSectorSpecialFlag(player_t *player, sectorspecialflags
 	return P_MobjTouchingSectorSpecialFlag(player->mo, flag);
 }
 
+sector_t *P_PlayerTouchingTeamBase(player_t *player, UINT8 base)
+{
+	if (!player->mo)
+		return NULL;
+
+	return P_MobjTouchingTeamBase(player->mo, base);
+}
+
 static sector_t *P_CheckPlayer3DFloorTrigger(player_t *player, sector_t *sector, line_t *sourceline)
 {
 	ffloor_t *rover;
@@ -4637,48 +4739,90 @@ static void P_ProcessExitSector(player_t *player, mtag_t sectag)
 
 static void P_ProcessTeamBase(player_t *player, UINT8 team)
 {
-	mobj_t *mo;
+	if (team == TEAM_NONE || team >= teamsingame)
+		return;
 
 	if (!(gametyperules & GTR_TEAMFLAGS))
 		return;
 
+	// Make sure the team still has their own flag at their base so they can score.
+	if (!P_TeamHasFlagAtBase(team))
+		return;
+
 	if (!P_IsObjectOnGround(player->mo))
 		return;
 
-	if (player->ctfteam != team)
+	if (!player->gotflag || player->ctfteam != team)
 		return;
 
-	UINT8 otherteam = team == TEAM_RED ? TEAM_BLUE : TEAM_RED;
-	UINT32 teamflag = teams[otherteam].flag;
+	UINT8 totalcaptured = 0;
+	UINT8 teamscaptured[MAXTEAMS];
 
-	if (!(player->gotflag & teamflag))
-		return;
+	memset(teamscaptured, 0, sizeof(teamscaptured));
 
-	// Make sure the team still has their own
-	// flag at their base so they can score.
-	if (!P_IsFlagAtBase(teams[team].flag_mobj_type))
-		return;
+	for (UINT8 i = 1; i < teamsingame; i++)
+	{
+		UINT8 otherteam = G_GetTeam(i);
+		UINT32 flagflag = teams[otherteam].flag;
+		if (!(player->gotflag & flagflag) || otherteam == player->ctfteam)
+			continue;
+
+		teamscaptured[totalcaptured++] = otherteam;
+		teamscores[team]++;
+
+		player->gotflag &= ~flagflag;
+
+		P_AddPlayerScore(player, 250);
+
+		if (splitscreen || players[consoleplayer].ctfteam == team)
+			S_StartSound(NULL, sfx_flgcap);
+		else if (players[consoleplayer].ctfteam != team)
+			S_StartSound(NULL, sfx_lose);
+
+		mobj_t *mo = P_SpawnTeamFlag(otherteam, player->mo->x, player->mo->y, player->mo->z);
+		if (mo)
+		{
+			mo->flags &= ~MF_SPECIAL;
+			mo->fuse = TICRATE;
+			mo->spawnpoint = flagpoints[otherteam];
+			mo->flags2 |= MF2_JUSTATTACKED;
+		}
+	}
 
 	HU_SetCEchoFlags(V_AUTOFADEOUT|V_ALLOWLOWERCASE);
 	HU_SetCEchoDuration(5);
-	HU_DoCEcho(va(M_GetText("%s%s\200\\captured the %s%s\200.\\\\\\\\"), GetChatColorForSkincolor(G_GetTeamColor(team)), player_names[player-players], GetChatColorForSkincolor(G_GetTeamColor(otherteam)), G_GetTeamFlagName(otherteam)));
-
-	if (splitscreen || players[consoleplayer].ctfteam == team)
-		S_StartSound(NULL, sfx_flgcap);
-	else if (players[consoleplayer].ctfteam != team)
-		S_StartSound(NULL, sfx_lose);
 
-	mo = P_SpawnTeamFlag(otherteam, player->mo->x, player->mo->y, player->mo->z);
-	if (mo)
+	if (totalcaptured == 1)
 	{
-		mo->flags &= ~MF_SPECIAL;
-		mo->fuse = TICRATE;
-		mo->spawnpoint = flagpoints[otherteam];
-		mo->flags2 |= MF2_JUSTATTACKED;
+		UINT8 otherteam = teamscaptured[0];
+		HU_DoCEcho(va(M_GetText("%s%s\200\\captured the %s%s\200.\\\\\\\\"), GetChatColorForSkincolor(G_GetTeamColor(team)), player_names[player-players], GetChatColorForSkincolor(G_GetTeamColor(otherteam)), G_GetTeamFlagName(otherteam)));
+	}
+	else
+	{
+		char *buffer = NULL;
+		size_t buffer_size = 0;
+
+		const char *text = va(M_GetText("%s%s\200 captured the:\\"), GetChatColorForSkincolor(G_GetTeamColor(team)), player_names[player-players]);
+		buffer_size += strlen(text) + 1;
+		buffer = Z_Realloc(buffer, buffer_size, PU_STATIC, NULL);
+		strcpy(buffer, text);
+
+		for (UINT8 i = 0; i < totalcaptured; i++)
+		{
+			UINT8 otherteam = teamscaptured[i];
+
+			text = va(M_GetText("%s%s\200\\"), GetChatColorForSkincolor(G_GetTeamColor(otherteam)), G_GetTeamFlagName(otherteam));
+
+			buffer_size += strlen(text) + 1;
+			buffer = Z_Realloc(buffer, buffer_size, PU_STATIC, NULL);
+
+			strcat(buffer, text);
+		}
+
+		HU_DoCEcho(buffer);
+
+		Z_Free(buffer);
 	}
-	player->gotflag &= ~teamflag;
-	teamscores[team]++;
-	P_AddPlayerScore(player, 250);
 }
 
 static void P_ProcessZoomTube(player_t *player, mtag_t sectag, boolean end)
@@ -4999,10 +5143,8 @@ static void P_EvaluateSpecialFlags(player_t *player, sector_t *sector, sector_t
 		P_ProcessExitSector(player, sectag);
 	if ((sector->specialflags & SSF_SPECIALSTAGEPIT) && isTouching)
 		P_ProcessSpecialStagePit(player);
-	if ((sector->specialflags & SSF_REDTEAMBASE) && isTouching)
-		P_ProcessTeamBase(player, TEAM_RED);
-	if ((sector->specialflags & SSF_BLUETEAMBASE) && isTouching)
-		P_ProcessTeamBase(player, TEAM_BLUE);
+	if ((sector->teambase != TEAM_NONE) && isTouching)
+		P_ProcessTeamBase(player, sector->teambase);
 	if (sector->specialflags & SSF_FAN)
 	{
 		player->mo->momz += mobjinfo[MT_FAN].mass/4;
diff --git a/src/p_spec.h b/src/p_spec.h
index 50ab6410f145b0d2ad180b492525cec51d786365..ea23d3b0df527c09cc95afbae4030b42f15c7878 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -506,6 +506,8 @@ sector_t *P_FindPlayerTrigger(player_t *player, line_t *sourceline);
 boolean P_IsPlayerValid(size_t playernum);
 boolean P_CanPlayerTrigger(size_t playernum);
 void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *roversector);
+sector_t *P_MobjTouchingTeamBase(mobj_t *mo, UINT8 team);
+sector_t *P_PlayerTouchingTeamBase(player_t *player, UINT8 team);
 
 fixed_t P_FindLowestFloorSurrounding(sector_t *sec);
 fixed_t P_FindHighestFloorSurrounding(sector_t *sec);
@@ -519,7 +521,7 @@ fixed_t P_FindHighestCeilingSurrounding(sector_t *sec);
 INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max);
 
 void P_SetupSignExit(player_t *player);
-boolean P_IsFlagAtBase(mobjtype_t flag);
+boolean P_TeamHasFlagAtBase(UINT8 team);
 
 boolean P_IsMobjTouchingSectorPlane(mobj_t *mo, sector_t *sec);
 boolean P_IsMobjTouching3DFloor(mobj_t *mo, ffloor_t *ffloor, sector_t *sec);
diff --git a/src/r_defs.h b/src/r_defs.h
index a9b9a4a0835a5969b93e4def8a2f231b5d565991..96b9711d1e15d522994503439034c39c759c8691 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -467,6 +467,7 @@ typedef struct sector_s
 	sectorflags_t flags;
 	sectorspecialflags_t specialflags;
 	UINT8 damagetype;
+	UINT8 teambase;
 
 	// Linedef executor triggering
 	mtag_t triggertag; // tag to call upon triggering