From 8652bbc1a56c327eee13d8fbb61e6bbeba0bd5d9 Mon Sep 17 00:00:00 2001
From: lachwright <lachlanwright17@gmail.com>
Date: Sat, 4 Jul 2020 01:54:00 +0800
Subject: [PATCH] Knuckles changes: - Buffed crouch landing; lasts slightly
 shorter and cuts speed less - Support for swimming! Knuckles has slightly
 different glide controls when he has swim sprites underwater - Improved glide
 controls in 2D mode - Super Knuckles perks: land from a glide or latch onto a
 wall to generate a shockwave and damage nearby enemies - Refactored skid dust
 - Made super sparks use FF_FULLBRIGHT - Added P_SpawnSkidDust and
 P_Earthquake to Lua

---
 src/d_player.h    |   1 +
 src/dehacked.c    |   1 +
 src/info.c        |  10 +-
 src/lua_baselib.c |  28 +++++
 src/p_local.h     |   2 +
 src/p_map.c       |   7 +-
 src/p_mobj.c      |   3 +-
 src/p_user.c      | 279 ++++++++++++++++++++++++++++------------------
 8 files changed, 216 insertions(+), 115 deletions(-)

diff --git a/src/d_player.h b/src/d_player.h
index fba2a6837..2c4483775 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -49,6 +49,7 @@ typedef enum
 	SF_MULTIABILITY     = 1<<13, // Revenge of Final Demo.
 	SF_NONIGHTSROTATION = 1<<14, // Disable sprite rotation for NiGHTS
 	SF_NONIGHTSSUPER    = 1<<15, // Disable super colors for NiGHTS (if you have SF_SUPER)
+	SF_NOSUPERJUMPBOOST = 1<<16, // Disable the jump boost given while super (i.e. Knuckles)
 	// free up to and including 1<<31
 } skinflags_t;
 
diff --git a/src/dehacked.c b/src/dehacked.c
index 78120c5c4..de93ce50e 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -9655,6 +9655,7 @@ struct {
 	{"SF_MULTIABILITY",SF_MULTIABILITY},
 	{"SF_NONIGHTSROTATION",SF_NONIGHTSROTATION},
 	{"SF_NONIGHTSSUPER",SF_NONIGHTSSUPER},
+	{"SF_NOSUPERJUMPBOOST",SF_NOSUPERJUMPBOOST},
 
 	// Dashmode constants
 	{"DASHMODE_THRESHOLD",DASHMODE_THRESHOLD},
diff --git a/src/info.c b/src/info.c
index 62048b64a..a9619d7c7 100644
--- a/src/info.c
+++ b/src/info.c
@@ -2929,11 +2929,11 @@ state_t states[NUMSTATES] =
 	{SPR_IVSP, FF_ANIMATE, 32, {NULL}, 31, 1, S_NULL},   // S_IVSP
 
 	// Super Sonic Spark
-	{SPR_SSPK, 0, 2, {NULL}, 0, 0, S_SSPK2}, // S_SSPK1
-	{SPR_SSPK, 1, 2, {NULL}, 0, 0, S_SSPK3}, // S_SSPK2
-	{SPR_SSPK, 2, 2, {NULL}, 0, 0, S_SSPK4}, // S_SSPK3
-	{SPR_SSPK, 1, 2, {NULL}, 0, 0, S_SSPK5}, // S_SSPK4
-	{SPR_SSPK, 0, 2, {NULL}, 0, 0, S_NULL},  // S_SSPK5
+	{SPR_SSPK,   FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SSPK2}, // S_SSPK1
+	{SPR_SSPK, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SSPK3}, // S_SSPK2
+	{SPR_SSPK, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SSPK4}, // S_SSPK3
+	{SPR_SSPK, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SSPK5}, // S_SSPK4
+	{SPR_SSPK,   FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL},  // S_SSPK5
 
 	// Flicky-sized bubble
 	{SPR_FBUB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_FLICKY_BUBBLE
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index db70a11a2..53bb6e328 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1259,6 +1259,19 @@ static int lib_pElementalFire(lua_State *L)
 	return 0;
 }
 
+static int lib_pSpawnSkidDust(lua_State *L)
+{
+	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	fixed_t radius = luaL_checkfixed(L, 2);
+	boolean sound = lua_optboolean(L, 3);
+	NOHUD
+	INLEVEL
+	if (!player)
+		return LUA_ErrInvalid(L, "player_t");
+	P_SpawnSkidDust(player, radius, sound);
+	return 0;
+}
+
 static int lib_pDoPlayerFinish(lua_State *L)
 {
 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@@ -1346,6 +1359,19 @@ static int lib_pNukeEnemies(lua_State *L)
 	return 0;
 }
 
+static int lib_pEarthquake(lua_State *L)
+{
+	mobj_t *inflictor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+	mobj_t *source = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
+	fixed_t radius = luaL_checkfixed(L, 3);
+	NOHUD
+	INLEVEL
+	if (!inflictor || !source)
+		return LUA_ErrInvalid(L, "mobj_t");
+	P_Earthquake(inflictor, source, radius);
+	return 0;
+}
+
 static int lib_pHomingAttack(lua_State *L)
 {
 	mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@@ -3262,6 +3288,7 @@ static luaL_Reg lib[] = {
 	{"P_DoBubbleBounce",lib_pDoBubbleBounce},
 	{"P_BlackOw",lib_pBlackOw},
 	{"P_ElementalFire",lib_pElementalFire},
+	{"P_SpawnSkidDust", lib_pSpawnSkidDust},
 	{"P_DoPlayerFinish",lib_pDoPlayerFinish},
 	{"P_DoPlayerExit",lib_pDoPlayerExit},
 	{"P_InstaThrust",lib_pInstaThrust},
@@ -3269,6 +3296,7 @@ static luaL_Reg lib[] = {
 	{"P_ReturnThrustY",lib_pReturnThrustY},
 	{"P_LookForEnemies",lib_pLookForEnemies},
 	{"P_NukeEnemies",lib_pNukeEnemies},
+	{"P_Earthquake",lib_pEarthquake},
 	{"P_HomingAttack",lib_pHomingAttack},
 	{"P_SuperReady",lib_pSuperReady},
 	{"P_DoJump",lib_pDoJump},
diff --git a/src/p_local.h b/src/p_local.h
index e89343ca8..f10975702 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -175,6 +175,7 @@ void P_DoAbilityBounce(player_t *player, boolean changemomz);
 void P_TwinSpinRejuvenate(player_t *player, mobjtype_t type);
 void P_BlackOw(player_t *player);
 void P_ElementalFire(player_t *player, boolean cropcircle);
+void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound);
 
 void P_DoPityCheck(player_t *player);
 void P_PlayerThink(player_t *player);
@@ -192,6 +193,7 @@ mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction,
 
 mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet);
 void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius);
+void P_Earthquake(mobj_t *inflictor, mobj_t *source, fixed_t radius);
 boolean P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user
 boolean P_SuperReady(player_t *player);
 void P_DoJump(player_t *player, boolean soundandstate);
diff --git a/src/p_map.c b/src/p_map.c
index 18d6a1853..acfffc901 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -3373,8 +3373,13 @@ static void PTR_GlideClimbTraverse(line_t *li)
 
 			if (!slidemo->player->climbing)
 			{
-				S_StartSound(slidemo->player->mo, sfx_s3k4a);
+				S_StartSound(slidemo, sfx_s3k4a);
 				slidemo->player->climbing = 5;
+				if (slidemo->player->powers[pw_super])
+				{
+					P_Earthquake(slidemo, slidemo, 256*slidemo->scale);
+					S_StartSound(slidemo, sfx_s3k49);
+				}
 			}
 
 			slidemo->player->pflags &= ~(PF_GLIDING|PF_SPINNING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED);
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 40d0755b3..e2d93611d 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -211,7 +211,8 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 		return P_SetPlayerMobjState(mobj, S_PLAY_FALL);
 
 	// Catch swimming versus flying
-	if ((state == S_PLAY_FLY || state == S_PLAY_GLIDE) && player->mo->eflags & MFE_UNDERWATER && !player->skidtime)
+	if ((state == S_PLAY_FLY || (state == S_PLAY_GLIDE && skins[player->skin].sprites[SPR2_SWIM].numframes))
+	&& player->mo->eflags & MFE_UNDERWATER && !player->skidtime)
 		return P_SetPlayerMobjState(player->mo, S_PLAY_SWIM);
 	else if (state == S_PLAY_SWIM && !(player->mo->eflags & MFE_UNDERWATER))
 	{
diff --git a/src/p_user.c b/src/p_user.c
index 7a51e7bf3..9d23143b3 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2322,6 +2322,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
 		}
 		else
 		{
+			P_MobjCheckWater(player->mo);
 			if (player->pflags & PF_SPINNING)
 			{
 				if (player->mo->state-states != S_PLAY_ROLL && !(player->pflags & PF_STARTDASH))
@@ -2336,23 +2337,31 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
 				{
 					player->skidtime = TICRATE;
 					P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
+					P_SpawnSkidDust(player, player->mo->radius, true); // make sure the player knows they landed
 					player->mo->tics = -1;
 				}
 				else if (!player->skidtime)
 					player->pflags &= ~PF_GLIDING;
 			}
-			else if (player->charability == CA_GLIDEANDCLIMB && player->pflags & PF_THOKKED && !(player->pflags & (PF_JUMPED|PF_SHIELDABILITY)) && player->mo->state-states == S_PLAY_FALL)
+			else if (player->charability == CA_GLIDEANDCLIMB && player->pflags & PF_THOKKED && !(player->pflags & (PF_JUMPED|PF_SHIELDABILITY))
+			&& (player->mo->floorz != player->mo->watertop) && player->mo->state-states == S_PLAY_FALL)
 			{
 				if (player->mo->state-states != S_PLAY_GLIDE_LANDING)
 				{
 					P_ResetPlayer(player);
 					P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE_LANDING);
-					S_StartSound(player->mo, sfx_s3k4c);
 					player->pflags |= PF_STASIS;
 					if (player->speed > FixedMul(player->runspeed, player->mo->scale))
 						player->skidtime += player->mo->tics;
-					player->mo->momx = ((player->mo->momx - player->cmomx) >> 1) + player->cmomx;
-					player->mo->momy = ((player->mo->momy - player->cmomy) >> 1) + player->cmomy;
+					player->mo->momx = ((player->mo->momx - player->cmomx)/2) + player->cmomx;
+					player->mo->momy = ((player->mo->momy - player->cmomy)/2) + player->cmomy;
+					if (player->powers[pw_super])
+					{
+						P_Earthquake(player->mo, player->mo, 256*player->mo->scale);
+						S_StartSound(player->mo, sfx_s3k49);
+					}
+					else
+						S_StartSound(player->mo, sfx_s3k4c);
 				}
 			}
 			else if (player->charability2 == CA2_MELEE
@@ -4447,7 +4456,7 @@ void P_DoJump(player_t *player, boolean soundandstate)
 		}
 		else if (maptol & TOL_NIGHTS)
 			player->mo->momz = 18*FRACUNIT;
-		else if (player->powers[pw_super])
+		else if (player->powers[pw_super] && !(player->charflags & SF_NOSUPERJUMPBOOST))
 		{
 			player->mo->momz = 13*FRACUNIT;
 
@@ -5346,8 +5355,11 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 						{
 							glidespeed >>= 1;
 							playerspeed >>= 1;
-							player->mo->momx = ((player->mo->momx - player->cmomx) >> 1) + player->cmomx;
-							player->mo->momy = ((player->mo->momy - player->cmomy) >> 1) + player->cmomy;
+							if (!(player->powers[pw_super] || player->powers[pw_sneakers]))
+							{
+								player->mo->momx = ((player->mo->momx - player->cmomx)/2) + player->cmomx;
+								player->mo->momy = ((player->mo->momy - player->cmomy)/2) + player->cmomy;
+							}
 						}
 
 						player->pflags |= PF_GLIDING|PF_THOKKED;
@@ -5774,7 +5786,7 @@ static void P_2dMovement(player_t *player)
 	if (player->climbing)
 	{
 		if (cmd->forwardmove != 0)
-			P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, 15*FRACUNIT>>1), false);
+			P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, player->powers[pw_super] ? 5*FRACUNIT : 15*FRACUNIT>>1), false); // 2/3 while super
 
 		player->mo->momx = 0;
 	}
@@ -5989,9 +6001,9 @@ static void P_3dMovement(player_t *player)
 		if (cmd->forwardmove)
 		{
 			if (player->mo->eflags & MFE_UNDERWATER)
-				P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, 10*FRACUNIT), false);
+				P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, player->powers[pw_super] ? 20*FRACUNIT/3 : 10*FRACUNIT), false); // 2/3 while super
 			else
-				P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, 15*FRACUNIT>>1), false);
+				P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, player->powers[pw_super] ? 5*FRACUNIT : 15*FRACUNIT>>1), false); // 2/3 while super
 		}
 	}
 	else if (!(controlstyle == CS_LMAOGALOG)
@@ -6024,9 +6036,9 @@ static void P_3dMovement(player_t *player)
 	if (player->climbing)
 	{
 		if (player->mo->eflags & MFE_UNDERWATER)
-			P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedDiv(cmd->sidemove*player->mo->scale, 10*FRACUNIT));
+			P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedDiv(cmd->sidemove*player->mo->scale, player->powers[pw_super] ? 20*FRACUNIT/3 : 10*FRACUNIT)); // 2/3 while super
 		else
-			P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedDiv(cmd->sidemove*player->mo->scale, 15*FRACUNIT>>1));
+			P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedDiv(cmd->sidemove*player->mo->scale, player->powers[pw_super] ? 5*FRACUNIT : 15*FRACUNIT>>1)); // 2/3 while super
 	}
 	// Analog movement control
 	else if (controlstyle == CS_LMAOGALOG)
@@ -7759,7 +7771,7 @@ void P_ElementalFire(player_t *player, boolean cropcircle)
 //
 // Spawns spindash dust randomly around the player within a certain radius.
 //
-static void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound)
+void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound)
 {
 	mobj_t *mo = player->mo;
 	mobj_t *particle;
@@ -7797,7 +7809,7 @@ static void P_SkidStuff(player_t *player)
 		{
 			player->skidtime = 0;
 			player->pflags &= ~(PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE);
-			player->pflags |= PF_THOKKED; // nice try, speedrunners (but for real this is just behavior from S3K)
+			player->pflags |= PF_THOKKED;
 			P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
 		}
 		// Get up and brush yourself off, idiot.
@@ -7808,8 +7820,8 @@ static void P_SkidStuff(player_t *player)
 			player->pflags |= PF_STASIS;
 			if (player->speed > FixedMul(player->runspeed, player->mo->scale))
 				player->skidtime += player->mo->tics;
-			player->mo->momx = ((player->mo->momx - player->cmomx) >> 1) + player->cmomx;
-			player->mo->momy = ((player->mo->momy - player->cmomy) >> 1) + player->cmomy;
+			player->mo->momx = ((player->mo->momx - player->cmomx)/2) + player->cmomx;
+			player->mo->momy = ((player->mo->momy - player->cmomy)/2) + player->cmomy;
 		}
 		// Didn't stop yet? Skid FOREVER!
 		else if (player->skidtime == 1)
@@ -8192,10 +8204,11 @@ static void P_MovePlayer(player_t *player)
 	if (player->pflags & PF_GLIDING)
 	{
 		mobj_t *mo = player->mo; // seriously why isn't this at the top of the function hngngngng
-		fixed_t leeway = (P_ControlStyle(player) != CS_LMAOGALOG) ? FixedAngle(cmd->sidemove*(FRACUNIT)) : 0;
 		fixed_t glidespeed = player->actionspd;
 		fixed_t momx = mo->momx - player->cmomx, momy = mo->momy - player->cmomy;
 		angle_t angle, moveangle = R_PointToAngle2(0, 0, momx, momy);
+		boolean swimming = mo->state - states == S_PLAY_SWIM;
+		boolean in2d = mo->flags2 & MF2_TWOD || twodlevel;
 
 		if (player->powers[pw_super] || player->powers[pw_sneakers])
 			glidespeed *= 2;
@@ -8212,42 +8225,83 @@ static void P_MovePlayer(player_t *player)
 		}
 
 		// Strafing while gliding.
-		angle = mo->angle - leeway;
+		if ((P_ControlStyle(player) & CS_LMAOGALOG) || in2d)
+			angle = mo->angle;
+		else if (swimming)
+			angle = mo->angle + R_PointToAngle2(0, 0, cmd->forwardmove<<FRACBITS, -cmd->sidemove<<FRACBITS);
+		else
+			angle = mo->angle - FixedAngle(cmd->sidemove * FRACUNIT);
 
 		if (!player->skidtime) // TODO: make sure this works in 2D!
 		{
+			angle_t anglediff = angle - moveangle;
+			fixed_t accelfactor = 4*FRACUNIT - 3*FINECOSINE((anglediff >> ANGLETOFINESHIFT) & FINEMASK);
 			fixed_t speed, scale = mo->scale;
-			fixed_t newMagnitude, oldMagnitude = R_PointToDist2(momx, momy, 0, 0);
-			fixed_t accelfactor = 4*FRACUNIT - 3*FINECOSINE(((angle-moveangle) >> ANGLETOFINESHIFT) & FINEMASK); // mamgic number BAD but this feels right
 
-			if (mo->eflags & MFE_UNDERWATER)
-				speed = FixedMul((glidespeed>>1) + player->glidetime*750, scale);
-			else
-				speed = FixedMul(glidespeed + player->glidetime*1500, scale);
+			if (in2d)
+			{
+				if (mo->eflags & MFE_UNDERWATER)
+					speed = FixedMul((glidespeed>>1) + player->glidetime*750, scale);
+				else
+					speed = FixedMul(glidespeed + player->glidetime*1500, scale);
+				P_InstaThrust(mo, angle, speed);
+			}
+			else if (swimming)
+			{
+				fixed_t minspeed;
+
+				if (anglediff > ANGLE_180)
+					anglediff = InvAngle(InvAngle(anglediff) >> 3);
+				else
+					anglediff = anglediff >> 3;
+
+				minspeed = FixedMul((glidespeed>>1) + player->glidetime*750, scale); // underwater-specific
+				speed = FixedHypot(momx, momy) - abs(P_ReturnThrustY(mo, anglediff, mo->scale));
 
-			P_Thrust(mo, angle, FixedMul(accelfactor, scale));
+				if (speed < minspeed)
+				{
+					momx += P_ReturnThrustX(mo, angle, FixedMul(accelfactor, scale));
+					momy += P_ReturnThrustY(mo, angle, FixedMul(accelfactor, scale));
+					speed = FixedHypot(momx, momy); // recalculate speed
+				}
 
-			newMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
-			if (newMagnitude > speed)
+				mo->momx = P_ReturnThrustX(mo, moveangle + anglediff, speed) + player->cmomx;
+				mo->momy = P_ReturnThrustY(mo, moveangle + anglediff, speed) + player->cmomy;
+			}
+			else
 			{
-				fixed_t tempmomx, tempmomy;
-				if (oldMagnitude > speed)
+				fixed_t newMagnitude, oldMagnitude = R_PointToDist2(momx, momy, 0, 0);
+				fixed_t accelfactor = 4*FRACUNIT - 3*FINECOSINE(((angle-moveangle) >> ANGLETOFINESHIFT) & FINEMASK);
+
+				if (mo->eflags & MFE_UNDERWATER)
+					speed = FixedMul((glidespeed>>1) + player->glidetime*750, scale);
+				else
+					speed = FixedMul(glidespeed + player->glidetime*1500, scale);
+
+				P_Thrust(mo, angle, FixedMul(accelfactor, scale));
+
+				newMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
+				if (newMagnitude > speed)
 				{
-					if (newMagnitude > oldMagnitude)
+					fixed_t tempmomx, tempmomy;
+					if (oldMagnitude > speed)
 					{
-						tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), oldMagnitude);
-						tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), oldMagnitude);
+						if (newMagnitude > oldMagnitude)
+						{
+							tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), oldMagnitude);
+							tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), oldMagnitude);
+							player->mo->momx = tempmomx + player->cmomx;
+							player->mo->momy = tempmomy + player->cmomy;
+						}
+						// else do nothing
+					}
+					else
+					{
+						tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), speed);
+						tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), speed);
 						player->mo->momx = tempmomx + player->cmomx;
 						player->mo->momy = tempmomy + player->cmomy;
 					}
-					// else do nothing
-				}
-				else
-				{
-					tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), speed);
-					tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), speed);
-					player->mo->momx = tempmomx + player->cmomx;
-					player->mo->momy = tempmomy + player->cmomy;
 				}
 			}
 		}
@@ -8563,6 +8617,7 @@ static void P_MovePlayer(player_t *player)
 		|| ((player->pflags & PF_JUMPED) && !(player->pflags & PF_NOJUMPDAMAGE))
 		|| (player->pflags & PF_SPINNING)
 		|| player->powers[pw_tailsfly] || player->pflags & PF_GLIDING
+		|| (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING)
 		|| (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED))
 			player->mo->height = P_GetPlayerSpinHeight(player);
 		else
@@ -8628,74 +8683,6 @@ static void P_MovePlayer(player_t *player)
 	if (CheckForBustableBlocks)
 		P_CheckBustableBlocks(player);
 
-	// Special handling for
-	// gliding in 2D mode
-	if ((twodlevel || player->mo->flags2 & MF2_TWOD) && player->pflags & PF_GLIDING && player->charability == CA_GLIDEANDCLIMB
-		&& !(player->mo->flags & MF_NOCLIP))
-	{
-		msecnode_t *node; // only place it's being used in P_MovePlayer now
-		fixed_t oldx;
-		fixed_t oldy;
-		fixed_t floorz, ceilingz;
-
-		oldx = player->mo->x;
-		oldy = player->mo->y;
-
-		P_UnsetThingPosition(player->mo);
-		player->mo->x += player->mo->momx;
-		player->mo->y += player->mo->momy;
-		P_SetThingPosition(player->mo);
-
-		for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next)
-		{
-			if (!node->m_sector)
-				break;
-
-			if (node->m_sector->ffloors)
-			{
-				ffloor_t *rover;
-				fixed_t topheight, bottomheight;
-
-				for (rover = node->m_sector->ffloors; rover; rover = rover->next)
-				{
-					if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER))
-						continue;
-
-					topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
-					bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
-					if (topheight > player->mo->z && bottomheight < player->mo->z)
-					{
-						P_ResetPlayer(player);
-						S_StartSound(player->mo, sfx_s3k4a);
-						player->climbing = 5;
-						player->mo->momx = player->mo->momy = player->mo->momz = 0;
-						break;
-					}
-				}
-			}
-
-			floorz = P_GetFloorZ(player->mo, node->m_sector, player->mo->x, player->mo->y, NULL);
-			ceilingz = P_GetCeilingZ(player->mo, node->m_sector, player->mo->x, player->mo->y, NULL);
-
-			if (player->mo->z+player->mo->height > ceilingz
-				&& node->m_sector->ceilingpic == skyflatnum)
-				continue;
-
-			if (floorz > player->mo->z || ceilingz < player->mo->z)
-			{
-				P_ResetPlayer(player);
-				S_StartSound(player->mo, sfx_s3k4a);
-				player->climbing = 5;
-				player->mo->momx = player->mo->momy = player->mo->momz = 0;
-				break;
-			}
-		}
-		P_UnsetThingPosition(player->mo);
-		player->mo->x = oldx;
-		player->mo->y = oldy;
-		P_SetThingPosition(player->mo);
-	}
-
 	// Check for a BOUNCY sector!
 	if (CheckForBouncySector)
 		P_CheckBouncySectors(player);
@@ -9001,6 +8988,82 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
 	}
 }
 
+//
+// P_Earthquake
+// Used for Super Knuckles' landing - damages enemies within the given radius
+// If inflictor is grounded, only grounded enemies are hurt
+//
+void P_Earthquake(mobj_t *inflictor, mobj_t *source, fixed_t radius)
+{
+	const fixed_t ns = radius/12;
+	mobj_t *mo;
+	angle_t fa;
+	thinker_t *think;
+	INT32 i;
+	boolean grounded = P_IsObjectOnGround(inflictor);
+
+	for (i = 0; i < 16; i++)
+	{
+		fa = (i*(FINEANGLES/16));
+		mo = P_SpawnMobjFromMobj(inflictor, 0, 0, 0, MT_SUPERSPARK);
+		if (!P_MobjWasRemoved(mo))
+		{
+			if (grounded)
+			{
+				mo->momx = FixedMul(FINESINE(fa),ns);
+				mo->momy = FixedMul(FINECOSINE(fa),ns);
+			}
+			else
+			{
+				P_InstaThrust(mo, inflictor->angle + ANGLE_90, FixedMul(FINECOSINE(fa),ns));
+				mo->momz = FixedMul(FINESINE(fa),ns);
+			}
+		}
+	}
+
+	if (inflictor->player && P_IsLocalPlayer(inflictor->player))
+	{
+		quake.epicenter = NULL;
+		quake.intensity = 8*FRACUNIT;
+		quake.time = 8;
+		quake.radius = radius;
+	}
+
+	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
+	{
+		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			continue;
+
+		mo = (mobj_t *)think;
+
+		if (grounded && !P_IsObjectOnGround(mo))
+			continue;
+
+		if (!(mo->flags & MF_SHOOTABLE) && !(mo->type == MT_EGGGUARD || mo->type == MT_MINUS))
+			continue;
+
+		if (mo->flags & MF_MONITOR)
+			continue; // Monitors cannot be 'nuked'.
+
+		if (mo->type == MT_PLAYER)
+			continue; // Don't hurt players
+
+		if (abs(inflictor->x - mo->x) > radius || abs(inflictor->y - mo->y) > radius || abs(inflictor->z - mo->z) > radius)
+			continue; // Workaround for possible integer overflow in the below -Red
+
+		if (P_AproxDistance(P_AproxDistance(inflictor->x - mo->x, inflictor->y - mo->y), inflictor->z - mo->z) > radius)
+			continue;
+
+		if (mo->type == MT_MINUS && !(mo->flags & (MF_SPECIAL|MF_SHOOTABLE)))
+			mo->flags = (mo->flags & ~MF_NOCLIPTHING)|MF_SPECIAL|MF_SHOOTABLE;
+
+		if (mo->type == MT_EGGGUARD && mo->tracer) //nuke Egg Guard's shield!
+			P_KillMobj(mo->tracer, inflictor, source, 0);
+
+		P_DamageMobj(mo, inflictor, source, 1, 0);
+	}
+}
+
 //
 // P_LookForFocusTarget
 // Looks for a target for a player to focus on, for Z-targeting etc.
-- 
GitLab