diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index 3751d781c6cbe20343c70048ccd631fe1b6ab7fc..76d65e663324f71ee421407bc6068d796b8daebf 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -2511,6 +2511,28 @@ udmf
 			}
 		}
 
+		402
+		{
+			title = "Copy Light Level";
+			prefix = "(402)";
+			arg0
+			{
+				title = "Target sector tag";
+				type = 13;
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Don't copy main light level";
+					2 = "Don't copy floor light level";
+					4 = "Don't copy ceiling light level";
+				}
+			}
+		}
+
 		408
 		{
 			title = "Set Tagged Sector's Flats";
@@ -2528,6 +2550,46 @@ udmf
 			}
 		}
 
+		420
+		{
+			title = "Fade Light Level";
+			prefix = "(420)";
+			arg0
+			{
+				title = "Target sector tag";
+				type = 13;
+			}
+			arg1
+			{
+				title = "Destination light level";
+			}
+			arg2
+			{
+				title = "Speed";
+			}
+			arg3
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Speed is tic duration";
+					2 = "Override existing fade";
+				}
+			}
+		}
+
+		421
+		{
+			title = "Stop Lighting Effect";
+			prefix = "(421)";
+			arg0
+			{
+				title = "Target sector tag";
+				type = 13;
+			}
+		}
+
 		435
 		{
 			title = "Change Plane Scroller Direction";
diff --git a/src/p_setup.c b/src/p_setup.c
index 7225da9856d942f26b4759d941c6a098631532aa..ffa81f3fb1e5773f739a9e36745bf9267586bb3b 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3657,6 +3657,10 @@ static void P_ConvertBinaryMap(void)
 			lines[i].args[2] = !!(lines[i].flags & ML_NOCLIMB);
 			lines[i].special = 400;
 			break;
+		case 402: //Copy light level
+			lines[i].args[0] = tag;
+			lines[i].args[1] = 0;
+			break;
 		case 403: //Move tagged sector's floor
 		case 404: //Move tagged sector's ceiling
 			lines[i].args[0] = tag;
@@ -3692,6 +3696,30 @@ static void P_ConvertBinaryMap(void)
 		case 411: //Stop plane movement
 			lines[i].args[0] = tag;
 			break;
+		case 420: //Fade light level
+			lines[i].args[0] = tag;
+			if (lines[i].flags & ML_DONTPEGBOTTOM)
+			{
+				lines[i].args[1] = max(sides[lines[i].sidenum[0]].textureoffset >> FRACBITS, 0);
+				// failsafe: if user specifies Back Y Offset and NOT Front Y Offset, use the Back Offset
+				// to be consistent with other light and fade specials
+				lines[i].args[2] = ((lines[i].sidenum[1] != 0xFFFF && !(sides[lines[i].sidenum[0]].rowoffset >> FRACBITS)) ?
+					max(min(sides[lines[i].sidenum[1]].rowoffset >> FRACBITS, 255), 0)
+					: max(min(sides[lines[i].sidenum[0]].rowoffset >> FRACBITS, 255), 0));
+			}
+			else
+			{
+				lines[i].args[1] = lines[i].frontsector->lightlevel;
+				lines[i].args[2] = abs(P_AproxDistance(lines[i].dx, lines[i].dy)) >> FRACBITS;
+			}
+			if (lines[i].flags & ML_EFFECT4)
+				lines[i].args[3] |= TMF_TICBASED;
+			if (lines[i].flags & ML_EFFECT5)
+				lines[i].args[3] |= TMF_FORCE;
+			break;
+		case 421: //Stop lighting effect
+			lines[i].args[0] = tag;
+			break;
 		case 428: //Start platform movement
 			lines[i].args[0] = tag;
 			lines[i].args[1] = P_AproxDistance(lines[i].dx, lines[i].dy) >> FRACBITS;
diff --git a/src/p_spec.c b/src/p_spec.c
index f69b495684ecc5eac59a97d3469e36235f4c3029..d682b4242bd48f52545ec70643acb41faffbc9c3 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2123,7 +2123,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				EV_DoCeiling(line->args[0], line, instantMoveCeilingByFrontSector);
 			break;
 
-		case 402: // Set tagged sector's light level
+		case 402: // Copy light level to tagged sectors
 			{
 				INT16 newlightlevel;
 				INT16 newfloorlightlevel, newceilinglightlevel;
@@ -2138,8 +2138,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				newfloorlightsec = line->frontsector->floorlightsec;
 				newceilinglightsec = line->frontsector->ceilinglightsec;
 
-				// act on all sectors with the same tag as the triggering linedef
-				TAG_ITER_SECTORS(tag, secnum)
+				TAG_ITER_SECTORS(line->args[0], secnum)
 				{
 					if (sectors[secnum].lightingdata)
 					{
@@ -2152,13 +2151,20 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 						// actually is: could be lightlevel_t, fireflicker_t, glow_t, etc.)
 					}
 
-					sectors[secnum].lightlevel = newlightlevel;
-					sectors[secnum].floorlightlevel = newfloorlightlevel;
-					sectors[secnum].floorlightabsolute = newfloorlightabsolute;
-					sectors[secnum].ceilinglightlevel = newceilinglightlevel;
-					sectors[secnum].ceilinglightabsolute = newceilinglightabsolute;
-					sectors[secnum].floorlightsec = newfloorlightsec;
-					sectors[secnum].ceilinglightsec = newceilinglightsec;
+					if (!(line->args[1] & TMLC_NOSECTOR))
+						sectors[secnum].lightlevel = newlightlevel;
+					if (!(line->args[1] & TMLC_NOFLOOR))
+					{
+						sectors[secnum].floorlightlevel = newfloorlightlevel;
+						sectors[secnum].floorlightabsolute = newfloorlightabsolute;
+						sectors[secnum].floorlightsec = newfloorlightsec;
+					}
+					if (!(line->args[1] & TMLC_NOCEILING))
+					{
+						sectors[secnum].ceilinglightlevel = newceilinglightlevel;
+						sectors[secnum].ceilinglightabsolute = newceilinglightabsolute;
+						sectors[secnum].ceilinglightsec = newceilinglightsec;
+					}
 				}
 			}
 			break;
@@ -2638,21 +2644,11 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			break;
 
 		case 420: // Fade light levels in tagged sectors to new value
-			P_FadeLight(tag,
-				(line->flags & ML_DONTPEGBOTTOM) ? max(sides[line->sidenum[0]].textureoffset>>FRACBITS, 0) : line->frontsector->lightlevel,
-				// failsafe: if user specifies Back Y Offset and NOT Front Y Offset, use the Back Offset
-				// to be consistent with other light and fade specials
-				(line->flags & ML_DONTPEGBOTTOM) ?
-					((line->sidenum[1] != 0xFFFF && !(sides[line->sidenum[0]].rowoffset>>FRACBITS)) ?
-						max(min(sides[line->sidenum[1]].rowoffset>>FRACBITS, 255), 0)
-						: max(min(sides[line->sidenum[0]].rowoffset>>FRACBITS, 255), 0))
-					: abs(P_AproxDistance(line->dx, line->dy))>>FRACBITS,
-				(line->flags & ML_EFFECT4),
-				(line->flags & ML_EFFECT5));
+			P_FadeLight(line->args[0], line->args[1], line->args[2], line->args[3] & TMF_TICBASED, line->args[3] & TMF_FORCE);
 			break;
 
 		case 421: // Stop lighting effect in tagged sectors
-			TAG_ITER_SECTORS(tag, secnum)
+			TAG_ITER_SECTORS(line->args[0], secnum)
 				if (sectors[secnum].lightingdata)
 				{
 					P_RemoveThinker(&((elevator_t *)sectors[secnum].lightingdata)->thinker);
diff --git a/src/p_spec.h b/src/p_spec.h
index f08ee2f1fd022bbeb581b4fd5e491ad3f3cb259a..19f8a847c783c2035c77bb0bde5f5aa20443e74f 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -112,6 +112,19 @@ typedef enum
 	TMP_BOTH = 2,
 } textmapplanes_t;
 
+typedef enum
+{
+	TMLC_NOSECTOR  = 1,
+	TMLC_NOFLOOR   = 1<<1,
+	TMLC_NOCEILING = 1<<2,
+} textmaplightcopyflags_t;
+
+typedef enum
+{
+	TMF_TICBASED = 1,
+	TMF_FORCE    = 1<<1,
+} textmapfadeflags_t;
+
 typedef enum
 {
 	TMSD_FRONT = 0,