diff --git a/src/d_player.h b/src/d_player.h
index c096ecd9f2c76a33acad990d8a828f1e9373a6cb..1dc3fb5293d7fd56baae6518f2ea29adea5baecc 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -47,13 +47,14 @@ typedef enum
 	SF_DASHMODE         = 1<<11, // Sonic Advance 2 style top speed increase?
 	SF_FASTWAIT         = 1<<12, // Faster wait animation?
 	SF_FASTEDGE         = 1<<13, // Faster edge teeter?
-	SF_MULTIABILITY     = 1<<14, // Revenge of Final Demo.
-	SF_NONIGHTSROTATION = 1<<15, // Disable sprite rotation for NiGHTS
-	SF_NONIGHTSSUPER    = 1<<16, // Disable super sprites and colors for NiGHTS
-	SF_NOSUPERSPRITES   = 1<<17, // Don't use super sprites while super
-	SF_NOSUPERJUMPBOOST = 1<<18, // Disable the jump boost given while super (i.e. Knuckles)
-	SF_CANBUSTWALLS     = 1<<19, // Can naturally bust walls on contact? (i.e. Knuckles)
-	SF_NOSHIELDABILITY  = 1<<20, // Disable shield abilities
+	SF_JETFUME          = 1<<14, // Follow item uses Metal Sonic's jet fume behavior
+	SF_MULTIABILITY     = 1<<15, // Revenge of Final Demo.
+	SF_NONIGHTSROTATION = 1<<16, // Disable sprite rotation for NiGHTS
+	SF_NONIGHTSSUPER    = 1<<17, // Disable super sprites and colors for NiGHTS
+	SF_NOSUPERSPRITES   = 1<<18, // Don't use super sprites while super
+	SF_NOSUPERJUMPBOOST = 1<<19, // Disable the jump boost given while super (i.e. Knuckles)
+	SF_CANBUSTWALLS     = 1<<20, // Can naturally bust walls on contact? (i.e. Knuckles)
+	SF_NOSHIELDABILITY  = 1<<21, // Disable shield abilities
 
 	// free up to and including 1<<31
 } skinflags_t;
diff --git a/src/deh_tables.c b/src/deh_tables.c
index d7146325352fe35a873d4bcfe2229e394e0346c0..a96adf8ae6fb432bf04fd472d525eec1b6dcbd2a 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2024 by Sonic Team Junior.
+// Copyright (C) 1999-2025 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -5260,6 +5260,7 @@ struct int_const_s const INT_CONST[] = {
 	{"SF_DASHMODE",SF_DASHMODE},
 	{"SF_FASTWAIT",SF_FASTWAIT},
 	{"SF_FASTEDGE",SF_FASTEDGE},
+	{"SF_JETFUME",SF_JETFUME},
 	{"SF_MULTIABILITY",SF_MULTIABILITY},
 	{"SF_NONIGHTSROTATION",SF_NONIGHTSROTATION},
 	{"SF_NONIGHTSSUPER",SF_NONIGHTSSUPER},
diff --git a/src/info.c b/src/info.c
index 4d3468e10fac23edeeea539cdac550ad9ab6057a..3998eb88d2b976fc4d718506a4e6ea687c04e49e 100644
--- a/src/info.c
+++ b/src/info.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2024 by Sonic Team Junior.
+// Copyright (C) 1999-2025 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -4187,7 +4187,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		S_JETFUMEFLASH, // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
diff --git a/src/p_user.c b/src/p_user.c
index 173bb9731d63ccd9104a28430975cf8b6bc54fe4..b5423e13d2e5525001b8eaa7ec59f66b2d5037aa 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2024 by Sonic Team Junior.
+// Copyright (C) 1999-2025 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -11515,6 +11515,18 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
 }
 
 // Metal Sonic's jet fume
+//
+// The follow object's state is set to its spawn state when deactivated.
+// When the player is on a moving animation, the follow object goes to its see state.
+// When dash mode is entered, the follow object switches to its melee state.
+//
+// If MF2_FRET is set, the jet fume will flash during dash mode.
+// MF2_AMBUSH can be used to enable Metal Sonic's skidding animation.
+// MF2_JUSTATTACKED will enable the color cycling.
+// If the follow item is MT_METALJETFUME, the above two effects are automatically applied.
+//
+// MF2_STRONGBOX is internally used to track if the jet fume is in its deactivated state or not.
+// MF2_BOSSNOTRAP is internally used to instantly reset the jet fume's scale to its intended scale.
 void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 {
 	static const UINT8 FUME_SKINCOLORS[] =
@@ -11541,19 +11553,29 @@ void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 	fixed_t heightoffset = ((mo->eflags & MFE_VERTICALFLIP) ? mo->height - (P_GetPlayerHeight(player) >> 1) : (P_GetPlayerHeight(player) >> 1));
 	panim_t panim = player->panim;
 	tic_t dashmode = min(player->dashmode, DASHMODE_MAX);
+	boolean ismetaljetfume = fume->type == MT_METALJETFUME;
+	boolean notmoving = panim != PA_WALK && panim != PA_RUN && panim != PA_DASH;
 	boolean underwater = mo->eflags & MFE_UNDERWATER;
 	statenum_t stat = fume->state-states;
 	boolean resetinterp = false;
 
-	if (panim != PA_WALK && panim != PA_RUN && panim != PA_DASH) // turn invisible when not in a coherent movement state
+	if (notmoving) // deactivate when not in a coherent movement state
 	{
-		if (stat != fume->info->spawnstate)
+		if ((fume->flags2 & MF2_STRONGBOX) == 0)
+		{
 			P_SetMobjState(fume, fume->info->spawnstate);
-		return;
+			fume->flags2 |= MF2_STRONGBOX;
+		}
+		if (P_MobjWasRemoved(fume) || ismetaljetfume)
+			return;
 	}
 
-	if (player->skidtime) // Rotate during metal sonic's new skid animation
-		angle += ANGLE_90;
+	// Rotate on skid animation if follow item is MT_METALJETFUME, or if MF2_AMBUSH is set
+	if (player->mo->sprite2 == SPR2_SKID)
+	{
+		if ((ismetaljetfume && (player->charflags & SF_JETFUME)) || (fume->flags2 & MF2_AMBUSH))
+			angle += ANGLE_90;
+	}
 
 	if (underwater) // No fume underwater; spawn bubbles instead!
 	{
@@ -11590,54 +11612,82 @@ void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 
 		if (panim == PA_WALK)
 		{
-			if (stat != fume->info->spawnstate)
+			if ((fume->flags2 & MF2_STRONGBOX) == 0)
 			{
-				fume->threshold = 0;
 				P_SetMobjState(fume, fume->info->spawnstate);
+				fume->threshold = 0;
+				fume->flags2 &= ~MF2_STRONGBOX;
 			}
-			return;
+			if (P_MobjWasRemoved(fume) || ismetaljetfume)
+				return;
 		}
 	}
 
-	if (stat == fume->info->spawnstate) // If currently inivisble, activate!
+	// If currently deactivated, activate!
+	if (!notmoving && !underwater && (fume->flags2 & MF2_STRONGBOX))
 	{
 		P_SetMobjState(fume, (stat = fume->info->seestate));
+		if (P_MobjWasRemoved(fume))
+			return;
 		P_SetScale(fume, mo->scale, false);
+		fume->flags2 &= ~MF2_STRONGBOX;
 		resetinterp = true;
 	}
 
-	if (dashmode > DASHMODE_THRESHOLD && stat != fume->info->seestate) // If in dashmode, grow really big and flash
+	// If in dash mode, grow really big
+	if (dashmode > DASHMODE_THRESHOLD && stat != fume->info->meleestate)
 	{
 		fume->destscale = mo->scale;
-		fume->flags2 ^= MF2_DONTDRAW;
 		fume->flags2 |= mo->flags2 & MF2_DONTDRAW;
+
+		// Flash if follow item is MT_METALJETFUME, or if MF2_FRET is set
+		if (ismetaljetfume || (fume->flags2 & MF2_FRET))
+			fume->flags2 ^= MF2_DONTDRAW;
 	}
 	else // Otherwise, pick a size and color depending on speed and proximity to dashmode
 	{
-		if (dashmode == DASHMODE_THRESHOLD && dashmode > (tic_t)fume->movecount) // If just about to enter dashmode, play the startup animation again
+		// If just about to enter dash mode, play the startup animation again
+		if (dashmode == DASHMODE_THRESHOLD && dashmode > (tic_t)fume->movecount)
 		{
-			P_SetMobjState(fume, (stat = fume->info->seestate));
+			P_SetMobjState(fume, fume->info->meleestate);
+			if (P_MobjWasRemoved(fume))
+				return;
 			P_SetScale(fume, 2*mo->scale, true);
 		}
+
 		fume->flags2 = (fume->flags2 & ~MF2_DONTDRAW) | (mo->flags2 & MF2_DONTDRAW);
 		fume->destscale = (mo->scale + FixedDiv(player->speed, player->normalspeed)) / (underwater ? 6 : 3);
-		fume->color = FUME_SKINCOLORS[(dashmode * sizeof(FUME_SKINCOLORS)) / (DASHMODE_MAX + 1)];
+
+		// Do color cycling if follow item is MT_METALJETFUME, or if MF2_JUSTATTACKED is set
+		if (ismetaljetfume || (fume->flags2 & MF2_JUSTATTACKED))
+			fume->color = FUME_SKINCOLORS[(dashmode * sizeof(FUME_SKINCOLORS)) / (DASHMODE_MAX + 1)];
 
 		if (underwater)
 		{
-			fume->frame = (fume->frame & FF_FRAMEMASK) | FF_ANIMATE | (P_RandomRange(0, 9) * FF_TRANS10);
+			fume->frame = (fume->frame & ~FF_TRANSMASK) | (P_RandomRange(0, 9) << FF_TRANSSHIFT);
 			fume->threshold = 1;
 		}
 		else if (fume->threshold)
 		{
-			fume->frame = (fume->frame & FF_FRAMEMASK) | fume->state->frame;
+			fume->frame = (fume->frame & FF_FRAMEMASK) | (fume->state->frame & ~FF_FRAMEMASK);
 			fume->threshold = 0;
 		}
 	}
 
-	fume->movecount = dashmode; // keeps track of previous dashmode value so we know whether Metal is entering or leaving it
-	fume->flags2 = (fume->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP); // Make sure to flip in reverse gravity!
-	fume->eflags = (fume->eflags & ~MFE_VERTICALFLIP) | (mo->eflags & MFE_VERTICALFLIP); // Make sure to flip in reverse gravity!
+	// keeps track of previous dash mode value so we know whether Metal is entering or leaving it
+	fume->movecount = dashmode;
+
+	// Make sure to flip in reverse gravity!
+	fume->flags2 = (fume->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP);
+	fume->eflags = (fume->eflags & ~MFE_VERTICALFLIP) | (mo->eflags & MFE_VERTICALFLIP);
+
+	// Set the appropriate scale at spawn
+	// This is... strange, but I had to choose a flag that a follow object would not ordinarily use.
+	if ((fume->flags2 & MF2_BOSSNOTRAP) == 0)
+	{
+		P_SetScale(fume, fume->destscale, true);
+		fume->flags2 |= MF2_BOSSNOTRAP;
+	}
 
 	// Finally, set its position
 	dist = -mo->radius - FixedMul(fume->info->radius, fume->destscale - mo->scale/3);
@@ -11647,9 +11697,10 @@ void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 	fume->y = mo->y + P_ReturnThrustY(fume, angle, dist);
 	fume->z = mo->z + heightoffset - (fume->height >> 1);
 	P_SetThingPosition(fume);
-	if (resetinterp) R_ResetMobjInterpolationState(fume);
+	if (resetinterp)
+		R_ResetMobjInterpolationState(fume);
 
-	// If dashmode is high enough, spawn a trail
+	// If dash mode is high enough, spawn a trail
 	if (player->normalspeed >= skins[player->skin]->normalspeed*2)
 	{
 		mobj_t *ghost = P_SpawnGhostMobj(fume);
@@ -11663,6 +11714,8 @@ void P_DoFollowMobj(player_t *player, mobj_t *followmobj)
 {
 	if (LUA_HookFollowMobj(player, followmobj) || P_MobjWasRemoved(followmobj))
 		{;}
+	else if (player->charflags & SF_JETFUME)
+		P_DoMetalJetFume(player, followmobj);
 	else
 	{
 		switch (followmobj->type)
@@ -13164,7 +13217,8 @@ void P_PlayerAfterThink(player_t *player)
 						player->followmobj->colorized = true;
 						break;
 					default:
-						player->followmobj->flags2 |= MF2_LINKDRAW;
+						if ((player->charflags & SF_JETFUME) == 0)
+							player->followmobj->flags2 |= MF2_LINKDRAW;
 						break;
 				}
 			}
diff --git a/src/r_skins.c b/src/r_skins.c
index 842350d26c3d215f7d2983b8572e43316f54d656..7b5e547148a6ba975775dfc933602614be33921d 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -739,6 +739,8 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
 	GETFLAG(MACHINE)
 	GETFLAG(DASHMODE)
 	GETFLAG(FASTEDGE)
+	GETFLAG(FASTWAIT)
+	GETFLAG(JETFUME)
 	GETFLAG(MULTIABILITY)
 	GETFLAG(NONIGHTSROTATION)
 	GETFLAG(NONIGHTSSUPER)