diff --git a/src/deh_tables.c b/src/deh_tables.c
index 0ab5a688279743b68526410b536b16d526ee2643..5f473654cf17792a0d6127eda25bcab0ea09f264 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -3690,6 +3690,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",
@@ -4407,6 +4408,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 47e4b6b9357b0a90a4705d05d971ff4bd1d1081b..bc5214f597c016365a2662b0f54b855837cff2f1 100644
--- a/src/info.c
+++ b/src/info.c
@@ -7149,6 +7149,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 5c7a9f3fd428579c674c7f969db44f7d71b88f07..f7c7f80d4930f036edfee969fd334dd5a529b4fb 100644
--- a/src/info.h
+++ b/src/info.h
@@ -4521,6 +4521,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 ecd3e5aa02dfe1dec0ed56cbe743ccc9341ab6cd..8a04034545d8e35487358f852a28f13d150d256a 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -552,6 +552,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
@@ -833,68 +887,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 //
 // ********************************** //
@@ -4389,13 +4381,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 fb8626b64633db61f9e8243aef9392de9a2e4d74..3e1c162ce0bf9b6d8d606de3650461c29a80e0a1 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -338,6 +338,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 ff8dd7c634d71179ee078b2b7cd679c9204c2ff1..2e6e68561763fa9e61a590b23836efba1d6d0f3a 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -44,6 +44,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;
@@ -2428,15 +2432,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:
@@ -2497,6 +2492,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;
 	}
 
@@ -2793,7 +2794,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);
@@ -9762,11 +9763,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
@@ -9894,6 +9890,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);
 
@@ -10058,6 +10061,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
@@ -10068,11 +10077,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)
 		{
@@ -10549,8 +10553,6 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing)
 
 		case MT_REDTEAMRING:
 		case MT_BLUETEAMRING:
-		case MT_REDFLAG:
-		case MT_BLUEFLAG:
 
 		case MT_EMBLEM:
 
@@ -10589,8 +10591,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;
@@ -11829,10 +11832,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 subsector_t *ss = R_PointInSubsector(x, y);
@@ -11962,7 +11961,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;
 	}
@@ -12061,13 +12061,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]))
 		{
@@ -12154,6 +12154,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)
@@ -13171,12 +13179,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)
@@ -13295,6 +13302,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;
 }
@@ -14102,6 +14122,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!
@@ -14185,19 +14237,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 9c598f59e3ce155e66a3155c4c95bd7712f839c7..6f81c5258e560551801a58c74d3c9a2d923ddd11 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 7d7c75e4cbf813a01d035d7ca1263c8aa118c8a5..c1703f8fe5ac38d3e0dca4cda77aa69ccd17588e 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -4668,12 +4668,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);
 }