From ad09f2603da9b5ae55e0b0beb6422ebad69f0dc1 Mon Sep 17 00:00:00 2001
From: toaster <rollerorbital@gmail.com>
Date: Thu, 17 Oct 2019 21:50:26 +0100
Subject: [PATCH] * New object types!     * Red and yellow Boost panels!       
  * Added because SUBARASHII and KIMOKAWAIII had several levels using them,
 and I wanted to make them look better.         *
 https://cdn.discordapp.com/attachments/359091121789468672/634486669202161674/srb20015.gif
         * Uses the mapthingnums of Glaber's SOC resource boosters, but
 absolutely nothing else - not even the magnitudes.         * Apply MF_AMBUSH
 to force the player into a spin - even if they don't have a spin ability!    
 * Banpyura!         * A Crushstacean with a spring instead of a crushclaw.   
      *
 https://cdn.discordapp.com/attachments/428262628893261828/634432099306176512/srb20019.gif
         * Wanted this for a while, finally added it. * Improved springs.    
 * Add pw_noautobrake to disable autobrake for half a second when touching
 yellow horizontal springs, and a second when touching red ones, even on the
 ground.     * Add pw_justsprung to disable directionchar for a few tics while
 touching any springs with a horizontal component to their velocity.     * Add
 the diagonal spring flag options Red and Yellow Diagonal Springs have to the
 Blue Diagonal Spring as well.     * Started but decided against a tip of the
 hat to the CD spring spin, hidden behind #define SPRINGSPIN. * Make
 directionchar "lag behind" a little bit in waterslides. * Improved flame
 jets.     * They now use new sprites for having their flames move upwards and
 sideways, instead of having them always face downwards like in 2.1! * Fixed a
 mixed declaration and code error in A_RolloutRock. (Sorry Lach!) * Make the
 Amy Cameo only spawn in SP, Record Attack, or Co-op unless it's the Clone
 Mode. * Improved ZB config.     * Add above new types.     * Add Blue
 Diagonal Spring, which existed in the source but not the config.     *
 Re-order enemies in ZB config by zone (but keep them in the Enemies section
 only).

---
 extras/conf/SRB2-22.cfg | 233 +++++++++++++++++++++++-----------------
 src/d_player.h          |   2 +
 src/dehacked.c          |  38 +++++++
 src/info.c              | 203 +++++++++++++++++++++++++++++++++-
 src/info.h              |  43 ++++++++
 src/p_enemy.c           |  14 +--
 src/p_inter.c           |   8 ++
 src/p_map.c             |  56 +++++++++-
 src/p_mobj.c            |  91 +++++++++++++++-
 src/p_user.c            |  29 +++--
 src/sounds.c            |   2 +-
 11 files changed, 596 insertions(+), 123 deletions(-)

diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index d936721b6..095af340a 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -3302,36 +3302,6 @@ thingtypes
 			height = 40;
 			flags8text = "[8] Cannot move";
 		}
-		124
-		{
-			title = "AquaBuzz";
-			sprite = "BBUZA1";
-			width = 20;
-			height = 24;
-		}
-		105
-		{
-			title = "Jetty-Syn Bomber";
-			sprite = "JETBB1";
-			width = 20;
-			height = 50;
-			flags8text = "[8] Cannot move";
-		}
-		106
-		{
-			title = "Jetty-Syn Gunner";
-			sprite = "JETGB1";
-			width = 20;
-			height = 48;
-			flags8text = "[8] Cannot move";
-		}
-		107
-		{
-			title = "Crawla Commander";
-			sprite = "CCOMA1";
-			width = 16;
-			height = 32;
-		}
 		108
 		{
 			title = "Deton";
@@ -3339,13 +3309,6 @@ thingtypes
 			width = 20;
 			height = 32;
 		}
-		109
-		{
-			title = "Skim";
-			sprite = "SKIMA1";
-			width = 16;
-			height = 24;
-		}
 		110
 		{
 			title = "Turret";
@@ -3361,10 +3324,24 @@ thingtypes
 			height = 64;
 			angletext = "Firing delay";
 		}
-		112
+		122
 		{
-			title = "Spincushion";
-			sprite = "SHRPA1";
+			title = "Spring Shell (Green)";
+			sprite = "SSHLA1";
+			width = 24;
+			height = 40;
+		}
+		125
+		{
+			title = "Spring Shell (Yellow)";
+			sprite = "SSHLI1";
+			width = 24;
+			height = 40;
+		}
+		109
+		{
+			title = "Skim";
+			sprite = "SKIMA1";
 			width = 16;
 			height = 24;
 		}
@@ -3375,26 +3352,21 @@ thingtypes
 			width = 12;
 			height = 20;
 		}
-		114
+		126
 		{
-			title = "Snailer";
-			sprite = "SNLRA3A7";
+			title = "Crushstacean";
+			sprite = "CRABA0";
 			width = 24;
-			height = 48;
-		}
-		115
-		{
-			title = "Bird Aircraft Strike Hazard";
-			sprite = "VLTRF1";
-			width = 12;
-			height = 24;
+			height = 32;
+			flags8text = "[8] Move left from spawn";
 		}
-		116
+		136
 		{
-			title = "Pointy";
-			sprite = "PNTYA1";
-			width = 8;
-			height = 16;
+			title = "Banpyura";
+			sprite = "CR2BA0";
+			width = 24;
+			height = 32;
+			flags8text = "[8] Move left from spawn";
 		}
 		117
 		{
@@ -3427,6 +3399,13 @@ thingtypes
 			flags4text = "[4] 90 degrees clockwise";
 			flags8text = "[8] Double speed";
 		}
+		115
+		{
+			title = "Bird Aircraft Strike Hazard";
+			sprite = "VLTRF1";
+			width = 12;
+			height = 24;
+		}
 		120
 		{
 			title = "Green Snapper";
@@ -3441,19 +3420,13 @@ thingtypes
 			width = 24;
 			height = 32;
 		}
-		122
-		{
-			title = "Spring Shell (Green)";
-			sprite = "SSHLA1";
-			width = 24;
-			height = 40;
-		}
-		125
+		134
 		{
-			title = "Spring Shell (Yellow)";
-			sprite = "SSHLI1";
-			width = 24;
-			height = 40;
+			title = "Canarivore";
+			sprite = "CANAA0";
+			width = 12;
+			height = 80;
+			hangs = 1;
 		}
 		123
 		{
@@ -3462,28 +3435,51 @@ thingtypes
 			width = 18;
 			height = 36;
 		}
-		126
+		135
 		{
-			title = "Crushstacean";
-			sprite = "CRABA0";
+			title = "Pterabyte Spawner";
+			sprite = "PTERA2A8";
+			width = 16;
+			height = 16;
+			parametertext = "No. Pterabytes";
+		}
+		136
+		{
+			title = "Pyre Fly";
+			sprite = "PYREA0";
 			width = 24;
-			height = 32;
-			flags8text = "[8] Move left from spawn";
+			height = 34;
+			flags8text = "[8] Start on fire";
 		}
-		127
+		105
 		{
-			title = "Hive Elemental";
-			sprite = "HIVEA0";
-			width = 32;
-			height = 80;
-			parametertext = "No. bees";
+			title = "Jetty-Syn Bomber";
+			sprite = "JETBB1";
+			width = 20;
+			height = 50;
+			flags8text = "[8] Cannot move";
 		}
-		128
+		106
 		{
-			title = "Bumble Bore";
-			sprite = "BUMBA1";
+			title = "Jetty-Syn Gunner";
+			sprite = "JETGB1";
+			width = 20;
+			height = 48;
+			flags8text = "[8] Cannot move";
+		}
+		112
+		{
+			title = "Spincushion";
+			sprite = "SHRPA1";
 			width = 16;
-			height = 32;
+			height = 24;
+		}
+		114
+		{
+			title = "Snailer";
+			sprite = "SNLRA3A7";
+			width = 24;
+			height = 48;
 		}
 		129
 		{
@@ -3499,6 +3495,13 @@ thingtypes
 			width = 24;
 			height = 32;
 		}
+		107
+		{
+			title = "Crawla Commander";
+			sprite = "CCOMA1";
+			width = 16;
+			height = 32;
+		}
 		131
 		{
 			title = "Spinbobert";
@@ -3522,29 +3525,34 @@ thingtypes
 			height = 24;
 			hangs = 1;
 		}
-		134
+		127
 		{
-			title = "Canarivore";
-			sprite = "CANAA0";
-			width = 12;
+			title = "Hive Elemental";
+			sprite = "HIVEA0";
+			width = 32;
 			height = 80;
-			hangs = 1;
+			parametertext = "No. bees";
 		}
-		135
+		128
 		{
-			title = "Pterabyte Spawner";
-			sprite = "PTERA2A8";
+			title = "Bumblebore";
+			sprite = "BUMBA1";
 			width = 16;
-			height = 16;
-			parametertext = "No. Pterabytes";
+			height = 32;
 		}
-		136
+		124
 		{
-			title = "Pyre Fly";
-			sprite = "PYREA0";
-			width = 24;
-			height = 34;
-			flags8text = "[8] Start on fire";
+			title = "AquaBuzz";
+			sprite = "BBUZA1";
+			width = 20;
+			height = 24;
+		}
+		116
+		{
+			title = "Pointy";
+			sprite = "PNTYA1";
+			width = 8;
+			height = 16;
 		}
 	}
 
@@ -4218,6 +4226,15 @@ thingtypes
 			flags4text = "[4] Ignore gravity";
 			flags8text = "[8] Rotate 22.5° CCW";
 		}
+		557
+		{
+			arrow = 1;
+			title = "Diagonal Blue Spring";
+			sprite = "BSPRD2";
+			width = 16;
+			flags4text = "[4] Ignore gravity";
+			flags8text = "[8] Rotate 22.5° CCW";
+		}
 		558
 		{
 			arrow = 1;
@@ -4248,6 +4265,24 @@ thingtypes
 			width = 16;
 			height = 32;
 		}
+		2045
+		{
+			arrow = 1;
+			title = "Yellow Boost Panel";
+			sprite = "BSTYA0";
+			flags8text = "[8] Force spin";
+			width = 28;
+			height = 2;
+		}
+		2046
+		{
+			arrow = 1;
+			title = "Red Boost Panel";
+			sprite = "BSTRA0";
+			flags8text = "[8] Force spin";
+			width = 28;
+			height = 2;
+		}
 	}
 
 	patterns
diff --git a/src/d_player.h b/src/d_player.h
index ff8c31203..90098917f 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -252,6 +252,8 @@ typedef enum
 	pw_spacetime, // In space, no one can hear you spin!
 	pw_extralife, // Extra Life timer
 	pw_pushing,
+	pw_justsprung,
+	pw_noautobrake,
 
 	pw_super, // Are you super?
 	pw_gravityboots, // gravity boots
diff --git a/src/dehacked.c b/src/dehacked.c
index 8d240326d..b5dabaaf4 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -4440,6 +4440,21 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_CRUSHCLAW_WAIT",
 	"S_CRUSHCHAIN",
 
+	// Banpyura
+	"S_BANPYURA_ROAM1",
+	"S_BANPYURA_ROAM2",
+	"S_BANPYURA_ROAM3",
+	"S_BANPYURA_ROAM4",
+	"S_BANPYURA_ROAMPAUSE",
+	"S_CDIAG1",
+	"S_CDIAG2",
+	"S_CDIAG3",
+	"S_CDIAG4",
+	"S_CDIAG5",
+	"S_CDIAG6",
+	"S_CDIAG7",
+	"S_CDIAG8",
+
 	// Jet Jaw
 	"S_JETJAW_ROAM1",
 	"S_JETJAW_ROAM2",
@@ -5906,6 +5921,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLAMEJETFLAME1",
 	"S_FLAMEJETFLAME2",
 	"S_FLAMEJETFLAME3",
+	"S_FLAMEJETFLAME4",
+	"S_FLAMEJETFLAME5",
+	"S_FLAMEJETFLAME6",
+	"S_FLAMEJETFLAME7",
+	"S_FLAMEJETFLAME8",
+	"S_FLAMEJETFLAME9",
 
 	// Spinning flame jets
 	"S_FJSPINAXISA1", // Counter-clockwise
@@ -6660,6 +6681,16 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_BHORIZ7",
 	"S_BHORIZ8",
 
+	"S_BOOSTERSOUND",
+	"S_YELLOWBOOSTERROLLER",
+	"S_YELLOWBOOSTERSEG_LEFT",
+	"S_YELLOWBOOSTERSEG_RIGHT",
+	"S_YELLOWBOOSTERSEG_FACE",
+	"S_REDBOOSTERROLLER",
+	"S_REDBOOSTERSEG_LEFT",
+	"S_REDBOOSTERSEG_RIGHT",
+	"S_REDBOOSTERSEG_FACE",
+
 	// Rain
 	"S_RAIN1",
 	"S_RAINRETURN",
@@ -7449,6 +7480,11 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_REDHORIZ",
 	"MT_BLUEHORIZ",
 
+	"MT_BOOSTERSEG",
+	"MT_BOOSTERROLLER",
+	"MT_YELLOWBOOSTER",
+	"MT_REDBOOSTER",
+
 	// Interactive Objects
 	"MT_BUBBLES", // Bubble source
 	"MT_SIGN", // Level end sign
@@ -8352,6 +8388,8 @@ static const char *const POWERS_LIST[] = {
 	"SPACETIME", // In space, no one can hear you spin!
 	"EXTRALIFE", // Extra Life timer
 	"PUSHING",
+	"JUSTSPRUNG",
+	"NOAUTOBRAKE",
 
 	"SUPER", // Are you super?
 	"GRAVITYBOOTS", // gravity boots
diff --git a/src/info.c b/src/info.c
index dd5338ef0..9b19cdec3 100644
--- a/src/info.c
+++ b/src/info.c
@@ -50,6 +50,8 @@ char sprnames[NUMSPRITES + 1][5] =
 	"TURR", // Pop-Up Turret
 	"SHRP", // Sharp
 	"CRAB", // Crushstacean
+	"CR2B", // Banpyura
+	"CSPR", // Banpyura spring
 	"JJAW", // Jet Jaw
 	"SNLR", // Snailer
 	"VLTR", // BASH
@@ -372,6 +374,8 @@ char sprnames[NUMSPRITES + 1][5] =
 	"SSWY", // Yellow Side Spring
 	"SSWR", // Red Side Spring
 	"SSWB", // Blue Side Spring
+	"BSTY", // Yellow Booster
+	"BSTR", // Red Booster
 
 	// Environmental Effects
 	"RAIN", // Rain
@@ -1005,6 +1009,22 @@ state_t states[NUMSTATES] =
 	{SPR_CRAB, 3, 37, {NULL},              0,                0, S_CRUSHCLAW_AIM}, // S_CRUSHCLAW_WAIT
 	{SPR_CRAB, 4, -1, {NULL}, 0, 0, S_NULL}, // S_CRUSHCHAIN
 
+	// Banpyura
+	{SPR_CR2B, 0,  3, {A_CrushstaceanWalk},  0, S_BANPYURA_ROAMPAUSE, S_BANPYURA_ROAM2}, // S_BANPYURA_ROAM1
+	{SPR_CR2B, 1,  3, {A_CrushstaceanWalk},  0, S_BANPYURA_ROAMPAUSE, S_BANPYURA_ROAM3}, // S_BANPYURA_ROAM2
+	{SPR_CR2B, 0,  3, {A_CrushstaceanWalk},  0, S_BANPYURA_ROAMPAUSE, S_BANPYURA_ROAM4}, // S_BANPYURA_ROAM3
+	{SPR_CR2B, 2,  3, {A_CrushstaceanWalk},  0, S_BANPYURA_ROAMPAUSE, S_BANPYURA_ROAM1}, // S_BANPYURA_ROAM4
+	{SPR_CR2B, 0, 40, {NULL},                0,                    0, S_BANPYURA_ROAM1}, // S_BANPYURA_ROAMPAUSE
+
+	{SPR_CSPR, 0, 1, {A_CrushclawAim}, 50, 20, S_CDIAG1}, // S_CDIAG1
+	{SPR_CSPR, 1, 1, {A_Pain},          0,  0, S_CDIAG3}, // S_CDIAG2
+	{SPR_CSPR, 2, 1, {A_CrushclawAim}, 50, 20, S_CDIAG4}, // S_CDIAG3
+	{SPR_CSPR, 3, 1, {A_CrushclawAim}, 50, 20, S_CDIAG5}, // S_CDIAG4
+	{SPR_CSPR, 4, 1, {A_CrushclawAim}, 50, 20, S_CDIAG6}, // S_CDIAG5
+	{SPR_CSPR, 3, 1, {A_CrushclawAim}, 50, 20, S_CDIAG7}, // S_CDIAG6
+	{SPR_CSPR, 2, 1, {A_CrushclawAim}, 50, 20, S_CDIAG8}, // S_CDIAG7
+	{SPR_CSPR, 1, 1, {A_CrushclawAim}, 50, 20, S_CDIAG1}, // S_CDIAG8
+
 	// Jet Jaw
 	{SPR_JJAW, 0, 1, {A_JetJawRoam},  0, 0, S_JETJAW_ROAM2},   // S_JETJAW_ROAM1
 	{SPR_JJAW, 0, 1, {A_JetJawRoam},  0, 0, S_JETJAW_ROAM3},   // S_JETJAW_ROAM2
@@ -2508,6 +2528,12 @@ state_t states[NUMSTATES] =
 	{SPR_FLME, FF_FULLBRIGHT  ,  4, {NULL}, 0, 0, S_FLAMEJETFLAME2}, // S_FLAMEJETFLAME1
 	{SPR_FLME, FF_FULLBRIGHT|1,  5, {NULL}, 0, 0, S_FLAMEJETFLAME3}, // S_FLAMEJETFLAME2
 	{SPR_FLME, FF_FULLBRIGHT|2, 11, {NULL}, 0, 0,           S_NULL}, // S_FLAMEJETFLAME3
+	{SPR_FLME, FF_FULLBRIGHT|3,  4, {NULL}, 0, 0, S_FLAMEJETFLAME5}, // S_FLAMEJETFLAME4
+	{SPR_FLME, FF_FULLBRIGHT|4,  5, {NULL}, 0, 0, S_FLAMEJETFLAME6}, // S_FLAMEJETFLAME5
+	{SPR_FLME, FF_FULLBRIGHT|5, 11, {NULL}, 0, 0,           S_NULL}, // S_FLAMEJETFLAME6
+	{SPR_FLME, FF_FULLBRIGHT|6,  4, {NULL}, 0, 0, S_FLAMEJETFLAME8}, // S_FLAMEJETFLAME7
+	{SPR_FLME, FF_FULLBRIGHT|7,  5, {NULL}, 0, 0, S_FLAMEJETFLAME9}, // S_FLAMEJETFLAME8
+	{SPR_FLME, FF_FULLBRIGHT|8, 11, {NULL}, 0, 0,           S_NULL}, // S_FLAMEJETFLAME9
 
 	// Spinning flame jets
 	// A: Counter-clockwise
@@ -3272,6 +3298,17 @@ state_t states[NUMSTATES] =
 	{SPR_SSWB, 2, 1, {NULL}, 0, 0, S_BHORIZ8},   // S_BHORIZ7
 	{SPR_SSWB, 1, 1, {NULL}, 0, 0, S_BHORIZ1},   // S_BHORIZ8
 
+	// Boosters
+	{SPR_NULL, 0, 1, {A_Pain}, 0, 0, S_INVISIBLE}, // S_BOOSTERSOUND
+	{SPR_BSTY,                  FF_ANIMATE, -1, {NULL}, 2, 1, S_NULL}, // S_YELLOWBOOSTERROLLER
+	{SPR_BSTY, 3|FF_PAPERSPRITE|FF_ANIMATE, -1, {NULL}, 2, 3, S_NULL}, // S_YELLOWBOOSTERSEG_LEFT
+	{SPR_BSTY, 6|FF_PAPERSPRITE|FF_ANIMATE, -1, {NULL}, 2, 3, S_NULL}, // S_YELLOWBOOSTERSEG_RIGHT
+	{SPR_BSTY, 9|FF_PAPERSPRITE,            -1, {NULL}, 0, 0, S_NULL}, // S_YELLOWBOOSTERSEG_FACE
+	{SPR_BSTR,                  FF_ANIMATE, -1, {NULL}, 2, 1, S_NULL}, // S_REDBOOSTERROLLER
+	{SPR_BSTR, 3|FF_PAPERSPRITE|FF_ANIMATE, -1, {NULL}, 2, 3, S_NULL}, // S_REDBOOSTERSEG_LEFT
+	{SPR_BSTR, 6|FF_PAPERSPRITE|FF_ANIMATE, -1, {NULL}, 2, 3, S_NULL}, // S_REDBOOSTERSEG_RIGHT
+	{SPR_BSTR, 9|FF_PAPERSPRITE,            -1, {NULL}, 0, 0, S_NULL}, // S_REDBOOSTERSEG_FACE
+
 	// Rain
 	{SPR_RAIN, FF_FULLBRIGHT|FF_TRANS50, -1, {NULL}, 0, 0, S_NULL}, // S_RAIN1
 	{SPR_RAIN, FF_FULLBRIGHT|FF_TRANS50, 1, {NULL}, 0, 0, S_RAIN1}, // S_RAINRETURN
@@ -4483,7 +4520,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_XPLD1,        // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		1,              // speed
+		600,            // speed
 		22*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
@@ -4521,6 +4558,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
+	{           // MT_BANPYURA
+		138,            // doomednum
+		S_BANPYURA_ROAM1, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		32,             // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_XPLD_FLICKY,  // deathstate
+		S_NULL,         // xdeathstate
+		sfx_pop,        // deathsound
+		8,              // speed
+		24*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_BAMPSPRING
+		-1,             // doomednum
+		S_CDIAG1,       // spawnstate
+		1,              // spawnhealth
+		S_CDIAG2,       // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_cdfm08,     // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_XPLD1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		300,            // speed
+		22*FRACUNIT,    // radius
+		22*FRACUNIT,    // height
+		0,              // display offset
+		11*FRACUNIT,    // mass
+		11*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_SPRING|MF_NOGRAVITY, // flags
+		S_CDIAG2        // raisestate
+	},
+
 	{           // MT_JETJAW
 		113,            // doomednum
 		S_JETJAW_ROAM1, // spawnstate
@@ -7560,12 +7651,120 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		32*FRACUNIT,    // height
 		0,              // display offset
 		0,              // mass
-		1*FRACUNIT,     // damage
+		11*FRACUNIT,    // damage
 		sfx_None,       // activesound
 		MF_SPRING|MF_NOGRAVITY, // flags
 		S_BHORIZ2       // raisestate
 	},
 
+	{          // MT_BOOSTERSEG
+		-1,             // doomednum
+		S_INVISIBLE,    // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // 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_None,       // deathsound
+		0,              // speed
+		28*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP,  // flags
+		S_NULL          // raisestate
+	},
+
+	{          // MT_BOOSTERROLLER
+		-1,             // doomednum
+		S_INVISIBLE,    // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // 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_None,       // deathsound
+		0,              // speed
+		14*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP,    // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_YELLOWBOOSTER
+		2045,           // doomednum -- Matched to Glaber's resource, otherwise custom-built.
+		S_INVISIBLE,    // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		3,              // painchance
+		sfx_cdfm62,     // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		28*FRACUNIT,    // radius
+		FRACUNIT,       // height
+		0,              // display offset
+		0,              // mass
+		36*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_SPRING|MF_NOGRAVITY, // flags
+		S_BOOSTERSOUND  // raisestate
+	},
+
+	{           // MT_REDBOOSTER
+		2046,           // doomednum -- Matched to Glaber's resource, otherwise custom-built.
+		S_INVISIBLE,    // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		3,              // painchance
+		sfx_cdfm62,     // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		28*FRACUNIT,    // radius
+		FRACUNIT,       // height
+		0,              // display offset
+		0,              // mass
+		72*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_SPRING|MF_NOGRAVITY, // flags
+		S_BOOSTERSOUND  // raisestate
+	},
+
 	{           // MT_BUBBLES
 		500,            // doomednum
 		S_BUBBLES1,     // spawnstate
diff --git a/src/info.h b/src/info.h
index 0502fd095..b930244fb 100644
--- a/src/info.h
+++ b/src/info.h
@@ -306,6 +306,8 @@ typedef enum sprite
 	SPR_TURR, // Pop-Up Turret
 	SPR_SHRP, // Sharp
 	SPR_CRAB, // Crushstacean
+	SPR_CR2B, // Banpyura
+	SPR_CSPR, // Banpyura spring
 	SPR_JJAW, // Jet Jaw
 	SPR_SNLR, // Snailer
 	SPR_VLTR, // BASH
@@ -628,6 +630,8 @@ typedef enum sprite
 	SPR_SSWY, // Yellow Side Spring
 	SPR_SSWR, // Red Side Spring
 	SPR_SSWB, // Blue Side Spring
+	SPR_BSTY, // Yellow Booster
+	SPR_BSTR, // Red Booster
 
 	// Environmental Effects
 	SPR_RAIN, // Rain
@@ -1164,6 +1168,21 @@ typedef enum state
 	S_CRUSHCLAW_WAIT,
 	S_CRUSHCHAIN,
 
+	// Banpyura
+	S_BANPYURA_ROAM1,
+	S_BANPYURA_ROAM2,
+	S_BANPYURA_ROAM3,
+	S_BANPYURA_ROAM4,
+	S_BANPYURA_ROAMPAUSE,
+	S_CDIAG1,
+	S_CDIAG2,
+	S_CDIAG3,
+	S_CDIAG4,
+	S_CDIAG5,
+	S_CDIAG6,
+	S_CDIAG7,
+	S_CDIAG8,
+
 	// Jet Jaw
 	S_JETJAW_ROAM1,
 	S_JETJAW_ROAM2,
@@ -2630,6 +2649,12 @@ typedef enum state
 	S_FLAMEJETFLAME1,
 	S_FLAMEJETFLAME2,
 	S_FLAMEJETFLAME3,
+	S_FLAMEJETFLAME4,
+	S_FLAMEJETFLAME5,
+	S_FLAMEJETFLAME6,
+	S_FLAMEJETFLAME7,
+	S_FLAMEJETFLAME8,
+	S_FLAMEJETFLAME9,
 
 	// Spinning flame jets
 	S_FJSPINAXISA1, // Counter-clockwise
@@ -3384,6 +3409,17 @@ typedef enum state
 	S_BHORIZ7,
 	S_BHORIZ8,
 
+	// Booster
+	S_BOOSTERSOUND,
+	S_YELLOWBOOSTERROLLER,
+	S_YELLOWBOOSTERSEG_LEFT,
+	S_YELLOWBOOSTERSEG_RIGHT,
+	S_YELLOWBOOSTERSEG_FACE,
+	S_REDBOOSTERROLLER,
+	S_REDBOOSTERSEG_LEFT,
+	S_REDBOOSTERSEG_RIGHT,
+	S_REDBOOSTERSEG_FACE,
+
 	// Rain
 	S_RAIN1,
 	S_RAINRETURN,
@@ -4058,6 +4094,8 @@ typedef enum mobj_type
 	MT_CRUSHSTACEAN, // Crushstacean
 	MT_CRUSHCLAW, // Big meaty claw
 	MT_CRUSHCHAIN, // Chain
+	MT_BANPYURA, // Banpyura
+	MT_BAMPSPRING, // Banpyura spring
 	MT_JETJAW, // Jet Jaw
 	MT_SNAILER, // Snailer
 	MT_VULTURE, // BASH
@@ -4195,6 +4233,11 @@ typedef enum mobj_type
 	MT_REDHORIZ,
 	MT_BLUEHORIZ,
 
+	MT_BOOSTERSEG,
+	MT_BOOSTERROLLER,
+	MT_YELLOWBOOSTER,
+	MT_REDBOOSTER,
+
 	// Interactive Objects
 	MT_BUBBLES, // Bubble source
 	MT_SIGN, // Level end sign
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 314e97606..7e3bebc3a 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -2027,6 +2027,7 @@ void A_CrushstaceanWalk(mobj_t *actor)
 	|| (actor->reactiontime-- <= 0))
 	{
 		actor->flags2 ^= MF2_AMBUSH;
+		P_SetTarget(&actor->target, NULL);
 		P_SetMobjState(actor, locvar2);
 		actor->reactiontime = actor->info->reactiontime;
 	}
@@ -2087,7 +2088,7 @@ void A_CrushclawAim(mobj_t *actor)
 		return; // there is only one step and it is crab
 	}
 
-	if (crab->target || P_LookForPlayers(crab, true, false, 600*crab->scale))
+	if (crab->target || P_LookForPlayers(crab, true, false, actor->info->speed*crab->scale))
 		ang = R_PointToAngle2(crab->x, crab->y, crab->target->x, crab->target->y);
 	else
 		ang = crab->angle + ((crab->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);
@@ -14214,18 +14215,17 @@ void A_RolloutRock(mobj_t *actor)
 {
 	INT32 locvar1 = var1;
 	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RolloutRock", actor))
-		return;
-#endif
-
 	UINT8 maxframes = actor->info->reactiontime; // number of frames the mobj cycles through
 	fixed_t pi = (22*FRACUNIT/7);
 	fixed_t circumference = FixedMul(2 * pi, actor->radius); // used to calculate when to change frame
 	fixed_t speed = P_AproxDistance(actor->momx, actor->momy), topspeed = FixedMul(actor->info->speed, actor->scale);
 	boolean inwater = actor->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER);
 
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RolloutRock", actor))
+		return;
+#endif
+
 	actor->friction = FRACUNIT; // turns out riding on solids sucks, so let's just make it easier on ourselves
 
 	if (actor->threshold)
diff --git a/src/p_inter.c b/src/p_inter.c
index cce9df91b..f4c92bfc9 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2613,6 +2613,14 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 			}
 			break;
 
+		case MT_BANPYURA:
+			if (target->tracer)
+			{
+				S_StopSound(target->tracer);
+				P_KillMobj(target->tracer, inflictor, source, damagetype);
+			}
+			break;
+
 		case MT_EGGSHIELD:
 			P_SetObjectMomZ(target, 4*target->scale, false);
 			P_InstaThrust(target, target->angle, 3*target->scale);
diff --git a/src/p_map.c b/src/p_map.c
index 810d85e34..19ea55192 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -124,6 +124,7 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
 //     Positive spring modes are minor variants of vanilla spring behaviour.
 //       1 = launch players in jump
 //       2 = don't modify player at all, just add momentum
+//       3 = speed-booster mode (force onto ground, MF_AMBUSH causes auto-spin)
 //     Negative spring modes are mildly-related gimmicks with customisation.
 //      -1 = pinball bumper
 //     Any other spring mode defaults to standard vanilla spring behaviour,
@@ -286,7 +287,27 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 	if (spring->info->painchance != 2)
 	{
 		if (object->player)
+		{
 			object->player->pflags &= ~PF_APPLYAUTOBRAKE;
+#ifndef SPRINGSPIN
+			object->player->powers[pw_justsprung] = 5;
+			if (horizspeed)
+				object->player->powers[pw_noautobrake] = ((horizspeed*TICRATE)>>(FRACBITS+3))/9; // TICRATE at 72*FRACUNIT
+			else if (P_MobjFlip(object) == P_MobjFlip(spring))
+				object->player->powers[pw_justsprung] |= (1<<15);
+#else
+			object->player->powers[pw_justsprung] = 15;
+			if (horizspeed)
+				object->player->powers[pw_noautobrake] = ((horizspeed*TICRATE)>>(FRACBITS+3))/9; // TICRATE at 72*FRACUNIT
+			else
+			{
+				if (abs(object->player->rmomx) > object->scale || abs(object->player->rmomy) > object->scale)
+					object->player->drawangle = R_PointToAngle2(0, 0, object->player->rmomx, object->player->rmomy);
+				if (P_MobjFlip(object) == P_MobjFlip(spring))
+					object->player->powers[pw_justsprung] |= (1<<15);
+			}
+#endif
+		}
 
 		if ((horizspeed && vertispeed) || (object->player && object->player->homing)) // Mimic SA
 		{
@@ -321,6 +342,14 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 
 			// Set position!
 			P_TryMove(object, spring->x + offx, spring->y + offy, true);
+
+			if ((spring->info->painchance == 3))
+			{
+				object->z = spring->z;
+				if (spring->eflags & MFE_VERTICALFLIP)
+					object->z -= object->height;
+				object->momz = 0;
+			}
 		}
 	}
 
@@ -344,8 +373,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 
 		if (horizspeed)
 		{
-			object->player->drawangle = spring->angle;
-			object->angle = spring->angle;
+			object->angle = object->player->drawangle = spring->angle;
 
 			if (!demoplayback || P_AnalogMove(object->player))
 			{
@@ -356,11 +384,25 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 			}
 		}
 
-		pflags = object->player->pflags & (PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // I still need these.
-		secondjump = object->player->secondjump;
-		washoming = object->player->homing;
 		if (object->player->pflags & PF_GLIDING)
 			P_SetPlayerMobjState(object, S_PLAY_FALL);
+		if ((spring->info->painchance == 3))
+		{
+			if (!(pflags = (object->player->pflags & PF_SPINNING)) &&
+				(((object->player->charability2 == CA2_SPINDASH) && (object->player->cmd.buttons & BT_USE))
+				|| (spring->flags2 & MF2_AMBUSH)))
+			{
+				pflags = PF_SPINNING;
+				P_SetPlayerMobjState(object, S_PLAY_ROLL);
+				S_StartSound(object, sfx_spin);
+			}
+			else
+				P_SetPlayerMobjState(object, S_PLAY_ROLL);
+		}
+		else
+			pflags = object->player->pflags & (PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // I still need these.
+		secondjump = object->player->secondjump;
+		washoming = object->player->homing;
 		P_ResetPlayer(object->player);
 
 		if (spring->info->painchance == 1) // For all those ancient, SOC'd abilities.
@@ -382,6 +424,10 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 				object->player->pflags |= pflags;
 				object->player->secondjump = secondjump;
 			}
+			else if (object->player->dashmode >= 3*TICRATE)
+				P_SetPlayerMobjState(object, S_PLAY_DASH);
+			else if (P_IsObjectOnGround(object) && horizspeed >= FixedMul(object->player->runspeed, object->scale))
+				P_SetPlayerMobjState(object, S_PLAY_RUN);
 			else
 				P_SetPlayerMobjState(object, S_PLAY_WALK);
 		}
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 7fa51111d..bf6f6263d 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -7552,6 +7552,7 @@ void P_MobjThinker(mobj_t *mobj)
 						mobj->fuse -= 2;
 
 					flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME);
+					P_SetMobjState(flame, S_FLAMEJETFLAME4);
 
 					flame->angle = mobj->angle;
 
@@ -7596,7 +7597,10 @@ void P_MobjThinker(mobj_t *mobj)
 						flame->momz = -strength;
 					}
 					else
+					{
 						flame->momz = strength;
+						P_SetMobjState(flame, S_FLAMEJETFLAME7);
+					}
 					P_InstaThrust(flame, mobj->angle, FixedDiv(mobj->fuse*FRACUNIT,3*FRACUNIT));
 					S_StartSound(flame, sfx_fire);
 				}
@@ -10283,6 +10287,15 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 				mobj->reactiontime >>= 1;
 			}
 			break;
+		case MT_BANPYURA:
+			{
+				mobj_t *bigmeatyclaw = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_BAMPSPRING);
+				bigmeatyclaw->angle = mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);;
+				P_SetTarget(&mobj->tracer, bigmeatyclaw);
+				P_SetTarget(&bigmeatyclaw->tracer, mobj);
+				mobj->reactiontime >>= 1;
+			}
+			break;
 		case MT_BIGMINE:
 			mobj->extravalue1 = FixedHypot(mobj->x, mobj->y)>>FRACBITS;
 			break;
@@ -11558,7 +11571,9 @@ You should think about modifying the deathmatch starts to take full advantage of
 
 	if (i == MT_ROSY)
 	{
-		if (mariomode)
+		if (!(gametype == GT_COOP || (mthing->options & MTF_EXTRA)))
+			return; // she doesn't hang out here
+		else if (mariomode)
 			i = MT_TOAD; // don't remove on penalty of death
 		else if (!(netgame || multiplayer) && players[consoleplayer].skin == 5)
 			return; // no doubles
@@ -12484,6 +12499,76 @@ ML_EFFECT5 : Don't stop thinking when too far away
 		}
 		break;
 	}
+	case MT_REDBOOSTER:
+	{
+		angle_t angle = FixedAngle(mthing->angle << FRACBITS);
+		fixed_t x1 = FINECOSINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
+		fixed_t y1 = FINESINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
+		fixed_t x2 = FINECOSINE(((angle+ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
+		fixed_t y2 = FINESINE(((angle+ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
+
+		mobj_t *seg = P_SpawnMobjFromMobj(mobj, 26*x1, 26*y1, 0, MT_BOOSTERSEG);
+		seg->angle = angle-ANGLE_90;
+		P_SetMobjState(seg, S_REDBOOSTERSEG_FACE);
+		seg = P_SpawnMobjFromMobj(mobj, -26*x1, -26*y1, 0, MT_BOOSTERSEG);
+		seg->angle = angle+ANGLE_90;
+		P_SetMobjState(seg, S_REDBOOSTERSEG_FACE);
+		seg = P_SpawnMobjFromMobj(mobj, 21*x2, 21*y2, 0, MT_BOOSTERSEG);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_REDBOOSTERSEG_LEFT);
+		seg = P_SpawnMobjFromMobj(mobj, -21*x2, -21*y2, 0, MT_BOOSTERSEG);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_REDBOOSTERSEG_RIGHT);
+
+		seg = P_SpawnMobjFromMobj(mobj, 13*(x1+x2), 13*(y1+y2), 0, MT_BOOSTERROLLER);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_REDBOOSTERROLLER);
+		seg = P_SpawnMobjFromMobj(mobj, 13*(x1-x2), 13*(y1-y2), 0, MT_BOOSTERROLLER);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_REDBOOSTERROLLER);
+		seg = P_SpawnMobjFromMobj(mobj, -13*(x1+x2), -13*(y1+y2), 0, MT_BOOSTERROLLER);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_REDBOOSTERROLLER);
+		seg = P_SpawnMobjFromMobj(mobj, -13*(x1-x2), -13*(y1-y2), 0, MT_BOOSTERROLLER);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_REDBOOSTERROLLER);
+		break;
+	}
+	case MT_YELLOWBOOSTER:
+	{
+		angle_t angle = FixedAngle(mthing->angle << FRACBITS);
+		fixed_t x1 = FINECOSINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
+		fixed_t y1 = FINESINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
+		fixed_t x2 = FINECOSINE(((angle+ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
+		fixed_t y2 = FINESINE(((angle+ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
+
+		mobj_t *seg = P_SpawnMobjFromMobj(mobj, 26*x1, 26*y1, 0, MT_BOOSTERSEG);
+		seg->angle = angle-ANGLE_90;
+		P_SetMobjState(seg, S_YELLOWBOOSTERSEG_FACE);
+		seg = P_SpawnMobjFromMobj(mobj, -26*x1, -26*y1, 0, MT_BOOSTERSEG);
+		seg->angle = angle+ANGLE_90;
+		P_SetMobjState(seg, S_YELLOWBOOSTERSEG_FACE);
+		seg = P_SpawnMobjFromMobj(mobj, 21*x2, 21*y2, 0, MT_BOOSTERSEG);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_YELLOWBOOSTERSEG_LEFT);
+		seg = P_SpawnMobjFromMobj(mobj, -21*x2, -21*y2, 0, MT_BOOSTERSEG);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_YELLOWBOOSTERSEG_RIGHT);
+
+		seg = P_SpawnMobjFromMobj(mobj, 13*(x1+x2), 13*(y1+y2), 0, MT_BOOSTERROLLER);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_YELLOWBOOSTERROLLER);
+		seg = P_SpawnMobjFromMobj(mobj, 13*(x1-x2), 13*(y1-y2), 0, MT_BOOSTERROLLER);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_YELLOWBOOSTERROLLER);
+		seg = P_SpawnMobjFromMobj(mobj, -13*(x1+x2), -13*(y1+y2), 0, MT_BOOSTERROLLER);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_YELLOWBOOSTERROLLER);
+		seg = P_SpawnMobjFromMobj(mobj, -13*(x1-x2), -13*(y1-y2), 0, MT_BOOSTERROLLER);
+		seg->angle = angle;
+		P_SetMobjState(seg, S_YELLOWBOOSTERROLLER);
+		break;
+	}
 	default:
 		break;
 	}
@@ -12682,7 +12767,7 @@ ML_EFFECT5 : Don't stop thinking when too far away
 	{
 		if (mthing->options & MTF_AMBUSH)
 		{
-			if (i == MT_YELLOWDIAG || i == MT_REDDIAG)
+			if (i == MT_YELLOWDIAG || i == MT_REDDIAG || i == MT_BLUEDIAG)
 				mobj->angle += ANGLE_22h;
 
 			if (i == MT_YELLOWHORIZ || i == MT_REDHORIZ || i == MT_BLUEHORIZ)
@@ -12721,7 +12806,7 @@ ML_EFFECT5 : Don't stop thinking when too far away
 
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
-			if (i == MT_YELLOWDIAG || i == MT_REDDIAG)
+			if (i == MT_YELLOWDIAG || i == MT_REDDIAG || i == MT_BLUEDIAG)
 				mobj->flags |= MF_NOGRAVITY;
 
 			if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0)
diff --git a/src/p_user.c b/src/p_user.c
index 33f84e2db..29c01c7a8 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11323,6 +11323,13 @@ void P_PlayerThink(player_t *player)
 					break;
 			}
 		}
+		else if (player->powers[pw_justsprung])
+		{
+#ifdef SPRINGSPIN
+			if (player->powers[pw_justsprung] & (1<<15))
+				player->drawangle += (player->powers[pw_justsprung] & ~(1<<15))*(ANG2+ANG1);
+#endif
+		}
 		else if ((player->skidtime > (TICRATE/2 - 2) || ((player->pflags & (PF_SPINNING|PF_STARTDASH)) == PF_SPINNING)) && (abs(player->rmomx) > 5*player->mo->scale || abs(player->rmomy) > 5*player->mo->scale)) // spin/skid force
 			player->drawangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
 		else if (((player->charability2 == CA2_GUNSLINGER || player->charability2 == CA2_MELEE) && player->panim == PA_ABILITY2) || player->pflags & PF_STASIS || player->skidtime)
@@ -11335,7 +11342,7 @@ void P_PlayerThink(player_t *player)
 				if (player->mo->eflags & MFE_TOUCHWATER || player->powers[pw_flashing] > (flashingtics/4)*3)
 				{
 					diff = (player->mo->angle - player->drawangle);
-					factor = 4;
+					factor = 32;
 				}
 				else
 				{
@@ -11344,7 +11351,7 @@ void P_PlayerThink(player_t *player)
 				}
 #else
 				diff = (player->mo->angle - player->drawangle);
-				factor = 4;
+				factor = 32;
 #endif
 			}
 			else if (player->pflags & PF_STARTDASH)
@@ -11379,7 +11386,9 @@ void P_PlayerThink(player_t *player)
 		{
 			boolean currentlyonground = P_IsObjectOnGround(player->mo);
 
-			if (!player->powers[pw_carry] && !player->powers[pw_nocontrol]
+			if (player->powers[pw_noautobrake])
+				;
+			else if (!player->powers[pw_carry] && !player->powers[pw_nocontrol]
 			&& ((player->pflags & (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE|PF_STASIS)) == (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE))
 			&& !(cmd->forwardmove || cmd->sidemove)
 			&& (player->rmomx || player->rmomy)
@@ -11421,9 +11430,6 @@ void P_PlayerThink(player_t *player)
 		}
 	}
 
-	if (player->powers[pw_pushing])
-		player->powers[pw_pushing]--;
-
 	player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame.
 
 	// Unset statis flags after moving.
@@ -11503,6 +11509,17 @@ void P_PlayerThink(player_t *player)
 	if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX && player->charability != CA_SWIM) // tails fly counter
 		player->powers[pw_tailsfly]--;
 
+	if (player->powers[pw_pushing] && player->powers[pw_pushing] < UINT16_MAX)
+		player->powers[pw_pushing]--;
+
+	if (player->powers[pw_justsprung] & ((1<<15)-1) && player->powers[pw_justsprung] < UINT16_MAX)
+		player->powers[pw_justsprung]--;
+	else
+		player->powers[pw_justsprung] = 0;
+
+	if (player->powers[pw_pushing] && player->powers[pw_pushing] < UINT16_MAX)
+		player->powers[pw_pushing]--;
+
 	if (player->powers[pw_underwater] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_PROTECTWATER)))
 	{
 		if (player->powers[pw_underwater] <= 12*TICRATE+1)
diff --git a/src/sounds.c b/src/sounds.c
index e5dfeec8a..8dc97b1e6 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -715,7 +715,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"cdfm59", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
   {"cdfm60", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
   {"cdfm61", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"cdfm62", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
+  {"cdfm62", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Speed boost"},
   {"cdfm63", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
   {"cdfm64", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
   {"cdfm65", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-- 
GitLab