diff --git a/src/d_player.h b/src/d_player.h
index e2a1081b00b1dfb954076368d3cb8d5d80b44f49..2425ea1bdd78048caa5e5ba3df8f6836733df0fb 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -166,7 +166,7 @@ typedef enum
 	PA_RUN,
 	PA_PAIN,
 	PA_ROLL,
-	PA_JUMP,
+	PA_SPRING,
 	PA_FALL,
 	PA_ABILITY,
 	PA_RIDE
diff --git a/src/dehacked.c b/src/dehacked.c
index 0ba054f07c8be58349ce7373524170633165abfa..d5240f9c9551aaf81d2d703295616147411ed2d7 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -3755,6 +3755,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_PLAY_DASH",
 	"S_PLAY_GASP",
 	"S_PLAY_JUMP",
+	"S_PLAY_SPRING",
 	"S_PLAY_FALL",
 	"S_PLAY_EDGE",
 	"S_PLAY_RIDE",
@@ -3779,6 +3780,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_PLAY_SUPER_SPIN",
 	"S_PLAY_SUPER_GASP",
 	"S_PLAY_SUPER_JUMP",
+	"S_PLAY_SUPER_SPRING",
 	"S_PLAY_SUPER_FALL",
 	"S_PLAY_SUPER_EDGE",
 	"S_PLAY_SUPER_RIDE",
@@ -7675,7 +7677,7 @@ struct {
 	{"PA_RUN",PA_RUN},
 	{"PA_PAIN",PA_PAIN},
 	{"PA_ROLL",PA_ROLL},
-	{"PA_JUMP",PA_JUMP},
+	{"PA_SPRING",PA_SPRING},
 	{"PA_FALL",PA_FALL},
 	{"PA_ABILITY",PA_ABILITY},
 	{"PA_RIDE",PA_RIDE},
diff --git a/src/info.c b/src/info.c
index 8d7c249adf02e473e336f349d057685d3fe5379a..ed9bff286011026e22afa5bf6fa9c7491c073834 100644
--- a/src/info.c
+++ b/src/info.c
@@ -69,6 +69,7 @@ char spr2names[NUMPLAYERSPRITES][5] =
 	"DASH",
 	"GASP",
 	"JUMP",
+	"SPNG",
 	"FALL",
 	"EDGE",
 	"RIDE",
@@ -94,6 +95,7 @@ char spr2names[NUMPLAYERSPRITES][5] =
 	"SSPN",
 	"SGSP",
 	"SJMP",
+	"SSPG",
 	"SFAL",
 	"SEDG",
 	"SRID",
@@ -135,7 +137,8 @@ state_t states[NUMSTATES] =
 	{SPR_PLAY, SPR2_SPIN,   1, {NULL}, 0, 0, S_PLAY_SPIN}, // S_PLAY_SPIN
 	{SPR_PLAY, SPR2_DASH,   2, {NULL}, 0, 0, S_PLAY_DASH}, // S_PLAY_DASH
 	{SPR_PLAY, SPR2_GASP,  14, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_GASP
-	{SPR_PLAY, SPR2_JUMP,   2, {NULL}, 0, 0, S_PLAY_JUMP}, // S_PLAY_JUMP
+	{SPR_PLAY, SPR2_JUMP,   1, {NULL}, 0, 0, S_PLAY_JUMP}, // S_PLAY_JUMP
+	{SPR_PLAY, SPR2_SPNG,   2, {NULL}, 0, 0, S_PLAY_SPRING}, // S_PLAY_SPRING
 	{SPR_PLAY, SPR2_FALL,   2, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_FALL
 	{SPR_PLAY, SPR2_EDGE,  12, {NULL}, 0, 0, S_PLAY_EDGE}, // S_PLAY_EDGE
 	{SPR_PLAY, SPR2_RIDE,   4, {NULL}, 0, 0, S_PLAY_RIDE}, // S_PLAY_RIDE
@@ -159,7 +162,8 @@ state_t states[NUMSTATES] =
 	{SPR_PLAY, SPR2_SDRN,   4, {NULL}, 0, 0, S_PLAY_SUPER_DRWN}, // S_PLAY_SUPER_DRWN
 	{SPR_PLAY, SPR2_SSPN,   1, {NULL}, 0, 0, S_PLAY_SUPER_SPIN}, // S_PLAY_SUPER_SPIN
 	{SPR_PLAY, SPR2_SGSP,  14, {NULL}, 0, 0, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_GASP
-	{SPR_PLAY, SPR2_SJMP,   2, {NULL}, 0, 0, S_PLAY_SUPER_JUMP}, // S_PLAY_SUPER_JUMP
+	{SPR_PLAY, SPR2_SJMP,   1, {NULL}, 0, 0, S_PLAY_SUPER_JUMP}, // S_PLAY_SUPER_JUMP
+	{SPR_PLAY, SPR2_SSPG,   2, {NULL}, 0, 0, S_PLAY_SUPER_SPRING}, // S_PLAY_SUPER_SPRING
 	{SPR_PLAY, SPR2_SFAL,   2, {NULL}, 0, 0, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_FALL
 	{SPR_PLAY, SPR2_SEDG,  12, {NULL}, 0, 0, S_PLAY_SUPER_EDGE}, // S_PLAY_SUPER_EDGE
 	{SPR_PLAY, SPR2_SRID,   4, {NULL}, 0, 0, S_PLAY_SUPER_RIDE}, // S_PLAY_SUPER_RIDE
diff --git a/src/info.h b/src/info.h
index e313526b92b18950114c1996c170cb56d7822d31..306b928e5ec1f93a2967974262f00f36846b80ee 100644
--- a/src/info.h
+++ b/src/info.h
@@ -588,6 +588,7 @@ enum playersprite
 	SPR2_DASH,
 	SPR2_GASP,
 	SPR2_JUMP,
+	SPR2_SPNG, // spring
 	SPR2_FALL,
 	SPR2_EDGE,
 	SPR2_RIDE,
@@ -613,6 +614,7 @@ enum playersprite
 	SPR2_SSPN,
 	SPR2_SGSP,
 	SPR2_SJMP,
+	SPR2_SSPG,
 	SPR2_SFAL,
 	SPR2_SEDG,
 	SPR2_SRID,
@@ -649,7 +651,8 @@ typedef enum state
 	S_PLAY_SPIN,
 	S_PLAY_DASH,
 	S_PLAY_GASP,
-	S_PLAY_JUMP,
+	S_PLAY_JUMP, // spin jump (todo: make jump separate from spring up for non-spin chars too?)
+	S_PLAY_SPRING,
 	S_PLAY_FALL,
 	S_PLAY_EDGE,
 	S_PLAY_RIDE,
@@ -673,7 +676,8 @@ typedef enum state
 	S_PLAY_SUPER_DRWN,
 	S_PLAY_SUPER_SPIN,
 	S_PLAY_SUPER_GASP,
-	S_PLAY_SUPER_JUMP,
+	S_PLAY_SUPER_JUMP, // see note above
+	S_PLAY_SUPER_SPRING,
 	S_PLAY_SUPER_FALL,
 	S_PLAY_SUPER_EDGE,
 	S_PLAY_SUPER_RIDE,
diff --git a/src/p_map.c b/src/p_map.c
index 214048fb3c0f1bd960c36d7a6efe15dd171ffe9b..2f9824641841618a732cd8991a91ce14434bff45 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -199,7 +199,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 		P_ResetPlayer(object->player);
 
 		if (P_MobjFlip(object)*vertispeed > 0)
-			P_SetPlayerMobjState(object, S_PLAY_JUMP);
+			P_SetPlayerMobjState(object, S_PLAY_SPRING);
 		else if (P_MobjFlip(object)*vertispeed < 0)
 			P_SetPlayerMobjState(object, S_PLAY_FALL);
 		else // horizontal spring
@@ -213,7 +213,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 		if (spring->info->painchance)
 		{
 			object->player->pflags |= PF_JUMPED;
-			P_SetPlayerMobjState(object, S_PLAY_SPIN);
+			P_SetPlayerMobjState(object, S_PLAY_JUMP);
 		}
 	}
 	return true;
@@ -1929,7 +1929,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 
 				// Don't 'step up' while springing,
 				// Only step up "if needed".
-				if (thing->player->panim == PA_JUMP
+				if (thing->player->panim == PA_SPRING
 				&& P_MobjFlip(thing)*thing->momz > FixedMul(FRACUNIT, thing->scale))
 					maxstep = 0;
 			}
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 915c742e8f28a820696d1ffbccfac2d57818dbb1..6ee020c2a0274dbec6dc408c8443f6f02027675e 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -163,7 +163,11 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 		case S_PLAY_GASP:
 			return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_GASP);
 		case S_PLAY_JUMP:
+			if (!(player->charflags & SF_SUPERSPIN))
+				return true;
 			return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_JUMP);
+		case S_PLAY_SPRING:
+			return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_SPRING);
 		case S_PLAY_FALL:
 			return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_FALL);
 		case S_PLAY_EDGE:
@@ -209,12 +213,14 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 		break;
 	case S_PLAY_SPIN:
 	case S_PLAY_DASH:
+	case S_PLAY_JUMP:
 	case S_PLAY_SUPER_SPIN:
+	case S_PLAY_SUPER_JUMP:
 		player->panim = PA_ROLL;
 		break;
-	case S_PLAY_JUMP:
-	case S_PLAY_SUPER_JUMP:
-		player->panim = PA_JUMP;
+	case S_PLAY_SPRING:
+	case S_PLAY_SUPER_SPRING:
+		player->panim = PA_SPRING;
 		break;
 	case S_PLAY_FALL:
 	case S_PLAY_SUPER_FALL:
@@ -317,9 +323,12 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 					spr2 = SPR2_SPIN;
 					break;
 				case SPR2_GASP:
-					spr2 = SPR2_JUMP;
+					spr2 = SPR2_SPNG;
 					break;
 				case SPR2_JUMP:
+					spr2 = SPR2_SPIN;
+					break;
+				case SPR2_SPNG: // spring
 					spr2 = SPR2_FALL;
 					break;
 				case SPR2_FALL:
@@ -330,7 +339,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 					break;
 
 				case SPR2_FLY:
-					spr2 = SPR2_JUMP;
+					spr2 = SPR2_SPNG;
 					break;
 				case SPR2_TIRE:
 					spr2 = SPR2_FLY;
@@ -379,6 +388,9 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 				case SPR2_SJMP:
 					spr2 = SPR2_JUMP;
 					break;
+				case SPR2_SSPG:
+					spr2 = SPR2_SPNG;
+					break;
 				case SPR2_SFAL:
 					spr2 = SPR2_FALL;
 					break;
diff --git a/src/p_user.c b/src/p_user.c
index 8854d8d645af1cb3f70af0d22294029a9436853f..f91c2a35d37e4189316d1234adb16d1974b5cdc8 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -1598,7 +1598,7 @@ void P_DoPlayerExit(player_t *player)
 	{
 		player->climbing = 0;
 		player->pflags |= PF_JUMPED;
-		P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 	}
 	player->powers[pw_underwater] = 0;
 	player->powers[pw_spacetime] = 0;
@@ -2689,21 +2689,21 @@ static void P_DoClimbing(player_t *player)
 
 			player->climbing = 0;
 			player->pflags |= PF_JUMPED;
-			P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 		}
 
 		if (skyclimber)
 		{
 			player->climbing = 0;
 			player->pflags |= PF_JUMPED;
-			P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 		}
 	}
 	else
 	{
 		player->climbing = 0;
 		player->pflags |= PF_JUMPED;
-		P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 	}
 
 	if (cmd->sidemove != 0 || cmd->forwardmove != 0)
@@ -2721,7 +2721,7 @@ static void P_DoClimbing(player_t *player)
 	{
 		player->climbing = 0;
 		player->pflags |= PF_JUMPED;
-		P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 		P_SetObjectMomZ(player->mo, 4*FRACUNIT, false);
 		P_InstaThrust(player->mo, player->mo->angle, FixedMul(-4*FRACUNIT, player->mo->scale));
 	}
@@ -2732,7 +2732,7 @@ static void P_DoClimbing(player_t *player)
 		localangle2 = player->mo->angle;
 
 	if (player->climbing == 0)
-		P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 
 	if (player->climbing && P_IsObjectOnGround(player->mo))
 	{
@@ -3520,8 +3520,9 @@ static void P_DoSuperStuff(player_t *player)
 
 			if (player->mo->health > 0)
 			{
-				if ((player->pflags & PF_JUMPED || player->pflags & PF_SPINNING)
-				&& player->mo->state-states != S_PLAY_DASH)
+				if (player->pflags & PF_JUMPED)
+					P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
+				else if (player->pflags & PF_SPINNING && player->mo->state-states != S_PLAY_DASH)
 					P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
 				else switch (player->mo->state-states)
 				{
@@ -3539,8 +3540,8 @@ static void P_DoSuperStuff(player_t *player)
 				case S_PLAY_SUPER_PAIN:
 					P_SetPlayerMobjState(player->mo, S_PLAY_PAIN);
 					break;
-				case S_PLAY_SUPER_JUMP:
-					P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
+				case S_PLAY_SUPER_SPRING:
+					P_SetPlayerMobjState(player->mo, S_PLAY_SPRING);
 					break;
 				case S_PLAY_SUPER_FALL:
 					P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
@@ -3760,9 +3761,9 @@ void P_DoJump(player_t *player, boolean soundandstate)
 			S_StartSound(player->mo, sfx_jump); // Play jump sound!
 
 		if (!(player->charability2 == CA2_SPINDASH))
-			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
+			P_SetPlayerMobjState(player->mo, S_PLAY_SPRING);
 		else
-			P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 	}
 }
 
@@ -6539,10 +6540,10 @@ static void P_MovePlayer(player_t *player)
 	}
 
 	// If Springing, but travelling DOWNWARD, change back!
-	if (player->panim == PA_JUMP && P_MobjFlip(player->mo)*player->mo->momz < 0)
+	if (player->panim == PA_SPRING && P_MobjFlip(player->mo)*player->mo->momz < 0)
 		P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
 	// If Springing but on the ground, change back!
-	else if (onground && (player->panim == PA_JUMP || player->panim == PA_FALL || player->panim == PA_RIDE) && !player->mo->momz)
+	else if (onground && (player->panim == PA_SPRING || player->panim == PA_FALL || player->panim == PA_RIDE) && !player->mo->momz)
 		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
 
 	// If you are stopped and are still walking, stand still!
@@ -6581,7 +6582,7 @@ static void P_MovePlayer(player_t *player)
 			else
 			{
 				player->pflags |= PF_JUMPED;
-				P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+				P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 			}
 		}
 		player->pflags &= ~PF_GLIDING;
@@ -6639,7 +6640,7 @@ static void P_MovePlayer(player_t *player)
 				|| (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && player->charability == CA_GLIDEANDCLIMB))
 			{
 				player->pflags |= PF_JUMPED;
-				P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+				P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 			}
 			else
 			{
@@ -6709,7 +6710,7 @@ static void P_MovePlayer(player_t *player)
 			else
 			{
 				player->pflags |= PF_JUMPED;
-				P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+				P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 			}
 		}
 		player->powers[pw_tailsfly] = 0;
@@ -7289,7 +7290,7 @@ static void P_DoRopeHang(player_t *player)
 
 		if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
 		&& !(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH)
-			P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 		return;
 	}
 
@@ -7406,7 +7407,7 @@ static void P_DoRopeHang(player_t *player)
 
 				if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
 				&& !(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH)
-					P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+					P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 			}
 
 			P_SetTarget(&player->mo->tracer, NULL);
@@ -8722,7 +8723,7 @@ void P_PlayerThink(player_t *player)
 			P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
 	}
 	else if ((player->pflags & PF_JUMPED) && !player->powers[pw_super] && player->panim != PA_ROLL && player->charability2 == CA2_SPINDASH)
-		P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 
 	if (player->flashcount)
 		player->flashcount--;
@@ -9350,7 +9351,7 @@ void P_PlayerAfterThink(player_t *player)
 	&& ((!player->powers[pw_super] && player->panim != PA_ROLL)
 	|| player->mo->state == &states[player->mo->info->painstate])
 	&& player->charability2 == CA2_SPINDASH)
-		P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 
 	if (player->pflags & PF_CARRIED && player->mo->tracer)
 	{