diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index c8b7ccf5f41644cfe0d1d2e7125514478ca068a8..b82ab30c91c4ec1ca5d3f448d66d53789e99620d 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -1589,6 +1589,57 @@ udmf
 			title = "None";
 			prefix = "(0)";
 		}
+
+		7
+		{
+			title = "Sector Flat Alignment";
+			prefix = "(7)";
+			arg0
+			{
+				title = "Target sector tag";
+				type = 13;
+			}
+			arg1
+			{
+				title = "Affected planes";
+				type = 11;
+				enum = "floorceiling";
+				default = 2;
+			}
+		}
+
+		10
+		{
+			title = "Culling Plane";
+			prefix = "(10)";
+			arg0
+			{
+				title = "Target sector tag";
+				type = 13;
+			}
+			arg1
+			{
+				title = "Culling behavior";
+				type = 11;
+				enum
+				{
+					0 = "Always";
+					1 = "Only while in sector";
+				}
+			}
+		}
+
+		40
+		{
+			title = "Visual Portal Between Tagged Linedefs";
+			prefix = "(40)";
+		}
+
+		41
+		{
+			title = "Horizon Effect";
+			prefix = "(41)";
+		}
 	}
 
 	parameters
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 83a8595d1bd5a961d3a2fb4979f2b59c08508b9a..4b97e8779e2b3bb01ed735a855c4a71f34231b92 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -3576,7 +3576,7 @@ static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float v
 		return false;
 
 	cullplane = FIXED_TO_FLOAT(cullheight->frontsector->floorheight);
-	if (cullheight->flags & ML_NOCLIMB) // Group culling
+	if (cullheight->args[1]) // Group culling
 	{
 		if (!viewcullheight)
 			return false;
diff --git a/src/p_setup.c b/src/p_setup.c
index e0c805a4bcfa0978dafbf6d5ff6f2264baff0a09..4564ec75e644e242eea254271e849cc85a23b6d2 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3179,6 +3179,43 @@ static void P_ConvertBinaryMap(void)
 
 		switch (lines[i].special)
 		{
+		case 7: //Sector flat alignment
+			lines[i].args[0] = tag;
+			if ((lines[i].flags & (ML_NETONLY|ML_NONET)) == (ML_NETONLY|ML_NONET))
+			{
+				CONS_Alert(CONS_WARNING, M_GetText("Flat alignment linedef (tag %d) doesn't have anything to do.\nConsider changing the linedef's flag configuration or removing it entirely.\n"), tag);
+				lines[i].special = 0;
+			}
+			else if (lines[i].flags & ML_NETONLY)
+				lines[i].args[1] = TMP_CEILING;
+			else if (lines[i].flags & ML_NONET)
+				lines[i].args[1] = TMP_FLOOR;
+			else
+				lines[i].args[1] = TMP_BOTH;
+			lines[i].flags &= ~(ML_NETONLY|ML_NONET);
+
+			if (lines[i].flags & ML_EFFECT6) // Set offset through x and y texture offsets
+			{
+				angle_t flatangle = InvAngle(R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y));
+				fixed_t xoffs = sides[lines[i].sidenum[0]].textureoffset;
+				fixed_t yoffs = sides[lines[i].sidenum[0]].rowoffset;
+
+				//If no tag is given, apply to front sector
+				if (lines[i].args[0] == 0)
+					P_ApplyFlatAlignment(lines[i].frontsector, flatangle, xoffs, yoffs, lines[i].args[1] != TMP_CEILING, lines[i].args[1] != TMP_FLOOR);
+				else
+				{
+					INT32 s;
+					TAG_ITER_SECTORS(lines[i].args[0], s)
+						P_ApplyFlatAlignment(sectors + s, flatangle, xoffs, yoffs, lines[i].args[1] != TMP_CEILING, lines[i].args[1] != TMP_FLOOR);
+				}
+				lines[i].special = 0;
+			}
+			break;
+		case 10: //Culling plane
+			lines[i].args[0] = tag;
+			lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB);
+			break;
 		case 20: //PolyObject first line
 		{
 			INT32 check = -1;
diff --git a/src/p_spec.c b/src/p_spec.c
index 4afc4946de0a8d5555817a58d566594f1b221195..ca08ad8756966efd78175d6162eba4d3fd3f40ea 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -5863,16 +5863,16 @@ void P_InitSpecials(void)
 	globalweather = mapheaderinfo[gamemap-1]->weather;
 }
 
-static void P_ApplyFlatAlignment(line_t *master, sector_t *sector, angle_t flatangle, fixed_t xoffs, fixed_t yoffs)
+void P_ApplyFlatAlignment(sector_t *sector, angle_t flatangle, fixed_t xoffs, fixed_t yoffs, boolean floor, boolean ceiling)
 {
-	if (!(master->flags & ML_NETONLY)) // Modify floor flat alignment unless ML_NETONLY flag is set
+	if (floor)
 	{
 		sector->floorpic_angle = flatangle;
 		sector->floor_xoffs += xoffs;
 		sector->floor_yoffs += yoffs;
 	}
 
-	if (!(master->flags & ML_NONET)) // Modify ceiling flat alignment unless ML_NONET flag is set
+	if (ceiling)
 	{
 		sector->ceilingpic_angle = flatangle;
 		sector->ceiling_xoffs += xoffs;
@@ -6046,23 +6046,20 @@ void P_SpawnSpecials(boolean fromnetsave)
 	{
 		mtag_t tag = Tag_FGet(&lines[i].tags);
 
-		if (lines[i].special != 7) // This is a hack. I can at least hope nobody wants to prevent flat alignment in netgames...
+		// set line specials to 0 here too, same reason as above
+		if (netgame || multiplayer)
 		{
-			// set line specials to 0 here too, same reason as above
-			if (netgame || multiplayer)
-			{
-				if (lines[i].flags & ML_NONET)
-				{
-					lines[i].special = 0;
-					continue;
-				}
-			}
-			else if (lines[i].flags & ML_NETONLY)
+			if (lines[i].flags & ML_NONET)
 			{
 				lines[i].special = 0;
 				continue;
 			}
 		}
+		else if (lines[i].flags & ML_NETONLY)
+		{
+			lines[i].special = 0;
+			continue;
+		}
 
 		switch (lines[i].special)
 		{
@@ -6102,37 +6099,22 @@ void P_SpawnSpecials(boolean fromnetsave)
 				break;
 
 			case 7: // Flat alignment - redone by toast
-				if ((lines[i].flags & (ML_NETONLY|ML_NONET)) != (ML_NETONLY|ML_NONET)) // If you can do something...
+			{
+				// Set calculated offsets such that line's v1 is the apparent origin
+				angle_t flatangle = InvAngle(R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y));
+				fixed_t xoffs = -lines[i].v1->x;
+				fixed_t yoffs = lines[i].v1->y;
+
+				//If no tag is given, apply to front sector
+				if (lines[i].args[0] == 0)
+					P_ApplyFlatAlignment(lines[i].frontsector, flatangle, xoffs, yoffs, lines[i].args[1] != TMP_CEILING, lines[i].args[1] != TMP_FLOOR);
+				else
 				{
-					angle_t flatangle = InvAngle(R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y));
-					fixed_t xoffs;
-					fixed_t yoffs;
-
-					if (lines[i].flags & ML_EFFECT6) // Set offset through x and y texture offsets if ML_EFFECT6 flag is set
-					{
-						xoffs = sides[lines[i].sidenum[0]].textureoffset;
-						yoffs = sides[lines[i].sidenum[0]].rowoffset;
-					}
-					else // Otherwise, set calculated offsets such that line's v1 is the apparent origin
-					{
-						xoffs = -lines[i].v1->x;
-						yoffs = lines[i].v1->y;
-					}
-
-					//If no tag is given, apply to front sector
-					if (tag == 0)
-						P_ApplyFlatAlignment(lines + i, lines[i].frontsector, flatangle, xoffs, yoffs);
-					else
-					{
-						TAG_ITER_SECTORS(tag, s)
-							P_ApplyFlatAlignment(lines + i, sectors + s, flatangle, xoffs, yoffs);
-					}
+					TAG_ITER_SECTORS(lines[i].args[0], s)
+						P_ApplyFlatAlignment(sectors + s, flatangle, xoffs, yoffs, lines[i].args[1] != TMP_CEILING, lines[i].args[1] != TMP_FLOOR);
 				}
-				else // Otherwise, print a helpful warning. Can I do no less?
-					CONS_Alert(CONS_WARNING,
-					M_GetText("Flat alignment linedef (tag %d) doesn't have anything to do.\nConsider changing the linedef's flag configuration or removing it entirely.\n"),
-					tag);
 				break;
+			}
 
 			case 8: // Sector Parameters
 				TAG_ITER_SECTORS(tag, s)
@@ -6162,7 +6144,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 				break;
 
 			case 10: // Vertical culling plane for sprites and FOFs
-				TAG_ITER_SECTORS(tag, s)
+				TAG_ITER_SECTORS(lines[i].args[0], s)
 					sectors[s].cullheight = &lines[i]; // This allows it to change in realtime!
 				break;
 
diff --git a/src/p_spec.h b/src/p_spec.h
index f77af5a0d88605482eb27adf27b5e23b15e31b94..6b98ca0e3798ac605db5afcbb05bd595e08730ff 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -454,6 +454,7 @@ void P_SetupLevelFlatAnims(void);
 
 // at map load
 void P_InitSpecials(void);
+void P_ApplyFlatAlignment(sector_t* sector, angle_t flatangle, fixed_t xoffs, fixed_t yoffs, boolean floor, boolean ceiling);
 void P_SpawnSpecials(boolean fromnetsave);
 
 // every tic
diff --git a/src/r_main.c b/src/r_main.c
index 8729b5dcb36ccedb8aed999dbf3afcb60e0faf04..b1d27fdff2f7ca55182dbb8f6ba3173f066d57d6 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -445,7 +445,7 @@ fixed_t R_ScaleFromGlobalAngle(angle_t visangle)
 // R_DoCulling
 // Checks viewz and top/bottom heights of an item against culling planes
 // Returns true if the item is to be culled, i.e it shouldn't be drawn!
-// if ML_NOCLIMB is set, the camera view is required to be in the same area for culling to occur
+// if args[1] is set, the camera view is required to be in the same area for culling to occur
 boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixed_t bottomh, fixed_t toph)
 {
 	fixed_t cullplane;
@@ -454,7 +454,7 @@ boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixe
 		return false;
 
 	cullplane = cullheight->frontsector->floorheight;
-	if (cullheight->flags & ML_NOCLIMB) // Group culling
+	if (cullheight->args[1]) // Group culling
 	{
 		if (!viewcullheight)
 			return false;