diff --git a/src/p_saveg.c b/src/p_saveg.c
index eeb05d328..c1af4167b 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1566,14 +1566,16 @@ static void SaveFadeThinker(const thinker_t *th, const UINT8 type)
 {
 	const fade_t *ht = (const void *)th;
 	WRITEUINT8(save_p, type);
-	WRITEINT32(save_p, ht->affectee);
+	WRITEINT32(save_p, ht->sectornum);
+	WRITEINT32(save_p, ht->ffloornum);
+	WRITEINT32(save_p, ht->alpha);
 	WRITEINT16(save_p, ht->destvalue);
 	WRITEINT16(save_p, ht->speed);
 	WRITEUINT8(save_p, ht->doexists);
 	WRITEUINT8(save_p, ht->dotranslucent);
-	WRITEUINT8(save_p, ht->dosolid);
-	WRITEUINT8(save_p, ht->dospawnflags);
+	WRITEUINT8(save_p, ht->docollision);
 	WRITEUINT8(save_p, ht->doghostfade);
+	WRITEUINT8(save_p, ht->exactalpha);
 }
 
 //
@@ -2568,18 +2570,33 @@ static inline void LoadFadeThinker(actionf_p1 thinker)
 {
 	fade_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
 	ht->thinker.function.acp1 = thinker;
-	ht->affectee = READINT32(save_p);
+	ht->sectornum = READINT32(save_p);
+	ht->ffloornum = READINT32(save_p);
+	ht->alpha = READINT32(save_p);
 	ht->destvalue = READINT16(save_p);
 	ht->speed = READINT16(save_p);
 	ht->doexists = READUINT8(save_p);
 	ht->dotranslucent = READUINT8(save_p);
-	ht->dosolid = READUINT8(save_p);
-	ht->dospawnflags = READUINT8(save_p);
+	ht->docollision = READUINT8(save_p);
 	ht->doghostfade = READUINT8(save_p);
+	ht->exactalpha = READUINT8(save_p);
 
-	line_t *ffloorline = LoadLine(ht->affectee);
-	if (ffloorline)
-		ffloorline->fadingdata = ht;
+	sector_t *ss = LoadSector(ht->sectornum);
+	if (ss)
+	{
+		size_t j = 0; // ss->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc
+		ffloor_t *rover;
+		for (rover = ss->ffloors; rover; rover = rover->next)
+		{
+			if (j == ht->ffloornum)
+			{
+				ht->rover = rover;
+				rover->fadingdata = ht;
+				break;
+			}
+			j++;
+		}
+	}
 
 	P_AddThinker(&ht->thinker);
 }
@@ -2770,7 +2787,7 @@ static void P_NetUnArchiveThinkers(void)
 	// clear sector thinker pointers so they don't point to non-existant thinkers for all of eternity
 	for (i = 0; i < numsectors; i++)
 	{
-		sectors[i].floordata = sectors[i].ceilingdata = sectors[i].lightingdata = sectors[i].fadingdata = NULL;
+		sectors[i].floordata = sectors[i].ceilingdata = sectors[i].lightingdata = NULL;
 	}
 
 	// read in saved thinkers
diff --git a/src/p_setup.c b/src/p_setup.c
index 67dbb8db7..c62f281b3 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -693,7 +693,6 @@ static void P_LoadRawSectors(UINT8 *data, size_t i)
 		ss->floordata = NULL;
 		ss->ceilingdata = NULL;
 		ss->lightingdata = NULL;
-		ss->fadingdata = NULL;
 
 		ss->linecount = 0;
 		ss->lines = NULL;
diff --git a/src/p_spec.c b/src/p_spec.c
index 89caf41f3..50cf049ff 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -103,14 +103,13 @@ static void P_SpawnFriction(void);
 static void P_SpawnPushers(void);
 static void Add_Pusher(pushertype_e type, fixed_t x_mag, fixed_t y_mag, mobj_t *source, INT32 affectee, INT32 referrer, INT32 exclusive, INT32 slider); //SoM: 3/9/2000
 static void Add_MasterDisappearer(tic_t appeartime, tic_t disappeartime, tic_t offset, INT32 line, INT32 sourceline);
-static void P_ResetFading(line_t *line, fade_t *data);
-#define P_RemoveFading(l) P_ResetFading(l, NULL);
-static INT32 P_FindFakeFloorsDoAlpha(INT16 destvalue, INT16 speed,
-	boolean doexists, boolean dotranslucent, boolean dosolid, boolean dospawnflags,
-	boolean doghostfade, INT32 line);
-static void P_AddMasterFader(INT16 destvalue, INT16 speed,
-	boolean doexists, boolean dotranslucent, boolean dosolid, boolean dospawnflags,
-	boolean doghostfade, INT32 line);
+static void P_ResetFakeFloorFader(ffloor_t *rover, fade_t *data);
+#define P_RemoveFakeFloorFader(l) P_ResetFakeFloorFader(l, NULL);
+static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 destvalue, INT16 speed,
+	boolean doexists, boolean dotranslucent, boolean docollision, boolean doghostfade, boolean exactalpha);
+static void P_AddFakeFloorFader(ffloor_t *rover, size_t sectornum, size_t ffloornum,
+	INT16 destvalue, INT16 speed,
+	boolean doexists, boolean dotranslucent, boolean docollision, boolean doghostfade, boolean exactalpha);
 static void P_AddBlockThinker(sector_t *sec, line_t *sourceline);
 static void P_AddFloatThinker(sector_t *sec, INT32 tag, line_t *sourceline);
 //static void P_AddBridgeThinker(line_t *sourceline, sector_t *sec);
@@ -3109,6 +3108,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS);
 			sector_t *sec; // Sector that the FOF is visible in
 			ffloor_t *rover; // FOF that we are going to crumble
+			size_t j = 0; // sec->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc
 
 			for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;)
 			{
@@ -3124,6 +3124,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				{
 					if (rover->master->frontsector->tag == foftag)
 						break;
+					j++;
 				}
 
 				if (!rover)
@@ -3133,23 +3134,23 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				}
 
 				if (speed > 0)
-					P_AddMasterFader(destvalue, speed,
-						(line->flags & ML_BLOCKMONSTERS),	// handle FF_EXISTS
-						!(line->flags & ML_NOCLIMB),		// do not handle FF_TRANSLUCENT
-						(line->flags & ML_BOUNCY), 			// handle FF_SOLID
-						(line->flags & ML_EFFECT1), 		// handle spawnflags
-						(line->flags & ML_EFFECT2), 		// enable flags on fade-in finish only
-						(INT32)(rover->master-lines));
+					P_AddFakeFloorFader(rover, secnum, j,
+						destvalue, speed,
+						!(line->flags & ML_BLOCKMONSTERS),  // do not handle FF_EXISTS
+						!(line->flags & ML_NOCLIMB),        // do not handle FF_TRANSLUCENT
+						!(line->flags & ML_BOUNCY),         // do not handle interactive flags
+						(line->flags & ML_EFFECT1),         // do ghost fade (no interactive flags during fade)
+						(line->flags & ML_TFERLINE));       // use exact alpha values (for opengl)
 				else
 				{
-					P_RemoveFading(&lines[(INT32)(sectors[s].lines[j]-lines)]);
-					P_FindFakeFloorsDoAlpha(destvalue, 0,   // set alpha immediately
-						(line->flags & ML_BLOCKMONSTERS),	// handle FF_EXISTS
-						!(line->flags & ML_NOCLIMB),		// do not handle FF_TRANSLUCENT
-						(line->flags & ML_BOUNCY), 			// handle FF_SOLID
-						(line->flags & ML_EFFECT1), 		// handle spawnflags
-						(line->flags & ML_EFFECT2), 		// enable flags on fade-in finish only
-						(INT32)(sectors[s].lines[j]-lines));
+					P_RemoveFakeFloorFader(rover);
+					P_FadeFakeFloor(rover,
+						destvalue, 0, // set alpha immediately
+						!(line->flags & ML_BLOCKMONSTERS),  // do not handle FF_EXISTS
+						!(line->flags & ML_NOCLIMB),        // do not handle FF_TRANSLUCENT
+						!(line->flags & ML_BOUNCY),         // do not handle interactive flags
+						(line->flags & ML_EFFECT1),         // do ghost fade (no interactive flags during fade)
+						(line->flags & ML_TFERLINE));       // use exact alpha values (for opengl)
 				}
 			}
 			break;
@@ -3184,7 +3185,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					return;
 				}
 
-				P_RemoveFading(&lines[(INT32)(sectors[s].lines[j]-lines)]);
+				P_RemoveFakeFloorFader(rover);
 			}
 			break;
 		}
@@ -5034,7 +5035,7 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
 	ffloor->spawnflags = ffloor->flags = flags;
 	ffloor->master = master;
 	ffloor->norender = INFTICS;
-
+	ffloor->fadingdata = NULL;
 
 	// Scan the thinkers to check for special conditions applying to this FOF.
 	// If we have thinkers sorted by sector, just check the relevant ones;
@@ -7195,224 +7196,212 @@ void T_Disappear(disappear_t *d)
  * \param line	line to search for target faders
  * \param data	pointer to set new fadingdata to. Can be NULL to erase.
  */
-static void P_ResetFading(line_t *line, fade_t *data)
+static void P_ResetFakeFloorFader(ffloor_t *rover, fade_t *data)
 {
-	ffloor_t *rover;
-	register INT32 s;
-
 	// find any existing thinkers and remove them, then replace with new data
-	for (s = -1; (s = P_FindSectorFromLineTag(line, s)) >= 0 ;)
+	if(((fade_t *)rover->fadingdata) != data)
 	{
-		for (rover = sectors[s].ffloors; rover; rover = rover->next)
-		{
-			if (rover->master != line)
-				continue;
+		if(&((fade_t *)rover->fadingdata)->thinker)
+			P_RemoveThinker(&((fade_t *)rover->fadingdata)->thinker);
 
-			if(((fade_t *)rover->master->frontsector->fadingdata) != data)
-			{
-				if(&((fade_t *)rover->master->frontsector->fadingdata)->thinker)
-					P_RemoveThinker(&((fade_t *)rover->master->frontsector->fadingdata)->thinker);
-
-				rover->master->frontsector->fadingdata = data;
-			}
-		}
+		rover->fadingdata = data;
 	}
 }
 
-static boolean P_DoFakeFloorAlpha(ffloor_t *rover, INT16 destvalue, INT16 speed,
-	boolean doexists, boolean dotranslucent, boolean dosolid, boolean dospawnflags,
-	boolean doghostfade)
+static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 destvalue, INT16 speed,
+	boolean doexists, boolean dotranslucent, boolean docollision, boolean doghostfade, boolean exactalpha)
 {
-	boolean result = false;
+	boolean stillfading = false;
+	INT32 alpha;
+	fade_t *fadingdata = (fade_t *)rover->fadingdata;
 
-	if (rover->alpha == destvalue)
-		return result;
-	// fade out
-	else if (rover->alpha > destvalue)
+	if (fadingdata)
+		alpha = fadingdata->alpha;
+	else
+		alpha = rover->alpha;
+
+	// routines specific to fade in and fade out
+	if (alpha == destvalue)
+		return stillfading;
+	else if (alpha > destvalue) // fade out
 	{
 		// finish fading out
-		if (speed < 1 || rover->alpha - speed <= destvalue + speed)
+		if (speed < 1 || alpha - speed <= destvalue + speed)
 		{
-			rover->alpha = destvalue;
+			alpha = destvalue;
 
-			if (doexists)
+			if (docollision)
 			{
-				if (rover->alpha <= 1)
-					rover->flags &= ~FF_EXISTS;
-				else
-					rover->flags |= FF_EXISTS;
-			}
-
-			if (dosolid
-				&& !(rover->flags & FF_SWIMMABLE)
-				&& !(rover->flags & FF_QUICKSAND))
-				rover->flags &= ~FF_SOLID; // make intangible at end of fade-out
-
-			if (dotranslucent)
-			{
-				if (rover->alpha >= 256)
-				{
-					rover->flags |= FF_CUTLEVEL;
-					rover->flags &= ~(FF_TRANSLUCENT|FF_EXTRA|FF_CUTEXTRA);
-				}
-				else
-				{
-					rover->flags |= FF_TRANSLUCENT|FF_EXTRA|FF_CUTEXTRA;
-					rover->flags &= ~FF_CUTLEVEL;
-				}
-
-				if (rover->flags & FF_SOLID)
-					rover->flags &= ~FF_CUTSPRITES;
-				else
-					rover->flags |= FF_CUTSPRITES;
+				if (rover->spawnflags & FF_SOLID)
+					rover->flags &= ~FF_SOLID;
+				if (rover->spawnflags & FF_SWIMMABLE)
+					rover->flags &= ~FF_SWIMMABLE;
+				if (rover->spawnflags & FF_QUICKSAND)
+					rover->flags &= ~FF_QUICKSAND;
+				if (rover->spawnflags & FF_BUSTUP)
+					rover->flags &= ~FF_BUSTUP;
 			}
 		}
 		else // continue fading out
 		{
-			rover->alpha -= speed;
-
-			if (doexists)
-				rover->flags |= FF_EXISTS;
-
-			if (dosolid
-				&& !(rover->flags & FF_SWIMMABLE)
-				&& !(rover->flags & FF_QUICKSAND))
-				rover->flags |= FF_SOLID; // keep solid during fade
-
-			if (dotranslucent)
-			{
-				rover->flags |= FF_TRANSLUCENT|FF_EXTRA|FF_CUTEXTRA;
-				rover->flags &= ~FF_CUTLEVEL;
-
-				if (rover->flags & FF_SOLID)
-					rover->flags &= ~FF_CUTSPRITES;
-				else
-					rover->flags |= FF_CUTSPRITES;
-			}
-
-			result = true;
+			alpha -= speed;
+			stillfading = true;
 		}
 	}
 	else // fade in
 	{
 		// finish fading in
-		if (speed < 1 || rover->alpha + speed >= destvalue - speed)
+		if (speed < 1 || alpha + speed >= destvalue - speed)
 		{
-			rover->alpha = destvalue;
+			alpha = destvalue;
 
-			if (doexists)
+			if (docollision)
 			{
-				if (rover->alpha <= 1)
-					rover->flags &= ~FF_EXISTS;
-				else
-					rover->flags |= FF_EXISTS;
-			}
-
-			if (dosolid
-				&& !(rover->flags & FF_SWIMMABLE)
-				&& !(rover->flags & FF_QUICKSAND))
-				rover->flags |= FF_SOLID; // make solid at end of fade-in
-
-			if (dotranslucent)
-			{
-				if (rover->alpha >= 256)
-				{
-					rover->flags |= FF_CUTLEVEL;
-					rover->flags &= ~(FF_TRANSLUCENT|FF_EXTRA|FF_CUTEXTRA);
-				}
-				else
-				{
-					rover->flags |= FF_TRANSLUCENT|FF_EXTRA|FF_CUTEXTRA;
-					rover->flags &= ~FF_CUTLEVEL;
-				}
-
-				if (rover->flags & FF_SOLID)
-					rover->flags &= ~FF_CUTSPRITES;
-				else
-					rover->flags |= FF_CUTSPRITES;
+				if (rover->spawnflags & FF_SOLID)
+					rover->flags |= FF_SOLID;
+				if (rover->spawnflags & FF_SWIMMABLE)
+					rover->flags |= FF_SWIMMABLE;
+				if (rover->spawnflags & FF_QUICKSAND)
+					rover->flags |= FF_QUICKSAND;
+				if (rover->spawnflags & FF_BUSTUP)
+					rover->flags |= FF_BUSTUP;
 			}
 		}
 		else // continue fading in
 		{
-			rover->alpha += speed;
-
-			if (doexists)
-				rover->flags |= FF_EXISTS;
-
-			if (dosolid
-				&& !(rover->flags & FF_SWIMMABLE)
-				&& !(rover->flags & FF_QUICKSAND))
-				rover->flags |= FF_SOLID; // keep solid during fade
-
-			if (dotranslucent)
-			{
-				rover->flags |= FF_TRANSLUCENT|FF_EXTRA|FF_CUTEXTRA;
-				rover->flags &= ~FF_CUTLEVEL;
-
-				if (rover->flags & FF_SOLID)
-					rover->flags &= ~FF_CUTSPRITES;
-				else
-					rover->flags |= FF_CUTSPRITES;
-			}
-
-			result = true;
+			alpha += speed;
+			stillfading = true;
 		}
 	}
 
-	return result;
-}
-
-static INT32 P_FindFakeFloorsDoAlpha(INT16 destvalue, INT16 speed,
-	boolean doexists, boolean dotranslucent, boolean dosolid, boolean dospawnflags,
-	boolean doghostfade, INT32 line)
-{
-	ffloor_t *rover;
-	register INT32 s;
-	INT32 affectedffloors = 0;
-
-	for (s = -1; (s = P_FindSectorFromLineTag(&lines[line], s)) >= 0 ;)
+	// routines common to both fade in and fade out
+	if (!stillfading)
 	{
-		for (rover = sectors[s].ffloors; rover; rover = rover->next)
+		if (doexists)
 		{
-			if (rover->master != &lines[line])
-				continue;
+			if (alpha <= 1)
+				rover->flags &= ~FF_EXISTS;
+			else
+				rover->flags |= FF_EXISTS;
+		}
 
-			affectedffloors += (INT32)P_DoFakeFloorAlpha(rover, destvalue, speed, doexists, dotranslucent, dosolid, dospawnflags, doghostfade);
+		if (dotranslucent)
+		{
+			if (alpha >= 256)
+			{
+				//rover->flags |= (FF_CUTLEVEL | FF_CUTEXTRA);
+				rover->flags &= ~FF_TRANSLUCENT;
+			}
+			else
+			{
+				rover->flags |= FF_TRANSLUCENT;
+				//rover->flags &= ~(FF_CUTLEVEL | FF_CUTEXTRA);
+			}
+		}
+	}
+	else
+	{
+		if (doexists)
+			rover->flags |= FF_EXISTS;
+
+		if (dotranslucent)
+		{
+			rover->flags |= FF_TRANSLUCENT;
+			//rover->flags &= ~(FF_CUTLEVEL | FF_CUTEXTRA);
+		}
+
+		if (docollision)
+		{
+			if (doghostfade) // remove collision flags during fade
+			{
+				if (rover->spawnflags & FF_SOLID)
+					rover->flags &= ~FF_SOLID;
+				if (rover->spawnflags & FF_SWIMMABLE)
+					rover->flags &= ~FF_SWIMMABLE;
+				if (rover->spawnflags & FF_QUICKSAND)
+					rover->flags &= ~FF_QUICKSAND;
+				if (rover->spawnflags & FF_BUSTUP)
+					rover->flags &= ~FF_BUSTUP;
+			}
+			else // keep collision during fade
+			{
+				if (rover->spawnflags & FF_SOLID)
+					rover->flags |= FF_SOLID;
+				if (rover->spawnflags & FF_SWIMMABLE)
+					rover->flags |= FF_SWIMMABLE;
+				if (rover->spawnflags & FF_QUICKSAND)
+					rover->flags |= FF_QUICKSAND;
+				if (rover->spawnflags & FF_BUSTUP)
+					rover->flags |= FF_BUSTUP;
+			}
 		}
 	}
 
-	return affectedffloors;
+	if (!stillfading || exactalpha)
+		rover->alpha = alpha;
+	else // clamp fadingdata->alpha to software's alpha levels
+	{
+		if (alpha < 12)
+			rover->alpha = destvalue < 12 ? destvalue : 1; // Don't even draw it
+		else if (alpha < 38)
+			rover->alpha = destvalue >= 12 && destvalue < 38 ? destvalue : 25;
+		else if (alpha < 64)
+			rover->alpha = destvalue >=38 && destvalue < 64 ? destvalue : 51;
+		else if (alpha < 89)
+			rover->alpha = destvalue >= 64 && destvalue < 89 ? destvalue : 76;
+		else if (alpha < 115)
+			rover->alpha = destvalue >= 89 && destvalue < 115 ? destvalue : 102;
+		else if (alpha < 140)
+			rover->alpha = destvalue >= 115 && destvalue < 140 ? destvalue : 128;
+		else if (alpha < 166)
+			rover->alpha = destvalue >= 140 && destvalue < 166 ? destvalue : 154;
+		else if (alpha < 192)
+			rover->alpha = destvalue >= 166 && destvalue < 192 ? destvalue : 179;
+		else if (alpha < 217)
+			rover->alpha = destvalue >= 192 && destvalue < 217 ? destvalue : 204;
+		else if (alpha < 243)
+			rover->alpha = destvalue >= 217 && destvalue < 243 ? destvalue : 230;
+		else // Opaque
+			rover->alpha = destvalue >= 243 ? destvalue : 256;
+	}
+
+	if (fadingdata)
+		fadingdata->alpha = alpha;
+
+	return stillfading;
 }
 
-/** Adds master fader thinker.
+/** Adds fake floor fader thinker.
   *
-  * \param destvalue	transparency value to fade to
-  * \param speed		speed to fade by
-  * \param doexists	handle FF_EXISTS
-  * \param dotranslucent	handle FF_TRANSLUCENT
-  * \param dosolid	handle FF_SOLID
-  * \param dospawnflags	handle spawnflags
-  * \param doghostfade	enable flags when fade-in is finished; never on fade-out
-  * \param line			line to target FOF
+  * \param destvalue    transparency value to fade to
+  * \param speed        speed to fade by
+  * \param doexists	    handle FF_EXISTS
+  * \param dotranslucent handle FF_TRANSLUCENT
+  * \param docollision handle interactive flags
+  * \param doghostfade  no interactive flags during fading
+  * \param exactalpha   use exact alpha values (opengl)
   */
-static void P_AddMasterFader(INT16 destvalue, INT16 speed,
-	boolean doexists, boolean dotranslucent, boolean dosolid, boolean dospawnflags,
-	boolean doghostfade, INT32 line)
+static void P_AddFakeFloorFader(ffloor_t *rover, size_t sectornum, size_t ffloornum,
+	INT16 destvalue, INT16 speed,
+	boolean doexists, boolean dotranslucent, boolean docollision, boolean doghostfade, boolean exactalpha)
 {
 	fade_t *d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL);
 
 	d->thinker.function.acp1 = (actionf_p1)T_Fade;
-	d->affectee = line;
+	d->rover = rover;
+	d->sectornum = (INT32)sectornum;
+	d->ffloornum = (INT32)ffloornum;
+	d->alpha = rover->alpha;
 	d->destvalue = max(1, min(256, destvalue)); // ffloor->alpha is 1-256
 	d->speed = max(1, speed); // minimum speed 1/tic // if speed < 1, alpha is set immediately in thinker
 	d->doexists = doexists;
 	d->dotranslucent = dotranslucent;
-	d->dosolid = dosolid;
-	d->dospawnflags = dospawnflags;
+	d->docollision = docollision;
 	d->doghostfade = doghostfade;
+	d->exactalpha = exactalpha;
 
 	// find any existing thinkers and remove them, then replace with new data
-	P_ResetFading(&lines[d->affectee], d);
+	P_ResetFakeFloorFader(rover, d);
 
 	P_AddThinker(&d->thinker);
 }
@@ -7420,17 +7409,12 @@ static void P_AddMasterFader(INT16 destvalue, INT16 speed,
 /** Makes a FOF fade
   *
   * \param d Fade thinker.
-  * \sa P_AddMasterFader
+  * \sa P_AddFakeFloorFader
   */
 void T_Fade(fade_t *d)
 {
-	INT32 affectedffloors = P_FindFakeFloorsDoAlpha(d->destvalue, d->speed,
-		d->doexists, d->dotranslucent, d->dosolid, d->dospawnflags,
-		d->doghostfade, d->affectee);
-
-	// no more ffloors to fade? remove myself
-	if (affectedffloors == 0)
-		P_RemoveFading(&lines[d->affectee]);
+	if (d->rover && !P_FadeFakeFloor(d->rover, d->destvalue, d->speed, d->doexists, d->dotranslucent, d->docollision, d->doghostfade, d->exactalpha))
+		P_RemoveFakeFloorFader(d->rover);
 }
 
 /*
diff --git a/src/p_spec.h b/src/p_spec.h
index 672b96f30..dc2ab0e6a 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -453,14 +453,17 @@ void T_Disappear(disappear_t *d);
 typedef struct
 {
 	thinker_t thinker;  ///< Thinker structure for effect.
-	INT32 affectee;     ///< Number of affected line
+	ffloor_t *rover;    ///< Target ffloor
+	INT32 sectornum;    ///< Number of ffloor target sector
+	INT32 ffloornum;    ///< Number of ffloor of target sector
+	INT32 alpha;        ///< Internal alpha counter
 	INT16 destvalue;    ///< Transparency value to fade to
 	INT16 speed;        ///< Speed to fade by
-	boolean doexists;      ///< Handle FF_EXISTS handling
-	boolean dotranslucent; ///< Handle FF_TRANSLUCENT handling
-	boolean dosolid;       ///< Handle FF_SOLID handling
-	boolean dospawnflags;  ///< Enable spawnflags handling
-	boolean doghostfade;  ///< Set flags only when fade-in is finished; never during fade-out
+	boolean doexists;   ///< Handle FF_EXISTS
+	boolean dotranslucent; ///< Handle FF_TRANSLUCENT
+	boolean docollision; ///< Handle interactive flags
+	boolean doghostfade; ///< No interactive flags during fading
+	boolean exactalpha; ///< Use exact alpha values (opengl)
 } fade_t;
 
 void T_Fade(fade_t *d);
diff --git a/src/r_defs.h b/src/r_defs.h
index b4c7d2112..ea5db3947 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -177,6 +177,8 @@ typedef struct ffloor_s
 	// these are saved for netgames, so do not let Lua touch these!
 	ffloortype_e spawnflags; // flags the 3D floor spawned with
 	INT32 spawnalpha; // alpha the 3D floor spawned with
+
+	void *fadingdata; // fading FOF thinker
 } ffloor_t;
 
 
@@ -308,7 +310,6 @@ typedef struct sector_s
 	void *floordata; // floor move thinker
 	void *ceilingdata; // ceiling move thinker
 	void *lightingdata; // lighting change thinker
-	void *fadingdata; // fading FOF thinker
 
 	// floor and ceiling texture offsets
 	fixed_t floor_xoffs, floor_yoffs;