diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 309ba86a112dcae1efbbba40bc3850ff10b802e3..f74314c70f9fc3e2d93f4236d9e3d3851ca60e87 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -157,13 +157,21 @@ static const char *const side_opt[] = {
 enum vertex_e {
 	vertex_valid = 0,
 	vertex_x,
-	vertex_y
+	vertex_y,
+	vertex_floorz,
+	vertex_floorzset,
+	vertex_ceilingz,
+	vertex_ceilingzset
 };
 
 static const char *const vertex_opt[] = {
 	"valid",
 	"x",
 	"y",
+	"floorz",
+	"floorzset",
+	"ceilingz",
+	"ceilingzset",
 	NULL};
 
 enum ffloor_e {
@@ -968,6 +976,18 @@ static int vertex_get(lua_State *L)
 	case vertex_y:
 		lua_pushfixed(L, vertex->y);
 		return 1;
+	case vertex_floorzset:
+		lua_pushboolean(L, vertex->floorzset);
+		return 1;
+	case vertex_ceilingzset:
+		lua_pushboolean(L, vertex->ceilingzset);
+		return 1;
+	case vertex_floorz:
+		lua_pushfixed(L, vertex->floorz);
+		return 1;
+	case vertex_ceilingz:
+		lua_pushfixed(L, vertex->ceilingz);
+		return 1;
 	}
 	return 0;
 }
diff --git a/src/p_setup.c b/src/p_setup.c
index cfe1413816cf0e2679625c6cf8f9511331ffb802..09e78beaa37e2caa07c4f1f355ed837ceafcca1c 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -846,6 +846,8 @@ static void P_LoadVertices(UINT8 *data)
 	{
 		v->x = SHORT(mv->x)<<FRACBITS;
 		v->y = SHORT(mv->y)<<FRACBITS;
+		v->floorzset = v->ceilingzset = false;
+		v->floorz = v->ceilingz = 0;
 	}
 }
 
@@ -1366,6 +1368,16 @@ static void ParseTextmapVertexParameter(UINT32 i, char *param, char *val)
 		vertexes[i].x = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "y"))
 		vertexes[i].y = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "zfloor"))
+	{
+		vertexes[i].floorz = FLOAT_TO_FIXED(atof(val));
+		vertexes[i].floorzset = true;
+	}
+	else if (fastcmp(param, "zceiling"))
+	{
+		vertexes[i].ceilingz = FLOAT_TO_FIXED(atof(val));
+		vertexes[i].ceilingzset = true;
+	}
 }
 
 static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val)
@@ -1573,6 +1585,8 @@ static void P_LoadTextmap(void)
 	{
 		// Defaults.
 		vt->x = vt->y = INT32_MAX;
+		vt->floorzset = vt->ceilingzset = false;
+		vt->floorz = vt->ceilingz = 0;
 
 		TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
 
@@ -3567,11 +3581,11 @@ boolean P_LoadLevel(boolean fromnetsave)
 		return false;
 
 	// init gravity, tag lists,
-	// anything that P_ResetDynamicSlopes/P_LoadThings needs to know
+	// anything that P_SpawnSlopes/P_LoadThings needs to know
 	P_InitSpecials();
 
 #ifdef ESLOPE
-	P_ResetDynamicSlopes(fromnetsave);
+	P_SpawnSlopes(fromnetsave);
 #endif
 
 	P_SpawnMapThings(!fromnetsave);
diff --git a/src/p_slopes.c b/src/p_slopes.c
index 2cf2c74ba83e1b3bf07b4a743c521d2ee6cc6652..d584bb92d0d27740b5ed140e3333bf1d615d5267 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -458,8 +458,8 @@ static pslope_t *MakeViaMapthings(INT16 tag1, INT16 tag2, INT16 tag3, UINT8 flag
 	return ret;
 }
 
-/// Create vertex based slopes.
-static void line_SpawnViaVertexes(const int linenum, const boolean spawnthinker)
+/// Create vertex based slopes using tagged mapthings.
+static void line_SpawnViaMapthingVertexes(const int linenum, const boolean spawnthinker)
 {
 	line_t *line = lines + linenum;
 	side_t *side;
@@ -507,6 +507,55 @@ static void line_SpawnViaVertexes(const int linenum, const boolean spawnthinker)
 	side->sector->hasslope = true;
 }
 
+/// Spawn textmap vertex slopes.
+static void SpawnVertexSlopes(void)
+{
+	line_t *l1, *l2;
+	sector_t* sc;
+	vertex_t *v1, *v2, *v3;
+	size_t i;
+	for (i = 0, sc = sectors; i < numsectors; i++, sc++)
+	{
+		// The vertex slopes only work for 3-vertex sectors (and thus 3-sided sectors).
+		if (sc->linecount != 3)
+			continue;
+
+		l1 = sc->lines[0];
+		l2 = sc->lines[1];
+
+		// Determine the vertexes.
+		v1 = l1->v1;
+		v2 = l1->v2;
+		if ((l2->v1 != v1) && (l2->v1 != v2))
+			v3 = l2->v1;
+		else
+			v3 = l2->v2;
+
+		if (v1->floorzset || v2->floorzset || v3->floorzset)
+		{
+			vector3_t vtx[3] = {
+				{v1->x, v1->y, v1->floorzset ? v1->floorz : sc->floorheight},
+				{v2->x, v2->y, v2->floorzset ? v2->floorz : sc->floorheight},
+				{v3->x, v3->y, v3->floorzset ? v3->floorz : sc->floorheight}};
+			pslope_t *slop = Slope_Add(0);
+			sc->f_slope = slop;
+			sc->hasslope = true;
+			ReconfigureViaVertexes(slop, vtx[0], vtx[1], vtx[2]);
+		}
+
+		if (v1->ceilingzset || v2->ceilingzset || v3->ceilingzset)
+		{
+			vector3_t vtx[3] = {
+				{v1->x, v1->y, v1->ceilingzset ? v1->ceilingz : sc->ceilingheight},
+				{v2->x, v2->y, v2->ceilingzset ? v2->ceilingz : sc->ceilingheight},
+				{v3->x, v3->y, v3->ceilingzset ? v3->ceilingz : sc->ceilingheight}};
+			pslope_t *slop = Slope_Add(0);
+			sc->c_slope = slop;
+			sc->hasslope = true;
+			ReconfigureViaVertexes(slop, vtx[0], vtx[1], vtx[2]);
+		}
+	}
+}
 
 //
 // P_CopySectorSlope
@@ -551,12 +600,16 @@ pslope_t *P_SlopeById(UINT16 id)
 	return ret;
 }
 
-/// Reset slopes and read them from special lines.
-void P_ResetDynamicSlopes(const boolean fromsave) {
+/// Initializes and reads the slopes from the map data.
+void P_SpawnSlopes(const boolean fromsave) {
 	size_t i;
+
 	slopelist = NULL;
 	slopecount = 0;
 
+	/// Generates vertex slopes.
+	SpawnVertexSlopes();
+
 	/// Generates line special-defined slopes.
 	for (i = 0; i < numlines; i++)
 	{
@@ -577,7 +630,7 @@ void P_ResetDynamicSlopes(const boolean fromsave) {
 			case 705:
 			case 714:
 			case 715:
-				line_SpawnViaVertexes(i, !fromsave);
+				line_SpawnViaMapthingVertexes(i, !fromsave);
 				break;
 
 			default:
diff --git a/src/p_slopes.h b/src/p_slopes.h
index 96764051b428cf013fb503db03fff1b6de5ff23c..0bf03f26dc06903f19f58414c6c2dec1d9c736da 100644
--- a/src/p_slopes.h
+++ b/src/p_slopes.h
@@ -23,7 +23,7 @@ extern UINT16 slopecount;
 void P_LinkSlopeThinkers (void);
 
 void P_CalculateSlopeNormal(pslope_t *slope);
-void P_ResetDynamicSlopes(const boolean fromsave);
+void P_SpawnSlopes(const boolean fromsave);
 
 //
 // P_CopySectorSlope
diff --git a/src/p_spec.c b/src/p_spec.c
index f2cb17e0e4d58edf077a8040e351b684fcc95d6a..9defc33a03cdf45616486b23e7f6ace7a74457e1 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -6353,7 +6353,7 @@ static void P_RunLevelLoadExecutors(void)
 }
 
 /** Before things are loaded, initialises certain stuff in case they're needed
-  * by P_ResetDynamicSlopes or P_LoadThings. This was split off from
+  * by P_SpawnSlopes or P_LoadThings. This was split off from
   * P_SpawnSpecials, in case you couldn't tell.
   *
   * \sa P_SpawnSpecials, P_InitTagLists
diff --git a/src/r_defs.h b/src/r_defs.h
index 52b9bea951d9ad0c914157cf8850c968794dafd1..eade61db51cadd0874a486cc29f39b4a71759bba 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -84,6 +84,8 @@ typedef struct extracolormap_s
 typedef struct
 {
 	fixed_t x, y;
+	boolean floorzset, ceilingzset;
+	fixed_t floorz, ceilingz;
 } vertex_t;
 
 // Forward of linedefs, for sectors.