diff --git a/src/dehacked.c b/src/dehacked.c
index 2d53bf78d90097a27943736f7c257cd50b1abb46..8b43b9b6c0dfdc0aee2ef6311d9a6feb84410173 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -6102,10 +6102,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FIREBALLEXP2",
 	"S_FIREBALLEXP3",
 	"S_SHELL",
-	"S_SHELL1",
-	"S_SHELL2",
-	"S_SHELL3",
-	"S_SHELL4",
 	"S_PUMA_UP1",
 	"S_PUMA_UP2",
 	"S_PUMA_UP3",
diff --git a/src/info.c b/src/info.c
index e867c0c41c033a10c3226cb2a4fc81c3c8949b21..37f7aebafa6a9fae04c1d6fb9395cc4d7f83a430 100644
--- a/src/info.c
+++ b/src/info.c
@@ -2742,11 +2742,7 @@ state_t states[NUMSTATES] =
 	{SPR_FBLL, FF_FULLBRIGHT|6, 3, {NULL}, 0, 0, S_NULL},         // S_FIREBALLEXP3
 
 	// Turtle Shell
-	{SPR_SHLL, 0, -1, {NULL}, 0, 0, S_NULL},   // S_SHELL
-	{SPR_SHLL, 0, 2,  {NULL}, 0, 0, S_SHELL2}, // S_SHELL1
-	{SPR_SHLL, 1, 2,  {NULL}, 0, 0, S_SHELL3}, // S_SHELL2
-	{SPR_SHLL, 2, 2,  {NULL}, 0, 0, S_SHELL4}, // S_SHELL3
-	{SPR_SHLL, 3, 2,  {NULL}, 0, 0, S_SHELL1}, // S_SHELL4
+	{SPR_SHLL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHELL
 
 	// Puma (Mario fireball)
 	{SPR_PUMA, FF_FULLBRIGHT  , 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_UP2},   // S_PUMA_UP1
@@ -13148,9 +13144,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		20*FRACUNIT,    // speed
-		8*FRACUNIT,     // radius
-		16*FRACUNIT,    // height
+		16,             // speed
+		16*FRACUNIT,    // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		1,              // damage
diff --git a/src/info.h b/src/info.h
index f7fc62bd4bd5eea3e159934c38d72e880f413a2e..d82c0d5783d91ecb709344096a345dd8c42e925a 100644
--- a/src/info.h
+++ b/src/info.h
@@ -2907,10 +2907,6 @@ typedef enum state
 	S_FIREBALLEXP2,
 	S_FIREBALLEXP3,
 	S_SHELL,
-	S_SHELL1,
-	S_SHELL2,
-	S_SHELL3,
-	S_SHELL4,
 	S_PUMA_UP1,
 	S_PUMA_UP2,
 	S_PUMA_UP3,
diff --git a/src/p_inter.c b/src/p_inter.c
index a0e7e990b93d04f15f8e59337f53208968afde63..669fabf9fe566844244aef8526bc3838f0a84276 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1174,15 +1174,33 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 // Mario //
 // ***** //
 		case MT_SHELL:
-			if (special->state == &states[S_SHELL]) // Resting anim
-			{
-				// Kick that sucker around!
-				special->angle = toucher->angle;
-				P_InstaThrust(special, special->angle, FixedMul(special->info->speed, special->scale));
-				S_StartSound(toucher, sfx_mario2);
-				P_SetMobjState(special, S_SHELL1);
-				P_SetTarget(&special->target, toucher);
-				special->threshold = (3*TICRATE)/2;
+			{
+				boolean bounceon = ((P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0));
+				if (special->threshold == TICRATE) // it's moving
+				{
+					if (bounceon)
+					{
+						// Stop it!
+						special->momx = special->momy = 0;
+						S_StartSound(toucher, sfx_mario2);
+						P_SetTarget(&special->target, NULL);
+						special->threshold = TICRATE - 1;
+						toucher->momz = -toucher->momz;
+					}
+					else // source can't be given as otherwise P_PlayerHitsPlayer will fail
+						P_DamageMobj(toucher, special, NULL/*special->target*/, 1, 0);
+				}
+				else if (special->threshold == 0)
+				{
+					// Kick that sucker around!
+					special->movedir = ((special->movedir == 1) ? -1 : 1);
+					P_InstaThrust(special, toucher->angle, (special->info->speed*special->scale));
+					S_StartSound(toucher, sfx_mario2);
+					P_SetTarget(&special->target, toucher);
+					special->threshold = (3*TICRATE)/2;
+					if (bounceon)
+						toucher->momz = -toucher->momz;
+				}
 			}
 			return;
 		case MT_AXE:
diff --git a/src/p_map.c b/src/p_map.c
index c65b96088fc9b815e5aeb5a3eeda7226e35c712b..d362caf80b1c87a9692da6791cb0412e0f66bdc4 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -768,8 +768,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			}
 		}
 
-		if (tmthing->type == MT_SHELL && tmthing->threshold > TICRATE)
-			return true;
 		// damage / explode
 		if (tmthing->flags & MF_ENEMY) // An actual ENEMY! (Like the deton, for example)
 			P_DamageMobj(thing, tmthing, tmthing, 1, 0);
@@ -810,7 +808,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			tmthing->y = thing->y;
 			P_SetThingPosition(tmthing);
 		}
-		else
+		else if (!(tmthing->type == MT_SHELL && thing->player)) // player collision handled in touchspecial
 			P_DamageMobj(thing, tmthing, tmthing->target, 1, 0);
 
 		// don't traverse any more
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 3028146f3e71706f567fd6182415b66b2df4ea0b..54815bf0f23acbac71ae026f4356e77b05c68258 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -7691,13 +7691,13 @@ void P_MobjThinker(mobj_t *mobj)
 				P_NightsItemChase(mobj);
 			break;
 		case MT_SHELL:
-			if (mobj->threshold > TICRATE)
+			if (mobj->threshold && mobj->threshold != TICRATE)
 				mobj->threshold--;
 
-			if (mobj->state != &states[S_SHELL])
+			if (mobj->threshold >= TICRATE)
 			{
-				mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy);
-				P_InstaThrust(mobj, mobj->angle, FixedMul(mobj->info->speed, mobj->scale));
+				mobj->angle += ((mobj->movedir == 1) ? ANGLE_22h : ANGLE_337h);
+				P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), (mobj->info->speed*mobj->scale));
 			}
 			break;
 		case MT_TURRET: