diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 30f9c4073af9f114fe6a6e8a0cc5e78a3036a31b..5b8f1178d403dcddb1b84d5f44449ee2a691210d 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -2927,64 +2927,16 @@ static void HWR_Subsector(size_t num)
 	}
 
 	//SoM: 4/7/2000: Test to make Boom water work in Hardware mode.
-	gl_frontsector = R_FakeFlat(gl_frontsector, &tempsec, &floorlightlevel,
-								&ceilinglightlevel, false);
-	//FIXME: Use floorlightlevel and ceilinglightlevel insted of lightlevel.
+	gl_frontsector = R_FakeFlat(gl_frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, false);
 
 	floorcolormap = ceilingcolormap = gl_frontsector->extra_colormap;
 
-	// ------------------------------------------------------------------------
-	// sector lighting, DISABLED because it's done in HWR_StoreWallRange
-	// ------------------------------------------------------------------------
-	/// \todo store a RGBA instead of just intensity, allow coloured sector lighting
-	//light = (FUBYTE)(sub->sector->lightlevel & 0xFF) / 255.0f;
-	//gl_cursectorlight.red   = light;
-	//gl_cursectorlight.green = light;
-	//gl_cursectorlight.blue  = light;
-	//gl_cursectorlight.alpha = light;
-
-// ----- end special tricks -----
 	cullFloorHeight   = P_GetSectorFloorZAt  (gl_frontsector, viewx, viewy);
 	cullCeilingHeight = P_GetSectorCeilingZAt(gl_frontsector, viewx, viewy);
 	locFloorHeight    = P_GetSectorFloorZAt  (gl_frontsector, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y);
 	locCeilingHeight  = P_GetSectorCeilingZAt(gl_frontsector, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y);
 
-	if (gl_frontsector->ffloors)
-	{
-		boolean anyMoved = gl_frontsector->moved;
-
-		if (anyMoved == false)
-		{
-			for (rover = gl_frontsector->ffloors; rover; rover = rover->next)
-			{
-				sector_t *controlSec = &sectors[rover->secnum];
-				if (controlSec->moved == true)
-				{
-					anyMoved = true;
-					break;
-				}
-			}
-		}
-
-		if (anyMoved == true)
-		{
-			gl_frontsector->numlights = sub->sector->numlights = 0;
-			R_Prep3DFloors(gl_frontsector);
-			sub->sector->lightlist = gl_frontsector->lightlist;
-			sub->sector->numlights = gl_frontsector->numlights;
-			sub->sector->moved = gl_frontsector->moved = false;
-		}
-
-		light = R_GetPlaneLight(gl_frontsector, locFloorHeight, false);
-		if (gl_frontsector->floorlightsec == -1 && !gl_frontsector->floorlightabsolute)
-			floorlightlevel = max(0, min(255, *gl_frontsector->lightlist[light].lightlevel + gl_frontsector->floorlightlevel));
-		floorcolormap = *gl_frontsector->lightlist[light].extra_colormap;
-
-		light = R_GetPlaneLight(gl_frontsector, locCeilingHeight, false);
-		if (gl_frontsector->ceilinglightsec == -1 && !gl_frontsector->ceilinglightabsolute)
-			ceilinglightlevel = max(0, min(255, *gl_frontsector->lightlist[light].lightlevel + gl_frontsector->ceilinglightlevel));
-		ceilingcolormap = *gl_frontsector->lightlist[light].extra_colormap;
-	}
+	R_CheckSectorLightLists(sub->sector, gl_frontsector, &floorlightlevel, &ceilinglightlevel, &floorcolormap, &ceilingcolormap);
 
 	sub->sector->extra_colormap = gl_frontsector->extra_colormap;
 
diff --git a/src/p_saveg.c b/src/p_saveg.c
index faecd13770b3d81b992017af51b4b663487aa50d..b3ac59e8186b7fe06e11ec58125c33959979b1d8 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -41,20 +41,19 @@ UINT8 *save_p;
 
 // Block UINT32s to attempt to ensure that the correct data is
 // being sent and received
-#define ARCHIVEBLOCK_MISC     0x7FEEDEED
-#define ARCHIVEBLOCK_PLAYERS  0x7F448008
-#define ARCHIVEBLOCK_WORLD    0x7F8C08C0
-#define ARCHIVEBLOCK_POBJS    0x7F928546
-#define ARCHIVEBLOCK_THINKERS 0x7F37037C
-#define ARCHIVEBLOCK_SPECIALS 0x7F228378
-#define ARCHIVEBLOCK_EMBLEMS  0x7F4A5445
+#define ARCHIVEBLOCK_MISC       0x7FEEDEED
+#define ARCHIVEBLOCK_PLAYERS    0x7F448008
+#define ARCHIVEBLOCK_WORLD      0x7F8C08C0
+#define ARCHIVEBLOCK_POBJS      0x7F928546
+#define ARCHIVEBLOCK_THINKERS   0x7F37037C
+#define ARCHIVEBLOCK_SPECIALS   0x7F228378
+#define ARCHIVEBLOCK_EMBLEMS    0x7F4A5445
+#define ARCHIVEBLOCK_SECPORTALS 0x7FBE34C9
 
 // Note: This cannot be bigger
 // than an UINT16
 typedef enum
 {
-//	RFLAGPOINT = 0x01,
-//	BFLAGPOINT = 0x02,
 	CAPSULE    = 0x04,
 	AWAYVIEW   = 0x08,
 	FIRSTAXIS  = 0x10,
@@ -853,20 +852,22 @@ static void P_NetUnArchiveWaypoints(void)
 #define SD_DIFF3     0x80
 
 // diff3 flags
-#define SD_TAGLIST   0x01
-#define SD_COLORMAP  0x02
+#define SD_TAGLIST      0x01
+#define SD_COLORMAP     0x02
 #define SD_CRUMBLESTATE 0x04
-#define SD_FLOORLIGHT 0x08
-#define SD_CEILLIGHT 0x10
-#define SD_FLAG      0x20
-#define SD_SPECIALFLAG 0x40
-#define SD_DIFF4     0x80
+#define SD_FLOORLIGHT   0x08
+#define SD_CEILLIGHT    0x10
+#define SD_FLAG         0x20
+#define SD_SPECIALFLAG  0x40
+#define SD_DIFF4        0x80
 
 //diff4 flags
-#define SD_DAMAGETYPE 0x01
-#define SD_TRIGGERTAG 0x02
-#define SD_TRIGGERER 0x04
-#define SD_GRAVITY   0x08
+#define SD_DAMAGETYPE  0x01
+#define SD_TRIGGERTAG  0x02
+#define SD_TRIGGERER   0x04
+#define SD_GRAVITY     0x08
+#define SD_FLOORPORTAL 0x10
+#define SD_CEILPORTAL  0x20
 
 #define LD_FLAG     0x01
 #define LD_SPECIAL  0x02
@@ -1064,6 +1065,10 @@ static void ArchiveSectors(void)
 			diff4 |= SD_TRIGGERER;
 		if (ss->gravity != spawnss->gravity)
 			diff4 |= SD_GRAVITY;
+		if (ss->portal_floor != spawnss->portal_floor)
+			diff4 |= SD_FLOORPORTAL;
+		if (ss->portal_ceiling != spawnss->portal_ceiling)
+			diff4 |= SD_CEILPORTAL;
 
 		if (ss->ffloors && CheckFFloorDiff(ss))
 			diff |= SD_FFLOORS;
@@ -1145,6 +1150,10 @@ static void ArchiveSectors(void)
 				WRITEUINT8(save_p, ss->triggerer);
 			if (diff4 & SD_GRAVITY)
 				WRITEFIXED(save_p, ss->gravity);
+			if (diff4 & SD_FLOORPORTAL)
+				WRITEUINT32(save_p, ss->portal_floor);
+			if (diff4 & SD_CEILPORTAL)
+				WRITEUINT32(save_p, ss->portal_ceiling);
 			if (diff & SD_FFLOORS)
 				ArchiveFFloors(ss);
 		}
@@ -1265,6 +1274,10 @@ static void UnArchiveSectors(void)
 			sectors[i].triggerer = READUINT8(save_p);
 		if (diff4 & SD_GRAVITY)
 			sectors[i].gravity = READFIXED(save_p);
+		if (diff4 & SD_FLOORPORTAL)
+			sectors[i].portal_floor = READUINT32(save_p);
+		if (diff4 & SD_CEILPORTAL)
+			sectors[i].portal_ceiling = READUINT32(save_p);
 
 		if (diff & SD_FFLOORS)
 			UnArchiveFFloors(&sectors[i]);
@@ -4735,6 +4748,82 @@ static inline void P_NetUnArchiveEmblems(void)
 	}
 }
 
+static void P_NetArchiveSectorPortals(void)
+{
+	WRITEUINT32(save_p, ARCHIVEBLOCK_SECPORTALS);
+
+	WRITEUINT32(save_p, secportalcount);
+
+	for (size_t i = 0; i < secportalcount; i++)
+	{
+		UINT8 type = secportals[i].type;
+
+		WRITEUINT8(save_p, type);
+
+		switch (type)
+		{
+		case SECPORTAL_LINE:
+			WRITEUINT32(save_p, SaveLine(secportals[i].line.start));
+			WRITEUINT32(save_p, SaveLine(secportals[i].line.dest));
+			break;
+		case SECPORTAL_PLANE:
+		case SECPORTAL_HORIZON:
+		case SECPORTAL_FLOOR:
+		case SECPORTAL_CEILING:
+			WRITEUINT32(save_p, SaveSector(secportals[i].sector));
+			break;
+		case SECPORTAL_OBJECT:
+			if (secportals[i].mobj && !P_MobjWasRemoved(secportals[i].mobj))
+				SaveMobjnum(secportals[i].mobj);
+			else
+				WRITEUINT32(save_p, 0);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void P_NetUnArchiveSectorPortals(void)
+{
+	if (READUINT32(save_p) != ARCHIVEBLOCK_SECPORTALS)
+		I_Error("Bad $$$.sav at archive block Secportals");
+
+	Z_Free(secportals);
+	P_InitSectorPortals();
+
+	UINT32 count = READUINT32(save_p);
+
+	for (UINT32 i = 0; i < count; i++)
+	{
+		UINT32 id = P_NewSectorPortal();
+
+		sectorportal_t *secportal = &secportals[id];
+
+		secportal->type = READUINT8(save_p);
+
+		switch (secportal->type)
+		{
+		case SECPORTAL_LINE:
+			secportal->line.start = LoadLine(READUINT32(save_p));
+			secportal->line.dest = LoadLine(READUINT32(save_p));
+			break;
+		case SECPORTAL_PLANE:
+		case SECPORTAL_HORIZON:
+		case SECPORTAL_FLOOR:
+		case SECPORTAL_CEILING:
+			secportal->sector = LoadSector(READUINT32(save_p));
+			break;
+		case SECPORTAL_OBJECT:
+			id = READUINT32(save_p);
+			secportal->mobj = (id == 0) ? NULL : P_FindNewPosition(id);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
 static inline void P_ArchiveLuabanksAndConsistency(void)
 {
 	UINT8 i, banksinuse = NUM_LUABANKS;
@@ -4821,6 +4910,7 @@ void P_SaveNetGame(boolean resending)
 		P_NetArchiveSpecials();
 		P_NetArchiveColormaps();
 		P_NetArchiveWaypoints();
+		P_NetArchiveSectorPortals();
 	}
 	LUA_Archive();
 
@@ -4861,6 +4951,7 @@ boolean P_LoadNetGame(boolean reloading)
 		P_NetUnArchiveSpecials();
 		P_NetUnArchiveColormaps();
 		P_NetUnArchiveWaypoints();
+		P_NetUnArchiveSectorPortals();
 		P_RelinkPointers();
 		P_FinishMobjs();
 	}
diff --git a/src/p_spec.c b/src/p_spec.c
index e76968b8abd53e9e0a139151d893836b6768b015..d6b7b18b6f6ca32fc2c361a5d3380e1810285556 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -6209,6 +6209,8 @@ UINT32 P_NewSectorPortal(void)
 		secportals = Z_Realloc(secportals, secportalcapacity * sizeof(sectorportal_t), PU_LEVEL, NULL);
 	}
 
+	secportals[i].type = SECPORTAL_NONE;
+
 	return (UINT32)i;
 }
 
@@ -6536,11 +6538,13 @@ void P_SpawnSpecials(boolean fromnetsave)
 					{
 						sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(lines[i].frontsector);
 						floorportal->type = type;
+						floorportal->sector = lines[i].frontsector;
 					}
 					if (ceiling)
 					{
 						sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(lines[i].frontsector);
 						ceilportal->type = type;
+						ceilportal->sector = lines[i].frontsector;
 					}
 					break;
 				}
diff --git a/src/r_bsp.c b/src/r_bsp.c
index 0486cd699501ec603d28b239bc4f94b1386999d6..91b4fdbc0d8c39f180a6a51f97f71e6812ea7419 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -847,51 +847,6 @@ static void R_AddPolyObjects(subsector_t *sub)
 
 drawseg_t *firstseg;
 
-static void R_CheckSectorLightLists(sector_t *sector, sector_t *fakeflat, INT32 *floorlightlevel, INT32 *ceilinglightlevel, extracolormap_t **floorcolormap, extracolormap_t **ceilingcolormap)
-{
-	// Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps.
-	if (fakeflat->ffloors)
-	{
-		fixed_t floorcenterz   = P_GetSectorFloorZAt  (fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y);
-		fixed_t ceilingcenterz = P_GetSectorCeilingZAt(fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y);
-
-		boolean anyMoved = fakeflat->moved;
-
-		if (anyMoved == false)
-		{
-			for (ffloor_t *rover = fakeflat->ffloors; rover; rover = rover->next)
-			{
-				sector_t *controlSec = &sectors[rover->secnum];
-
-				if (controlSec->moved == true)
-				{
-					anyMoved = true;
-					break;
-				}
-			}
-		}
-
-		if (anyMoved == true)
-		{
-			fakeflat->numlights = sector->numlights = 0;
-			R_Prep3DFloors(fakeflat);
-			sector->lightlist = fakeflat->lightlist;
-			sector->numlights = fakeflat->numlights;
-			sector->moved = fakeflat->moved = false;
-		}
-
-		INT32 light = R_GetPlaneLight(fakeflat, floorcenterz, false);
-		if (fakeflat->floorlightsec == -1 && !fakeflat->floorlightabsolute)
-			*floorlightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->floorlightlevel));
-		*floorcolormap = *fakeflat->lightlist[light].extra_colormap;
-
-		light = R_GetPlaneLight(fakeflat, ceilingcenterz, false);
-		if (fakeflat->ceilinglightsec == -1 && !fakeflat->ceilinglightabsolute)
-			*ceilinglightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->ceilinglightlevel));
-		*ceilingcolormap = *fakeflat->lightlist[light].extra_colormap;
-	}
-}
-
 static void R_Subsector(size_t num)
 {
 	INT32 count, floorlightlevel, ceilinglightlevel, light;
@@ -1098,18 +1053,18 @@ static void R_Subsector(size_t num)
 		}
 	}
 
-   // killough 9/18/98: Fix underwater slowdown, by passing real sector
-   // instead of fake one. Improve sprite lighting by basing sprite
-   // lightlevels on floor & ceiling lightlevels in the surrounding area.
-   //
-   // 10/98 killough:
-   //
-   // NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!!
-   // That is part of the 242 effect!!!  If you simply pass sub->sector to
-   // the old code you will not get correct lighting for underwater sprites!!!
-   // Either you must pass the fake sector and handle validcount here, on the
-   // real sector, or you must account for the lighting in some other way,
-   // like passing it as an argument.
+	// killough 9/18/98: Fix underwater slowdown, by passing real sector
+	// instead of fake one. Improve sprite lighting by basing sprite
+	// lightlevels on floor & ceiling lightlevels in the surrounding area.
+	//
+	// 10/98 killough:
+	//
+	// NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!!
+	// That is part of the 242 effect!!!  If you simply pass sub->sector to
+	// the old code you will not get correct lighting for underwater sprites!!!
+	// Either you must pass the fake sector and handle validcount here, on the
+	// real sector, or you must account for the lighting in some other way,
+	// like passing it as an argument.
 	R_AddSprites(sub->sector, (floorlightlevel+ceilinglightlevel)/2);
 
 	firstseg = NULL;
@@ -1120,7 +1075,6 @@ static void R_Subsector(size_t num)
 
 	while (count--)
 	{
-//		CONS_Debug(DBG_GAMELOGIC, "Adding normal line %d...(%d)\n", line->linedef-lines, leveltime);
 		if (!line->glseg && !line->polyseg) // ignore segs that belong to polyobjects
 			R_AddLine(line);
 		line++;
@@ -1128,6 +1082,51 @@ static void R_Subsector(size_t num)
 	}
 }
 
+void R_CheckSectorLightLists(sector_t *sector, sector_t *fakeflat, INT32 *floorlightlevel, INT32 *ceilinglightlevel, extracolormap_t **floorcolormap, extracolormap_t **ceilingcolormap)
+{
+	// Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps.
+	if (fakeflat->ffloors)
+	{
+		fixed_t floorcenterz   = P_GetSectorFloorZAt  (fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y);
+		fixed_t ceilingcenterz = P_GetSectorCeilingZAt(fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y);
+
+		boolean anyMoved = fakeflat->moved;
+
+		if (anyMoved == false)
+		{
+			for (ffloor_t *rover = fakeflat->ffloors; rover; rover = rover->next)
+			{
+				sector_t *controlSec = &sectors[rover->secnum];
+
+				if (controlSec->moved == true)
+				{
+					anyMoved = true;
+					break;
+				}
+			}
+		}
+
+		if (anyMoved == true)
+		{
+			fakeflat->numlights = sector->numlights = 0;
+			R_Prep3DFloors(fakeflat);
+			sector->lightlist = fakeflat->lightlist;
+			sector->numlights = fakeflat->numlights;
+			sector->moved = fakeflat->moved = false;
+		}
+
+		INT32 light = R_GetPlaneLight(fakeflat, floorcenterz, false);
+		if (fakeflat->floorlightsec == -1 && !fakeflat->floorlightabsolute)
+			*floorlightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->floorlightlevel));
+		*floorcolormap = *fakeflat->lightlist[light].extra_colormap;
+
+		light = R_GetPlaneLight(fakeflat, ceilingcenterz, false);
+		if (fakeflat->ceilinglightsec == -1 && !fakeflat->ceilinglightabsolute)
+			*ceilinglightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->ceilinglightlevel));
+		*ceilingcolormap = *fakeflat->lightlist[light].extra_colormap;
+	}
+}
+
 //
 // R_Prep3DFloors
 //
diff --git a/src/r_bsp.h b/src/r_bsp.h
index 3effb916a50bf2b7c414e5be5568ca64adc325cc..44ddd0b1bfbf175f5a5b5efb07263b995c7d1cfb 100644
--- a/src/r_bsp.h
+++ b/src/r_bsp.h
@@ -57,4 +57,5 @@ boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back);
 
 INT32 R_GetPlaneLight(sector_t *sector, fixed_t planeheight, boolean underside);
 void R_Prep3DFloors(sector_t *sector);
+void R_CheckSectorLightLists(sector_t *sector, sector_t *fakeflat, INT32 *floorlightlevel, INT32 *ceilinglightlevel, extracolormap_t **floorcolormap, extracolormap_t **ceilingcolormap);
 #endif
diff --git a/src/r_defs.h b/src/r_defs.h
index dee15975feb481bf295bdba87261d56589f161c4..b6ea8d6e574b89154c775f53083d8c3d4c72354c 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -216,7 +216,8 @@ typedef enum
 	SECPORTAL_HORIZON, // Eternity Engine's horizon portal type
 	SECPORTAL_OBJECT,  // Uses an object as the reference view
 	SECPORTAL_FLOOR,   // Uses a sector as the reference view; the view height is aligned with the sector's floor
-	SECPORTAL_CEILING  // Uses a sector as the reference view; the view height is aligned with the sector's ceiling
+	SECPORTAL_CEILING, // Uses a sector as the reference view; the view height is aligned with the sector's ceiling
+	SECPORTAL_NONE = 0xFF
 } secportaltype_e;
 
 typedef struct sectorportal_s
diff --git a/src/r_portal.c b/src/r_portal.c
index e4af45f9f478d340544988baa56d8772c30bd664..327821b9db9cc5da95f4e5fb89b49882e6041645 100644
--- a/src/r_portal.c
+++ b/src/r_portal.c
@@ -373,7 +373,7 @@ void Portal_AddSectorPortal (const visplane_t* plane)
 	case SECPORTAL_PLANE:
 	case SECPORTAL_HORIZON:
 		portal->is_horizon = true;
-		portal->horizon_sector = plane->sector;
+		portal->horizon_sector = secportal->sector;
 		x = plane->sector->soundorg.x;
 		y = plane->sector->soundorg.y;
 		if (secportal->type == SECPORTAL_PLANE)