diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 21f7c6c45fd2a26aff061aba3df305a845adcca9..a7527461597db16e43f7a3ea83bb5f34497268c6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -37,6 +37,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
 	m_random.c
 	m_tokenizer.c
 	m_queue.c
+	m_vector.c
 	info.c
 	p_ceilng.c
 	p_enemy.c
diff --git a/src/Sourcefile b/src/Sourcefile
index ad5eacfeb9e96f75316ce0d4c66ae8d1e31b4fe1..60ee5db5baf16fe20657c93e5143afe60615bc89 100644
--- a/src/Sourcefile
+++ b/src/Sourcefile
@@ -31,6 +31,7 @@ m_perfstats.c
 m_random.c
 m_tokenizer.c
 m_queue.c
+m_vector.c
 info.c
 p_ceilng.c
 p_enemy.c
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 5b80d4d38c9db705c59c7003f37af2ce87bb0272..f8e2808a200837c133a8a4ebbc8438dc5aef38f7 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -2609,12 +2609,18 @@ static int slope_set(lua_State *L)
 			slope->o.z = luaL_checkfixed(L, -1);
 		else
 			slope->o.z = 0;
+		DVector3_Load(&slope->dorigin,
+			FixedToDouble(slope->o.x),
+			FixedToDouble(slope->o.y),
+			FixedToDouble(slope->o.z)
+		);
 		lua_pop(L, 1);
 		break;
 	}
-	case slope_zdelta: { // zdelta, this is temp until i figure out wtf to do
+	case slope_zdelta: { // zdelta
 		slope->zdelta = luaL_checkfixed(L, 3);
 		slope->zangle = R_PointToAngle2(0, 0, FRACUNIT, -slope->zdelta);
+		slope->dzdelta = FixedToDouble(slope->zdelta);
 		P_CalculateSlopeNormal(slope);
 		break;
 	}
@@ -2624,6 +2630,7 @@ static int slope_set(lua_State *L)
 			return luaL_error(L, "invalid zangle for slope!");
 		slope->zangle = zangle;
 		slope->zdelta = -FINETANGENT(((slope->zangle+ANGLE_90)>>ANGLETOFINESHIFT) & 4095);
+		slope->dzdelta = FixedToDouble(slope->zdelta);
 		P_CalculateSlopeNormal(slope);
 		break;
 	}
@@ -2631,6 +2638,8 @@ static int slope_set(lua_State *L)
 		slope->xydirection = luaL_checkangle(L, 3);
 		slope->d.x = -FINECOSINE((slope->xydirection>>ANGLETOFINESHIFT) & FINEMASK);
 		slope->d.y = -FINESINE((slope->xydirection>>ANGLETOFINESHIFT) & FINEMASK);
+		slope->dnormdir.x = FixedToDouble(slope->d.x);
+		slope->dnormdir.y = FixedToDouble(slope->d.y);
 		P_CalculateSlopeNormal(slope);
 		break;
 	}
diff --git a/src/m_fixed.c b/src/m_fixed.c
index b674e3b2c8e230524d57ea540dada83b8bf75eb6..19c1e80919315071fe40086dc93ec42b937ffb26 100644
--- a/src/m_fixed.c
+++ b/src/m_fixed.c
@@ -3,6 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 2009 by Stephen McGranahan.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_fixed.h b/src/m_fixed.h
index 4e644c9b6d5db3687a211526e3a50a5f03e9702f..f40c7b30870baffdd978cec0ecb447b65f74ba6f 100644
--- a/src/m_fixed.h
+++ b/src/m_fixed.h
@@ -50,6 +50,20 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FloatToFixed(float f)
 	return (fixed_t)(f * FRACUNIT);
 }
 
+/*!
+  \brief convert fixed_t into double-precision floating number
+*/
+
+FUNCMATH FUNCINLINE static ATTRINLINE double FixedToDouble(fixed_t x)
+{
+	return x / (double)FRACUNIT;
+}
+
+FUNCMATH FUNCINLINE static ATTRINLINE fixed_t DoubleToFixed(double f)
+{
+	return (fixed_t)(f * FRACUNIT);
+}
+
 // for backwards compat
 #define FIXED_TO_FLOAT(x) FixedToFloat(x) // (((float)(x)) / ((float)FRACUNIT))
 #define FLOAT_TO_FIXED(f) FloatToFixed(f) // (fixed_t)((f) * ((float)FRACUNIT))
diff --git a/src/m_vector.c b/src/m_vector.c
new file mode 100644
index 0000000000000000000000000000000000000000..3132a869d458c83aac517ac609258b506b6395b8
--- /dev/null
+++ b/src/m_vector.c
@@ -0,0 +1,53 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2024 by Sonic Team Junior.
+// Copyright (C) 2009 by Stephen McGranahan.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  m_vector.c
+/// \brief Basic vector functions
+
+#include "doomdef.h"
+
+#include "m_vector.h"
+
+void DVector3_Load(dvector3_t *vec, double x, double y, double z)
+{
+	vec->x = x;
+	vec->y = y;
+	vec->z = z;
+}
+
+double DVector3_Magnitude(const dvector3_t *a_normal)
+{
+	double xs = a_normal->x * a_normal->x;
+	double ys = a_normal->y * a_normal->y;
+	double zs = a_normal->z * a_normal->z;
+	return sqrt(xs + ys + zs);
+}
+
+double DVector3_Normalize(dvector3_t *a_normal)
+{
+	double magnitude = DVector3_Magnitude(a_normal);
+	a_normal->x /= magnitude;
+	a_normal->y /= magnitude;
+	a_normal->z /= magnitude;
+	return magnitude;
+}
+
+void DVector3_Negate(dvector3_t *a_o)
+{
+	a_o->x = -a_o->x;
+	a_o->y = -a_o->y;
+	a_o->z = -a_o->z;
+}
+
+void DVector3_Cross(const dvector3_t *a_1, const dvector3_t *a_2, dvector3_t *a_o)
+{
+	a_o->x = (a_1->y * a_2->z) - (a_1->z * a_2->y);
+	a_o->y = (a_1->z * a_2->x) - (a_1->x * a_2->z);
+	a_o->z = (a_1->x * a_2->y) - (a_1->y * a_2->x);
+}
diff --git a/src/m_vector.h b/src/m_vector.h
new file mode 100644
index 0000000000000000000000000000000000000000..55669be037c9ce54ffd70bf3782225e913e1a1b4
--- /dev/null
+++ b/src/m_vector.h
@@ -0,0 +1,27 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2024 by Sonic Team Junior.
+// Copyright (C) 2009 by Stephen McGranahan.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  m_vector.h
+/// \brief Basic vector functions
+
+#ifndef __M_VECTOR__
+#define __M_VECTOR__
+
+typedef struct
+{
+	double x, y, z;
+} dvector3_t;
+
+void DVector3_Load(dvector3_t *vec, double x, double y, double z);
+double DVector3_Magnitude(const dvector3_t *a_normal);
+double DVector3_Normalize(dvector3_t *a_normal);
+void DVector3_Negate(dvector3_t *a_o);
+void DVector3_Cross(const dvector3_t *a_1, const dvector3_t *a_2, dvector3_t *a_o);
+
+#endif
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 8e2517473de63c166a1867518485495174b9fed9..6c6548c567f195cfc7bbc884e1e45dea18afde1a 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -3221,6 +3221,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		slope->normal.x = READFIXED(save_p);
 		slope->normal.y = READFIXED(save_p);
 		slope->normal.z = READFIXED(save_p);
+
+		slope->moved = true;
 	}
 	if (diff2 & MD2_DRAWONLYFORPLAYER)
 		mobj->drawonlyforplayer = &players[READUINT8(save_p)];
diff --git a/src/p_setup.c b/src/p_setup.c
index 0eafda9da5014f5d98145656d264c3229dcfff87..fb22f6524c99aaa31a2012ffa991586194a742ed 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1650,15 +1650,15 @@ textmap_colormap_t textmap_colormap = { false, 0, 25, 0, 25, 0, 31, 0 };
 
 typedef enum
 {
-    PD_A = 1,
-    PD_B = 1<<1,
-    PD_C = 1<<2,
-    PD_D = 1<<3,
+	PD_A = 1,
+	PD_B = 1<<1,
+	PD_C = 1<<2,
+	PD_D = 1<<3,
 } planedef_t;
 
 typedef struct textmap_plane_s {
-    UINT8 defined;
-    fixed_t a, b, c, d;
+	UINT8 defined;
+	double a, b, c, d;
 } textmap_plane_t;
 
 textmap_plane_t textmap_planefloor = {0, 0, 0, 0, 0};
@@ -1719,42 +1719,42 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char
 	else if (fastcmp(param, "floorplane_a"))
 	{
 		textmap_planefloor.defined |= PD_A;
-		textmap_planefloor.a = FLOAT_TO_FIXED(atof(val));
+		textmap_planefloor.a = atof(val);
 	}
 	else if (fastcmp(param, "floorplane_b"))
 	{
 		textmap_planefloor.defined |= PD_B;
-		textmap_planefloor.b = FLOAT_TO_FIXED(atof(val));
+		textmap_planefloor.b = atof(val);
 	}
 	else if (fastcmp(param, "floorplane_c"))
 	{
 		textmap_planefloor.defined |= PD_C;
-		textmap_planefloor.c = FLOAT_TO_FIXED(atof(val));
+		textmap_planefloor.c = atof(val);
 	}
 	else if (fastcmp(param, "floorplane_d"))
 	{
 		textmap_planefloor.defined |= PD_D;
-		textmap_planefloor.d = FLOAT_TO_FIXED(atof(val));
+		textmap_planefloor.d = atof(val);
 	}
 	else if (fastcmp(param, "ceilingplane_a"))
 	{
 		textmap_planeceiling.defined |= PD_A;
-		textmap_planeceiling.a = FLOAT_TO_FIXED(atof(val));
+		textmap_planeceiling.a = atof(val);
 	}
 	else if (fastcmp(param, "ceilingplane_b"))
 	{
 		textmap_planeceiling.defined |= PD_B;
-		textmap_planeceiling.b = FLOAT_TO_FIXED(atof(val));
+		textmap_planeceiling.b = atof(val);
 	}
 	else if (fastcmp(param, "ceilingplane_c"))
 	{
 		textmap_planeceiling.defined |= PD_C;
-		textmap_planeceiling.c = FLOAT_TO_FIXED(atof(val));
+		textmap_planeceiling.c = atof(val);
 	}
 	else if (fastcmp(param, "ceilingplane_d"))
 	{
 		textmap_planeceiling.defined |= PD_D;
-		textmap_planeceiling.d = FLOAT_TO_FIXED(atof(val));
+		textmap_planeceiling.d = atof(val);
 	}
 	else if (fastcmp(param, "lightcolor"))
 	{
@@ -2992,13 +2992,13 @@ static void P_LoadTextmap(void)
 
 		if (textmap_planefloor.defined == (PD_A|PD_B|PD_C|PD_D))
 		{
-			sc->f_slope = MakeViaEquationConstants(textmap_planefloor.a, textmap_planefloor.b, textmap_planefloor.c, textmap_planefloor.d);
+			sc->f_slope = P_MakeSlopeViaEquationConstants(textmap_planefloor.a, textmap_planefloor.b, textmap_planefloor.c, textmap_planefloor.d);
 			sc->hasslope = true;
 		}
 
 		if (textmap_planeceiling.defined == (PD_A|PD_B|PD_C|PD_D))
 		{
-			sc->c_slope = MakeViaEquationConstants(textmap_planeceiling.a, textmap_planeceiling.b, textmap_planeceiling.c, textmap_planeceiling.d);
+			sc->c_slope = P_MakeSlopeViaEquationConstants(textmap_planeceiling.a, textmap_planeceiling.b, textmap_planeceiling.c, textmap_planeceiling.d);
 			sc->hasslope = true;
 		}
 
diff --git a/src/p_slopes.c b/src/p_slopes.c
index 1c0ee81a7e9453d204049d2f0ab5986a07849edb..e75d36edefe8d8484cb0c9bebb4d02d5f1b1e174 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2004      by Stephen McGranahan
+// Copyright (C) 2009      by Stephen McGranahan.
 // Copyright (C) 2015-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
@@ -14,6 +14,7 @@
 #include "r_defs.h"
 #include "r_state.h"
 #include "m_bbox.h"
+#include "m_vector.h"
 #include "z_zone.h"
 #include "p_local.h"
 #include "p_spec.h"
@@ -28,12 +29,40 @@ pslope_t *slopelist = NULL;
 UINT16 slopecount = 0;
 
 // Calculate line normal
-void P_CalculateSlopeNormal(pslope_t *slope) {
+void P_CalculateSlopeNormal(pslope_t *slope)
+{
 	slope->normal.z = FINECOSINE(slope->zangle>>ANGLETOFINESHIFT);
 	slope->normal.x = FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.x);
 	slope->normal.y = FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.y);
 }
 
+static void CalculateNormalDir(pslope_t *slope, dvector3_t *dnormal)
+{
+	double hyp = hypot(dnormal->x, dnormal->y);
+
+	if (fpclassify(hyp) == FP_NORMAL)
+	{
+		slope->dnormdir.x = -dnormal->x / hyp;
+		slope->dnormdir.y = -dnormal->y / hyp;
+		slope->dzdelta = hyp / dnormal->z;
+	}
+	else
+	{
+		slope->dnormdir.x = slope->dnormdir.y = 0.0;
+		slope->dzdelta = 0.0;
+	}
+}
+
+void P_CalculateSlopeVectors(pslope_t *slope)
+{
+	dvector3_t dnormal;
+
+	DVector3_Load(&dnormal, FixedToDouble(slope->normal.x), FixedToDouble(slope->normal.y), FixedToDouble(slope->normal.z));
+	DVector3_Load(&slope->dorigin, FixedToDouble(slope->o.x), FixedToDouble(slope->o.y), FixedToDouble(slope->o.z));
+
+	CalculateNormalDir(slope, &dnormal);
+}
+
 /// Setup slope via 3 vertexes.
 static void ReconfigureViaVertexes (pslope_t *slope, const vector3_t v1, const vector3_t v2, const vector3_t v3)
 {
@@ -89,22 +118,31 @@ static void ReconfigureViaVertexes (pslope_t *slope, const vector3_t v1, const v
 		slope->xydirection = R_PointToAngle2(0, 0, slope->d.x, slope->d.y)+ANGLE_180;
 		slope->zangle = InvAngle(R_PointToAngle2(0, 0, FRACUNIT, slope->zdelta));
 	}
+
+	P_CalculateSlopeVectors(slope);
 }
 
 /// Setup slope via constants.
-static void ReconfigureViaConstants (pslope_t *slope, const fixed_t a, const fixed_t b, const fixed_t c, const fixed_t d)
+static void ReconfigureViaConstants (pslope_t *slope, const double pa, const double pb, const double pc, const double pd)
 {
 	fixed_t m;
 	fixed_t o = 0;
-	vector3_t *normal = &slope->normal;
+	double d_o = 0.0;
+
+	fixed_t a = DoubleToFixed(pa), b = DoubleToFixed(pb), c = DoubleToFixed(pc), d = DoubleToFixed(pd);
 
 	if (c)
+	{
+		d_o = abs(c) <= FRACUNIT ? -(pd * (1.0 / pc)) : -(pd / pc);
 		o = abs(c) <= FRACUNIT ? -FixedMul(d, FixedDiv(FRACUNIT, c)) : -FixedDiv(d, c);
+	}
 
 	// Set origin.
 	FV3_Load(&slope->o, 0, 0, o);
 
 	// Get slope's normal.
+	vector3_t *normal = &slope->normal;
+
 	FV3_Load(normal, a, b, c);
 	FV3_Normalize(normal);
 
@@ -123,6 +161,17 @@ static void ReconfigureViaConstants (pslope_t *slope, const fixed_t a, const fix
 	// Get angles
 	slope->xydirection = R_PointToAngle2(0, 0, slope->d.x, slope->d.y)+ANGLE_180;
 	slope->zangle = InvAngle(R_PointToAngle2(0, 0, FRACUNIT, slope->zdelta));
+
+	dvector3_t dnormal;
+
+	DVector3_Load(&dnormal, pa, pb, pc);
+	DVector3_Normalize(&dnormal);
+	if (dnormal.z < 0)
+		DVector3_Negate(&dnormal);
+
+	DVector3_Load(&slope->dorigin, 0, 0, d_o);
+
+	CalculateNormalDir(slope, &dnormal);
 }
 
 /// Recalculate dynamic slopes.
@@ -161,6 +210,7 @@ void T_DynamicSlopeLine (dynlineplanethink_t* th)
 	if (slope->zdelta != FixedDiv(zdelta, th->extent)) {
 		slope->zdelta = FixedDiv(zdelta, th->extent);
 		slope->zangle = R_PointToAngle2(0, 0, th->extent, -zdelta);
+		slope->moved = true;
 		P_CalculateSlopeNormal(slope);
 	}
 }
@@ -392,6 +442,7 @@ static void line_SpawnViaLine(const int linenum, const boolean spawnthinker)
 			fslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y);
 
 			P_CalculateSlopeNormal(fslope);
+			P_CalculateSlopeVectors(fslope);
 
 			if (spawnthinker && (flags & SL_DYNAMIC))
 				P_AddDynLineSlopeThinker(fslope, DP_FRONTFLOOR, line, extent);
@@ -409,6 +460,7 @@ static void line_SpawnViaLine(const int linenum, const boolean spawnthinker)
 			cslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y);
 
 			P_CalculateSlopeNormal(cslope);
+			P_CalculateSlopeVectors(cslope);
 
 			if (spawnthinker && (flags & SL_DYNAMIC))
 				P_AddDynLineSlopeThinker(cslope, DP_FRONTCEIL, line, extent);
@@ -449,6 +501,7 @@ static void line_SpawnViaLine(const int linenum, const boolean spawnthinker)
 			fslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y);
 
 			P_CalculateSlopeNormal(fslope);
+			P_CalculateSlopeVectors(fslope);
 
 			if (spawnthinker && (flags & SL_DYNAMIC))
 				P_AddDynLineSlopeThinker(fslope, DP_BACKFLOOR, line, extent);
@@ -466,6 +519,7 @@ static void line_SpawnViaLine(const int linenum, const boolean spawnthinker)
 			cslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y);
 
 			P_CalculateSlopeNormal(cslope);
+			P_CalculateSlopeVectors(cslope);
 
 			if (spawnthinker && (flags & SL_DYNAMIC))
 				P_AddDynLineSlopeThinker(cslope, DP_BACKCEIL, line, extent);
@@ -695,7 +749,7 @@ pslope_t *P_SlopeById(UINT16 id)
 }
 
 /// Creates a new slope from equation constants.
-pslope_t *MakeViaEquationConstants(const fixed_t a, const fixed_t b, const fixed_t c, const fixed_t d)
+pslope_t *P_MakeSlopeViaEquationConstants(const double a, const double b, const double c, const double d)
 {
 	pslope_t* ret = Slope_Add(0);
 
diff --git a/src/p_slopes.h b/src/p_slopes.h
index 096bf8f82a3342494b692a5e16cb6fbde6647438..fdc07f67e257ba05eef35fc481c3be3a0ebdfd7c 100644
--- a/src/p_slopes.h
+++ b/src/p_slopes.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2004      by Stephen McGranahan
+// Copyright (C) 2009      by Stephen McGranahan.
 // Copyright (C) 2015-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
@@ -13,7 +13,7 @@
 #ifndef P_SLOPES_H__
 #define P_SLOPES_H__
 
-#include "m_fixed.h" // Vectors
+#include "m_fixed.h"
 
 extern pslope_t *slopelist;
 extern UINT16 slopecount;
@@ -51,6 +51,7 @@ typedef enum
 void P_LinkSlopeThinkers (void);
 
 void P_CalculateSlopeNormal(pslope_t *slope);
+void P_CalculateSlopeVectors(pslope_t *slope);
 void P_InitSlopes(void);
 void P_SpawnSlopes(const boolean fromsave);
 
@@ -88,7 +89,7 @@ fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope);
 void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope);
 void P_ButteredSlope(mobj_t *mo);
 
-pslope_t *MakeViaEquationConstants(const fixed_t a, const fixed_t b, const fixed_t c, const fixed_t d);
+pslope_t *P_MakeSlopeViaEquationConstants(const double a, const double b, const double c, const double d);
 
 /// Dynamic plane type enum for the thinker. Will have a different functionality depending on this.
 typedef enum {
diff --git a/src/r_defs.h b/src/r_defs.h
index 16c660b01938ed86669550f709f7b65336cfaebc..6e0375e615aed5596e68eb67b113fe61a907e477 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -16,6 +16,7 @@
 
 // Some more or less basic data types we depend on.
 #include "m_fixed.h"
+#include "m_vector.h"
 
 // We rely on the thinker data struct to handle sound origins in sectors.
 #include "d_think.h"
@@ -343,6 +344,13 @@ typedef struct pslope_s
 	angle_t zangle;		/// Precomputed angle of the plane going up from the ground (not measured in degrees).
 	angle_t xydirection;/// Precomputed angle of the normal's projection on the XY plane.
 
+	dvector3_t dorigin;
+	dvector3_t dnormdir;
+
+	double dzdelta;
+
+	boolean moved : 1;
+
 	UINT8 flags; // Slope options
 } pslope_t;
 
diff --git a/src/r_draw.c b/src/r_draw.c
index ff2e43df31d9291ed6ae07facf2da0c1beaaa4e1..d51f2d3bdb047ab88c543b1b7a54b6c766d85696 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -113,8 +113,9 @@ UINT8 *ds_source; // points to the start of a flat
 UINT8 *ds_transmap; // one of the translucency tables
 
 // Vectors for Software's tilted slope drawers
-floatv3_t ds_su, ds_sv, ds_sz, ds_slopelight;
-float focallengthf, zeroheight;
+dvector3_t ds_su, ds_sv, ds_sz, ds_slopelight;
+double zeroheight;
+float focallengthf;
 
 /**	\brief Variable flat sizes
 */
diff --git a/src/r_draw.h b/src/r_draw.h
index 29370015a1c46cb6405bf5b698d6c23425ee79e2..329877e7f0b0e8c875b174e1096530e26bbc2050 100644
--- a/src/r_draw.h
+++ b/src/r_draw.h
@@ -66,13 +66,10 @@ extern boolean ds_powersoftwo, ds_solidcolor, ds_fog;
 extern UINT8 *ds_source;
 extern UINT8 *ds_transmap;
 
-typedef struct {
-	float x, y, z;
-} floatv3_t;
-
 // Vectors for Software's tilted slope drawers
-extern floatv3_t ds_su, ds_sv, ds_sz, ds_slopelight;
-extern float focallengthf, zeroheight;
+extern dvector3_t ds_su, ds_sv, ds_sz, ds_slopelight;
+extern double zeroheight;
+extern float focallengthf;
 
 // Variable flat sizes
 extern UINT32 nflatxshift;
diff --git a/src/r_fps.c b/src/r_fps.c
index c8ceab760fd3d5e3ead1de4df5a50aec63ef0ca8..83fd0eec15f4c9fd0f41fa60104be022b9b4c7eb 100644
--- a/src/r_fps.c
+++ b/src/r_fps.c
@@ -19,9 +19,9 @@
 #include "i_video.h"
 #include "r_plane.h"
 #include "p_spec.h"
+#include "p_slopes.h"
 #include "r_state.h"
 #include "z_zone.h"
-#include "console.h" // con_startup_loadprogress
 #include "m_perfstats.h" // ps_metric_t
 #ifdef HWRENDER
 #include "hardware/hw_main.h" // for cv_glshearing
@@ -646,6 +646,7 @@ void R_ApplyLevelInterpolators(fixed_t frac)
 			R_LerpVector3(&interp->dynslope.oldo, &interp->dynslope.bako, frac, &interp->dynslope.slope->o);
 			R_LerpVector2(&interp->dynslope.oldd, &interp->dynslope.bakd, frac, &interp->dynslope.slope->d);
 			interp->dynslope.slope->zdelta = R_LerpFixed(interp->dynslope.oldzdelta, interp->dynslope.bakzdelta, frac);
+			interp->dynslope.slope->moved = true;
 			break;
 		}
 	}
diff --git a/src/r_plane.c b/src/r_plane.c
index 612c650fcb0dd8c66cab3dcfaaefbbe15477b446..11aa6c941ae916cfcd924d0ea55c20709f1f3081 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -84,11 +84,11 @@ fixed_t yslopetab[MAXVIDHEIGHT*16];
 fixed_t *yslope;
 
 static fixed_t xoffs, yoffs;
-static floatv3_t slope_origin, slope_u, slope_v;
-static floatv3_t slope_lightu, slope_lightv;
+static dvector3_t slope_origin, slope_u, slope_v;
+static dvector3_t slope_lightu, slope_lightv;
 
 static void CalcSlopePlaneVectors(visplane_t *pl, fixed_t xoff, fixed_t yoff);
-static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t height, float ang, angle_t plangle);
+static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, double height, float ang, angle_t plangle);
 
 static void DoSlopeCrossProducts(void);
 static void DoSlopeLightCrossProduct(void);
@@ -660,16 +660,18 @@ static void R_DrawSkyPlane(visplane_t *pl)
 	}
 }
 
-// Returns the height of the sloped plane at (x, y) as a 32.16 fixed_t
-static INT64 R_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y)
+// Returns the height of the sloped plane at (x, y) as a double
+static double R_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y)
 {
-	INT64 x64 = ((INT64)x - (INT64)slope->o.x);
-	INT64 y64 = ((INT64)y - (INT64)slope->o.y);
+	// If you want to reimplement this using just the equation constants, use this instead:
+	// (d + a*x + b*y) * -(1.0 / c)
 
-	x64 = (x64 * (INT64)slope->d.x) / FRACUNIT;
-	y64 = (y64 * (INT64)slope->d.y) / FRACUNIT;
+	double px = FixedToDouble(x) - slope->dorigin.x;
+	double py = FixedToDouble(y) - slope->dorigin.y;
 
-	return (INT64)slope->o.z + ((x64 + y64) * (INT64)slope->zdelta) / FRACUNIT;
+	double dist = (px * slope->dnormdir.x) + (py * slope->dnormdir.y);
+
+	return slope->dorigin.z + (dist * slope->dzdelta);
 }
 
 // Sets the texture origin vector of the sloped plane.
@@ -687,19 +689,25 @@ static void R_SetSlopePlaneOrigin(pslope_t *slope, fixed_t xpos, fixed_t ypos, f
 	// errors if the flat is rotated.
 	slope_origin.x = vxf * cos(ang) - vyf * sin(ang);
 	slope_origin.z = vxf * sin(ang) + vyf * cos(ang);
-	slope_origin.y = (R_GetSlopeZAt(slope, -xoff, yoff) - zpos) / (float)FRACUNIT;
+	slope_origin.y = R_GetSlopeZAt(slope, -xoff, yoff) - FixedToDouble(zpos);
 }
 
 // This function calculates all of the vectors necessary for drawing a sloped plane.
 void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle)
 {
 	// I copied ZDoom's code and adapted it to SRB2... -Red
-	fixed_t height, z_at_xy;
+	double height, z_at_xy;
 	float ang;
 
+	if (slope->moved)
+	{
+		P_CalculateSlopeVectors(slope);
+		slope->moved = false;
+	}
+
 	R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, xoff, yoff, angle);
-	height = P_GetSlopeZAt(slope, xpos, ypos);
-	zeroheight = FixedToFloat(height - zpos);
+	height = R_GetSlopeZAt(slope, xpos, ypos);
+	zeroheight = height - FixedToDouble(zpos);
 
 	ang = ANG2RAD(ANGLE_180 - (angle + plangle));
 
@@ -711,19 +719,19 @@ void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos,
 		return;
 	}
 
-	// slope_v is the v direction vector in view space
+	// the v direction vector in view space
 	slope_v.x = cos(ang);
 	slope_v.z = sin(ang);
 
-	// slope_u is the u direction vector in view space
+	// the u direction vector in view space
 	slope_u.x = sin(ang);
 	slope_u.z = -cos(ang);
 
 	plangle >>= ANGLETOFINESHIFT;
-	z_at_xy = P_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle));
-	slope_v.y = FixedToFloat(z_at_xy - height);
-	z_at_xy = P_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle));
-	slope_u.y = FixedToFloat(z_at_xy - height);
+	z_at_xy = R_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle));
+	slope_v.y = z_at_xy - height;
+	z_at_xy = R_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle));
+	slope_u.y = z_at_xy - height;
 
 	DoSlopeCrossProducts();
 	DoSlopeLightCrossProduct();
@@ -732,13 +740,18 @@ void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos,
 // This function calculates all of the vectors necessary for drawing a sloped and scaled plane.
 void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle)
 {
-	fixed_t height, z_at_xy;
-
+	double height, z_at_xy;
 	float ang;
 
+	if (slope->moved)
+	{
+		P_CalculateSlopeVectors(slope);
+		slope->moved = false;
+	}
+
 	R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, xoff, yoff, angle);
-	height = P_GetSlopeZAt(slope, xpos, ypos);
-	zeroheight = FixedToFloat(height - zpos);
+	height = R_GetSlopeZAt(slope, xpos, ypos);
+	zeroheight = height - FixedToDouble(zpos);
 
 	ang = ANG2RAD(ANGLE_180 - (angle + plangle));
 
@@ -753,27 +766,27 @@ void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t
 	float xscale = FixedToFloat(xs);
 	float yscale = FixedToFloat(ys);
 
-	// m is the v direction vector in view space
+	// the v direction vector in view space
 	slope_v.x = yscale * cos(ang);
 	slope_v.z = yscale * sin(ang);
 
-	// n is the u direction vector in view space
+	// the u direction vector in view space
 	slope_u.x = xscale * sin(ang);
 	slope_u.z = -xscale * cos(ang);
 
 	ang = ANG2RAD(plangle);
-	z_at_xy = P_GetSlopeZAt(slope, xpos + FloatToFixed(yscale * sin(ang)), ypos + FloatToFixed(yscale * cos(ang)));
-	slope_v.y = FixedToFloat(z_at_xy - height);
-	z_at_xy = P_GetSlopeZAt(slope, xpos + FloatToFixed(xscale * cos(ang)), ypos - FloatToFixed(xscale * sin(ang)));
-	slope_u.y = FixedToFloat(z_at_xy - height);
+	z_at_xy = R_GetSlopeZAt(slope, xpos + FloatToFixed(yscale * sin(ang)), ypos + FloatToFixed(yscale * cos(ang)));
+	slope_v.y = z_at_xy - height;
+	z_at_xy = R_GetSlopeZAt(slope, xpos + FloatToFixed(xscale * cos(ang)), ypos - FloatToFixed(xscale * sin(ang)));
+	slope_u.y = z_at_xy - height;
 
 	DoSlopeCrossProducts();
 	DoSlopeLightCrossProduct();
 }
 
-static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t height, float ang, angle_t plangle)
+static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, double height, float ang, angle_t plangle)
 {
-	fixed_t z_at_xy;
+	double z_at_xy;
 
 	slope_lightv.x = cos(ang);
 	slope_lightv.z = sin(ang);
@@ -782,25 +795,17 @@ static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, f
 	slope_lightu.z = -cos(ang);
 
 	plangle >>= ANGLETOFINESHIFT;
-	z_at_xy = P_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle));
-	slope_lightv.y = FixedToFloat(z_at_xy - height);
-	z_at_xy = P_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle));
-	slope_lightu.y = FixedToFloat(z_at_xy - height);
+	z_at_xy = R_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle));
+	slope_lightv.y = z_at_xy - height;
+	z_at_xy = R_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle));
+	slope_lightu.y = z_at_xy - height;
 }
 
-// Eh. I tried making this stuff fixed-point and it exploded on me. Here's a macro for the only floating-point vector function I recall using.
-#define CROSS(d, v1, v2) \
-d.x = (v1.y * v2.z) - (v1.z * v2.y);\
-d.y = (v1.z * v2.x) - (v1.x * v2.z);\
-d.z = (v1.x * v2.y) - (v1.y * v2.x)
-
 static void DoSlopeCrossProducts(void)
 {
-	float sfmult = 65536.f;
-
-	CROSS(ds_su, slope_origin, slope_v);
-	CROSS(ds_sv, slope_origin, slope_u);
-	CROSS(ds_sz, slope_v, slope_u);
+	DVector3_Cross(&slope_origin, &slope_v, &ds_su);
+	DVector3_Cross(&slope_origin, &slope_u, &ds_sv);
+	DVector3_Cross(&slope_v, &slope_u, &ds_sz);
 
 	ds_su.z *= focallengthf;
 	ds_sv.z *= focallengthf;
@@ -810,6 +815,8 @@ static void DoSlopeCrossProducts(void)
 		return;
 
 	// Premultiply the texture vectors with the scale factors
+	float sfmult = 65536.f;
+
 	if (ds_powersoftwo)
 		sfmult *= 1 << nflatshiftup;
 
@@ -823,13 +830,11 @@ static void DoSlopeCrossProducts(void)
 
 static void DoSlopeLightCrossProduct(void)
 {
-	CROSS(ds_slopelight, slope_lightv, slope_lightu);
+	DVector3_Cross(&slope_lightv, &slope_lightu, &ds_slopelight);
 
 	ds_slopelight.z *= focallengthf;
 }
 
-#undef CROSS
-
 static void CalcSlopePlaneVectors(visplane_t *pl, fixed_t xoff, fixed_t yoff)
 {
 	if (!ds_fog && (pl->xscale != FRACUNIT || pl->yscale != FRACUNIT))
diff --git a/src/r_segs.c b/src/r_segs.c
index 9340ca50c7cf2e01f4c4a40f65df869d8d72dfc1..7877f1353e5b24739d86a62bb940dbe729d85afc 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -1705,26 +1705,23 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 		// left
 		temp = xtoviewangle[start]+viewangle;
 
-#define FIXED_TO_DOUBLE(x) (((double)(x)) / ((double)FRACUNIT))
-#define DOUBLE_TO_FIXED(x) (fixed_t)((x) * ((double)FRACUNIT))
-
 		{
 			// Both lines can be written in slope-intercept form, so figure out line intersection
 			double a1, b1, c1, a2, b2, c2, det; // 1 is the seg, 2 is the view angle vector...
 			///TODO: convert to fixed point
 
-			a1 = FIXED_TO_DOUBLE(curline->v2->y-curline->v1->y);
-			b1 = FIXED_TO_DOUBLE(curline->v1->x-curline->v2->x);
-			c1 = a1*FIXED_TO_DOUBLE(curline->v1->x) + b1*FIXED_TO_DOUBLE(curline->v1->y);
+			a1 = FixedToDouble(curline->v2->y-curline->v1->y);
+			b1 = FixedToDouble(curline->v1->x-curline->v2->x);
+			c1 = a1*FixedToDouble(curline->v1->x) + b1*FixedToDouble(curline->v1->y);
 
-			a2 = -FIXED_TO_DOUBLE(FINESINE(temp>>ANGLETOFINESHIFT));
-			b2 = FIXED_TO_DOUBLE(FINECOSINE(temp>>ANGLETOFINESHIFT));
-			c2 = a2*FIXED_TO_DOUBLE(viewx) + b2*FIXED_TO_DOUBLE(viewy);
+			a2 = -FixedToDouble(FINESINE(temp>>ANGLETOFINESHIFT));
+			b2 = FixedToDouble(FINECOSINE(temp>>ANGLETOFINESHIFT));
+			c2 = a2*FixedToDouble(viewx) + b2*FixedToDouble(viewy);
 
 			det = a1*b2 - a2*b1;
 
-			ds_p->leftpos.x = segleft.x = DOUBLE_TO_FIXED((b2*c1 - b1*c2)/det);
-			ds_p->leftpos.y = segleft.y = DOUBLE_TO_FIXED((a1*c2 - a2*c1)/det);
+			ds_p->leftpos.x = segleft.x = DoubleToFixed((b2*c1 - b1*c2)/det);
+			ds_p->leftpos.y = segleft.y = DoubleToFixed((a1*c2 - a2*c1)/det);
 		}
 
 		// right
@@ -1735,26 +1732,21 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			double a1, b1, c1, a2, b2, c2, det; // 1 is the seg, 2 is the view angle vector...
 			///TODO: convert to fixed point
 
-			a1 = FIXED_TO_DOUBLE(curline->v2->y-curline->v1->y);
-			b1 = FIXED_TO_DOUBLE(curline->v1->x-curline->v2->x);
-			c1 = a1*FIXED_TO_DOUBLE(curline->v1->x) + b1*FIXED_TO_DOUBLE(curline->v1->y);
+			a1 = FixedToDouble(curline->v2->y-curline->v1->y);
+			b1 = FixedToDouble(curline->v1->x-curline->v2->x);
+			c1 = a1*FixedToDouble(curline->v1->x) + b1*FixedToDouble(curline->v1->y);
 
-			a2 = -FIXED_TO_DOUBLE(FINESINE(temp>>ANGLETOFINESHIFT));
-			b2 = FIXED_TO_DOUBLE(FINECOSINE(temp>>ANGLETOFINESHIFT));
-			c2 = a2*FIXED_TO_DOUBLE(viewx) + b2*FIXED_TO_DOUBLE(viewy);
+			a2 = -FixedToDouble(FINESINE(temp>>ANGLETOFINESHIFT));
+			b2 = FixedToDouble(FINECOSINE(temp>>ANGLETOFINESHIFT));
+			c2 = a2*FixedToDouble(viewx) + b2*FixedToDouble(viewy);
 
 			det = a1*b2 - a2*b1;
 
-			ds_p->rightpos.x = segright.x = DOUBLE_TO_FIXED((b2*c1 - b1*c2)/det);
-			ds_p->rightpos.y = segright.y = DOUBLE_TO_FIXED((a1*c2 - a2*c1)/det);
+			ds_p->rightpos.x = segright.x = DoubleToFixed((b2*c1 - b1*c2)/det);
+			ds_p->rightpos.y = segright.y = DoubleToFixed((a1*c2 - a2*c1)/det);
 		}
-
-#undef FIXED_TO_DOUBLE
-#undef DOUBLE_TO_FIXED
-
 	}
 
-
 #define SLOPEPARAMS(slope, end1, end2, normalheight) \
 	end1 = P_GetZAt(slope,  segleft.x,  segleft.y, normalheight); \
 	end2 = P_GetZAt(slope, segright.x, segright.y, normalheight);
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 7142cb64cb571e5b1c84489b1b38c9c48bdbf561..4621700787f0c9b6395b5899730bc303a2699f1d 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -295,6 +295,7 @@
     <ClInclude Include="..\m_tokenizer.h" />
     <ClInclude Include="..\m_perfstats.h" />
     <ClInclude Include="..\m_queue.h" />
+    <ClInclude Include="..\m_vector.h" />
     <ClInclude Include="..\m_random.h" />
     <ClInclude Include="..\m_swap.h" />
     <ClInclude Include="..\netcode\client_connection.h" />
@@ -473,6 +474,7 @@
     <ClCompile Include="..\m_tokenizer.c" />
     <ClCompile Include="..\m_perfstats.c" />
     <ClCompile Include="..\m_queue.c" />
+    <ClCompile Include="..\m_vector.c" />
     <ClCompile Include="..\m_random.c" />
     <ClCompile Include="..\netcode\client_connection.c" />
     <ClCompile Include="..\netcode\commands.c" />
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index 44c353ae29899fd561065a64b50d517cff34505b..59bb76b52089708abec222a46052d89f6b25ab6f 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -348,6 +348,9 @@
     <ClInclude Include="..\m_queue.h">
       <Filter>M_Misc</Filter>
     </ClInclude>
+    <ClInclude Include="..\m_vector.h">
+      <Filter>M_Misc</Filter>
+    </ClInclude>
     <ClInclude Include="..\m_random.h">
       <Filter>M_Misc</Filter>
     </ClInclude>
@@ -846,6 +849,9 @@
     <ClCompile Include="..\m_queue.c">
       <Filter>M_Misc</Filter>
     </ClCompile>
+    <ClCompile Include="..\m_vector.c">
+      <Filter>M_Misc</Filter>
+    </ClCompile>
     <ClCompile Include="..\m_random.c">
       <Filter>M_Misc</Filter>
     </ClCompile>
diff --git a/src/tables.c b/src/tables.c
index 315fe1d7a81717974e51538e6fc3fcd7210ab84b..d0fb428ba8909653cc7f1d340caa9590f1373391 100644
--- a/src/tables.c
+++ b/src/tables.c
@@ -3,6 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 2009 by Stephen McGranahan.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.