diff --git a/src/d_main.c b/src/d_main.c
index e25ef998e040bc51dda58ff80fc84a1c9be4a9f2..0654e09278c2fe82969f2580e892ee4e84d57a3e 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1462,7 +1462,7 @@ void D_SRB2Main(void)
 	{
 		levelstarttic = gametic;
 		G_SetGamestate(GS_LEVEL);
-		if (!P_SetupLevel(false))
+		if (!P_LoadLevel(false))
 			I_Quit(); // fail so reset game stuff
 	}
 }
diff --git a/src/g_game.c b/src/g_game.c
index 19b18ef8c2f760de8756baaaed0c516f9182425f..e6a28aa41dcd6a3a749265cc2b78aa750ccf87ef 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1847,7 +1847,7 @@ void G_DoLoadLevel(boolean resetplayer)
 	}
 
 	// Setup the level.
-	if (!P_SetupLevel(false)) // this never returns false?
+	if (!P_LoadLevel(false)) // this never returns false?
 	{
 		// fail so reset game stuff
 		Command_ExitGame_f();
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 85efacf8852195302cd1023ceff75733f4cc04bc..4cfeab6f84aa28ab77e685c9a4545880af868138 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -4064,7 +4064,7 @@ static inline boolean P_NetUnArchiveMisc(void)
 
 	tokenlist = READUINT32(save_p);
 
-	if (!P_SetupLevel(true))
+	if (!P_LoadLevel(true))
 		return false;
 
 	// get the time
diff --git a/src/p_setup.c b/src/p_setup.c
index ce6027c4e38fc73caf92b297c0aa6ee4c8f87adf..2605faa0d669248f219e3aabac3ab2aeee10ebf3 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -971,7 +971,7 @@ static void P_SpawnEmeraldHunt(void)
 		mobjinfo[MT_EMERHUNT].spawnstate+2);
 }
 
-static void P_LoadThings(boolean loademblems)
+static void P_SpawnMapThings(boolean spawnemblems)
 {
 	size_t i;
 	mapthing_t *mt;
@@ -1001,7 +1001,7 @@ static void P_LoadThings(boolean loademblems)
 			|| mt->type == 1702) // MT_AXISTRANSFERLINE
 			continue; // These were already spawned
 
-		if (!loademblems && mt->type == mobjinfo[MT_EMBLEM].doomednum)
+		if (!spawnemblems && mt->type == mobjinfo[MT_EMBLEM].doomednum)
 			continue;
 
 		mt->mobj = NULL;
@@ -2016,6 +2016,75 @@ static void P_LoadMapData(const virtres_t* virt)
 	memcpy(spawnsides, sides, numsides * sizeof (*sides));
 }
 
+/** Compute MD5 message digest for bytes read from memory source
+  *
+  * The resulting message digest number will be written into the 16 bytes
+  * beginning at RESBLOCK.
+  *
+  * \param filename path of file
+  * \param resblock resulting MD5 checksum
+  * \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found
+  */
+static INT32 P_MakeBufferMD5(const char* buffer, size_t len, void* resblock)
+{
+#ifdef NOMD5
+	(void)buffer;
+	(void)len;
+	memset(resblock, 0x00, 16);
+	return 1;
+#else
+	tic_t t = I_GetTime();
+	CONS_Debug(DBG_SETUP, "Making MD5\n");
+	if (md5_buffer(buffer, len, resblock) == NULL)
+		return 1;
+	CONS_Debug(DBG_SETUP, "MD5 calc took %f seconds\n", (float)(I_GetTime() - t)/NEWTICRATE);
+	return 0;
+#endif
+}
+
+static void P_MakeMapMD5(virtres_t* virt, void* dest)
+{
+	unsigned char linemd5[16];
+	unsigned char sectormd5[16];
+	unsigned char thingmd5[16];
+	unsigned char sidedefmd5[16];
+	unsigned char resmd5[16];
+	UINT8 i;
+
+	// Create a hash for the current map
+	// get the actual lumps!
+	virtlump_t *virtlines = vres_Find(virt, "LINEDEFS");
+	virtlump_t *virtsectors = vres_Find(virt, "SECTORS");
+	virtlump_t *virtmthings = vres_Find(virt, "THINGS");
+	virtlump_t *virtsides = vres_Find(virt, "SIDEDEFS");
+
+	P_MakeBufferMD5((char*)virtlines->data, virtlines->size, linemd5);
+	P_MakeBufferMD5((char*)virtsectors->data, virtsectors->size, sectormd5);
+	P_MakeBufferMD5((char*)virtmthings->data, virtmthings->size, thingmd5);
+	P_MakeBufferMD5((char*)virtsides->data, virtsides->size, sidedefmd5);
+
+	for (i = 0; i < 16; i++)
+		resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF;
+
+	M_Memcpy(dest, &resmd5, 16);
+}
+
+static void P_LoadMapFromFile(void)
+{
+	virtres_t *virt = vres_GetMap(lastloadedmaplumpnum);
+
+	P_LoadMapData(virt);
+	P_LoadMapBSP(virt);
+	P_LoadMapLUT(virt);
+
+	P_LoadLineDefs2();
+	P_GroupLines();
+
+	P_MakeMapMD5(virt, &mapmd5);
+
+	vres_Free(virt);
+}
+
 #if 0
 static char *levellumps[] =
 {
@@ -2084,7 +2153,7 @@ lumpnum_t lastloadedmaplumpnum; // for comparative savegame
 //
 // Some player initialization for map start.
 //
-static void P_LevelInitStuff(void)
+static void P_InitLevelSettings(void)
 {
 	INT32 i;
 	boolean canresetlives = true;
@@ -2209,68 +2278,15 @@ void P_LoadThingsOnly(void)
 		P_RemoveMobj((mobj_t *)think);
 	}
 
-	P_LevelInitStuff();
+	P_InitLevelSettings();
 
-	P_LoadThings(true);
+	P_SpawnMapThings(true);
 
 	// restore skybox viewpoint/centerpoint if necessary, set them to defaults if we can't do that
 	skyboxmo[0] = skyboxviewpnts[(viewid >= 0) ? viewid : 0];
 	skyboxmo[1] = skyboxcenterpnts[(centerid >= 0) ? centerid : 0];
 }
 
-/** Compute MD5 message digest for bytes read from memory source
-  *
-  * The resulting message digest number will be written into the 16 bytes
-  * beginning at RESBLOCK.
-  *
-  * \param filename path of file
-  * \param resblock resulting MD5 checksum
-  * \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found
-  */
-static INT32 P_MakeBufferMD5(const char *buffer, size_t len, void *resblock)
-{
-#ifdef NOMD5
-	(void)buffer;
-	(void)len;
-	memset(resblock, 0x00, 16);
-	return 1;
-#else
-	tic_t t = I_GetTime();
-	CONS_Debug(DBG_SETUP, "Making MD5\n");
-	if (md5_buffer(buffer, len, resblock) == NULL)
-		return 1;
-	CONS_Debug(DBG_SETUP, "MD5 calc took %f seconds\n", (float)(I_GetTime() - t)/NEWTICRATE);
-	return 0;
-#endif
-}
-
-static void P_MakeMapMD5(virtres_t* virt, void *dest)
-{
-	unsigned char linemd5[16];
-	unsigned char sectormd5[16];
-	unsigned char thingmd5[16];
-	unsigned char sidedefmd5[16];
-	unsigned char resmd5[16];
-	UINT8 i;
-
-	// Create a hash for the current map
-	// get the actual lumps!
-	virtlump_t* virtlines   = vres_Find(virt, "LINEDEFS");
-	virtlump_t* virtsectors = vres_Find(virt, "SECTORS");
-	virtlump_t* virtmthings = vres_Find(virt, "THINGS");
-	virtlump_t* virtsides   = vres_Find(virt, "SIDEDEFS");
-
-	P_MakeBufferMD5((char*)virtlines->data,   virtlines->size, linemd5);
-	P_MakeBufferMD5((char*)virtsectors->data, virtsectors->size,  sectormd5);
-	P_MakeBufferMD5((char*)virtmthings->data, virtmthings->size,   thingmd5);
-	P_MakeBufferMD5((char*)virtsides->data,   virtsides->size, sidedefmd5);
-
-	for (i = 0; i < 16; i++)
-		resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF;
-
-	M_Memcpy(dest, &resmd5, 16);
-}
-
 static void P_RunLevelScript(const char *scriptname)
 {
 	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_SCRIPTISFILE))
@@ -2335,6 +2351,26 @@ static void P_ForceCharacter(const char *forcecharskin)
 	}
 }
 
+static void P_ResetSpawnpoints(void)
+{
+	UINT8 i;
+
+	numdmstarts = numredctfstarts = numbluectfstarts = 0;
+
+	// reset the player starts
+	for (i = 0; i < MAXPLAYERS; i++)
+		playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL;
+
+	for (i = 0; i < MAX_DM_STARTS; i++)
+		deathmatchstarts[i] = NULL;
+
+	for (i = 0; i < 2; i++)
+		skyboxmo[i] = NULL;
+
+	for (i = 0; i < 16; i++)
+		skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL;
+}
+
 static void P_LoadRecordGhosts(void)
 {
 	const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
@@ -2434,6 +2470,45 @@ static void P_LoadNightsGhosts(void)
 	free(gpath);
 }
 
+static void P_InitTagGametype(void)
+{
+	UINT8 i;
+	INT32 realnumplayers = 0;
+	INT32 playersactive[MAXPLAYERS];
+
+	//I just realized how problematic this code can be.
+	//D_NumPlayers() will not always cover the scope of the netgame.
+	//What if one player is node 0 and the other node 31?
+	//The solution? Make a temp array of all players that are currently playing and pick from them.
+	//Future todo? When a player leaves, shift all nodes down so D_NumPlayers() can be used as intended?
+	//Also, you'd never have to loop through all 32 players slots to find anything ever again.
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i] && !players[i].spectator)
+		{
+			playersactive[realnumplayers] = i; //stores the player's node in the array.
+			realnumplayers++;
+		}
+	}
+
+	if (!realnumplayers) //this should also fix the dedicated crash bug. You only pick a player if one exists to be picked.
+	{
+		CONS_Printf(M_GetText("No player currently available to become IT. Awaiting available players.\n"));
+		return;
+	}
+
+	i = P_RandomKey(realnumplayers);
+	players[playersactive[i]].pflags |= PF_TAGIT; //choose our initial tagger before map starts.
+
+	// Taken and modified from G_DoReborn()
+	// Remove the player so he can respawn elsewhere.
+	// first disassociate the corpse
+	if (players[playersactive[i]].mo)
+		P_RemoveMobj(players[playersactive[i]].mo);
+
+	G_SpawnPlayer(playersactive[i], false); //respawn the lucky player in his dedicated spawn location.
+}
+
 static void P_SetupCamera(void)
 {
 	if (players[displayplayer].mo && (server || addedtogame))
@@ -2471,6 +2546,52 @@ static void P_SetupCamera(void)
 	}
 }
 
+static void P_InitCamera(void)
+{
+	if (!dedicated)
+	{
+		P_SetupCamera();
+
+		// Salt: CV_ClearChangedFlags() messes with your settings :(
+		/*if (!cv_cam_height.changed)
+			CV_Set(&cv_cam_height, cv_cam_height.defaultvalue);
+		if (!cv_cam2_height.changed)
+			CV_Set(&cv_cam2_height, cv_cam2_height.defaultvalue);
+
+		if (!cv_cam_dist.changed)
+			CV_Set(&cv_cam_dist, cv_cam_dist.defaultvalue);
+		if (!cv_cam2_dist.changed)
+			CV_Set(&cv_cam2_dist, cv_cam2_dist.defaultvalue);*/
+
+			// Though, I don't think anyone would care about cam_rotate being reset back to the only value that makes sense :P
+		if (!cv_cam_rotate.changed)
+			CV_Set(&cv_cam_rotate, cv_cam_rotate.defaultvalue);
+		if (!cv_cam2_rotate.changed)
+			CV_Set(&cv_cam2_rotate, cv_cam2_rotate.defaultvalue);
+
+		if (!cv_analog.changed)
+			CV_SetValue(&cv_analog, 0);
+		if (!cv_analog2.changed)
+			CV_SetValue(&cv_analog2, 0);
+
+		displayplayer = consoleplayer; // Start with your OWN view, please!
+	}
+
+	if (twodlevel)
+	{
+		CV_SetValue(&cv_analog, false);
+		CV_SetValue(&cv_analog2, false);
+	}
+	else
+	{
+		if (cv_useranalog.value)
+			CV_SetValue(&cv_analog, true);
+
+		if ((splitscreen && cv_useranalog2.value) || botingame)
+			CV_SetValue(&cv_analog2, true);
+	}
+}
+
 static boolean CanSaveLevel(INT32 mapnum)
 {
 	if (ultimatemode) // never save in ultimate (probably redundant with cursaveslot also being checked)
@@ -2486,21 +2607,187 @@ static boolean CanSaveLevel(INT32 mapnum)
 	return (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME || gamecomplete || !lastmaploaded);
 }
 
+static void P_RunSpecialStageWipe(void)
+{
+	tic_t starttime = I_GetTime();
+	tic_t endtime = starttime + (3*TICRATE)/2;
+	tic_t nowtime;
+
+	S_StartSound(NULL, sfx_s3kaf);
+
+	// Fade music! Time it to S3KAF: 0.25 seconds is snappy.
+	if (RESETMUSIC ||
+		strnicmp(S_MusicName(),
+		(mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap - 1]->musname : mapmusname, 7))
+		S_FadeOutStopMusic(MUSICRATE/4); //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)
+
+	F_WipeStartScreen();
+	wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
+
+#ifdef HWRENDER
+	// uh..........
+	if (rendermode == render_opengl)
+		F_WipeColorFill(0);
+#endif
+
+	F_WipeEndScreen();
+	F_RunWipe(wipedefs[wipe_speclevel_towhite], false);
+
+	I_OsPolling();
+	I_FinishUpdate(); // page flip or blit buffer
+	if (moviemode)
+		M_SaveFrame();
+
+	nowtime = lastwipetic;
+
+	// Hold on white for extra effect.
+	while (nowtime < endtime)
+	{
+		// wait loop
+		while (!((nowtime = I_GetTime()) - lastwipetic))
+			I_Sleep();
+		lastwipetic = nowtime;
+		if (moviemode) // make sure we save frames for the white hold too
+			M_SaveFrame();
+	}
+}
+
+static void P_RunLevelWipe(void)
+{
+	F_WipeStartScreen();
+	wipestyleflags |= WSF_FADEOUT;
+
+#ifdef HWRENDER
+	// uh..........
+	if (rendermode == render_opengl)
+		F_WipeColorFill(31);
+#endif
+
+	F_WipeEndScreen();
+	// for titlemap: run a specific wipe if specified
+	// needed for exiting time attack
+	if (wipetypepre != INT16_MAX)
+		F_RunWipe(
+		(wipetypepre >= 0 && F_WipeExists(wipetypepre)) ? wipetypepre : wipedefs[wipe_level_toblack],
+			false);
+	wipetypepre = -1;
+}
+
+static void P_InitPlayers(void)
+{
+	UINT8 i;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i])
+			continue;
+
+		// Start players with pity shields if possible
+		players[i].pity = -1;
+
+		players[i].mo = NULL;
+
+		if (!G_PlatformGametype())
+			G_DoReborn(i);
+		else // gametype is GT_COOP or GT_RACE
+		{
+			G_SpawnPlayer(i, players[i].starposttime);
+			if (players[i].starposttime)
+				P_ClearStarPost(players[i].starpostnum);
+		}
+	}
+}
+
+static void P_WriteLetter(void)
+{
+	char *buf, *b;
+
+	if (!unlockables[27].unlocked) // pandora's box
+		return;
+
+	if (modeattacking)
+		return;
+
+#ifndef DEVELOP
+	if (modifiedgame)
+		return;
+#endif
+
+	if (netgame || multiplayer)
+		return;
+
+	if (gamemap != 0x1d35 - 016464)
+		return;
+
+	P_SpawnMobj(0640370000, 0x11000000, 0x3180000, MT_LETTER)->angle = ANGLE_90;
+
+	if (textprompts[199]->page[1].backcolor == 259)
+		return;
+
+	buf = W_CacheLumpName("WATERMAP", PU_STATIC);
+	b = buf;
+
+	while ((*b != 65) && (b - buf < 256))
+	{
+		*b = (*b - 65) & 255;
+		b++;
+	}
+	*b = '\0';
+
+	Z_Free(textprompts[199]->page[1].text);
+	textprompts[199]->page[1].text = Z_StrDup(buf);
+	textprompts[199]->page[1].lines = 4;
+	textprompts[199]->page[1].backcolor = 259;
+	Z_Free(buf);
+}
+
+static void P_InitGametype(void)
+{
+	UINT8 i;
+
+	P_InitPlayers();
+
+	// restore time in netgame (see also g_game.c)
+	if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2)
+	{
+		// is this a hack? maybe
+		tic_t maxstarposttime = 0;
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			if (playeringame[i] && players[i].starposttime > maxstarposttime)
+				maxstarposttime = players[i].starposttime;
+		}
+		leveltime = maxstarposttime;
+	}
+
+	P_WriteLetter();
+
+	if (modeattacking == ATTACKING_RECORD && !demoplayback)
+		P_LoadRecordGhosts();
+	else if (modeattacking == ATTACKING_NIGHTS && !demoplayback)
+		P_LoadNightsGhosts();
+
+	if (G_TagGametype())
+		P_InitTagGametype();
+	else if (gametype == GT_RACE && server)
+		CV_StealthSetValue(&cv_numlaps,
+		(cv_basenumlaps.value)
+			? cv_basenumlaps.value
+			: mapheaderinfo[gamemap - 1]->numlaps);
+}
+
 /** Loads a level from a lump or external wad.
   *
-  * \param skipprecip If true, don't spawn precipitation.
+  * \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot.
   * \todo Clean up, refactor, split up; get rid of the bloat.
   */
-boolean P_SetupLevel(boolean skipprecip)
+boolean P_LoadLevel(boolean fromnetsave)
 {
 	// use gamemap to get map number.
 	// 99% of the things already did, so.
 	// Map header should always be in place at this point
-	INT32 i, loadprecip = 1, ranspecialwipe = 0;
-	INT32 loademblems = 1;
-	INT32 fromnetsave = 0;
+	INT32 i, ranspecialwipe = 0;
 	sector_t *ss;
-	boolean chase;
 	levelloading = true;
 
 	// This is needed. Don't touch.
@@ -2531,19 +2818,18 @@ boolean P_SetupLevel(boolean skipprecip)
 	if (cv_runscripts.value && mapheaderinfo[gamemap-1]->scriptname[0] != '#')
 		P_RunLevelScript(mapheaderinfo[gamemap-1]->scriptname);
 
-	P_LevelInitStuff();
+	P_InitLevelSettings();
 
 	postimgtype = postimgtype2 = postimg_none;
 
 	if (mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0')
 		P_ForceCharacter(mapheaderinfo[gamemap-1]->forcecharacter);
 
-	// chasecam on in chaos, race, coop
-	// chasecam off in match, tag, capture the flag
-	chase = (!(gametyperules & GTR_FIRSTPERSON)) || (maptol & TOL_2D);
-
 	if (!dedicated)
 	{
+		// chasecam on in first-person gametypes and 2D
+		boolean chase = (!(gametyperules & GTR_FIRSTPERSON)) || (maptol & TOL_2D);
+
 		// Salt: CV_ClearChangedFlags() messes with your settings :(
 		/*if (!cv_cam_speed.changed)
 			CV_Set(&cv_cam_speed, cv_cam_speed.defaultvalue);*/
@@ -2570,48 +2856,7 @@ boolean P_SetupLevel(boolean skipprecip)
 		ranspecialwipe = 2;
 	else if (rendermode != render_none && G_IsSpecialStage(gamemap))
 	{
-		tic_t starttime = I_GetTime();
-		tic_t endtime = starttime + (3*TICRATE)/2;
-		tic_t nowtime;
-
-		S_StartSound(NULL, sfx_s3kaf);
-
-		// Fade music! Time it to S3KAF: 0.25 seconds is snappy.
-		if (RESETMUSIC ||
-			strnicmp(S_MusicName(),
-				(mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap-1]->musname : mapmusname, 7))
-			S_FadeOutStopMusic(MUSICRATE/4); //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)
-
-		F_WipeStartScreen();
-		wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
-
-#ifdef HWRENDER
-		// uh..........
-		if (rendermode == render_opengl)
-			F_WipeColorFill(0);
-#endif
-
-		F_WipeEndScreen();
-		F_RunWipe(wipedefs[wipe_speclevel_towhite], false);
-
-		I_OsPolling();
-		I_FinishUpdate(); // page flip or blit buffer
-		if (moviemode)
-			M_SaveFrame();
-
-		nowtime = lastwipetic;
-
-		// Hold on white for extra effect.
-		while (nowtime < endtime)
-		{
-			// wait loop
-			while (!((nowtime = I_GetTime()) - lastwipetic))
-				I_Sleep();
-			lastwipetic = nowtime;
-			if (moviemode) // make sure we save frames for the white hold too
-				M_SaveFrame();
-		}
-
+		P_RunSpecialStageWipe();
 		ranspecialwipe = 1;
 	}
 
@@ -2637,25 +2882,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	// Let's fade to black here
 	// But only if we didn't do the special stage wipe
 	if (rendermode != render_none && !ranspecialwipe)
-	{
-		F_WipeStartScreen();
-		wipestyleflags |= WSF_FADEOUT;
-
-#ifdef HWRENDER
-		// uh..........
-		if (rendermode == render_opengl)
-			F_WipeColorFill(31);
-#endif
-
-		F_WipeEndScreen();
-		// for titlemap: run a specific wipe if specified
-		// needed for exiting time attack
-		if (wipetypepre != INT16_MAX)
-			F_RunWipe(
-				(wipetypepre >= 0 && F_WipeExists(wipetypepre)) ? wipetypepre : wipedefs[wipe_level_toblack],
-				false);
-		wipetypepre = -1;
-	}
+		P_RunLevelWipe();
 
 	if (!titlemapinaction)
 	{
@@ -2713,14 +2940,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	P_InitThinkers();
 	P_InitCachedActions();
 
-	/// \note for not spawning precipitation, etc. when loading netgame snapshots
-	if (skipprecip)
-	{
-		fromnetsave = 1;
-		loadprecip = 0;
-		loademblems = 0;
-	}
-	else if (savedata.lives > 0)
+	if (!fromnetsave && savedata.lives > 0)
 	{
 		numgameovers = savedata.numgameovers;
 		players[consoleplayer].continues = savedata.continues;
@@ -2734,9 +2954,7 @@ boolean P_SetupLevel(boolean skipprecip)
 
 	// internal game map
 	maplumpname = G_BuildMapName(gamemap);
-	//lastloadedmaplumpnum = LUMPERROR;
 	lastloadedmaplumpnum = W_CheckNumForName(maplumpname);
-
 	if (lastloadedmaplumpnum == INT16_MAX)
 		I_Error("Map %s not found.\n", maplumpname);
 
@@ -2746,38 +2964,12 @@ boolean P_SetupLevel(boolean skipprecip)
 	// SRB2 determines the sky texture to be used depending on the map header.
 	P_SetupLevelSky(mapheaderinfo[gamemap-1]->skynum, true);
 
-	numdmstarts = numredctfstarts = numbluectfstarts = 0;
-
-	// reset the player starts
-	for (i = 0; i < MAXPLAYERS; i++)
-		playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL;
-
-	for (i = 0; i < MAX_DM_STARTS; i++)
-		deathmatchstarts[i] = NULL;
-
-	for (i = 0; i < 2; i++)
-		skyboxmo[i] = NULL;
-
-	for (i = 0; i < 16; i++)
-		skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL;
+	P_ResetSpawnpoints();
 
 	P_MapStart();
 
 	if (lastloadedmaplumpnum)
-	{
-		virtres_t* virt = vres_GetMap(lastloadedmaplumpnum);
-
-		P_LoadMapData(virt);
-		P_LoadMapBSP(virt);
-		P_LoadMapLUT(virt);
-
-		P_LoadLineDefs2();
-		P_GroupLines();
-
-		P_MakeMapMD5(virt, &mapmd5);
-
-		vres_Free(virt);
-	}
+		P_LoadMapFromFile();
 
 	// init gravity, tag lists,
 	// anything that P_ResetDynamicSlopes/P_LoadThings needs to know
@@ -2787,7 +2979,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	P_ResetDynamicSlopes(fromnetsave);
 #endif
 
-	P_LoadThings(loademblems);
+	P_SpawnMapThings(!fromnetsave);
 	skyboxmo[0] = skyboxviewpnts[0];
 	skyboxmo[1] = skyboxcenterpnts[0];
 
@@ -2798,7 +2990,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	// set up world state
 	P_SpawnSpecials(fromnetsave);
 
-	if (loadprecip) //  ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
+	if (!fromnetsave) //  ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
 		P_SpawnPrecipitation();
 
 #ifdef HWRENDER // not win32 only 19990829 by Kin
@@ -2826,161 +3018,10 @@ boolean P_SetupLevel(boolean skipprecip)
 	//  none of this needs to be done because it's not the beginning of the map when
 	//  a netgame save is being loaded, and could actively be harmful by messing with
 	//  the client's view of the data.)
-	if (fromnetsave)
-		goto netgameskip;
-	// ==========
-
-	for (i = 0; i < MAXPLAYERS; i++)
-		if (playeringame[i])
-		{
-			// Start players with pity shields if possible
-			players[i].pity = -1;
-
-			if (!G_PlatformGametype())
-			{
-				players[i].mo = NULL;
-				G_DoReborn(i);
-			}
-			else // gametype is GT_COOP or GT_RACE
-			{
-				players[i].mo = NULL;
-
-				if (players[i].starposttime)
-				{
-					G_SpawnPlayer(i, true);
-					P_ClearStarPost(players[i].starpostnum);
-				}
-				else
-					G_SpawnPlayer(i, false);
-			}
-		}
-
-	// restore time in netgame (see also g_game.c)
-	if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2)
-	{
-		// is this a hack? maybe
-		tic_t maxstarposttime = 0;
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i] && players[i].starposttime > maxstarposttime)
-				maxstarposttime = players[i].starposttime;
-		}
-		leveltime = maxstarposttime;
-	}
-
-	if (unlockables[27].unlocked && !modeattacking // pandora's box
-#ifndef DEVELOP
-	&& !modifiedgame
-#endif
-	&& !(netgame || multiplayer) && gamemap == 0x1d35-016464)
-	{
-		P_SpawnMobj(0640370000, 0x11000000, 0x3180000, MT_LETTER)->angle = ANGLE_90;
-		if (textprompts[199]->page[1].backcolor != 259)
-		{
-			char *buf = W_CacheLumpName("WATERMAP", PU_STATIC), *b = buf;
-			while ((*b != 65) && (b-buf < 256)) { *b = (*b - 65)&255; b++; } *b = '\0';
-			Z_Free(textprompts[199]->page[1].text);
-			textprompts[199]->page[1].text = Z_StrDup(buf);
-			textprompts[199]->page[1].lines = 4;
-			textprompts[199]->page[1].backcolor = 259;
-			Z_Free(buf);
-		}
-	}
-
-	if (modeattacking == ATTACKING_RECORD && !demoplayback)
-		P_LoadRecordGhosts();
-	else if (modeattacking == ATTACKING_NIGHTS && !demoplayback)
-		P_LoadNightsGhosts();
-
-	if (G_TagGametype())
-	{
-		INT32 realnumplayers = 0;
-		INT32 playersactive[MAXPLAYERS];
-
-		//I just realized how problematic this code can be.
-		//D_NumPlayers() will not always cover the scope of the netgame.
-		//What if one player is node 0 and the other node 31?
-		//The solution? Make a temp array of all players that are currently playing and pick from them.
-		//Future todo? When a player leaves, shift all nodes down so D_NumPlayers() can be used as intended?
-		//Also, you'd never have to loop through all 32 players slots to find anything ever again.
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i] && !players[i].spectator)
-			{
-				playersactive[realnumplayers] = i; //stores the player's node in the array.
-				realnumplayers++;
-			}
-		}
+	if (!fromnetsave)
+		P_InitGametype();
 
-		if (realnumplayers) //this should also fix the dedicated crash bug. You only pick a player if one exists to be picked.
-		{
-			i = P_RandomKey(realnumplayers);
-			players[playersactive[i]].pflags |= PF_TAGIT; //choose our initial tagger before map starts.
-
-			// Taken and modified from G_DoReborn()
-			// Remove the player so he can respawn elsewhere.
-			// first dissasociate the corpse
-			if (players[playersactive[i]].mo)
-				P_RemoveMobj(players[playersactive[i]].mo);
-
-			G_SpawnPlayer(playersactive[i], false); //respawn the lucky player in his dedicated spawn location.
-		}
-		else
-			CONS_Printf(M_GetText("No player currently available to become IT. Awaiting available players.\n"));
-
-	}
-	else if (gametype == GT_RACE && server)
-		CV_StealthSetValue(&cv_numlaps,
-			(cv_basenumlaps.value)
-			? cv_basenumlaps.value
-			: mapheaderinfo[gamemap - 1]->numlaps);
-
-	// ===========
-	// landing point for netgames.
-	netgameskip:
-
-	if (!dedicated)
-	{
-		P_SetupCamera();
-
-		// Salt: CV_ClearChangedFlags() messes with your settings :(
-		/*if (!cv_cam_height.changed)
-			CV_Set(&cv_cam_height, cv_cam_height.defaultvalue);
-		if (!cv_cam2_height.changed)
-			CV_Set(&cv_cam2_height, cv_cam2_height.defaultvalue);
-
-		if (!cv_cam_dist.changed)
-			CV_Set(&cv_cam_dist, cv_cam_dist.defaultvalue);
-		if (!cv_cam2_dist.changed)
-			CV_Set(&cv_cam2_dist, cv_cam2_dist.defaultvalue);*/
-
-		// Though, I don't think anyone would care about cam_rotate being reset back to the only value that makes sense :P
-		if (!cv_cam_rotate.changed)
-			CV_Set(&cv_cam_rotate, cv_cam_rotate.defaultvalue);
-		if (!cv_cam2_rotate.changed)
-			CV_Set(&cv_cam2_rotate, cv_cam2_rotate.defaultvalue);
-
-		if (!cv_analog.changed)
-			CV_SetValue(&cv_analog, 0);
-		if (!cv_analog2.changed)
-			CV_SetValue(&cv_analog2, 0);
-
-		displayplayer = consoleplayer; // Start with your OWN view, please!
-	}
-
-	if (cv_useranalog.value)
-		CV_SetValue(&cv_analog, true);
-
-	if (splitscreen && cv_useranalog2.value)
-		CV_SetValue(&cv_analog2, true);
-	else if (botingame)
-		CV_SetValue(&cv_analog2, true);
-
-	if (twodlevel)
-	{
-		CV_SetValue(&cv_analog2, false);
-		CV_SetValue(&cv_analog, false);
-	}
+	P_InitCamera();
 
 	// clear special respawning que
 	iquehead = iquetail = 0;
@@ -3015,7 +3056,7 @@ boolean P_SetupLevel(boolean skipprecip)
 
 	lastmaploaded = gamemap; // HAS to be set after saving!!
 
-	if (loadprecip) // uglier hack
+	if (!fromnetsave) // uglier hack
 	{ // to make a newly loaded level start on the second frame.
 		INT32 buf = gametic % BACKUPTICS;
 		for (i = 0; i < MAXPLAYERS; i++)
diff --git a/src/p_setup.h b/src/p_setup.h
index c95dd0f630cc516aec9509e73a921609a025a05f..de791677c2af47ff947af0d6dd032f9ebfc0f866 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -97,7 +97,7 @@ void P_SetupLevelSky(INT32 skynum, boolean global);
 void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum);
 #endif
 void P_LoadThingsOnly(void);
-boolean P_SetupLevel(boolean skipprecip);
+boolean P_LoadLevel(boolean fromnetsave);
 boolean P_AddWadFile(const char *wadfilename);
 boolean P_RunSOC(const char *socfilename);
 void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num);
diff --git a/src/p_slopes.c b/src/p_slopes.c
index 89d265fa4d352009f66482afd475377dac5bebc8..f35e7d7326d2b670fac532d734fb42758b9e5f08 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -553,11 +553,8 @@ pslope_t *P_SlopeById(UINT16 id)
 }
 
 /// Reset slopes and read them from special lines.
-void P_ResetDynamicSlopes(const UINT32 fromsave) {
+void P_ResetDynamicSlopes(const boolean fromsave) {
 	size_t i;
-
-	boolean spawnthinkers = !(boolean)fromsave;
-
 	slopelist = NULL;
 	slopecount = 0;
 
@@ -574,14 +571,14 @@ void P_ResetDynamicSlopes(const UINT32 fromsave) {
 			case 711:
 			case 712:
 			case 713:
-				line_SpawnViaLine(i, spawnthinkers);
+				line_SpawnViaLine(i, !fromsave);
 				break;
 
 			case 704:
 			case 705:
 			case 714:
 			case 715:
-				line_SpawnViaVertexes(i, spawnthinkers);
+				line_SpawnViaVertexes(i, !fromsave);
 				break;
 
 			default:
diff --git a/src/p_slopes.h b/src/p_slopes.h
index 076fd87969454ca0746f7bed2758ea13ca2c1879..96764051b428cf013fb503db03fff1b6de5ff23c 100644
--- a/src/p_slopes.h
+++ b/src/p_slopes.h
@@ -23,7 +23,7 @@ extern UINT16 slopecount;
 void P_LinkSlopeThinkers (void);
 
 void P_CalculateSlopeNormal(pslope_t *slope);
-void P_ResetDynamicSlopes(const UINT32 fromsave);
+void P_ResetDynamicSlopes(const boolean fromsave);
 
 //
 // P_CopySectorSlope
diff --git a/src/p_spec.c b/src/p_spec.c
index 0b0ba940d699a7725969787bcd6796a92943f5be..20f1c8278f518bc26748ffbf957ba76fa3d80cfb 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -6426,7 +6426,7 @@ static void P_ApplyFlatAlignment(line_t *master, sector_t *sector, angle_t flata
   *       as they'll just be erased by UnArchiveThinkers.
   * \sa P_SpawnPrecipitation, P_SpawnFriction, P_SpawnPushers, P_SpawnScrollers
   */
-void P_SpawnSpecials(INT32 fromnetsave)
+void P_SpawnSpecials(boolean fromnetsave)
 {
 	sector_t *sector;
 	size_t i;
diff --git a/src/p_spec.h b/src/p_spec.h
index 14d3ebb59a5ed5ca6ad5894d039c5c1f64e15e4c..630bcb3de1045cdc9eec2077a6972c0662167bea 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -35,7 +35,7 @@ void P_SetupLevelFlatAnims(void);
 
 // at map load
 void P_InitSpecials(void);
-void P_SpawnSpecials(INT32 fromnetsave);
+void P_SpawnSpecials(boolean fromnetsave);
 
 // every tic
 void P_UpdateSpecials(void);