diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index f5a06387570091b4c93499c721748461e5970395..e49f37e2a39ceb156cd6b5c827aca81d69080025 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -531,6 +531,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 	rsp->deadtimer = players[i].deadtimer;
 	rsp->exiting = (tic_t)LONG(players[i].exiting);
 	rsp->homing = players[i].homing;
+	rsp->dashmode = (tic_t)LONG(players[i].dashmode);
 	rsp->skidtime = (tic_t)LONG(players[i].skidtime);
 	rsp->cmomx = (fixed_t)LONG(players[i].cmomx);
 	rsp->cmomy = (fixed_t)LONG(players[i].cmomy);
@@ -657,6 +658,7 @@ static void resynch_read_player(resynch_pak *rsp)
 	players[i].deadtimer = rsp->deadtimer;
 	players[i].exiting = (tic_t)LONG(rsp->exiting);
 	players[i].homing = rsp->homing;
+	players[i].dashmode = (tic_t)LONG(rsp->dashmode);
 	players[i].skidtime = (tic_t)LONG(rsp->skidtime);
 	players[i].cmomx = (fixed_t)LONG(rsp->cmomx);
 	players[i].cmomy = (fixed_t)LONG(rsp->cmomy);
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 14b590926fdd476cf3705d2c63fd30f803d041e7..f9e33dc4c2d16f53da50b60990c5ba0571023c1c 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -191,6 +191,7 @@ typedef struct
 	INT32 deadtimer;
 	tic_t exiting;
 	UINT8 homing;
+	tic_t dashmode;
 	tic_t skidtime;
 	fixed_t cmomx;
 	fixed_t cmomy;
diff --git a/src/d_player.h b/src/d_player.h
index f2e6fe735796efb9c62f785b4cfc74d6d417ae25..4a5d0e702b860ba3103f6800f854e213662a4183 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -57,7 +57,8 @@ typedef enum
 	CA_FALLSWITCH,
 	CA_JUMPBOOST,
 	CA_AIRDRILL,
-	CA_JUMPTHOK
+	CA_JUMPTHOK,
+	CA_DASHMODE
 } charability_t;
 
 //Secondary skin abilities
@@ -164,6 +165,7 @@ typedef enum
 	PA_EDGE,
 	PA_WALK,
 	PA_RUN,
+	PA_PEEL,
 	PA_PAIN,
 	PA_ROLL,
 	PA_SPRING,
@@ -351,6 +353,7 @@ typedef struct player_s
 	tic_t exiting; // Exitlevel timer
 
 	UINT8 homing; // Are you homing?
+	tic_t dashmode; // counter for dashmode ability
 
 	tic_t skidtime; // Skid timer
 
diff --git a/src/dehacked.c b/src/dehacked.c
index 36dc862cbe3326739e71cc91b31a1c80f88134d3..4909c490041f12e18b921eae6a0ab5f56ce7bb79 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -3794,6 +3794,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_PLAY_WAIT",
 	"S_PLAY_WALK",
 	"S_PLAY_RUN",
+	"S_PLAY_PEEL",
 	"S_PLAY_PAIN",
 	"S_PLAY_DEAD",
 	"S_PLAY_DRWN",
@@ -3819,6 +3820,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_PLAY_SUPER_STND",
 	"S_PLAY_SUPER_WALK",
 	"S_PLAY_SUPER_RUN",
+	"S_PLAY_SUPER_PEEL",
 	"S_PLAY_SUPER_PAIN",
 	"S_PLAY_SUPER_STUN",
 	"S_PLAY_SUPER_DEAD",
@@ -7141,6 +7143,7 @@ struct {
 	{"CA_JUMPBOOST",CA_JUMPBOOST},
 	{"CA_AIRDRILL",CA_AIRDRILL},
 	{"CA_JUMPTHOK",CA_JUMPTHOK},
+	{"CA_DASHMODE",CA_DASHMODE},
 	// Secondary
 	{"CA2_NONE",CA2_NONE}, // now slot 0!
 	{"CA2_SPINDASH",CA2_SPINDASH},
@@ -7191,6 +7194,7 @@ struct {
 	{"PA_EDGE",PA_EDGE},
 	{"PA_WALK",PA_WALK},
 	{"PA_RUN",PA_RUN},
+	{"PA_PEEL",PA_PEEL},
 	{"PA_PAIN",PA_PAIN},
 	{"PA_ROLL",PA_ROLL},
 	{"PA_SPRING",PA_SPRING},
diff --git a/src/info.c b/src/info.c
index dc71ad1632af933268cf6db2c8fd5c119dce3596..8eec2481871c6b3c6be345a77a5bec4a3dd3cc1b 100644
--- a/src/info.c
+++ b/src/info.c
@@ -62,6 +62,7 @@ char spr2names[NUMPLAYERSPRITES][5] =
 	"WAIT",
 	"WALK",
 	"RUN_",
+	"PEEL",
 	"PAIN",
 	"DEAD",
 	"DRWN",
@@ -88,6 +89,7 @@ char spr2names[NUMPLAYERSPRITES][5] =
 	"SSTD",
 	"SWLK",
 	"SRUN",
+	"SPEE",
 	"SPAN",
 	"SMSL",
 	"SDTH",
@@ -132,6 +134,7 @@ state_t states[NUMSTATES] =
 	{SPR_PLAY, SPR2_WAIT,  16, {NULL}, 0, 0, S_PLAY_WAIT}, // S_PLAY_WAIT
 	{SPR_PLAY, SPR2_WALK,   4, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_WALK
 	{SPR_PLAY, SPR2_RUN ,   2, {NULL}, 0, 0, S_PLAY_RUN},  // S_PLAY_RUN
+	{SPR_PLAY, SPR2_PEEL,   2, {NULL}, 0, 0, S_PLAY_PEEL}, // S_PLAY_PEEL
 	{SPR_PLAY, SPR2_PAIN, 350, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_PAIN
 	{SPR_PLAY, SPR2_DEAD,   4, {NULL}, 0, 0, S_PLAY_DEAD}, // S_PLAY_DEAD
 	{SPR_PLAY, SPR2_DRWN,   4, {NULL}, 0, 0, S_PLAY_DRWN}, // S_PLAY_DRWN
@@ -157,6 +160,7 @@ state_t states[NUMSTATES] =
 	{SPR_PLAY, SPR2_SSTD,   7, {NULL}, 0, 0, S_PLAY_SUPER_STND}, // S_PLAY_SUPER_STND
 	{SPR_PLAY, SPR2_SWLK,   7, {NULL}, 0, 0, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_WALK
 	{SPR_PLAY, SPR2_SRUN,   7, {NULL}, 0, 0, S_PLAY_SUPER_RUN},  // S_PLAY_SUPER_RUN
+	{SPR_PLAY, SPR2_SPEE,   7, {NULL}, 0, 0, S_PLAY_SUPER_PEEL}, // S_PLAY_SUPER_PEEL
 	{SPR_PLAY, SPR2_SPAN,  -1, {NULL}, 0, 0, S_PLAY_SUPER_STND}, // S_PLAY_SUPER_PAIN
 	{SPR_PLAY, SPR2_SMSL,  -1, {NULL}, 0, 0, S_PLAY_SUPER_STND}, // S_PLAY_SUPER_STUN
 	{SPR_PLAY, SPR2_SDTH,   4, {NULL}, 0, 0, S_PLAY_SUPER_DEAD}, // S_PLAY_SUPER_DEAD
diff --git a/src/info.h b/src/info.h
index 46fdde46ba3fb9af5898394b264719fbb012bf98..46b76096b7999e5777d3fe4c2c7fdb7206f12b45 100644
--- a/src/info.h
+++ b/src/info.h
@@ -581,6 +581,7 @@ enum playersprite
 	SPR2_WAIT,
 	SPR2_WALK,
 	SPR2_RUN ,
+	SPR2_PEEL,
 	SPR2_PAIN,
 	SPR2_DEAD,
 	SPR2_DRWN,
@@ -607,6 +608,7 @@ enum playersprite
 	SPR2_SSTD,
 	SPR2_SWLK,
 	SPR2_SRUN,
+	SPR2_SPEE,
 	SPR2_SPAN,
 	SPR2_SMSL,
 	SPR2_SDTH,
@@ -645,6 +647,7 @@ typedef enum state
 	S_PLAY_WAIT,
 	S_PLAY_WALK,
 	S_PLAY_RUN,
+	S_PLAY_PEEL,
 	S_PLAY_PAIN,
 	S_PLAY_DEAD,
 	S_PLAY_DRWN,
@@ -670,6 +673,7 @@ typedef enum state
 	S_PLAY_SUPER_STND,
 	S_PLAY_SUPER_WALK,
 	S_PLAY_SUPER_RUN,
+	S_PLAY_SUPER_PEEL,
 	S_PLAY_SUPER_PAIN,
 	S_PLAY_SUPER_STUN,
 	S_PLAY_SUPER_DEAD,
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index bd5605f235afa1f89425729e19d6443dacb34eeb..60119deae070609f06885f4cf2f52d598be6077a 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -202,6 +202,8 @@ static int player_get(lua_State *L)
 		lua_pushinteger(L, plr->exiting);
 	else if (fastcmp(field,"homing"))
 		lua_pushinteger(L, plr->homing);
+	else if (fastcmp(field,"dashmode"))
+		lua_pushinteger(L, plr->dashmode);
 	else if (fastcmp(field,"skidtime"))
 		lua_pushinteger(L, plr->skidtime);
 	else if (fastcmp(field,"cmomx"))
@@ -459,6 +461,8 @@ static int player_set(lua_State *L)
 		plr->exiting = (tic_t)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"homing"))
 		plr->homing = (UINT8)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"dashmode"))
+		plr->dashmode = (tic_t)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"skidtime"))
 		plr->skidtime = (tic_t)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"cmomx"))
diff --git a/src/p_map.c b/src/p_map.c
index a91e5fd11447f19ba9d994179f2542d8a9eeacd8..c902b0774ab14571c72570625f0db288566865dd 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -448,6 +448,35 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		return true;
 	}
 
+	// Dashmode users destroy spikes and monitors.
+	if ((tmthing->player) && (tmthing->player->charability == CA_DASHMODE) && (tmthing->player->dashmode >= 3*TICRATE)
+	&& (thing->flags & (MF_MONITOR) || thing->type == MT_SPIKE))
+	{
+		if ((thing->flags & (MF_MONITOR)) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE)))
+			return true;
+		blockdist = thing->radius + tmthing->radius;
+		if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
+			return true; // didn't hit it
+		// see if it went over / under
+		if (tmthing->z > thing->z + thing->height)
+			return true; // overhead
+		if (tmthing->z + tmthing->height < thing->z)
+			return true; // underneath
+		if (thing->type == MT_SPIKE)
+		{
+			S_StartSound(tmthing, thing->info->deathsound);
+			for (thing = thing->subsector->sector->thinglist; thing; thing = thing->snext)
+				if (thing->type == MT_SPIKE && thing->health > 0 && thing->flags & MF_SOLID && P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y) < FixedMul(56*FRACUNIT, thing->scale))
+					P_KillMobj(thing, tmthing, tmthing, 0);
+		}
+		else
+		{
+			thing->health = 0;
+			P_KillMobj(thing, tmthing, tmthing, 0);
+		}
+		return true;
+	}
+
 	if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE)))
 		return true;
 
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 999f2bf7285878caa9572c04d37f7db78f20c5ce..3c271551284c854baa07a39fe9a096876b4e4940 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -175,6 +175,8 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 			return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_WALK);
 		case S_PLAY_RUN:
 			return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_RUN);
+		case S_PLAY_PEEL:
+			return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_PEEL);
 		case S_PLAY_PAIN:
 			return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_PAIN);
 		case S_PLAY_DEAD:
@@ -231,6 +233,10 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 	case S_PLAY_SUPER_RUN:
 		player->panim = PA_RUN;
 		break;
+	case S_PLAY_PEEL:
+	case S_PLAY_SUPER_PEEL:
+		player->panim = PA_PEEL;
+		break;
 	case S_PLAY_PAIN:
 	case S_PLAY_SUPER_PAIN:
 	case S_PLAY_SUPER_STUN:
@@ -316,7 +322,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 					else
 						mobj->tics = 4;
 				}
-				else if (player->panim == PA_RUN)
+				else if ((player->panim == PA_RUN) || (player->panim == PA_PEEL))
 				{
 					if (speed > 52<<FRACBITS)
 						mobj->tics = 1;
@@ -339,6 +345,9 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 			{
 				switch(spr2)
 				{
+				case SPR2_PEEL:
+					spr2 = SPR2_RUN;
+					break;
 				case SPR2_RUN:
 					spr2 = SPR2_WALK;
 					break;
@@ -393,6 +402,9 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 				case SPR2_SRUN:
 					spr2 = SPR2_RUN;
 					break;
+				case SPR2_SPEE:
+					spr2 = SPR2_PEEL;
+					break;
 				case SPR2_SPAN:
 					spr2 = SPR2_PAIN;
 					break;
@@ -2969,7 +2981,9 @@ static void P_PlayerZMovement(mobj_t *mo)
 					{
 						if (mo->player->cmomx || mo->player->cmomy)
 						{
-							if (mo->player->speed >= FixedMul(mo->player->runspeed, mo->scale) && mo->player->panim != PA_RUN)
+							if (mo->player->dashmode >= 3*TICRATE && mo->player->panim != PA_PEEL)
+								P_SetPlayerMobjState(mo, S_PLAY_PEEL);
+							else if (mo->player->speed >= FixedMul(mo->player->runspeed, mo->scale) && mo->player->panim != PA_RUN)
 								P_SetPlayerMobjState(mo, S_PLAY_RUN);
 							else if ((mo->player->rmomx || mo->player->rmomy) && (mo->player->panim != PA_WALK || mo->state-states == S_PLAY_SUPER_FLOAT))
 								P_SetPlayerMobjState(mo, S_PLAY_WALK);
@@ -2978,6 +2992,8 @@ static void P_PlayerZMovement(mobj_t *mo)
 						}
 						else
 						{
+							if (mo->player->dashmode >= 3*TICRATE && mo->player->panim != PA_PEEL)
+								P_SetPlayerMobjState(mo, S_PLAY_PEEL);
 							if (mo->player->speed >= FixedMul(mo->player->runspeed, mo->scale) && mo->player->panim != PA_RUN)
 								P_SetPlayerMobjState(mo, S_PLAY_RUN);
 							else if ((mo->momx || mo->momy) && (mo->player->panim != PA_WALK || mo->state-states == S_PLAY_SUPER_FLOAT))
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 48f283bd3dbd480710ac72cd2571a48a82d48214..964e8b7742bf123d708492b8b9c97b2234f4ab46 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -162,6 +162,7 @@ static void P_NetArchivePlayers(void)
 		WRITEINT32(save_p, players[i].deadtimer);
 		WRITEUINT32(save_p, players[i].exiting);
 		WRITEUINT8(save_p, players[i].homing);
+		WRITEUINT32(save_p, players[i].dashmode);
 		WRITEUINT32(save_p, players[i].skidtime);
 
 		////////////////////////////
@@ -337,6 +338,7 @@ static void P_NetUnArchivePlayers(void)
 		players[i].deadtimer = READINT32(save_p); // End game if game over lasts too long
 		players[i].exiting = READUINT32(save_p); // Exitlevel timer
 		players[i].homing = READUINT8(save_p); // Are you homing?
+		players[i].dashmode = READUINT32(save_p); // counter for dashmode ability
 		players[i].skidtime = READUINT32(save_p); // Skid timer
 
 		////////////////////////////
diff --git a/src/p_user.c b/src/p_user.c
index 09941745b48c4f2d7a47e5ccb12a9290da20aff3..18846a5ca75e7abd4a5fc46a4b4272452198cf33 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -3452,6 +3452,9 @@ static void P_DoSuperStuff(player_t *player)
 				case S_PLAY_SUPER_RUN:
 					P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
 					break;
+				case S_PLAY_SUPER_PEEL:
+					P_SetPlayerMobjState(player->mo, S_PLAY_PEEL);
+					break;
 				case S_PLAY_SUPER_PAIN:
 					P_SetPlayerMobjState(player->mo, S_PLAY_PAIN);
 					break;
@@ -3991,19 +3994,26 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 				case CA_THOK:
 				case CA_HOMINGTHOK:
 				case CA_JUMPTHOK: // Credit goes to CZ64 and Sryder13 for the original
+				case CA_DASHMODE: // Credit goes to Iceman404
 					// Now it's Sonic's abilities turn!
 					// THOK!
 					if (!(player->pflags & PF_THOKKED) || (player->charability2 == CA2_MULTIABILITY))
 					{
 						// Catapult the player
 						fixed_t actionspd = player->actionspd;
+						
+						if (player->charability == CA_DASHMODE)
+							actionspd = max(player->normalspeed, FixedDiv(player->speed, player->mo->scale));
+						
 						if (player->mo->eflags & MFE_UNDERWATER)
 							actionspd >>= 1;
+						
 						if ((player->charability == CA_JUMPTHOK) && !(player->pflags & PF_THOKKED))
 						{
 							player->pflags &= ~PF_JUMPED;
 							P_DoJump(player, false);
 						}
+						
 						P_InstaThrust(player->mo, player->mo->angle, FixedMul(actionspd, player->mo->scale));
 
 						if (maptol & TOL_2D)
@@ -6451,9 +6461,12 @@ static void P_MovePlayer(player_t *player)
 
 	if ((cmd->forwardmove != 0 || cmd->sidemove != 0) || (player->powers[pw_super] && !onground))
 	{
+		// If the player is in dashmode, here's their peelout.
+		if (player->charability == CA_DASHMODE && player->dashmode >= 3*TICRATE && player->panim == PA_RUN && !player->skidtime && (onground || player->powers[pw_super]))
+			P_SetPlayerMobjState (player->mo, S_PLAY_PEEL);
 		// If the player is moving fast enough,
 		// break into a run!
-		if (player->speed >= runspd && player->panim == PA_WALK && !player->skidtime && (onground || player->powers[pw_super]))
+		else if (player->speed >= runspd && player->panim == PA_WALK && !player->skidtime && (onground || player->powers[pw_super]))
 			P_SetPlayerMobjState (player->mo, S_PLAY_RUN);
 
 		// Super floating at slow speeds has its own special animation.
@@ -6465,6 +6478,11 @@ static void P_MovePlayer(player_t *player)
 			P_SetPlayerMobjState (player->mo, S_PLAY_WALK);
 	}
 
+	// If your peelout animation is playing, and you're
+	// going too slow, switch back to the run.
+	if (player->panim == PA_PEEL && player->dashmode < 3*TICRATE)
+		P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
+
 	// If your running animation is playing, and you're
 	// going too slow, switch back to the walking frames.
 	if (player->panim == PA_RUN && player->speed < runspd)
@@ -6795,7 +6813,7 @@ static void P_MovePlayer(player_t *player)
 #endif
 		}
 		// Otherwise, face the direction you're travelling.
-		else if (player->panim == PA_WALK || player->panim == PA_RUN || player->panim == PA_ROLL
+		else if (player->panim == PA_WALK || player->panim == PA_RUN || player->panim == PA_PEEL || player->panim == PA_ROLL
 		|| (player->mo->state-states == S_PLAY_FLY || player->mo->state-states == S_PLAY_FLY_TIRED))
 			player->mo->angle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
 
@@ -9113,6 +9131,49 @@ void P_PlayerThink(player_t *player)
 
 	player->pflags &= ~PF_SLIDING;
 
+#define dashmode player->dashmode
+	// Dash mode ability for Metal Sonic
+	if ((player->charability == CA_DASHMODE) && !(maptol & TOL_NIGHTS)) // woo, dashmode! no nights tho.
+	{
+		if (player->speed >= FixedMul(player->runspeed, player->mo->scale) || (player->pflags & PF_STARTDASH))
+		{
+			dashmode++; // Counter. Adds 1 to dash mode per tic in top speed.
+			if (dashmode == 3*TICRATE) // This isn't in the ">=" equation because it'd cause the sound to play infinitely.
+				S_StartSound(player->mo, sfx_s3ka2); // If the player enters dashmode, play this sound on the the tic it starts.		
+		}
+		else if (!(player->pflags & PF_SPINNING))
+		{
+			if (dashmode > 3)
+				dashmode -= 3; // Rather than lose it all, it gently counts back down!
+			else
+				dashmode = 0;
+		}
+
+		if (dashmode < 3*TICRATE) // Exits Dash Mode if you drop below speed/dash counter tics. Not in the above block so it doesn't keep disabling in midair.
+		{
+			player->normalspeed = skins[player->skin].normalspeed; // Reset to default if not capable of entering dash mode.
+			player->jumpfactor = skins[player->skin].jumpfactor;
+		}
+		else if (P_IsObjectOnGround(player->mo)) // Activate dash mode if we're on the ground.
+		{
+			if (player->normalspeed < skins[player->skin].actionspd) // If the player normalspeed is not currently at actionspd in dash mode, add speed each tic
+				player->normalspeed = player->normalspeed + 1*FRACUNIT/5; // Enter Dash Mode smoothly.
+
+			if (player->jumpfactor < FixedMul(skins[player->skin].jumpfactor, 5*FRACUNIT/4)) // Boost jump height.
+				player->jumpfactor = player->jumpfactor + 1*FRACUNIT/300;
+		}
+
+		dashmode = min(dashmode, 3*TICRATE + 3);
+
+		if (player->normalspeed >= skins[player->skin].actionspd)
+		{
+			mobj_t *ghost = P_SpawnGhostMobj(player->mo); // Spawns afterimages
+			ghost->fuse = 2; // Makes the images fade quickly
+		}
+	}
+	else
+		dashmode = 0;
+#undef dashmode
 /*
 //	Colormap verification
 	{