diff --git a/src/deh_tables.c b/src/deh_tables.c
index 77cae0e70f019b61b874039a0ca7d0ea086e0235..588bbea9beab385695b191c74e0b22630af72584 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -3697,6 +3697,7 @@ const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for sanity t
 	"MT_TOKEN", // Special Stage token for special stage
 	"MT_REDFLAG", // Red CTF Flag
 	"MT_BLUEFLAG", // Blue CTF Flag
+	"MT_TEAMFLAG", // Team flag
 	"MT_EMBLEM",
 	"MT_EMERALD1",
 	"MT_EMERALD2",
@@ -4415,6 +4416,7 @@ const char *const MOBJEFLAG_LIST[] = {
 	"TRACERANGLE", // Compute and trigger on mobj angle relative to tracer
 	"FORCESUPER", // Forces an object to use super sprites with SPR_PLAY.
 	"FORCENOSUPER", // Forces an object to NOT use super sprites with SPR_PLAY.
+	"TEAMFLAG", // Object is a team flag
 	NULL
 };
 
diff --git a/src/info.c b/src/info.c
index 15ff40c675e8d3c05486c50c3aa2cdc110dd080d..bab6945d6ec15900cac9764572280ba95b83dff8 100644
--- a/src/info.c
+++ b/src/info.c
@@ -7157,6 +7157,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
+	{           // MT_TEAMFLAG
+		323,            // doomednum
+		S_NULL,         // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_lvpass,     // deathsound
+		8,              // speed
+		24*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SPECIAL,     // flags
+		S_NULL          // raisestate
+	},
+
 	{           // MT_EMBLEM
 		322,            // doomednum
 		S_EMBLEM1,      // spawnstate
diff --git a/src/info.h b/src/info.h
index a2d87dbdc10644edaaf34187d45495d70ba49f85..f5ab1595e87ffc104c87fb45976a9f32d13065cc 100644
--- a/src/info.h
+++ b/src/info.h
@@ -4529,6 +4529,7 @@ typedef enum mobj_type
 	MT_TOKEN, // Special Stage token for special stage
 	MT_REDFLAG, // Red CTF Flag
 	MT_BLUEFLAG, // Blue CTF Flag
+	MT_TEAMFLAG, // Team flag
 	MT_EMBLEM,
 	MT_EMERALD1,
 	MT_EMERALD2,
diff --git a/src/p_inter.c b/src/p_inter.c
index f0531c3b39f3c1c18cebce443e39e614c7b6cd85..0d5c4af74e84b2e9a06545c087ef4989e037ea6a 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -553,6 +553,60 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 		P_DamageMobj(toucher, special, special, 1, DMG_FIRE);
 		return;
 	}
+	// CTF Flags
+	else if (special->eflags & MFE_TEAMFLAG)
+	{
+		if (player->bot && player->bot != BOT_MPAI)
+			return;
+		if (player->powers[pw_flashing] || player->tossdelay)
+			return;
+		if (!special->spawnpoint)
+			return;
+		if (special->fuse == 1)
+			return;
+		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];
+
+			flagtext = G_GetTeamFlagName(flagteam);
+			flagcolor = GetChatColorForSkincolor(G_GetTeamColor(flagteam));
+			snprintf(plname, sizeof(plname), "%s%s%s",
+					 CTFTEAMCODE(player),
+					 player_names[player - players],
+					 CTFTEAMENDCODE(player));
+
+			if (player->ctfteam == flagteam) // Player is on the same team as the flag
+			{
+				// Ignore height, only check x/y for now
+				// avoids stupid problems with some flags constantly returning
+				if (special->x>>FRACBITS != special->spawnpoint->x
+				    || special->y>>FRACBITS != special->spawnpoint->y)
+				{
+					special->fuse = 1;
+					special->flags2 |= MF2_JUSTATTACKED;
+
+					if (!P_PlayerTouchingSectorSpecialFlag(player, specialflag))
+						CONS_Printf(M_GetText("%s returned the %s%s%c to base.\n"), plname, flagcolor, flagtext, 0x80);
+				}
+				return;
+			}
+			else if (player->ctfteam) // Player is on the other team (and not a spectator)
+			{
+				if (player->powers[pw_super])
+					return;
+
+				player->gotflag |= teams[flagteam].flag;
+				CONS_Printf(M_GetText("%s picked up the %s%s%c!\n"), plname, flagcolor, flagtext, 0x80);
+				P_SetTarget(&flagmobjs[flagteam], NULL);
+			}
+		}
+		else
+			return;
+	}
 	else
 	{
 	// We now identify by object type, not sprite! Tails 04-11-2001
@@ -834,68 +888,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				}
 			}
 
-		// CTF Flags
-		case MT_REDFLAG:
-		case MT_BLUEFLAG:
-			if (player->bot && player->bot != BOT_MPAI)
-				return;
-			if (player->powers[pw_flashing] || player->tossdelay)
-				return;
-			if (!special->spawnpoint)
-				return;
-			if (special->fuse == 1)
-				return;
-//			if (special->momz > 0)
-//				return;
-			if (special->info->mass < numteams)
-			{
-				UINT8 flagteam = special->info->mass;
-				sectorspecialflags_t specialflag = flagteam == TEAM_RED ? SSF_REDTEAMBASE : SSF_BLUETEAMBASE;
-				const char *flagtext;
-				const char *flagcolor;
-				char plname[MAXPLAYERNAME+4];
-
-				flagtext = G_GetTeamFlagName(flagteam);
-				flagcolor = GetChatColorForSkincolor(G_GetTeamColor(flagteam));
-				snprintf(plname, sizeof(plname), "%s%s%s",
-						 CTFTEAMCODE(player),
-						 player_names[player - players],
-						 CTFTEAMENDCODE(player));
-
-				if (player->ctfteam == flagteam) // Player is on the same team as the flag
-				{
-					// Ignore height, only check x/y for now
-					// avoids stupid problems with some flags constantly returning
-					if (special->x>>FRACBITS != special->spawnpoint->x
-					    || special->y>>FRACBITS != special->spawnpoint->y)
-					{
-						special->fuse = 1;
-						special->flags2 |= MF2_JUSTATTACKED;
-
-						if (!P_PlayerTouchingSectorSpecialFlag(player, specialflag))
-						{
-							CONS_Printf(M_GetText("%s returned the %s%s%c to base.\n"), plname, flagcolor, flagtext, 0x80);
-
-							// The fuse code plays this sound effect
-							//if (players[consoleplayer].ctfteam == player->ctfteam)
-							//	S_StartSound(NULL, sfx_hoop1);
-						}
-					}
-				}
-				else if (player->ctfteam) // Player is on the other team (and not a spectator)
-				{
-					if (player->powers[pw_super])
-						return;
-
-					player->gotflag |= teams[flagteam].flag;
-					CONS_Printf(M_GetText("%s picked up the %s%s%c!\n"), plname, flagcolor, flagtext, 0x80);
-					P_SetTarget(&flagmobjs[flagteam], NULL);
-					// code for dealing with abilities is handled elsewhere now
-					break;
-				}
-			}
-			return;
-
 // ********************************** //
 // NiGHTS gameplay items and powerups //
 // ********************************** //
@@ -4392,13 +4384,16 @@ void P_PlayerFlagBurst(player_t *player, boolean toss)
 	if (!player->gotflag)
 		return;
 
-	for (UINT8 team = TEAM_RED; team < numteams; team++)
+	for (UINT8 i = 1; i < numteams; i++)
 	{
+		UINT8 team = G_GetTeam(i);
 		UINT32 flagflag = 1 << (team - 1);
 		if (!(player->gotflag & flagflag))
 			continue;
 
-		mobj_t *flag = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, teams[team].flag_mobj_type);
+		mobj_t *flag = P_SpawnTeamFlag(team, player->mo->x, player->mo->y, player->mo->z);
+		if (flag == NULL)
+			continue;
 
 		if (player->mo->eflags & MFE_VERTICALFLIP)
 		{
diff --git a/src/p_local.h b/src/p_local.h
index b0d3acf685d0e3e0390dfba118a82e0a00a78bca..5ee5dfd5d2ef288b9ecff04672ab439577f8218e 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -345,6 +345,7 @@ FUNCMATH boolean P_WeaponOrPanel(mobjtype_t type);
 
 mobj_t *P_GetTeamFlag(UINT8 team);
 mapthing_t *P_GetTeamFlagMapthing(UINT8 team);
+mobj_t *P_SpawnTeamFlag(UINT8 team, fixed_t x, fixed_t y, fixed_t z);
 
 void P_CalcChasePostImg(player_t *player, camera_t *thiscam);
 boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled);
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 077c6b68be83c77caaa8fa9f32166de52813ed8c..4755583c13cb44217fff351d164fb5ff3648e6bf 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -45,6 +45,10 @@ actioncache_t actioncachehead;
 
 static mobj_t *overlaycap = NULL;
 
+#define MAXHUNTEMERALDS 64
+mapthing_t *huntemeralds[MAXHUNTEMERALDS];
+INT32 numhuntemeralds;
+
 void P_InitCachedActions(void)
 {
 	actioncachehead.prev = actioncachehead.next = &actioncachehead;
@@ -2430,15 +2434,6 @@ boolean P_ZMovement(mobj_t *mo)
 				return false;
 			}
 			break;
-		case MT_REDFLAG:
-		case MT_BLUEFLAG:
-			// Remove from death pits.  DON'T FUCKING DESPAWN IT DAMMIT
-			if (P_CheckDeathPitCollide(mo))
-			{
-				mo->fuse = 1;
-				return false;
-			}
-			break;
 
 		case MT_RING: // Ignore still rings
 		case MT_COIN:
@@ -2499,6 +2494,12 @@ boolean P_ZMovement(mobj_t *mo)
 			}
 			break;
 		default:
+			// Respawn flags whenever they hit a death pit
+			if (mo->eflags & MFE_TEAMFLAG && P_CheckDeathPitCollide(mo))
+			{
+				mo->fuse = 1;
+				return false;
+			}
 			break;
 	}
 
@@ -2795,7 +2796,7 @@ boolean P_ZMovement(mobj_t *mo)
 				mo->momz = -mo->momz;
 			else
 			// Flags bounce
-			if (mo->type == MT_REDFLAG || mo->type == MT_BLUEFLAG)
+			if (mo->eflags & MFE_TEAMFLAG)
 			{
 				if (maptol & TOL_NIGHTS)
 					mo->momz = -FixedDiv(mo->momz, 10*FRACUNIT);
@@ -9763,11 +9764,6 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 		if (!P_TurretThink(mobj))
 			return false;
 		break;
-	case MT_BLUEFLAG:
-	case MT_REDFLAG:
-		if (P_MobjTouchingSectorSpecialFlag(mobj, SSF_RETURNFLAG))
-			mobj->fuse = 1; // Return to base.
-		break;
 	case MT_SPINDUST: // Spindash dust
 		mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000
 		mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same
@@ -9895,6 +9891,13 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 		}
 		/* FALLTHRU */
 	default:
+		// Return team flags to base whenever they touch sectors that return team flags
+		if (mobj->eflags & MFE_TEAMFLAG)
+		{
+			if (P_MobjTouchingSectorSpecialFlag(mobj, SSF_RETURNFLAG))
+				mobj->fuse = 1; // Return to base.
+		}
+
 		// check mobj against possible water content, before movement code
 		P_MobjCheckWater(mobj);
 
@@ -10059,6 +10062,12 @@ static boolean P_FuseThink(mobj_t *mobj)
 		P_MonitorFuseThink(mobj);
 		return false;
 	}
+	else if (mobj->eflags & MFE_TEAMFLAG)
+	{
+		P_FlagFuseThink(mobj);
+		P_RemoveMobj(mobj);
+		return false;
+	}
 	else switch (mobj->type)
 	{
 		// gargoyle and snowman handled in P_PushableThinker, not here
@@ -10069,11 +10078,6 @@ static boolean P_FuseThink(mobj_t *mobj)
 	case MT_LHRT:
 		P_KillMobj(mobj, NULL, NULL, 0);
 		break;
-	case MT_BLUEFLAG:
-	case MT_REDFLAG:
-		P_FlagFuseThink(mobj);
-		P_RemoveMobj(mobj);
-		return false;
 	case MT_FANG:
 		if (mobj->flags2 & MF2_SLIDEPUSH)
 		{
@@ -10551,8 +10555,6 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing)
 
 		case MT_REDTEAMRING:
 		case MT_BLUETEAMRING:
-		case MT_REDFLAG:
-		case MT_BLUEFLAG:
 
 		case MT_BOUNCERING:
 		case MT_AUTOMATICRING:
@@ -10614,8 +10616,9 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing)
 			return FRACUNIT;
 
 		default:
-
-			if (thing->flags & (MF_ENEMY|MF_BOSS))
+			if (thing->eflags & MFE_TEAMFLAG)
+				return 2*FRACUNIT/3;
+			else if (thing->flags & (MF_ENEMY|MF_BOSS))
 				return FRACUNIT;
 			else
 				return 0;
@@ -11845,10 +11848,6 @@ void P_MovePlayerToStarpost(INT32 playernum)
 		leveltime = p->starposttime;
 }
 
-#define MAXHUNTEMERALDS 64
-mapthing_t *huntemeralds[MAXHUNTEMERALDS];
-INT32 numhuntemeralds;
-
 fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const fixed_t y, const fixed_t dz, const fixed_t offset, const boolean flip, const fixed_t scale, const boolean absolutez)
 {
 	const subsector_t *ss = R_PointInSubsector(x, y);
@@ -11979,7 +11978,8 @@ static boolean P_SpawnNonMobjMapThing(mapthing_t *mthing)
 		return true;
 	}
 	else if (metalrecording && mthing->type == mobjinfo[MT_METALSONIC_RACE].doomednum)
-	{ // If recording, you ARE Metal Sonic. Do not spawn it, do not save normal spawnpoints.
+	{
+		// If recording, you ARE Metal Sonic. Do not spawn it, do not save normal spawnpoints.
 		playerstarts[0] = mthing;
 		return true;
 	}
@@ -12078,13 +12078,13 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
 
 	if (!(gametyperules & GTR_TEAMFLAGS)) // CTF specific things
 	{
-		if (i == MT_BLUEFLAG || i == MT_REDFLAG)
+		if (i == MT_BLUEFLAG || i == MT_REDFLAG || i == MT_TEAMFLAG)
 			return false; // No flags in non-CTF modes!
 	}
-	else if (i == MT_BLUEFLAG || i == MT_REDFLAG)
+	else if (i == MT_BLUEFLAG || i == MT_REDFLAG || i == MT_TEAMFLAG)
 	{
-		UINT8 team = mobjinfo[i].mass;
-		if (team >= numteams)
+		UINT8 team = i == MT_TEAMFLAG ? mthing->args[0] : mobjinfo[i].mass;
+		if (team == TEAM_NONE || team >= numteams)
 			return false;
 		else if (flagmobjs[team] && !P_MobjWasRemoved(flagmobjs[team]))
 		{
@@ -12171,6 +12171,14 @@ static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i)
 			return MT_NIGHTSCHIP;
 	}
 
+	if (i == MT_TEAMFLAG)
+	{
+		INT32 team = mthing->args[0];
+		if (team == TEAM_NONE || team >= numteams)
+			return MT_NULL;
+		return teams[team].flag_mobj_type;
+	}
+
 	if (!(gametyperules & GTR_TEAMS))
 	{
 		if (i == MT_BLUETEAMRING || i == MT_REDTEAMRING)
@@ -13188,12 +13196,11 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
 		}
 		break;
 	case MT_REDFLAG:
-		P_SetTarget(&flagmobjs[TEAM_RED], mobj);
-		flagpoints[TEAM_RED] = mobj->spawnpoint;
-		break;
 	case MT_BLUEFLAG:
-		P_SetTarget(&flagmobjs[TEAM_BLUE], mobj);
-		flagpoints[TEAM_BLUE] = mobj->spawnpoint;
+		mobj->extravalue1 = G_GetTeam(mobj->info->mass);
+		/* FALLTHRU */
+	case MT_TEAMFLAG:
+		mobj->eflags |= MFE_TEAMFLAG;
 		break;
 	case MT_NIGHTSSTAR:
 		if (maptol & TOL_XMAS)
@@ -13312,6 +13319,19 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
 		mobj->threshold = mobj->info->seesound;
 		mobj->health = mobj->info->spawnhealth;
 	}
+	// Team flag
+	if (mobj->eflags & MFE_TEAMFLAG)
+	{
+		if (mobj->extravalue1 == 0)
+			mobj->extravalue1 = mthing->args[0];
+
+		INT32 team = mobj->extravalue1;
+		if (team > TEAM_NONE && team < numteams)
+		{
+			P_SetTarget(&flagmobjs[team], mobj);
+			flagpoints[team] = mobj->spawnpoint;
+		}
+	}
 
 	return true;
 }
@@ -14119,6 +14139,38 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
 	return slope ? th : NULL;
 }
 
+mobj_t *P_SpawnTeamFlag(UINT8 team, fixed_t x, fixed_t y, fixed_t z)
+{
+	if (team == TEAM_NONE || team >= numteams || teams[team].flag_mobj_type == 0)
+		return NULL;
+
+	mobj_t *flag = P_SpawnMobj(x, y, z, teams[team].flag_mobj_type);
+
+	if (flag)
+	{
+		flag->eflags |= MFE_TEAMFLAG;
+		flag->extravalue1 = team;
+	}
+
+	return flag;
+}
+
+mobj_t *P_GetTeamFlag(UINT8 team)
+{
+	if (team >= teamsingame || P_MobjWasRemoved(flagmobjs[team]))
+		return NULL;
+
+	return flagmobjs[team];
+}
+
+mapthing_t *P_GetTeamFlagMapthing(UINT8 team)
+{
+	if (team >= teamsingame)
+		return NULL;
+
+	return flagpoints[team];
+}
+
 //
 // P_FlashPal
 // Flashes a player's palette.  ARMAGEDDON BLASTS!
@@ -14202,19 +14254,3 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
 
 	return newmobj;
 }
-
-mobj_t *P_GetTeamFlag(UINT8 team)
-{
-	if (team >= teamsingame || P_MobjWasRemoved(flagmobjs[team]))
-		return NULL;
-
-	return flagmobjs[team];
-}
-
-mapthing_t *P_GetTeamFlagMapthing(UINT8 team)
-{
-	if (team >= teamsingame)
-		return NULL;
-
-	return flagpoints[team];
-}
diff --git a/src/p_mobj.h b/src/p_mobj.h
index a980691beb8b296b4fd16415141e4f9e5f484af8..11d03fdaed2b291da37b92045787def12b2e60d2 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -250,9 +250,11 @@ typedef enum
 	// Forces an object to NOT use super sprites with SPR_PLAY.
 	MFE_FORCENOSUPER		= 1<<13,
 	// Makes an object use super sprites where they wouldn't have otherwise and vice-versa
-	MFE_REVERSESUPER		= MFE_FORCESUPER|MFE_FORCENOSUPER
+	MFE_REVERSESUPER		= MFE_FORCESUPER|MFE_FORCENOSUPER,
+	// Object is a team flag
+	MFE_TEAMFLAG            = 1<<14,
 
-	// free: to and including 1<<15
+	// free: 1<<15
 } mobjeflag_t;
 
 //
diff --git a/src/p_spec.c b/src/p_spec.c
index 736498a55615922bf390f787ae9d7a28cc1c789d..a4ed36f8b32f3f8f676101220759b54259a87c11 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -4667,12 +4667,15 @@ static void P_ProcessTeamBase(player_t *player, UINT8 team)
 	else if (players[consoleplayer].ctfteam != team)
 		S_StartSound(NULL, sfx_lose);
 
-	mo = P_SpawnMobj(player->mo->x,player->mo->y,player->mo->z, teams[otherteam].flag_mobj_type);
+	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;
+	}
 	player->gotflag &= ~teamflag;
-	mo->flags &= ~MF_SPECIAL;
-	mo->fuse = TICRATE;
-	mo->spawnpoint = flagpoints[otherteam];
-	mo->flags2 |= MF2_JUSTATTACKED;
 	teamscores[team]++;
 	P_AddPlayerScore(player, 250);
 }