From 1e1b01c1572f8dcb941e06ba0e914ef881ed1643 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Sat, 8 Sep 2018 22:10:51 -0400
Subject: [PATCH 1/9] Implemented tic-based light fading

* ML_BLOCKMONSTERS specifies destvalue and speed by texture offsets
* ML_NOCLIMB toggles tic-based logic
* Added props `duration`, `interval`, and `firsttic` to `lightlevel_t`
---
 src/lua_baselib.c |  3 ++-
 src/p_lights.c    | 47 ++++++++++++++++++++++++++++++++++++++++++-----
 src/p_saveg.c     |  6 ++++++
 src/p_spec.c      |  5 ++++-
 src/p_spec.h      |  7 ++++++-
 5 files changed, 60 insertions(+), 8 deletions(-)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index c1fd87af3..840863eb0 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1809,9 +1809,10 @@ static int lib_pFadeLight(lua_State *L)
 	INT16 tag = (INT16)luaL_checkinteger(L, 1);
 	INT32 destvalue = (INT32)luaL_checkinteger(L, 2);
 	INT32 speed = (INT32)luaL_checkinteger(L, 3);
+	boolean ticbased = lua_optboolean(L, 4);
 	NOHUD
 	INLEVEL
-	P_FadeLight(tag, destvalue, speed);
+	P_FadeLight(tag, destvalue, speed, ticbased);
 	return 0;
 }
 
diff --git a/src/p_lights.c b/src/p_lights.c
index 8aa2eedca..e37eda5fb 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -13,6 +13,7 @@
 ///        Fire flicker, light flash, strobe flash, lightning flash, glow, and fade.
 
 #include "doomdef.h"
+#include "doomstat.h" // gametic
 #include "p_local.h"
 #include "r_state.h"
 #include "z_zone.h"
@@ -329,12 +330,10 @@ glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector,
   * \param destvalue The final light value in these sectors.
   * \param speed     Speed of the fade; the change to the ligh
   *                  level in each sector per tic.
-  * \todo Calculate speed better so that it is possible to specify
-  *       the time for completion of the fade, and all lights fade
-  *       in this time regardless of initial values.
+  * \param ticbased  Use a specific duration for the fade, defined by speed
   * \sa T_LightFade
   */
-void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed)
+void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased)
 {
 	INT32 i;
 	lightlevel_t *ll;
@@ -346,6 +345,13 @@ void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed)
 		sector = &sectors[i];
 
 		P_RemoveLighting(sector); // remove the old lighting effect first
+
+		if ((ticbased && !speed) || sector->lightlevel == destvalue) // set immediately
+		{
+			sector->lightlevel = destvalue;
+			continue;
+		}
+
 		ll = Z_Calloc(sizeof (*ll), PU_LEVSPEC, NULL);
 		ll->thinker.function.acp1 = (actionf_p1)T_LightFade;
 		sector->lightingdata = ll; // set it to the lightlevel_t
@@ -354,7 +360,21 @@ void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed)
 
 		ll->sector = sector;
 		ll->destlevel = destvalue;
-		ll->speed = speed;
+
+		if (ticbased)
+		{
+			ll->duration = abs(speed);
+			ll->speed = FixedFloor(FixedDiv(destvalue - sector->lightlevel, ll->duration))/FRACUNIT;
+			if (!ll->speed)
+				ll->speed = (destvalue < sector->lightlevel) ? -1 : 1;
+			ll->interval = max(FixedFloor(FixedDiv(ll->duration, abs(destvalue - sector->lightlevel)))/FRACUNIT, 1);
+			ll->firsttic = gametic;
+		}
+		else
+		{
+			ll->duration = -1;
+			ll->speed = abs(speed);
+		}
 	}
 }
 
@@ -365,6 +385,23 @@ void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed)
   */
 void T_LightFade(lightlevel_t *ll)
 {
+	if (ll->duration >= 0) // tic-based
+	{
+		if (gametic - ll->firsttic >= ll->duration)
+		{
+			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
+		}
+		else if (!((gametic - ll->firsttic) % ll->interval))
+		{
+			if (ll->speed < 0)
+				ll->sector->lightlevel = max(ll->sector->lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
+			else
+				ll->sector->lightlevel = min(ll->sector->lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
+		}
+		return;
+	}
+
 	if (ll->sector->lightlevel < ll->destlevel)
 	{
 		// increase the lightlevel
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 22d43f358..8ec5b5ea8 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1539,6 +1539,9 @@ static void SaveLightlevelThinker(const thinker_t *th, const UINT8 type)
 	WRITEUINT32(save_p, SaveSector(ht->sector));
 	WRITEINT32(save_p, ht->destlevel);
 	WRITEINT32(save_p, ht->speed);
+	WRITEINT32(save_p, ht->duration);
+	WRITEUINT32(save_p, ht->interval);
+	WRITEUINT32(save_p, (UINT32)ht->firsttic);
 }
 
 //
@@ -2512,6 +2515,9 @@ static inline void LoadLightlevelThinker(actionf_p1 thinker)
 	ht->sector = LoadSector(READUINT32(save_p));
 	ht->destlevel = READINT32(save_p);
 	ht->speed = READINT32(save_p);
+	ht->duration = READINT32(save_p);
+	ht->interval = READUINT32(save_p);
+	ht->firsttic = (tic_t)READUINT32(save_p);
 	if (ht->sector)
 		ht->sector->lightingdata = ht;
 	P_AddThinker(&ht->thinker);
diff --git a/src/p_spec.c b/src/p_spec.c
index 6c359c9cc..a894dcbfb 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2778,7 +2778,10 @@ 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(line->tag, line->frontsector->lightlevel, P_AproxDistance(line->dx, line->dy)>>FRACBITS);
+			P_FadeLight(line->tag,
+				(line->flags & ML_BLOCKMONSTERS) ? max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 255), 0) : line->frontsector->lightlevel,
+				(line->flags & ML_BLOCKMONSTERS) ? max(sides[line->sidenum[0]].rowoffset>>FRACBITS, 0) : P_AproxDistance(line->dx, line->dy)>>FRACBITS,
+				(line->flags & ML_NOCLIMB));
 			break;
 
 		case 421: // Stop lighting effect in tagged sectors
diff --git a/src/p_spec.h b/src/p_spec.h
index e0bcc18eb..8e296891b 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -138,6 +138,11 @@ typedef struct
 	sector_t *sector;  ///< Sector where action is taking place.
 	INT32 destlevel;   ///< Light level we're fading to.
 	INT32 speed;       ///< Speed at which to change light level.
+
+	// Tic-based behavior
+	INT32 duration;    ///< If <0, do not use tic-based behavior. If 0, set instantly. If >0, fade lasts this duration.
+	UINT32 interval;   ///< Interval to deduct light level
+	tic_t firsttic;    ///< First gametic to count from
 } lightlevel_t;
 
 #define GLOWSPEED 8
@@ -156,7 +161,7 @@ strobe_t * P_SpawnAdjustableStrobeFlash(sector_t *minsector, sector_t *maxsector
 void T_Glow(glow_t *g);
 glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector, INT32 length);
 
-void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed);
+void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased);
 void T_LightFade(lightlevel_t *ll);
 
 typedef enum

From 68e67917f1b345633151e3315ffd061d4fde5837 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Sat, 8 Sep 2018 22:14:49 -0400
Subject: [PATCH 2/9] Split P_FadeLight into P_FadeLightBySector

---
 src/p_lights.c | 80 +++++++++++++++++++++++++-------------------------
 src/p_spec.h   |  1 +
 2 files changed, 41 insertions(+), 40 deletions(-)

diff --git a/src/p_lights.c b/src/p_lights.c
index e37eda5fb..8be3d793c 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -323,59 +323,59 @@ glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector,
 	return g;
 }
 
-/** Fades all the lights in sectors with a particular tag to a new
+/** Fades all the lights in specified sector to a new
   * value.
   *
-  * \param tag       Tag to look for sectors by.
+  * \param sector    Target sector
   * \param destvalue The final light value in these sectors.
   * \param speed     Speed of the fade; the change to the ligh
   *                  level in each sector per tic.
   * \param ticbased  Use a specific duration for the fade, defined by speed
   * \sa T_LightFade
   */
+void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased)
+{
+	lightlevel_t *ll;
+
+	P_RemoveLighting(sector); // remove the old lighting effect first
+
+	if ((ticbased && !speed) || sector->lightlevel == destvalue) // set immediately
+	{
+		sector->lightlevel = destvalue;
+		return;
+	}
+
+	ll = Z_Calloc(sizeof (*ll), PU_LEVSPEC, NULL);
+	ll->thinker.function.acp1 = (actionf_p1)T_LightFade;
+	sector->lightingdata = ll; // set it to the lightlevel_t
+
+	P_AddThinker(&ll->thinker); // add thinker
+
+	ll->sector = sector;
+	ll->destlevel = destvalue;
+
+	if (ticbased)
+	{
+		ll->duration = abs(speed);
+		ll->speed = FixedFloor(FixedDiv(destvalue - sector->lightlevel, ll->duration))/FRACUNIT;
+		if (!ll->speed)
+			ll->speed = (destvalue < sector->lightlevel) ? -1 : 1;
+		ll->interval = max(FixedFloor(FixedDiv(ll->duration, abs(destvalue - sector->lightlevel)))/FRACUNIT, 1);
+		ll->firsttic = gametic;
+	}
+	else
+	{
+		ll->duration = -1;
+		ll->speed = abs(speed);
+	}
+}
+
 void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased)
 {
 	INT32 i;
-	lightlevel_t *ll;
-	sector_t *sector;
-
 	// search all sectors for ones with tag
 	for (i = -1; (i = P_FindSectorFromTag(tag, i)) >= 0 ;)
-	{
-		sector = &sectors[i];
-
-		P_RemoveLighting(sector); // remove the old lighting effect first
-
-		if ((ticbased && !speed) || sector->lightlevel == destvalue) // set immediately
-		{
-			sector->lightlevel = destvalue;
-			continue;
-		}
-
-		ll = Z_Calloc(sizeof (*ll), PU_LEVSPEC, NULL);
-		ll->thinker.function.acp1 = (actionf_p1)T_LightFade;
-		sector->lightingdata = ll; // set it to the lightlevel_t
-
-		P_AddThinker(&ll->thinker); // add thinker
-
-		ll->sector = sector;
-		ll->destlevel = destvalue;
-
-		if (ticbased)
-		{
-			ll->duration = abs(speed);
-			ll->speed = FixedFloor(FixedDiv(destvalue - sector->lightlevel, ll->duration))/FRACUNIT;
-			if (!ll->speed)
-				ll->speed = (destvalue < sector->lightlevel) ? -1 : 1;
-			ll->interval = max(FixedFloor(FixedDiv(ll->duration, abs(destvalue - sector->lightlevel)))/FRACUNIT, 1);
-			ll->firsttic = gametic;
-		}
-		else
-		{
-			ll->duration = -1;
-			ll->speed = abs(speed);
-		}
-	}
+		P_FadeLightBySector(&sectors[i], destvalue, speed, ticbased);
 }
 
 /** Fades the light level in a sector to a new value.
diff --git a/src/p_spec.h b/src/p_spec.h
index 8e296891b..c2c4c8976 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -161,6 +161,7 @@ strobe_t * P_SpawnAdjustableStrobeFlash(sector_t *minsector, sector_t *maxsector
 void T_Glow(glow_t *g);
 glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector, INT32 length);
 
+void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased);
 void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased);
 void T_LightFade(lightlevel_t *ll);
 

From cc26d03c93c91d6209b2b51acc63758c837b2cf4 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Sat, 8 Sep 2018 23:01:35 -0400
Subject: [PATCH 3/9] Snap light level to software values (32 levels)

* New properties `exactlightlevel` and `lightlevel` in `lightlevel_t`
---
 src/lua_baselib.c |  3 +-
 src/p_lights.c    | 75 +++++++++++++++++++++++++++++++----------------
 src/p_saveg.c     |  4 +++
 src/p_spec.c      |  3 +-
 src/p_spec.h      |  8 +++--
 5 files changed, 63 insertions(+), 30 deletions(-)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 840863eb0..74b842e2a 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1810,9 +1810,10 @@ static int lib_pFadeLight(lua_State *L)
 	INT32 destvalue = (INT32)luaL_checkinteger(L, 2);
 	INT32 speed = (INT32)luaL_checkinteger(L, 3);
 	boolean ticbased = lua_optboolean(L, 4);
+	boolean exactlightlevel = lua_optboolean(L, 5);
 	NOHUD
 	INLEVEL
-	P_FadeLight(tag, destvalue, speed, ticbased);
+	P_FadeLight(tag, destvalue, speed, ticbased, exactlightlevel);
 	return 0;
 }
 
diff --git a/src/p_lights.c b/src/p_lights.c
index 8be3d793c..b2934b792 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -15,6 +15,7 @@
 #include "doomdef.h"
 #include "doomstat.h" // gametic
 #include "p_local.h"
+#include "r_main.h" // LIGHTSEGSHIFT
 #include "r_state.h"
 #include "z_zone.h"
 #include "m_random.h"
@@ -331,9 +332,10 @@ glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector,
   * \param speed     Speed of the fade; the change to the ligh
   *                  level in each sector per tic.
   * \param ticbased  Use a specific duration for the fade, defined by speed
+  * \param exactlightlevel Do not snap to software values (for OpenGL)
   * \sa T_LightFade
   */
-void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased)
+void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel)
 {
 	lightlevel_t *ll;
 
@@ -353,6 +355,8 @@ void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean
 
 	ll->sector = sector;
 	ll->destlevel = destvalue;
+	ll->exactlightlevel = exactlightlevel;
+	ll->lightlevel = sector->lightlevel;
 
 	if (ticbased)
 	{
@@ -370,12 +374,12 @@ void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean
 	}
 }
 
-void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased)
+void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel)
 {
 	INT32 i;
 	// search all sectors for ones with tag
 	for (i = -1; (i = P_FindSectorFromTag(tag, i)) >= 0 ;)
-		P_FadeLightBySector(&sectors[i], destvalue, speed, ticbased);
+		P_FadeLightBySector(&sectors[i], destvalue, speed, ticbased, exactlightlevel);
 }
 
 /** Fades the light level in a sector to a new value.
@@ -385,47 +389,66 @@ void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased)
   */
 void T_LightFade(lightlevel_t *ll)
 {
+	boolean stillfading = false;
+	INT16 lightlevel = ll->lightlevel;
+
 	if (ll->duration >= 0) // tic-based
 	{
+		stillfading = !(gametic - ll->firsttic >= ll->duration);
 		if (gametic - ll->firsttic >= ll->duration)
 		{
-			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+			lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
 			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
 		}
 		else if (!((gametic - ll->firsttic) % ll->interval))
 		{
 			if (ll->speed < 0)
-				ll->sector->lightlevel = max(ll->sector->lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
+				lightlevel = max(lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
 			else
-				ll->sector->lightlevel = min(ll->sector->lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
+				lightlevel = min(lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
 		}
-		return;
 	}
-
-	if (ll->sector->lightlevel < ll->destlevel)
+	else // x/tic speed-based
 	{
-		// increase the lightlevel
-		if (ll->sector->lightlevel + ll->speed >= ll->destlevel)
+		if (lightlevel < ll->destlevel)
 		{
-			// stop changing light level
-			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+			// increase the lightlevel
+			if (lightlevel + ll->speed >= ll->destlevel)
+			{
+				// stop changing light level
+				lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
 
-			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
+				P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
+			}
+			else
+			{
+				stillfading = true;
+				lightlevel = (INT16)(lightlevel + (INT16)ll->speed); // move lightlevel
+			}
 		}
 		else
-			ll->sector->lightlevel = (INT16)(ll->sector->lightlevel + (INT16)ll->speed); // move lightlevel
+		{
+			// decrease lightlevel
+			if (lightlevel - ll->speed <= ll->destlevel)
+			{
+				// stop changing light level
+				lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+
+				P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
+			}
+			else
+			{
+				stillfading = true;
+				lightlevel = (INT16)(lightlevel - (INT16)ll->speed); // move lightlevel
+			}
+		}
 	}
+
+	// Snap light level to software values
+	if (!stillfading || ll->exactlightlevel)
+		ll->sector->lightlevel = lightlevel;
 	else
-	{
-		// decrease lightlevel
-		if (ll->sector->lightlevel - ll->speed <= ll->destlevel)
-		{
-			// stop changing light level
-			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+		ll->sector->lightlevel = (lightlevel >> LIGHTSEGSHIFT) << LIGHTSEGSHIFT;
 
-			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
-		}
-		else
-			ll->sector->lightlevel = (INT16)(ll->sector->lightlevel - (INT16)ll->speed); // move lightlevel
-	}
+	ll->lightlevel = lightlevel;
 }
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 8ec5b5ea8..95d073fc9 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1542,6 +1542,8 @@ static void SaveLightlevelThinker(const thinker_t *th, const UINT8 type)
 	WRITEINT32(save_p, ht->duration);
 	WRITEUINT32(save_p, ht->interval);
 	WRITEUINT32(save_p, (UINT32)ht->firsttic);
+	WRITEUINT8(save_p, (UINT8)ht->exactlightlevel);
+	WRITEINT16(save_p, ht->lightlevel);
 }
 
 //
@@ -2518,6 +2520,8 @@ static inline void LoadLightlevelThinker(actionf_p1 thinker)
 	ht->duration = READINT32(save_p);
 	ht->interval = READUINT32(save_p);
 	ht->firsttic = (tic_t)READUINT32(save_p);
+	ht->exactlightlevel = (boolean)READUINT8(save_p);
+	ht->lightlevel = READINT16(save_p);
 	if (ht->sector)
 		ht->sector->lightingdata = ht;
 	P_AddThinker(&ht->thinker);
diff --git a/src/p_spec.c b/src/p_spec.c
index a894dcbfb..707e19f08 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2781,7 +2781,8 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			P_FadeLight(line->tag,
 				(line->flags & ML_BLOCKMONSTERS) ? max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 255), 0) : line->frontsector->lightlevel,
 				(line->flags & ML_BLOCKMONSTERS) ? max(sides[line->sidenum[0]].rowoffset>>FRACBITS, 0) : P_AproxDistance(line->dx, line->dy)>>FRACBITS,
-				(line->flags & ML_NOCLIMB));
+				(line->flags & ML_NOCLIMB),
+				(line->flags & ML_TFERLINE));
 			break;
 
 		case 421: // Stop lighting effect in tagged sectors
diff --git a/src/p_spec.h b/src/p_spec.h
index c2c4c8976..b354e6772 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -143,6 +143,10 @@ typedef struct
 	INT32 duration;    ///< If <0, do not use tic-based behavior. If 0, set instantly. If >0, fade lasts this duration.
 	UINT32 interval;   ///< Interval to deduct light level
 	tic_t firsttic;    ///< First gametic to count from
+
+	// Snap to software values
+	boolean exactlightlevel; ///< Use exact values for OpenGL
+	INT16 lightlevel;        ///< Internal counter for fading
 } lightlevel_t;
 
 #define GLOWSPEED 8
@@ -161,8 +165,8 @@ strobe_t * P_SpawnAdjustableStrobeFlash(sector_t *minsector, sector_t *maxsector
 void T_Glow(glow_t *g);
 glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector, INT32 length);
 
-void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased);
-void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased);
+void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel);
+void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel);
 void T_LightFade(lightlevel_t *ll);
 
 typedef enum

From 675f69afea115c284383dde25d54fac17b9c81c8 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Sun, 9 Sep 2018 09:29:18 -0400
Subject: [PATCH 4/9] Flag re-organization

* Change alternate param flag from BLOCKMONSTERS to DONTPEGBOTTOM
* Change tic-based flag from NOCLIMB to EFFECT5
---
 src/p_spec.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index 707e19f08..87f5676bb 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2779,9 +2779,9 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 
 		case 420: // Fade light levels in tagged sectors to new value
 			P_FadeLight(line->tag,
-				(line->flags & ML_BLOCKMONSTERS) ? max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 255), 0) : line->frontsector->lightlevel,
-				(line->flags & ML_BLOCKMONSTERS) ? max(sides[line->sidenum[0]].rowoffset>>FRACBITS, 0) : P_AproxDistance(line->dx, line->dy)>>FRACBITS,
-				(line->flags & ML_NOCLIMB),
+				(line->flags & ML_DONTPEGBOTTOM) ? max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 255), 0) : line->frontsector->lightlevel,
+				(line->flags & ML_DONTPEGBOTTOM) ? max(sides[line->sidenum[0]].rowoffset>>FRACBITS, 0) : P_AproxDistance(line->dx, line->dy)>>FRACBITS,
+				(line->flags & ML_EFFECT5),
 				(line->flags & ML_TFERLINE));
 			break;
 

From aeb45132c5d86e64a641f3311fff7f70e163af36 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Sun, 9 Sep 2018 13:43:00 -0400
Subject: [PATCH 5/9] Revert "Snap light level to software values (32 levels)"

This reverts commit cc26d03c93c91d6209b2b51acc63758c837b2cf4.
---
 src/lua_baselib.c |  3 +-
 src/p_lights.c    | 79 +++++++++++++++++------------------------------
 src/p_saveg.c     |  4 ---
 src/p_spec.c      |  3 +-
 src/p_spec.h      |  8 ++---
 5 files changed, 32 insertions(+), 65 deletions(-)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 74b842e2a..840863eb0 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1810,10 +1810,9 @@ static int lib_pFadeLight(lua_State *L)
 	INT32 destvalue = (INT32)luaL_checkinteger(L, 2);
 	INT32 speed = (INT32)luaL_checkinteger(L, 3);
 	boolean ticbased = lua_optboolean(L, 4);
-	boolean exactlightlevel = lua_optboolean(L, 5);
 	NOHUD
 	INLEVEL
-	P_FadeLight(tag, destvalue, speed, ticbased, exactlightlevel);
+	P_FadeLight(tag, destvalue, speed, ticbased);
 	return 0;
 }
 
diff --git a/src/p_lights.c b/src/p_lights.c
index b2934b792..8be3d793c 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -15,7 +15,6 @@
 #include "doomdef.h"
 #include "doomstat.h" // gametic
 #include "p_local.h"
-#include "r_main.h" // LIGHTSEGSHIFT
 #include "r_state.h"
 #include "z_zone.h"
 #include "m_random.h"
@@ -332,10 +331,9 @@ glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector,
   * \param speed     Speed of the fade; the change to the ligh
   *                  level in each sector per tic.
   * \param ticbased  Use a specific duration for the fade, defined by speed
-  * \param exactlightlevel Do not snap to software values (for OpenGL)
   * \sa T_LightFade
   */
-void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel)
+void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased)
 {
 	lightlevel_t *ll;
 
@@ -355,8 +353,6 @@ void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean
 
 	ll->sector = sector;
 	ll->destlevel = destvalue;
-	ll->exactlightlevel = exactlightlevel;
-	ll->lightlevel = sector->lightlevel;
 
 	if (ticbased)
 	{
@@ -374,12 +370,12 @@ void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean
 	}
 }
 
-void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel)
+void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased)
 {
 	INT32 i;
 	// search all sectors for ones with tag
 	for (i = -1; (i = P_FindSectorFromTag(tag, i)) >= 0 ;)
-		P_FadeLightBySector(&sectors[i], destvalue, speed, ticbased, exactlightlevel);
+		P_FadeLightBySector(&sectors[i], destvalue, speed, ticbased);
 }
 
 /** Fades the light level in a sector to a new value.
@@ -389,66 +385,47 @@ void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, bool
   */
 void T_LightFade(lightlevel_t *ll)
 {
-	boolean stillfading = false;
-	INT16 lightlevel = ll->lightlevel;
-
 	if (ll->duration >= 0) // tic-based
 	{
-		stillfading = !(gametic - ll->firsttic >= ll->duration);
 		if (gametic - ll->firsttic >= ll->duration)
 		{
-			lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
 			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
 		}
 		else if (!((gametic - ll->firsttic) % ll->interval))
 		{
 			if (ll->speed < 0)
-				lightlevel = max(lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
+				ll->sector->lightlevel = max(ll->sector->lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
 			else
-				lightlevel = min(lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
+				ll->sector->lightlevel = min(ll->sector->lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
 		}
+		return;
 	}
-	else // x/tic speed-based
-	{
-		if (lightlevel < ll->destlevel)
-		{
-			// increase the lightlevel
-			if (lightlevel + ll->speed >= ll->destlevel)
-			{
-				// stop changing light level
-				lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
 
-				P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
-			}
-			else
-			{
-				stillfading = true;
-				lightlevel = (INT16)(lightlevel + (INT16)ll->speed); // move lightlevel
-			}
+	if (ll->sector->lightlevel < ll->destlevel)
+	{
+		// increase the lightlevel
+		if (ll->sector->lightlevel + ll->speed >= ll->destlevel)
+		{
+			// stop changing light level
+			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+
+			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
 		}
 		else
-		{
-			// decrease lightlevel
-			if (lightlevel - ll->speed <= ll->destlevel)
-			{
-				// stop changing light level
-				lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
-
-				P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
-			}
-			else
-			{
-				stillfading = true;
-				lightlevel = (INT16)(lightlevel - (INT16)ll->speed); // move lightlevel
-			}
-		}
+			ll->sector->lightlevel = (INT16)(ll->sector->lightlevel + (INT16)ll->speed); // move lightlevel
 	}
-
-	// Snap light level to software values
-	if (!stillfading || ll->exactlightlevel)
-		ll->sector->lightlevel = lightlevel;
 	else
-		ll->sector->lightlevel = (lightlevel >> LIGHTSEGSHIFT) << LIGHTSEGSHIFT;
+	{
+		// decrease lightlevel
+		if (ll->sector->lightlevel - ll->speed <= ll->destlevel)
+		{
+			// stop changing light level
+			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
 
-	ll->lightlevel = lightlevel;
+			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
+		}
+		else
+			ll->sector->lightlevel = (INT16)(ll->sector->lightlevel - (INT16)ll->speed); // move lightlevel
+	}
 }
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 95d073fc9..8ec5b5ea8 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1542,8 +1542,6 @@ static void SaveLightlevelThinker(const thinker_t *th, const UINT8 type)
 	WRITEINT32(save_p, ht->duration);
 	WRITEUINT32(save_p, ht->interval);
 	WRITEUINT32(save_p, (UINT32)ht->firsttic);
-	WRITEUINT8(save_p, (UINT8)ht->exactlightlevel);
-	WRITEINT16(save_p, ht->lightlevel);
 }
 
 //
@@ -2520,8 +2518,6 @@ static inline void LoadLightlevelThinker(actionf_p1 thinker)
 	ht->duration = READINT32(save_p);
 	ht->interval = READUINT32(save_p);
 	ht->firsttic = (tic_t)READUINT32(save_p);
-	ht->exactlightlevel = (boolean)READUINT8(save_p);
-	ht->lightlevel = READINT16(save_p);
 	if (ht->sector)
 		ht->sector->lightingdata = ht;
 	P_AddThinker(&ht->thinker);
diff --git a/src/p_spec.c b/src/p_spec.c
index 87f5676bb..917ad68d4 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2781,8 +2781,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			P_FadeLight(line->tag,
 				(line->flags & ML_DONTPEGBOTTOM) ? max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 255), 0) : line->frontsector->lightlevel,
 				(line->flags & ML_DONTPEGBOTTOM) ? max(sides[line->sidenum[0]].rowoffset>>FRACBITS, 0) : P_AproxDistance(line->dx, line->dy)>>FRACBITS,
-				(line->flags & ML_EFFECT5),
-				(line->flags & ML_TFERLINE));
+				(line->flags & ML_EFFECT5));
 			break;
 
 		case 421: // Stop lighting effect in tagged sectors
diff --git a/src/p_spec.h b/src/p_spec.h
index b354e6772..c2c4c8976 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -143,10 +143,6 @@ typedef struct
 	INT32 duration;    ///< If <0, do not use tic-based behavior. If 0, set instantly. If >0, fade lasts this duration.
 	UINT32 interval;   ///< Interval to deduct light level
 	tic_t firsttic;    ///< First gametic to count from
-
-	// Snap to software values
-	boolean exactlightlevel; ///< Use exact values for OpenGL
-	INT16 lightlevel;        ///< Internal counter for fading
 } lightlevel_t;
 
 #define GLOWSPEED 8
@@ -165,8 +161,8 @@ strobe_t * P_SpawnAdjustableStrobeFlash(sector_t *minsector, sector_t *maxsector
 void T_Glow(glow_t *g);
 glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector, INT32 length);
 
-void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel);
-void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel);
+void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased);
+void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased);
 void T_LightFade(lightlevel_t *ll);
 
 typedef enum

From 3d5f225702bbb9cae9a52ec00ff1a32304d8eb71 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Sun, 9 Sep 2018 20:18:43 -0400
Subject: [PATCH 6/9] Replace firsttic with decrement timer

---
 src/p_lights.c | 16 ++++++++--------
 src/p_saveg.c  |  8 ++++----
 src/p_spec.h   |  4 ++--
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/p_lights.c b/src/p_lights.c
index 8be3d793c..77d05dad3 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -356,16 +356,16 @@ void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean
 
 	if (ticbased)
 	{
-		ll->duration = abs(speed);
-		ll->speed = FixedFloor(FixedDiv(destvalue - sector->lightlevel, ll->duration))/FRACUNIT;
+		ll->ticbased = ticbased;
+		ll->timer = abs(speed);
+		ll->speed = FixedFloor(FixedDiv(destvalue - sector->lightlevel, ll->timer))/FRACUNIT;
 		if (!ll->speed)
 			ll->speed = (destvalue < sector->lightlevel) ? -1 : 1;
-		ll->interval = max(FixedFloor(FixedDiv(ll->duration, abs(destvalue - sector->lightlevel)))/FRACUNIT, 1);
-		ll->firsttic = gametic;
+		ll->interval = max(FixedFloor(FixedDiv(ll->timer, abs(destvalue - sector->lightlevel)))/FRACUNIT, 1);
 	}
 	else
 	{
-		ll->duration = -1;
+		ll->timer = -1;
 		ll->speed = abs(speed);
 	}
 }
@@ -385,14 +385,14 @@ void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased)
   */
 void T_LightFade(lightlevel_t *ll)
 {
-	if (ll->duration >= 0) // tic-based
+	if (ll->ticbased)
 	{
-		if (gametic - ll->firsttic >= ll->duration)
+		if (--ll->timer <= 0)
 		{
 			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
 			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
 		}
-		else if (!((gametic - ll->firsttic) % ll->interval))
+		else if (!(ll->timer % ll->interval))
 		{
 			if (ll->speed < 0)
 				ll->sector->lightlevel = max(ll->sector->lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 8ec5b5ea8..eefee072d 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1539,9 +1539,9 @@ static void SaveLightlevelThinker(const thinker_t *th, const UINT8 type)
 	WRITEUINT32(save_p, SaveSector(ht->sector));
 	WRITEINT32(save_p, ht->destlevel);
 	WRITEINT32(save_p, ht->speed);
-	WRITEINT32(save_p, ht->duration);
+	WRITEUINT8(save_p, (UINT8)ht->ticbased);
+	WRITEINT32(save_p, ht->timer);
 	WRITEUINT32(save_p, ht->interval);
-	WRITEUINT32(save_p, (UINT32)ht->firsttic);
 }
 
 //
@@ -2515,9 +2515,9 @@ static inline void LoadLightlevelThinker(actionf_p1 thinker)
 	ht->sector = LoadSector(READUINT32(save_p));
 	ht->destlevel = READINT32(save_p);
 	ht->speed = READINT32(save_p);
-	ht->duration = READINT32(save_p);
+	ht->ticbased = (boolean)READUINT8(save_p);
+	ht->timer = READINT32(save_p);
 	ht->interval = READUINT32(save_p);
-	ht->firsttic = (tic_t)READUINT32(save_p);
 	if (ht->sector)
 		ht->sector->lightingdata = ht;
 	P_AddThinker(&ht->thinker);
diff --git a/src/p_spec.h b/src/p_spec.h
index c2c4c8976..1e327c5f2 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -140,9 +140,9 @@ typedef struct
 	INT32 speed;       ///< Speed at which to change light level.
 
 	// Tic-based behavior
-	INT32 duration;    ///< If <0, do not use tic-based behavior. If 0, set instantly. If >0, fade lasts this duration.
+	boolean ticbased;  ///< Tic-based logic
+	INT32 timer;    ///< Tic-based timer
 	UINT32 interval;   ///< Interval to deduct light level
-	tic_t firsttic;    ///< First gametic to count from
 } lightlevel_t;
 
 #define GLOWSPEED 8

From 88d9da79e6433164336663f3df6f66b64662d9ba Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Mon, 10 Sep 2018 12:00:44 -0400
Subject: [PATCH 7/9] Move tic-based to EFFECT4

---
 src/p_spec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index 917ad68d4..aa3a81f23 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2781,7 +2781,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			P_FadeLight(line->tag,
 				(line->flags & ML_DONTPEGBOTTOM) ? max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 255), 0) : line->frontsector->lightlevel,
 				(line->flags & ML_DONTPEGBOTTOM) ? max(sides[line->sidenum[0]].rowoffset>>FRACBITS, 0) : P_AproxDistance(line->dx, line->dy)>>FRACBITS,
-				(line->flags & ML_EFFECT5));
+				(line->flags & ML_EFFECT4));
 			break;
 
 		case 421: // Stop lighting effect in tagged sectors

From 36923ae7b0ae7da2459af0ecee5cf231b0419369 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Tue, 11 Sep 2018 10:05:25 -0400
Subject: [PATCH 8/9] Use percentage calc instead of interval decrement for
 tic-based timing

---
 src/p_lights.c | 32 ++++++++++++++++----------------
 src/p_saveg.c  | 12 ++++++------
 src/p_spec.h   |  8 ++++----
 3 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/src/p_lights.c b/src/p_lights.c
index 77d05dad3..95171155e 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -352,19 +352,17 @@ void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean
 	P_AddThinker(&ll->thinker); // add thinker
 
 	ll->sector = sector;
+	ll->sourcelevel = sector->lightlevel;
 	ll->destlevel = destvalue;
 
 	if (ticbased)
 	{
-		ll->ticbased = ticbased;
-		ll->timer = abs(speed);
-		ll->speed = FixedFloor(FixedDiv(destvalue - sector->lightlevel, ll->timer))/FRACUNIT;
-		if (!ll->speed)
-			ll->speed = (destvalue < sector->lightlevel) ? -1 : 1;
-		ll->interval = max(FixedFloor(FixedDiv(ll->timer, abs(destvalue - sector->lightlevel)))/FRACUNIT, 1);
+		ll->ticbased = true;
+		ll->timer = ll->speed = abs(speed); // use ll->speed for total duration
 	}
 	else
 	{
+		ll->ticbased = false;
 		ll->timer = -1;
 		ll->speed = abs(speed);
 	}
@@ -389,15 +387,17 @@ void T_LightFade(lightlevel_t *ll)
 	{
 		if (--ll->timer <= 0)
 		{
-			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+			ll->sector->lightlevel = ll->destlevel; // set to dest lightlevel
 			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
 		}
-		else if (!(ll->timer % ll->interval))
+		else
 		{
-			if (ll->speed < 0)
-				ll->sector->lightlevel = max(ll->sector->lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
-			else
-				ll->sector->lightlevel = min(ll->sector->lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
+			INT16 delta = abs(ll->destlevel - ll->sourcelevel);
+			fixed_t factor = min(FixedDiv(ll->speed - ll->timer, ll->speed), 1*FRACUNIT);
+			if (ll->destlevel < ll->sourcelevel)
+				ll->sector->lightlevel = max(min(ll->sector->lightlevel, ll->sourcelevel - (INT16)FixedMul(delta, factor)), ll->destlevel);
+			else if (ll->destlevel > ll->sourcelevel)
+				ll->sector->lightlevel = min(max(ll->sector->lightlevel, ll->sourcelevel + (INT16)FixedMul(delta, factor)), ll->destlevel);
 		}
 		return;
 	}
@@ -408,12 +408,12 @@ void T_LightFade(lightlevel_t *ll)
 		if (ll->sector->lightlevel + ll->speed >= ll->destlevel)
 		{
 			// stop changing light level
-			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+			ll->sector->lightlevel = ll->destlevel; // set to dest lightlevel
 
 			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
 		}
 		else
-			ll->sector->lightlevel = (INT16)(ll->sector->lightlevel + (INT16)ll->speed); // move lightlevel
+			ll->sector->lightlevel += ll->speed; // move lightlevel
 	}
 	else
 	{
@@ -421,11 +421,11 @@ void T_LightFade(lightlevel_t *ll)
 		if (ll->sector->lightlevel - ll->speed <= ll->destlevel)
 		{
 			// stop changing light level
-			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
+			ll->sector->lightlevel = ll->destlevel; // set to dest lightlevel
 
 			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
 		}
 		else
-			ll->sector->lightlevel = (INT16)(ll->sector->lightlevel - (INT16)ll->speed); // move lightlevel
+			ll->sector->lightlevel -= ll->speed; // move lightlevel
 	}
 }
diff --git a/src/p_saveg.c b/src/p_saveg.c
index eefee072d..e9f07de4c 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1537,11 +1537,11 @@ static void SaveLightlevelThinker(const thinker_t *th, const UINT8 type)
 	const lightlevel_t *ht = (const void *)th;
 	WRITEUINT8(save_p, type);
 	WRITEUINT32(save_p, SaveSector(ht->sector));
-	WRITEINT32(save_p, ht->destlevel);
-	WRITEINT32(save_p, ht->speed);
+	WRITEINT16(save_p, ht->sourcelevel);
+	WRITEINT16(save_p, ht->destlevel);
+	WRITEINT16(save_p, ht->speed);
 	WRITEUINT8(save_p, (UINT8)ht->ticbased);
 	WRITEINT32(save_p, ht->timer);
-	WRITEUINT32(save_p, ht->interval);
 }
 
 //
@@ -2513,11 +2513,11 @@ static inline void LoadLightlevelThinker(actionf_p1 thinker)
 	lightlevel_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
 	ht->thinker.function.acp1 = thinker;
 	ht->sector = LoadSector(READUINT32(save_p));
-	ht->destlevel = READINT32(save_p);
-	ht->speed = READINT32(save_p);
+	ht->sourcelevel = READINT16(save_p);
+	ht->destlevel = READINT16(save_p);
+	ht->speed = READINT16(save_p);
 	ht->ticbased = (boolean)READUINT8(save_p);
 	ht->timer = READINT32(save_p);
-	ht->interval = READUINT32(save_p);
 	if (ht->sector)
 		ht->sector->lightingdata = ht;
 	P_AddThinker(&ht->thinker);
diff --git a/src/p_spec.h b/src/p_spec.h
index 1e327c5f2..69087d6c4 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -136,13 +136,13 @@ typedef struct
 {
 	thinker_t thinker; ///< Thinker in use for the effect.
 	sector_t *sector;  ///< Sector where action is taking place.
-	INT32 destlevel;   ///< Light level we're fading to.
-	INT32 speed;       ///< Speed at which to change light level.
+	INT16 sourcelevel; ///< Light level we're fading from.
+	INT16 destlevel;   ///< Light level we're fading to.
+	INT16 speed;       ///< Speed at which to change light level. OR: Tic-based duration
 
 	// Tic-based behavior
 	boolean ticbased;  ///< Tic-based logic
-	INT32 timer;    ///< Tic-based timer
-	UINT32 interval;   ///< Interval to deduct light level
+	INT32 timer;       ///< Tic-based timer
 } lightlevel_t;
 
 #define GLOWSPEED 8

From 41fe080a68b36df41d0a6161e570e59ad0bb037a Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Wed, 12 Sep 2018 21:32:12 -0400
Subject: [PATCH 9/9] 420: Allow Back Y Offset for timing parameter

---
 src/p_spec.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index aa3a81f23..eed083cd9 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2779,8 +2779,14 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 
 		case 420: // Fade light levels in tagged sectors to new value
 			P_FadeLight(line->tag,
-				(line->flags & ML_DONTPEGBOTTOM) ? max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 255), 0) : line->frontsector->lightlevel,
-				(line->flags & ML_DONTPEGBOTTOM) ? max(sides[line->sidenum[0]].rowoffset>>FRACBITS, 0) : P_AproxDistance(line->dx, line->dy)>>FRACBITS,
+				(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));
 			break;