From d941dea0050723c03888144a391f9cf63a5f9960 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@users.noreply.github.com>
Date: Mon, 28 Jan 2019 02:41:29 +0100
Subject: [PATCH] - added a level iterator for operations that need to make
 changes to all open levels.

Since currently there is only one level, this will obvciously only run once on that level for the time being.

This is mainly used for CCMDs and CVARs which either print some diagnostics or change some user-settable configuration.
---
 src/c_cmds.cpp                            |  3 +-
 src/d_main.cpp                            | 11 +--
 src/dobject.cpp                           |  6 +-
 src/dobjgc.cpp                            |  3 +-
 src/doomstat.cpp                          | 11 ++-
 src/g_game.cpp                            |  2 +-
 src/g_level.cpp                           | 27 ++++++--
 src/g_levellocals.h                       |  7 ++
 src/g_shared/a_action.cpp                 | 13 ++--
 src/g_shared/a_decals.cpp                 |  8 +--
 src/g_shared/a_dynlight.cpp               | 82 ++++++++++++-----------
 src/hu_scores.cpp                         |  2 +-
 src/hwrenderer/dynlights/hw_shadowmap.cpp |  4 +-
 src/m_cheat.cpp                           |  5 +-
 src/p_3dfloors.cpp                        |  6 +-
 src/p_mobj.cpp                            | 13 ++--
 src/p_secnodes.cpp                        |  4 +-
 src/p_setup.cpp                           | 77 +++++++++++----------
 src/p_tags.cpp                            |  7 +-
 src/portal.cpp                            | 14 ++--
 src/r_data/r_sections.cpp                 | 12 ++--
 src/r_utility.cpp                         |  2 +-
 src/swrenderer/r_swcolormaps.cpp          |  5 +-
 23 files changed, 198 insertions(+), 126 deletions(-)

diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp
index 57171eb5f..e2f9b06f4 100644
--- a/src/c_cmds.cpp
+++ b/src/c_cmds.cpp
@@ -1061,7 +1061,8 @@ CCMD(changesky)
 		FTextureID newsky = TexMan.GetTextureID(sky1name, ETextureType::Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst);
 		if (newsky.Exists())
 		{
-			sky1texture = level.skytexture1 = newsky;
+			// This only alters the primary level's sky setting.
+			sky1texture = currentUILevel->skytexture1 = newsky;
 		}
 		else
 		{
diff --git a/src/d_main.cpp b/src/d_main.cpp
index 0f354c8bc..2853cceb3 100644
--- a/src/d_main.cpp
+++ b/src/d_main.cpp
@@ -746,12 +746,15 @@ void D_Display ()
 		//E_RenderFrame();
 		//
 		
-		// Check for the presence of dynamic lights at the start of the frame once.
-		if ((gl_lights && vid_rendermode == 4) || (r_dynlights && vid_rendermode != 4))
+		for (auto Level : AllLevels())
 		{
-			level.HasDynamicLights = !!level.lights;
+			// Check for the presence of dynamic lights at the start of the frame once.
+			if ((gl_lights && vid_rendermode == 4) || (r_dynlights && vid_rendermode != 4))
+			{
+				Level->HasDynamicLights = !!level.lights;
+			}
+			else Level->HasDynamicLights = false;	// lights are off so effectively we have none.
 		}
-		else level.HasDynamicLights = false;	// lights are off so effectively we have none.
 		
 		viewsec = screen->RenderView(&players[consoleplayer]);
 		screen->Begin2D();
diff --git a/src/dobject.cpp b/src/dobject.cpp
index 9e2e01a3a..b16e69d9b 100644
--- a/src/dobject.cpp
+++ b/src/dobject.cpp
@@ -488,6 +488,8 @@ void DObject::StaticPointerSubstitution (AActor *old, AActor *notOld)
 	DObject *probe;
 	size_t changed = 0;
 	int i;
+	
+	if (old == nullptr) return;
 
 	// Go through all objects.
 	i = 0;DObject *last=0;
@@ -515,8 +517,8 @@ void DObject::StaticPointerSubstitution (AActor *old, AActor *notOld)
 		}
 	}
 
-	// Go through sectors.
-	for (auto &sec : level.sectors)
+	// Go through sectors. Only the level this actor belongs to is relevant.
+	for (auto &sec : old->Level->sectors)
 	{
 		if (sec.SoundTarget == old) sec.SoundTarget = notOld;
 	}
diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp
index 8359eba3c..169baf32c 100644
--- a/src/dobjgc.cpp
+++ b/src/dobjgc.cpp
@@ -306,7 +306,8 @@ static void MarkRoot()
 	DThinker::MarkRoots();
 	Mark(E_FirstEventHandler);
 	Mark(E_LastEventHandler);
-	level.Mark();
+	for (auto Level : AllLevels())
+		Level->Mark();
 
 	// Mark players.
 	for (i = 0; i < MAXPLAYERS; i++)
diff --git a/src/doomstat.cpp b/src/doomstat.cpp
index edca955e1..b6f0e7871 100644
--- a/src/doomstat.cpp
+++ b/src/doomstat.cpp
@@ -50,14 +50,21 @@ CVAR (Bool, alwaysapplydmflags, false, CVAR_SERVERINFO);
 
 CUSTOM_CVAR (Float, teamdamage, 0.f, CVAR_SERVERINFO)
 {
-	level.teamdamage = self;
+	for (auto Level : AllLevels())
+	{
+		Level->teamdamage = self;
+	}
 }
 
 CUSTOM_CVAR (String, language, "auto", CVAR_ARCHIVE)
 {
 	SetLanguageIDs ();
 	GStrings.LoadStrings (false);
-	if (level.info != NULL) level.LevelName = level.info->LookupLevelName();
+	for (auto Level : AllLevels())
+	{
+		// does this even make sense on secondary levels...?
+		if (Level->info != nullptr) Level->LevelName = Level->info->LookupLevelName();
+	}
 }
 
 // [RH] Network arbitrator
diff --git a/src/g_game.cpp b/src/g_game.cpp
index ba4b9589e..c22c5b4ad 100644
--- a/src/g_game.cpp
+++ b/src/g_game.cpp
@@ -769,7 +769,7 @@ void G_AddViewPitch (int look, bool mouse)
 		return;
 	}
 	look = LookAdjust(look);
-	if (!level.IsFreelookAllowed())
+	if (!currentUILevel->IsFreelookAllowed())
 	{
 		LocalViewPitch = 0;
 	}
diff --git a/src/g_level.cpp b/src/g_level.cpp
index 1eabc17a8..4b4b17603 100644
--- a/src/g_level.cpp
+++ b/src/g_level.cpp
@@ -109,17 +109,26 @@ void G_VerifySkill();
 
 CUSTOM_CVAR(Bool, gl_brightfog, false, CVAR_ARCHIVE | CVAR_NOINITCALL)
 {
-	if (level.info == nullptr || level.info->brightfog == -1) level.brightfog = self;
+	for (auto Level : AllLevels())
+	{
+		if (Level->info == nullptr || Level->info->brightfog == -1) Level->brightfog = self;
+	}
 }
 
 CUSTOM_CVAR(Bool, gl_lightadditivesurfaces, false, CVAR_ARCHIVE | CVAR_NOINITCALL)
 {
-	if (level.info == nullptr || level.info->lightadditivesurfaces == -1) level.lightadditivesurfaces = self;
+	for (auto Level : AllLevels())
+	{
+		if (Level->info == nullptr || Level->info->lightadditivesurfaces == -1) Level->lightadditivesurfaces = self;
+	}
 }
 
 CUSTOM_CVAR(Bool, gl_notexturefill, false, CVAR_NOINITCALL)
 {
-	if (level.info == nullptr || level.info->notexturefill == -1) level.notexturefill = self;
+	for (auto Level : AllLevels())
+	{
+		if (Level->info == nullptr || Level->info->notexturefill == -1) Level->notexturefill = self;
+	}
 }
 
 CUSTOM_CVAR(Int, gl_lightmode, 3, CVAR_ARCHIVE | CVAR_NOINITCALL)
@@ -129,7 +138,10 @@ CUSTOM_CVAR(Int, gl_lightmode, 3, CVAR_ARCHIVE | CVAR_NOINITCALL)
 	else if (newself > 4) newself = 8;
 	else if (newself < 0) newself = 0;
 	if (self != newself) self = newself;
-	else if ((level.info == nullptr || level.info->lightmode == ELightMode::NotSet)) level.lightMode = (ELightMode)*self;
+	else for (auto Level : AllLevels())
+	{
+		if ((level.info == nullptr || level.info->lightmode == ELightMode::NotSet)) level.lightMode = (ELightMode)*self;
+	}
 }
 
 
@@ -202,7 +214,7 @@ CCMD (map)
 	if (argv.argc() > 1)
 	{
 		const char *mapname = argv[1];
-		if (!strcmp(mapname, "*")) mapname = level.MapName.GetChars();
+		if (!strcmp(mapname, "*")) mapname = currentUILevel->MapName.GetChars();
 
 		try
 		{
@@ -252,7 +264,7 @@ UNSAFE_CCMD(recordmap)
 	if (argv.argc() > 2)
 	{
 		const char *mapname = argv[2];
-		if (!strcmp(mapname, "*")) mapname = level.MapName.GetChars();
+		if (!strcmp(mapname, "*")) mapname = currentUILevel->MapName.GetChars();
 
 		try
 		{
@@ -2180,7 +2192,8 @@ CCMD(skyfog)
 {
 	if (argv.argc()>1)
 	{
-		level.skyfog = MAX(0, (int)strtoull(argv[1], NULL, 0));
+		// Do this only on the primary level.
+		currentUILevel->skyfog = MAX(0, (int)strtoull(argv[1], NULL, 0));
 	}
 }
 
diff --git a/src/g_levellocals.h b/src/g_levellocals.h
index 4c0691668..41e5e2851 100644
--- a/src/g_levellocals.h
+++ b/src/g_levellocals.h
@@ -635,3 +635,10 @@ inline AActor *P_SpawnPlayer(FPlayerStart *mthing, int playernum, int flags = 0)
 {
 	return level.SpawnPlayer(mthing, playernum, flags);
 }
+
+// This must later be extended to return an array with all levels.
+// It is meant for code that needs to iterate over all levels to make some global changes, e.g. configuation CCMDs.
+inline TArrayView<FLevelLocals *> AllLevels()
+{
+	return TArrayView<FLevelLocals *>(&currentUILevel, 1);
+}
diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp
index 43500b7c5..fb3b9a309 100644
--- a/src/g_shared/a_action.cpp
+++ b/src/g_shared/a_action.cpp
@@ -92,12 +92,15 @@ CUSTOM_CVAR(Int, sv_corpsequeuesize, 64, CVAR_ARCHIVE|CVAR_SERVERINFO)
 {
 	if (self > 0)
 	{
-		auto &corpsequeue = level.CorpseQueue;
-		while (corpsequeue.Size() > (unsigned)self)
+		for (auto Level : AllLevels())
 		{
-			AActor *corpse = corpsequeue[0];
-			if (corpse) corpse->Destroy();
-			corpsequeue.Delete(0);
+			auto &corpsequeue = Level->CorpseQueue;
+			while (corpsequeue.Size() > (unsigned)self)
+			{
+				AActor *corpse = corpsequeue[0];
+				if (corpse) corpse->Destroy();
+				corpsequeue.Delete(0);
+			}
 		}
 	}
 }
diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp
index 8373231a8..10fec6ce0 100644
--- a/src/g_shared/a_decals.cpp
+++ b/src/g_shared/a_decals.cpp
@@ -538,9 +538,9 @@ CUSTOM_CVAR (Int, cl_maxdecals, 1024, CVAR_ARCHIVE)
 	{
 		self = 0;
 	}
-	else
+	else for (auto Level : AllLevels())
 	{
-		while (level.ImpactDecalCount > self)
+		while (Level->ImpactDecalCount > self)
 		{
 			DThinker *thinker = DThinker::FirstThinker(STAT_AUTODECAL);
 			if (thinker != NULL)
@@ -553,13 +553,13 @@ CUSTOM_CVAR (Int, cl_maxdecals, 1024, CVAR_ARCHIVE)
 
 void DImpactDecal::CheckMax ()
 {
-	if (++level.ImpactDecalCount >= cl_maxdecals)
+	if (++Level->ImpactDecalCount >= cl_maxdecals)
 	{
 		DThinker *thinker = DThinker::FirstThinker (STAT_AUTODECAL);
 		if (thinker != NULL)
 		{
 			thinker->Destroy();
-			level.ImpactDecalCount--;
+			Level->ImpactDecalCount--;
 		}
 	}
 }
diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp
index 07e245983..e333bffc5 100644
--- a/src/g_shared/a_dynlight.cpp
+++ b/src/g_shared/a_dynlight.cpp
@@ -880,48 +880,52 @@ CCMD(listlights)
 	int allwalls=0, allsectors=0, allsubsecs = 0;
 	int i=0, shadowcount = 0;
 	FDynamicLight * dl;
-
-	for (dl = level.lights; dl; dl = dl->next)
+	
+	for (auto Level : AllLevels())
 	{
-		walls=0;
-		sectors=0;
-		Printf("%s at (%f, %f, %f), color = 0x%02x%02x%02x, radius = %f %s %s",
-			dl->target->GetClass()->TypeName.GetChars(),
-			dl->X(), dl->Y(), dl->Z(), dl->GetRed(), dl->GetGreen(), dl->GetBlue(), 
-			dl->radius, dl->IsAttenuated()? "attenuated" : "", dl->shadowmapped? "shadowmapped" : "");
-		i++;
-		shadowcount += dl->shadowmapped;
-
-		if (dl->target)
+		Printf("Lights for %s\n", Level->MapName.GetChars());
+		for (dl = Level->lights; dl; dl = dl->next)
 		{
-			FTextureID spr = sprites[dl->target->sprite].GetSpriteFrame(dl->target->frame, 0, 0., nullptr);
-			Printf(", frame = %s ", TexMan.GetTexture(spr)->GetName().GetChars());
+			walls=0;
+			sectors=0;
+			Printf("%s at (%f, %f, %f), color = 0x%02x%02x%02x, radius = %f %s %s",
+				   dl->target->GetClass()->TypeName.GetChars(),
+				   dl->X(), dl->Y(), dl->Z(), dl->GetRed(), dl->GetGreen(), dl->GetBlue(),
+				   dl->radius, dl->IsAttenuated()? "attenuated" : "", dl->shadowmapped? "shadowmapped" : "");
+			i++;
+			shadowcount += dl->shadowmapped;
+			
+			if (dl->target)
+			{
+				FTextureID spr = sprites[dl->target->sprite].GetSpriteFrame(dl->target->frame, 0, 0., nullptr);
+				Printf(", frame = %s ", TexMan.GetTexture(spr)->GetName().GetChars());
+			}
+			
+			
+			FLightNode * node;
+			
+			node=dl->touching_sides;
+			
+			while (node)
+			{
+				walls++;
+				allwalls++;
+				node = node->nextTarget;
+			}
+			
+			
+			node = dl->touching_sector;
+			
+			while (node)
+			{
+				allsectors++;
+				sectors++;
+				node = node->nextTarget;
+			}
+			Printf("- %d walls, %d sectors\n", walls, sectors);
+			
 		}
-
-
-		FLightNode * node;
-
-		node=dl->touching_sides;
-
-		while (node)
-		{
-			walls++;
-			allwalls++;
-			node = node->nextTarget;
-		}
-
-
-		node = dl->touching_sector;
-
-		while (node)
-		{
-			allsectors++;
-			sectors++;
-			node = node->nextTarget;
-		}
-		Printf("- %d walls, %d sectors\n", walls, sectors);
-
+		Printf("%i dynamic lights, %d shadowmapped, %d walls, %d sectors\n\n\n", i, shadowcount, allwalls, allsectors);
 	}
-	Printf("%i dynamic lights, %d shadowmapped, %d walls, %d sectors\n\n\n", i, shadowcount, allwalls, allsectors);
 }
 
diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp
index a4a261a77..ceddaca23 100644
--- a/src/hu_scores.cpp
+++ b/src/hu_scores.cpp
@@ -368,7 +368,7 @@ static void HU_DrawTimeRemaining (int y)
 	if (deathmatch && timelimit && gamestate == GS_LEVEL)
 	{
 		char str[80];
-		int timeleft = (int)(timelimit * TICRATE * 60) - level.maptime;
+		int timeleft = (int)(timelimit * TICRATE * 60) - currentUILevel->maptime;
 		int hours, minutes, seconds;
 
 		if (timeleft < 0)
diff --git a/src/hwrenderer/dynlights/hw_shadowmap.cpp b/src/hwrenderer/dynlights/hw_shadowmap.cpp
index 3f9dc89eb..5bed01063 100644
--- a/src/hwrenderer/dynlights/hw_shadowmap.cpp
+++ b/src/hwrenderer/dynlights/hw_shadowmap.cpp
@@ -83,9 +83,9 @@ CUSTOM_CVAR(Int, gl_shadowmap_quality, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
 
 CUSTOM_CVAR (Bool, gl_light_shadowmap, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
 {
-    if (!self)
+	if (!self) for (auto Level : AllLevels())
     {
-		auto light = level.lights;
+		auto light = Level->lights;
 		while (light)
 		{
             light->mShadowmapIndex = 1024;
diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp
index 3effff3ea..b58479c08 100644
--- a/src/m_cheat.cpp
+++ b/src/m_cheat.cpp
@@ -287,7 +287,10 @@ void cht_DoCheat (player_t *player, int cheat)
 
 		if (i == 4)
 		{
-			level.flags2 ^= LEVEL2_ALLMAP;
+			for (auto Level : AllLevels())
+			{
+				Level->flags2 ^= LEVEL2_ALLMAP;
+			}
 		}
 		else if (player->mo != NULL && player->health >= 0)
 		{
diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp
index 4da592cf4..213987718 100644
--- a/src/p_3dfloors.cpp
+++ b/src/p_3dfloors.cpp
@@ -867,13 +867,15 @@ CCMD (dump3df)
 {
 	if (argv.argc() > 1) 
 	{
+		// Print 3D floor info for a single sector.
+		// This only checks the primary level.
 		int sec = (int)strtoll(argv[1], NULL, 10);
-		if ((unsigned)sec >= level.sectors.Size())
+		if ((unsigned)sec >= currentUILevel->sectors.Size())
 		{
 			Printf("Sector %d does not exist.\n", sec);
 			return;
 		}
-		sector_t *sector = &level.sectors[sec];
+		sector_t *sector = &currentUILevel->sectors[sec];
 		TArray<F3DFloor*> & ffloors=sector->e->XFloor.ffloors;
 
 		for (unsigned int i = 0; i < ffloors.Size(); i++)
diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
index fc2c43f02..02abc3be0 100644
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -147,7 +147,10 @@ FRandom pr_spawnmobj ("SpawnActor");
 
 CUSTOM_CVAR (Float, sv_gravity, 800.f, CVAR_SERVERINFO|CVAR_NOSAVE)
 {
-	level.gravity = self;
+	for (auto Level : AllLevels())
+	{
+		Level->gravity = self;
+	}
 }
 
 CVAR (Bool, cl_missiledecals, true, CVAR_ARCHIVE)
@@ -3021,9 +3024,11 @@ int FLevelLocals::FindUniqueTID(int start_tid, int limit)
 
 CCMD(utid)
 {
-	Printf("%d\n",
-		level.FindUniqueTID(argv.argc() > 1 ? atoi(argv[1]) : 0,
-		(argv.argc() > 2 && atoi(argv[2]) >= 0) ? atoi(argv[2]) : 0));
+	for (auto Level : AllLevels())
+	{
+		Printf("%s, %d\n", Level->MapName.GetChars(), Level->FindUniqueTID(argv.argc() > 1 ? atoi(argv[1]) : 0,
+			(argv.argc() > 2 && atoi(argv[2]) >= 0) ? atoi(argv[2]) : 0));
+	}
 }
 
 //==========================================================================
diff --git a/src/p_secnodes.cpp b/src/p_secnodes.cpp
index 4400960ad..9f5554b7c 100644
--- a/src/p_secnodes.cpp
+++ b/src/p_secnodes.cpp
@@ -466,7 +466,7 @@ FBlockNode *FBlockNode::Create(AActor *who, int x, int y, int group)
 	{
 		block = (FBlockNode *)secnodearena.Alloc(sizeof(FBlockNode));
 	}
-	block->BlockIndex = x + y * level.blockmap.bmapwidth;
+	block->BlockIndex = x + y * who->Level->blockmap.bmapwidth;
 	block->Me = who;
 	block->NextActor = nullptr;
 	block->PrevActor = nullptr;
@@ -479,4 +479,4 @@ void FBlockNode::Release()
 {
 	NextBlock = FreeBlocks;
 	FreeBlocks = this;
-}
\ No newline at end of file
+}
diff --git a/src/p_setup.cpp b/src/p_setup.cpp
index 3017d6aed..8b1d59160 100644
--- a/src/p_setup.cpp
+++ b/src/p_setup.cpp
@@ -594,39 +594,43 @@ static void P_Shutdown ()
 
 CCMD(dumpgeometry)
 {
-	for (auto &sector : level.sectors)
+	for (auto Level : AllLevels())
 	{
-		Printf(PRINT_LOG, "Sector %d\n", sector.sectornum);
-		for (int j = 0; j<sector.subsectorcount; j++)
+		Printf("Geometry for %s\n", Level->MapName.GetChars());
+		for (auto &sector : Level->sectors)
 		{
-			subsector_t * sub = sector.subsectors[j];
-
-			Printf(PRINT_LOG, "    Subsector %d - real sector = %d - %s\n", int(sub->Index()), sub->sector->sectornum, sub->hacked & 1 ? "hacked" : "");
-			for (uint32_t k = 0; k<sub->numlines; k++)
+			Printf(PRINT_LOG, "Sector %d\n", sector.sectornum);
+			for (int j = 0; j<sector.subsectorcount; j++)
 			{
-				seg_t * seg = sub->firstline + k;
-				if (seg->linedef)
+				subsector_t * sub = sector.subsectors[j];
+				
+				Printf(PRINT_LOG, "    Subsector %d - real sector = %d - %s\n", int(sub->Index()), sub->sector->sectornum, sub->hacked & 1 ? "hacked" : "");
+				for (uint32_t k = 0; k<sub->numlines; k++)
 				{
-					Printf(PRINT_LOG, "      (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, linedef %d, side %d",
-						seg->v1->fX(), seg->v1->fY(), seg->v2->fX(), seg->v2->fY(),
-						seg->Index(), seg->linedef->Index(), seg->sidedef != seg->linedef->sidedef[0]);
+					seg_t * seg = sub->firstline + k;
+					if (seg->linedef)
+					{
+						Printf(PRINT_LOG, "      (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, linedef %d, side %d",
+							   seg->v1->fX(), seg->v1->fY(), seg->v2->fX(), seg->v2->fY(),
+							   seg->Index(), seg->linedef->Index(), seg->sidedef != seg->linedef->sidedef[0]);
+					}
+					else
+					{
+						Printf(PRINT_LOG, "      (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, miniseg",
+							   seg->v1->fX(), seg->v1->fY(), seg->v2->fX(), seg->v2->fY(), seg->Index());
+					}
+					if (seg->PartnerSeg)
+					{
+						subsector_t * sub2 = seg->PartnerSeg->Subsector;
+						Printf(PRINT_LOG, ", back sector = %d, real back sector = %d", sub2->render_sector->sectornum, seg->PartnerSeg->frontsector->sectornum);
+					}
+					else if (seg->backsector)
+					{
+						Printf(PRINT_LOG, ", back sector = %d (no partnerseg)", seg->backsector->sectornum);
+					}
+					
+					Printf(PRINT_LOG, "\n");
 				}
-				else
-				{
-					Printf(PRINT_LOG, "      (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, miniseg",
-						seg->v1->fX(), seg->v1->fY(), seg->v2->fX(), seg->v2->fY(), seg->Index());
-				}
-				if (seg->PartnerSeg)
-				{
-					subsector_t * sub2 = seg->PartnerSeg->Subsector;
-					Printf(PRINT_LOG, ", back sector = %d, real back sector = %d", sub2->render_sector->sectornum, seg->PartnerSeg->frontsector->sectornum);
-				}
-				else if (seg->backsector)
-				{
-					Printf(PRINT_LOG, ", back sector = %d (no partnerseg)", seg->backsector->sectornum);
-				}
-
-				Printf(PRINT_LOG, "\n");
 			}
 		}
 	}
@@ -640,14 +644,18 @@ CCMD(dumpgeometry)
 
 CCMD(listmapsections)
 {
-	for (int i = 0; i < 100; i++)
+	for (auto Level : AllLevels())
 	{
-		for (auto &sub : level.subsectors)
+		Printf("Map sections for %s:\n", Level->MapName.GetChars());
+		for (int i = 0; i < 100; i++)
 		{
-			if (sub.mapsection == i)
+			for (auto &sub : Level->subsectors)
 			{
-				Printf("Mapsection %d, sector %d, line %d\n", i, sub.render_sector->Index(), sub.firstline->linedef->Index());
-				break;
+				if (sub.mapsection == i)
+				{
+					Printf("Mapsection %d, sector %d, line %d\n", i, sub.render_sector->Index(), sub.firstline->linedef->Index());
+					break;
+				}
 			}
 		}
 	}
@@ -661,9 +669,8 @@ CCMD(listmapsections)
 
 CUSTOM_CVAR(Bool, forcewater, false, CVAR_ARCHIVE | CVAR_SERVERINFO)
 {
-	if (gamestate == GS_LEVEL)
+	if (gamestate == GS_LEVEL) for (auto Level : AllLevels())
 	{
-		auto Level = currentUILevel;
 		for (auto &sec : Level->sectors)
 		{
 			sector_t *hsec = sec.GetHeightSec();
diff --git a/src/p_tags.cpp b/src/p_tags.cpp
index db113daab..35d6902a8 100644
--- a/src/p_tags.cpp
+++ b/src/p_tags.cpp
@@ -316,7 +316,10 @@ void FTagManager::DumpTags()
 
 CCMD(dumptags)
 {
-	level.tagManager.DumpTags();
+	for (auto Level : AllLevels())
+	{
+		Level->tagManager.DumpTags();
+	}
 }
 
 //-----------------------------------------------------------------------------
@@ -369,7 +372,7 @@ int FSectorTagIterator::NextCompat(bool compat, int start)
 
 	for (unsigned i = start + 1; i < tagManager.Level->sectors.Size(); i++)
 	{
-		if (level.SectorHasTag(i, searchtag)) return i;
+		if (tagManager.Level->SectorHasTag(i, searchtag)) return i;
 	}
 	return -1;
 }
diff --git a/src/portal.cpp b/src/portal.cpp
index 7b10f1d9b..128905bf7 100644
--- a/src/portal.cpp
+++ b/src/portal.cpp
@@ -1208,14 +1208,18 @@ bool FLevelLocals::CollectConnectedGroups(int startgroup, const DVector3 &positi
 
 CCMD(dumplinktable)
 {
-	for (int x = 1; x < level.Displacements.size; x++)
+	for (auto Level : AllLevels())
 	{
-		for (int y = 1; y < level.Displacements.size; y++)
+		Printf("Portal displacements for %s:\n", Level->MapName.GetChars());
+		for (int x = 1; x < Level->Displacements.size; x++)
 		{
-			FDisplacement &disp = level.Displacements(x, y);
-			Printf("%c%c(%6d, %6d)", TEXTCOLOR_ESCAPE, 'C' + disp.indirect, int(disp.pos.X), int(disp.pos.Y));
+			for (int y = 1; y < Level->Displacements.size; y++)
+			{
+				FDisplacement &disp = Level->Displacements(x, y);
+				Printf("%c%c(%6d, %6d)", TEXTCOLOR_ESCAPE, 'C' + disp.indirect, int(disp.pos.X), int(disp.pos.Y));
+			}
+			Printf("\n");
 		}
-		Printf("\n");
 	}
 }
 
diff --git a/src/r_data/r_sections.cpp b/src/r_data/r_sections.cpp
index b896f1ddb..79790a6cd 100644
--- a/src/r_data/r_sections.cpp
+++ b/src/r_data/r_sections.cpp
@@ -813,9 +813,10 @@ public:
 //
 //=============================================================================
 
-void PrintSections(FSectionContainer &container)
+void PrintSections(FLevelLocals *Level)
 {
-	auto Level = &level;
+	FSectionContainer &container = Level->sections;
+	Printf("Sections for %s\n", Level->MapName.GetChars());
 	for (unsigned i = 0; i < container.allSections.Size(); i++)
 	{
 		auto &section = container.allSections[i];
@@ -852,7 +853,7 @@ void PrintSections(FSectionContainer &container)
 			}
 		}
 	}
-	Printf(PRINT_LOG, "%d sectors, %d subsectors, %d sections\n", Level->sectors.Size(), Level->subsectors.Size(), container.allSections.Size());
+	Printf(PRINT_LOG, "%d sectors, %d subsectors, %d sections\n\n", Level->sectors.Size(), Level->subsectors.Size(), container.allSections.Size());
 }
 
 
@@ -876,7 +877,10 @@ void CreateSections(FLevelLocals *Level)
 
 CCMD(printsections)
 {
-	PrintSections(level.sections);
+	for (auto Level : AllLevels())
+	{
+		PrintSections(Level);
+	}
 }
 
 
diff --git a/src/r_utility.cpp b/src/r_utility.cpp
index 9278a94d3..9a6f675cb 100644
--- a/src/r_utility.cpp
+++ b/src/r_utility.cpp
@@ -1029,7 +1029,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor
 	// However, to set up a projection matrix this needs to be adjusted.
 	double radPitch = viewpoint.Angles.Pitch.Normalized180().Radians();
 	double angx = cos(radPitch);
-	double angy = sin(radPitch) * level.info->pixelstretch;
+	double angy = sin(radPitch) * actor->Level->info->pixelstretch;
 	double alen = sqrt(angx*angx + angy*angy);
 	viewpoint.HWAngles.Pitch = RAD2DEG((float)asin(angy / alen));
 	
diff --git a/src/swrenderer/r_swcolormaps.cpp b/src/swrenderer/r_swcolormaps.cpp
index d5b374ad1..712107be8 100644
--- a/src/swrenderer/r_swcolormaps.cpp
+++ b/src/swrenderer/r_swcolormaps.cpp
@@ -524,7 +524,10 @@ CCMD (testfade)
 		{
 			color = V_GetColorFromString (NULL, argv[1]);
 		}
-		level.fadeto = color;
+		for (auto Level : AllLevels())
+		{
+			Level->fadeto = color;
+		}
 		NormalLight.ChangeFade (color);
 	}
 }