diff --git a/src/m_vector.c b/src/m_vector.c
index 53b869adc287f766f5b8bc6e532e56821cb452cc..274a806a6dfb493d00cad6c90dc2b3ad177887ba 100644
--- a/src/m_vector.c
+++ b/src/m_vector.c
@@ -409,6 +409,50 @@ angles3d_t *M_VectorAlignTo(float Pitch, float Yaw, float Roll, v3float_t v, byt
 
 }
 
+//
+// RotateVector
+//
+// Rotates a vector around another vector
+//
+void M_VecRotate(v3fixed_t *rotVec, const v3fixed_t *axisVec, const angle_t angle)
+{
+	// Rotate the point (x,y,z) around the vector (u,v,w)
+	fixed_t ux = FixedMul(axisVec->x, rotVec->x);
+	fixed_t uy = FixedMul(axisVec->x, rotVec->y);
+	fixed_t uz = FixedMul(axisVec->x, rotVec->z);
+	fixed_t vx = FixedMul(axisVec->y, rotVec->x);
+	fixed_t vy = FixedMul(axisVec->y, rotVec->y);
+	fixed_t vz = FixedMul(axisVec->y, rotVec->z);
+	fixed_t wx = FixedMul(axisVec->z, rotVec->x);
+	fixed_t wy = FixedMul(axisVec->z, rotVec->y);
+	fixed_t wz = FixedMul(axisVec->z, rotVec->z);
+	fixed_t sa = FINESINE(angle>>ANGLETOFINESHIFT);
+	fixed_t ca = FINECOSINE(angle>>ANGLETOFINESHIFT);
+	fixed_t ua = ux+vy+wz;
+	fixed_t ax = FixedMul(axisVec->x,ua);
+	fixed_t ay = FixedMul(axisVec->y,ua);
+	fixed_t az = FixedMul(axisVec->z,ua);
+	fixed_t xs = FixedMul(axisVec->x,axisVec->x);
+	fixed_t ys = FixedMul(axisVec->y,axisVec->y);
+	fixed_t zs = FixedMul(axisVec->z,axisVec->z);
+	fixed_t bx = FixedMul(rotVec->x,ys+zs);
+	fixed_t by = FixedMul(rotVec->y,xs+zs);
+	fixed_t bz = FixedMul(rotVec->z,xs+ys);
+	fixed_t cx = FixedMul(axisVec->x,vy+wz);
+	fixed_t cy = FixedMul(axisVec->y,ux+wz);
+	fixed_t cz = FixedMul(axisVec->z,ux+vy);
+	fixed_t dx = FixedMul(bx-cx, ca);
+	fixed_t dy = FixedMul(by-cy, ca);
+	fixed_t dz = FixedMul(bz-cz, ca);
+	fixed_t ex = FixedMul(vz-wy, sa);
+	fixed_t ey = FixedMul(wx-uz, sa);
+	fixed_t ez = FixedMul(uy-vx, sa);
+
+	rotVec->x = ax+dx+ex;
+	rotVec->y = ay+dy+ey;
+	rotVec->z = az+dz+ez;
+}
+
 
 
 
@@ -977,50 +1021,6 @@ boolean FV_IntersectedPolygon(const fvector_t *vPoly, const fvector_t *vLine, co
 	return false;
 }
 
-//
-// RotateVector
-//
-// Rotates a vector around another vector
-//
-void FV_Rotate(fvector_t *rotVec, const fvector_t *axisVec, const angle_t angle)
-{
-	// Rotate the point (x,y,z) around the vector (u,v,w)
-	fixed_t ux = FixedMul(axisVec->x, rotVec->x);
-	fixed_t uy = FixedMul(axisVec->x, rotVec->y);
-	fixed_t uz = FixedMul(axisVec->x, rotVec->z);
-	fixed_t vx = FixedMul(axisVec->y, rotVec->x);
-	fixed_t vy = FixedMul(axisVec->y, rotVec->y);
-	fixed_t vz = FixedMul(axisVec->y, rotVec->z);
-	fixed_t wx = FixedMul(axisVec->z, rotVec->x);
-	fixed_t wy = FixedMul(axisVec->z, rotVec->y);
-	fixed_t wz = FixedMul(axisVec->z, rotVec->z);
-	fixed_t sa = FINESINE(angle);
-	fixed_t ca = FINECOSINE(angle);
-	fixed_t ua = ux+vy+wz;
-	fixed_t ax = FixedMul(axisVec->x,ua);
-	fixed_t ay = FixedMul(axisVec->y,ua);
-	fixed_t az = FixedMul(axisVec->z,ua);
-	fixed_t xs = FixedMul(axisVec->x,axisVec->x);
-	fixed_t ys = FixedMul(axisVec->y,axisVec->y);
-	fixed_t zs = FixedMul(axisVec->z,axisVec->z);
-	fixed_t bx = FixedMul(rotVec->x,ys+zs);
-	fixed_t by = FixedMul(rotVec->y,xs+zs);
-	fixed_t bz = FixedMul(rotVec->z,xs+ys);
-	fixed_t cx = FixedMul(axisVec->x,vy+wz);
-	fixed_t cy = FixedMul(axisVec->y,ux+wz);
-	fixed_t cz = FixedMul(axisVec->z,ux+vy);
-	fixed_t dx = FixedMul(bx-cx, ca);
-	fixed_t dy = FixedMul(by-cy, ca);
-	fixed_t dz = FixedMul(bz-cz, ca);
-	fixed_t ex = FixedMul(vz-wy, sa);
-	fixed_t ey = FixedMul(wx-uz, sa);
-	fixed_t ez = FixedMul(uy-vx, sa);
-
-	rotVec->x = ax+dx+ex;
-	rotVec->y = ay+dy+ey;
-	rotVec->z = az+dz+ez;
-}
-
 void FM_Rotate(fmatrix_t *dest, angle_t angle, fixed_t x, fixed_t y, fixed_t z)
 {
 #define M(row,col) dest->m[row * 4 + col]
diff --git a/src/m_vector.h b/src/m_vector.h
index 743a26023c46c0ee3493292d9b9282e230995a7d..37d30c746d9d881071b93d1a161e969bea0a9100 100644
--- a/src/m_vector.h
+++ b/src/m_vector.h
@@ -1,4 +1,4 @@
-// Emacs style mode select   -*- C++ -*- 
+// Emacs style mode select   -*- C++ -*-
 //-----------------------------------------------------------------------------
 //
 // Copyright(C) 2004 Stephen McGranahan
@@ -7,12 +7,12 @@
 // it under the terms of the GNU General Public License as published by
 // the Free Software Foundation; either version 2 of the License, or
 // (at your option) any later version.
-// 
+//
 // This program is distributed in the hope that it will be useful,
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 // GNU General Public License for more details.
-// 
+//
 // You should have received a copy of the GNU General Public License
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
@@ -114,6 +114,8 @@ float M_VectorYaw(v3float_t v);
 float M_VectorPitch(v3float_t v);
 angles3d_t *M_VectorAlignTo(float Pitch, float Yaw, float Roll, v3float_t v, byte AngleAxis, float Rate);
 
+void M_VecRotate(v3fixed_t *rotVec, const v3fixed_t *axisVec, const angle_t angle);
+
 
 
 #endif
diff --git a/src/p_mobj.c b/src/p_mobj.c
index d57e398ec6d62169fbcf6ad847b1ddb4898f46ad..e0e5cc9e9aba1c1bdb6964599e492a605a4302aa 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1239,7 +1239,11 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy)
 		}
 		else if (abs(player->rmomx) < FixedMul(STOPSPEED, mo->scale)
 		    && abs(player->rmomy) < FixedMul(STOPSPEED, mo->scale)
-		    && (!(player->cmd.forwardmove && !(twodlevel || mo->flags2 & MF2_TWOD)) && !player->cmd.sidemove && !(player->pflags & PF_SPINNING)))
+		    && (!(player->cmd.forwardmove && !(twodlevel || mo->flags2 & MF2_TWOD)) && !player->cmd.sidemove && !(player->pflags & PF_SPINNING))
+#ifdef ESLOPE
+			&& !(player->mo->standingslope && abs(player->mo->standingslope->zdelta) >= FRACUNIT/2)
+#endif
+				)
 		{
 			// if in a walking frame, stop moving
 			if (player->panim == PA_WALK)
@@ -1383,6 +1387,11 @@ void P_XYMovement(mobj_t *mo)
 	fixed_t xmove, ymove;
 	fixed_t oldx, oldy; // reducing bobbing/momentum on ice when up against walls
 	boolean moved;
+#ifdef ESLOPE
+	pslope_t *oldslope = NULL;
+	v3fixed_t slopemom;
+	fixed_t predictedz;
+#endif
 
 	I_Assert(mo != NULL);
 	I_Assert(!P_MobjWasRemoved(mo));
@@ -1414,6 +1423,29 @@ void P_XYMovement(mobj_t *mo)
 	oldx = mo->x;
 	oldy = mo->y;
 
+#ifdef ESLOPE
+	// adjust various things based on slope
+	if (mo->standingslope) {
+		if (!P_IsObjectOnGround(mo)) { // We fell off at some point? Do the twisty thing!
+			P_SlopeLaunch(mo);
+			xmove = mo->momx;
+			ymove = mo->momy;
+		} else { // Still on the ground.
+			slopemom.x = xmove;
+			slopemom.y = ymove;
+			slopemom.z = 0;
+			P_QuantizeMomentumToSlope(&slopemom, mo->standingslope);
+
+			xmove = slopemom.x;
+			ymove = slopemom.y;
+
+			predictedz = mo->z + slopemom.z; // We'll use this later...
+
+			oldslope = mo->standingslope;
+		}
+	}
+#endif
+
 	// Pushables can break some blocks
 	if (CheckForBustableBlocks && mo->flags & MF_PUSHABLE)
 		P_PushableCheckBustables(mo);
@@ -1534,6 +1566,29 @@ void P_XYMovement(mobj_t *mo)
 	if (P_MobjWasRemoved(mo)) // MF_SPECIAL touched a player! O_o;;
 		return;
 
+#ifdef ESLOPE
+	if (moved && oldslope) { // Check to see if we ran off
+		if (oldslope != mo->standingslope) { // First, compare different slopes
+			// Start by quantizing momentum on this slope
+			v3fixed_t test;
+			test.x = mo->momx;
+			test.y = mo->momy;
+			test.z = 0;
+			if (mo->standingslope) // Don't fuss with the rotation if we don't HAVE a slope
+				P_QuantizeMomentumToSlope(&test, mo->standingslope);
+
+			// Now compare the Zs of the different quantizations
+			if (slopemom.z - test.z > 2*FRACUNIT) { // Allow for a bit of sticking - this value can be adjusted later
+				mo->standingslope = oldslope;
+				P_SlopeLaunch(mo);
+			}
+		} else if (predictedz-mo->z > 2*FRACUNIT) { // Now check if we were supposed to stick to this slope
+			mo->standingslope = oldslope;
+			P_SlopeLaunch(mo);
+		}
+	}
+#endif
+
 	// Check the gravity status.
 	P_CheckGravity(mo, false);
 
@@ -1819,6 +1874,11 @@ static boolean P_ZMovement(mobj_t *mo)
 	I_Assert(mo != NULL);
 	I_Assert(!P_MobjWasRemoved(mo));
 
+#ifdef ESLOPE
+	if (mo->standingslope && !P_IsObjectOnGround(mo))
+			P_SlopeLaunch(mo);
+#endif
+
 	// Intercept the stupid 'fall through 3dfloors' bug
 	if (mo->subsector->sector->ffloors)
 		P_AdjustMobjFloorZ_FFloors(mo, mo->subsector->sector, 0);
@@ -2231,6 +2291,11 @@ static void P_PlayerZMovement(mobj_t *mo)
 	if (!mo->player)
 		return;
 
+#ifdef ESLOPE
+	if (mo->standingslope && !P_IsObjectOnGround(mo))
+			P_SlopeLaunch(mo);
+#endif
+
 	// Intercept the stupid 'fall through 3dfloors' bug
 	if (mo->subsector->sector->ffloors)
 		P_AdjustMobjFloorZ_FFloors(mo, mo->subsector->sector, 0);
@@ -3183,6 +3248,10 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
 
 	P_MobjCheckWater(mobj);
 
+#ifdef ESLOPE
+	P_ButteredSlope(mobj);
+#endif
+
 	// momentum movement
 	mobj->eflags &= ~MFE_JUSTSTEPPEDDOWN;
 
diff --git a/src/p_slopes.c b/src/p_slopes.c
index 518599849acecbb541b5349396a2ef66c977c2e6..6b6f147d9506fa08d6efdeb6e821944c21218c57 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -768,6 +768,69 @@ float P_DistFromPlanef(const v3float_t *point, const v3float_t *pori,
           (point->z - pori->z) * pnormal->z;
 }
 
+//
+// P_QuantizeMomentumToSlope
+//
+// When given a vector, rotates it and aligns it to a slope
+void P_QuantizeMomentumToSlope(v3fixed_t *momentum, pslope_t *slope)
+{
+	v3fixed_t axis;
+	axis.x = -slope->d.y;
+	axis.y = slope->d.x;
+	axis.z = 0;
+
+	M_VecRotate(momentum, &axis, slope->zangle);
+}
+
+//
+// P_SlopeLaunch
+//
+// Handles slope ejection for objects
+void P_SlopeLaunch(mobj_t *mo)
+{
+	// Double the pre-rotation Z, then halve the post-rotation Z. This reduces the
+	// vertical launch given from slopes while increasing the horizontal launch
+	// given. Good for SRB2's gravity and horizontal speeds.
+	v3fixed_t slopemom;
+	slopemom.x = mo->momx;
+	slopemom.y = mo->momy;
+	slopemom.z = mo->momz*2;
+	P_QuantizeMomentumToSlope(&slopemom, mo->standingslope);
+
+	mo->momx = slopemom.x;
+	mo->momy = slopemom.y;
+	mo->momz = slopemom.z/2;
+
+	CONS_Printf("Launched off of slope.\n");
+	mo->standingslope = NULL;
+}
+
+// https://yourlogicalfallacyis.com/slippery-slope
+// Handles sliding down slopes, like if they were made of butter :)
+void P_ButteredSlope(mobj_t *mo)
+{
+	fixed_t thrust;
+
+	if (!mo->standingslope)
+		return;
+
+	if (abs(mo->standingslope->zdelta) < FRACUNIT/2)
+		return; // Don't apply physics to slopes that aren't steep enough
+
+	thrust = FINESINE(mo->standingslope->zangle>>ANGLETOFINESHIFT) * (mo->eflags & MFE_VERTICALFLIP ? 1 : -1);
+
+	if (!(mo->player && (mo->player->pflags & PF_SPINNING))) {
+		if (mo->momx || mo->momy) // Slightly increase thrust based on the object's speed parallel to the slope direction
+			thrust = FixedMul(thrust, FRACUNIT+P_AproxDistance(mo->momx, mo->momy)/4);
+		// This solves the issue of being able to zigzag up steep slopes
+	}
+
+	// Multiply by gravity
+	thrust = FixedMul(thrust, FRACUNIT/2); // TODO actually get this
+
+	P_Thrust(mo, mo->standingslope->xydirection, thrust);
+}
+
 // EOF
 #endif // #ifdef ESLOPE
 
diff --git a/src/p_slopes.h b/src/p_slopes.h
index 8f408d6f0598f57c71738b9b1d179d5d439d56db..f82d8a83d6688ca90b8d719ed493f1e4a0af6672 100644
--- a/src/p_slopes.h
+++ b/src/p_slopes.h
@@ -76,6 +76,11 @@ float P_GetZAtf(pslope_t *slope, float x, float y);
 float P_DistFromPlanef(const v3float_t *point, const v3float_t *pori,
                        const v3float_t *pnormal);
 
+// Lots of physics-based bullshit
+void P_QuantizeMomentumToSlope(v3fixed_t *momentum, pslope_t *slope);
+void P_SlopeLaunch(mobj_t *mo);
+void P_ButteredSlope(mobj_t *mo);
+
 #endif
 
 // EOF