diff --git a/src/d_main.c b/src/d_main.c
index 7b62435883689fbfd9ff3da2c3d0ea538156b753..37637edd60c708a01034385c62bd9945b6ffd356 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -129,6 +129,7 @@ char srb2home[256] = ".";
 char srb2path[256] = ".";
 boolean usehome = true;
 const char *pandf = "%s" PATHSEP "%s";
+static char addonsdir[MAX_WADPATH];
 
 //
 // EVENT HANDLING
@@ -1039,7 +1040,6 @@ void D_SRB2Main(void)
 			// can't use sprintf since there is %u in savegamename
 			strcatbf(savegamename, srb2home, PATHSEP);
 
-			I_mkdir(srb2home, 0700);
 #else
 			snprintf(srb2home, sizeof srb2home, "%s", userhome);
 			snprintf(downloaddir, sizeof downloaddir, "%s", userhome);
@@ -1056,6 +1056,10 @@ void D_SRB2Main(void)
 		configfile[sizeof configfile - 1] = '\0';
 	}
 
+	// Create addons dir
+	snprintf(addonsdir, sizeof addonsdir, "%s%s%s", srb2home, PATHSEP, "addons");
+	I_mkdir(addonsdir, 0755);
+
 	// rand() needs seeded regardless of password
 	srand((unsigned int)time(NULL));
 
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 6b870091ebbc076d47ff18485b9afbf2a044e07c..7d5963ecc1ebc1601e36adee3743c8cdfb5cc420 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -637,6 +637,8 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_screenshot_folder);
 	CV_RegisterVar(&cv_screenshot_colorprofile);
 	CV_RegisterVar(&cv_moviemode);
+	CV_RegisterVar(&cv_movie_option);
+	CV_RegisterVar(&cv_movie_folder);
 	// PNG variables
 	CV_RegisterVar(&cv_zlib_level);
 	CV_RegisterVar(&cv_zlib_memory);
diff --git a/src/g_game.c b/src/g_game.c
index 7a876e968542b8f47e71198d47665e6e5916976a..a3025f9491f2b2c756229c275ae55916b5f82b7c 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2100,7 +2100,7 @@ static inline void G_PlayerFinishLevel(INT32 player)
 // G_PlayerReborn
 // Called after a player dies. Almost everything is cleared and initialized.
 //
-void G_PlayerReborn(INT32 player)
+void G_PlayerReborn(INT32 player, boolean betweenmaps)
 {
 	player_t *p;
 	INT32 score;
@@ -2205,7 +2205,7 @@ void G_PlayerReborn(INT32 player)
 	bot = players[player].bot;
 	pity = players[player].pity;
 
-	if (!G_IsSpecialStage(gamemap))
+	if (betweenmaps || !G_IsSpecialStage(gamemap))
 	{
 		rings = (ultimatemode ? 0 : mapheaderinfo[gamemap-1]->startrings);
 		spheres = 0;
@@ -2284,6 +2284,28 @@ void G_PlayerReborn(INT32 player)
 	//if ((netgame || multiplayer) && !p->spectator) -- moved into P_SpawnPlayer to account for forced changes there
 		//p->powers[pw_flashing] = flashingtics-1; // Babysitting deterrent
 
+	// Check to make sure their color didn't change somehow...
+	if (G_GametypeHasTeams())
+	{
+		if (p->ctfteam == 1 && p->skincolor != skincolor_redteam)
+		{
+			if (p == &players[consoleplayer])
+				CV_SetValue(&cv_playercolor, skincolor_redteam);
+			else if (p == &players[secondarydisplayplayer])
+				CV_SetValue(&cv_playercolor2, skincolor_redteam);
+		}
+		else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam)
+		{
+			if (p == &players[consoleplayer])
+				CV_SetValue(&cv_playercolor, skincolor_blueteam);
+			else if (p == &players[secondarydisplayplayer])
+				CV_SetValue(&cv_playercolor2, skincolor_blueteam);
+		}
+	}
+
+	if (betweenmaps)
+		return;
+
 	if (p-players == consoleplayer)
 	{
 		if (mapmusflags & MUSIC_RELOADRESET)
@@ -2303,9 +2325,6 @@ void G_PlayerReborn(INT32 player)
 	if (gametype == GT_COOP)
 		P_FindEmerald(); // scan for emeralds to hunt for
 
-	// Reset Nights score and max link to 0 on death
-	p->marescore = p->maxlink = 0;
-
 	// If NiGHTS, find lowest mare to start with.
 	p->mare = P_FindLowestMare();
 
@@ -2313,27 +2332,6 @@ void G_PlayerReborn(INT32 player)
 
 	if (p->mare == 255)
 		p->mare = 0;
-
-	p->marelap = p->marebonuslap = 0;
-
-	// Check to make sure their color didn't change somehow...
-	if (G_GametypeHasTeams())
-	{
-		if (p->ctfteam == 1 && p->skincolor != skincolor_redteam)
-		{
-			if (p == &players[consoleplayer])
-				CV_SetValue(&cv_playercolor, skincolor_redteam);
-			else if (p == &players[secondarydisplayplayer])
-				CV_SetValue(&cv_playercolor2, skincolor_redteam);
-		}
-		else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam)
-		{
-			if (p == &players[consoleplayer])
-				CV_SetValue(&cv_playercolor, skincolor_blueteam);
-			else if (p == &players[secondarydisplayplayer])
-				CV_SetValue(&cv_playercolor2, skincolor_blueteam);
-		}
-	}
 }
 
 //
diff --git a/src/g_game.h b/src/g_game.h
index e161bc8ed4a7cf11eefe93bbf6715825426b582c..df1301dd7c392071a14fa66870c9f8fc7f099883 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -100,7 +100,7 @@ extern INT32 localaiming, localaiming2; // should be an angle_t but signed
 //
 void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo);
 void G_DoReborn(INT32 playernum);
-void G_PlayerReborn(INT32 player);
+void G_PlayerReborn(INT32 player, boolean betweenmaps);
 void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer,
 	boolean skipprecutscene, boolean FLS);
 char *G_BuildMapTitle(INT32 mapnum);
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 7b6367cf33fba40174d08ab75fd3925dae33f2a0..db1c1f72702f78791b06ae08e90a5cdd41fc180a 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1202,7 +1202,7 @@ static UINT8 P_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t *p
 	if (!md2 || !skin)
 		return 0;
 
-	if ((unsigned)(spr2 & ~FF_SPR2SUPER) >= free_spr2)
+	if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2)
 		return 0;
 
 	while (!(md2->model->spr2frames[spr2*2 + 1])
diff --git a/src/info.c b/src/info.c
index e627b949d9fbb6a9e9c89474a41800e2bcf3bec8..dae028f6250658de118cae049f97547a3288ad44 100644
--- a/src/info.c
+++ b/src/info.c
@@ -739,7 +739,7 @@ state_t states[NUMSTATES] =
 
 	// CA_FLY/CA_SWIM
 	{SPR_PLAY, SPR2_FLY ,                 2, {NULL}, 0,  0, S_PLAY_FLY},  // S_PLAY_FLY
-	{SPR_PLAY, SPR2_SWIM,                 2, {NULL}, 0,  0, S_PLAY_SWIM}, // S_PLAY_SWIM
+	{SPR_PLAY, SPR2_SWIM,                 4, {NULL}, 0,  0, S_PLAY_SWIM}, // S_PLAY_SWIM
 	{SPR_PLAY, SPR2_TIRE,                12, {NULL}, 0,  0, S_PLAY_FLY_TIRED}, // S_PLAY_FLY_TIRED
 
 	// CA_GLIDEANDCLIMB
@@ -10041,7 +10041,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SPAWNCEILING|MF_NOGRAVITY,  // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SPAWNCEILING|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10068,7 +10068,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		8,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SPECIAL|MF_SCENERY,     // flags
+		MF_SPECIAL|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10089,13 +10089,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		29*FRACUNIT,     // radius
+		29*FRACUNIT,    // radius
 		40*FRACUNIT,    // height
 		0,              // display offset
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY,     // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10116,13 +10116,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		30*FRACUNIT,     // radius
+		30*FRACUNIT,    // radius
 		53*FRACUNIT,    // height
 		0,              // display offset
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY,     // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10143,13 +10143,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		28*FRACUNIT,     // radius
+		28*FRACUNIT,    // radius
 		41*FRACUNIT,    // height
 		0,              // display offset
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY,     // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10170,13 +10170,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		56*FRACUNIT,     // radius
-		112*FRACUNIT,    // height
+		56*FRACUNIT,    // radius
+		112*FRACUNIT,   // height
 		0,              // display offset
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY,     // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10197,13 +10197,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		56*FRACUNIT,     // radius
-		112*FRACUNIT,    // height
+		56*FRACUNIT,    // radius
+		112*FRACUNIT,   // height
 		0,              // display offset
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY,     // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10230,13 +10230,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY,     // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
 	{           // MT_KELP
 		1007,           // doomednum
-		S_KELP, // spawnstate
+		S_KELP,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10251,13 +10251,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,     // radius
-		292*FRACUNIT,    // height
+		16*FRACUNIT,    // radius
+		292*FRACUNIT,   // height
 		0,              // display offset
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SCENERY|MF_NOBLOCKMAP,     // flags
+		MF_SCENERY|MF_NOBLOCKMAP, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10284,7 +10284,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4,               // mass
 		0,               // damage
 		sfx_None,        // activesound
-		MF_NOCLIP|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_RUNSPAWNFUNC,     // flags
+		MF_NOCLIP|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10311,13 +10311,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIP|MF_NOBLOCKMAP|MF_NOGRAVITY,     // flags
+		MF_NOCLIP|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
 	{           // MT_DSZSTALAGMITE
 		1008,           // doomednum
-		S_DSZSTALAGMITE, // spawnstate
+		S_DSZSTALAGMITE,// spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10333,12 +10333,12 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		8*FRACUNIT,     // radius
-		116*FRACUNIT,    // height
+		116*FRACUNIT,   // height
 		0,              // display offset
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SCENERY|MF_SOLID,     // flags
+		MF_SCENERY|MF_SOLID, // flags
 		S_NULL          // raisestate
 	},
 
@@ -10360,18 +10360,18 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		8*FRACUNIT,     // radius
-		116*FRACUNIT,    // height
+		116*FRACUNIT,   // height
 		0,              // display offset
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SCENERY|MF_SOLID,     // flags
+		MF_SCENERY|MF_SOLID, // flags
 		S_NULL          // raisestate
 	},
 
 	{           // MT_LIGHTBEAM
 		1010,           // doomednum
-		S_LIGHTBEAM1, // spawnstate
+		S_LIGHTBEAM1,   // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10386,7 +10386,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,     // radius
+		16*FRACUNIT,    // radius
 		16*FRACUNIT,    // height
 		0,              // display offset
 		4,              // mass
@@ -10452,7 +10452,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_FLAMEPARTICLE
 		-1,             // doomednum
-		S_FLAMEPARTICLE, // spawnstate
+		S_FLAMEPARTICLE,// spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11737,7 +11737,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		24*FRACUNIT,    // radius
-		224*FRACUNIT,    // height
+		224*FRACUNIT,   // height
 		0,              // display offset
 		DMG_SPIKE,      // mass
 		0,              // damage
@@ -11764,7 +11764,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		24*FRACUNIT,    // radius
-		256*FRACUNIT,    // height
+		256*FRACUNIT,   // height
 		0,              // display offset
 		DMG_SPIKE,      // mass
 		0,              // damage
diff --git a/src/m_menu.c b/src/m_menu.c
index d05048a9d44ffccbad62a60e87f344cf19da7db7..ef5d01ce6df23ff633d20b8907838e78152e7fb1 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -118,7 +118,7 @@ const char *quitmsg[NUM_QUITMESSAGES];
 
 // Stuff for customizing the player select screen Tails 09-22-2003
 description_t description[MAXSKINS];
-INT16 char_on = -1, startchar = 1;
+INT16 char_on = -1, startchar = 0;
 static char *char_notes = NULL;
 static fixed_t char_scroll = 0;
 
@@ -1363,36 +1363,39 @@ static menuitem_t OP_ScreenshotOptionsMenu[] =
 {
 	{IT_HEADER, NULL, "General", NULL, 0},
 	{IT_STRING|IT_CVAR, NULL, "Use color profile", &cv_screenshot_colorprofile,     6},
-	{IT_STRING|IT_CVAR, NULL, "Storage Location",  &cv_screenshot_option,          11},
-	{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_screenshot_folder, 16},
 
-	{IT_HEADER, NULL, "Screenshots (F8)", NULL, 30},
-	{IT_STRING|IT_CVAR, NULL, "Memory Level",      &cv_zlib_memory,                36},
-	{IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_level,                 41},
-	{IT_STRING|IT_CVAR, NULL, "Strategy",          &cv_zlib_strategy,              46},
-	{IT_STRING|IT_CVAR, NULL, "Window Size",       &cv_zlib_window_bits,           51},
-
-	{IT_HEADER, NULL, "Movie Mode (F9)", NULL, 60},
-	{IT_STRING|IT_CVAR, NULL, "Capture Mode",      &cv_moviemode,                  66},
-
-	{IT_STRING|IT_CVAR, NULL, "Region Optimizing", &cv_gif_optimize,               71},
-	{IT_STRING|IT_CVAR, NULL, "Downscaling",       &cv_gif_downscale,              76},
-
-	{IT_STRING|IT_CVAR, NULL, "Memory Level",      &cv_zlib_memorya,               71},
-	{IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_levela,                76},
-	{IT_STRING|IT_CVAR, NULL, "Strategy",          &cv_zlib_strategya,             81},
-	{IT_STRING|IT_CVAR, NULL, "Window Size",       &cv_zlib_window_bitsa,          86},
+	{IT_HEADER, NULL, "Screenshots (F8)", NULL, 16},
+	{IT_STRING|IT_CVAR, NULL, "Storage Location",  &cv_screenshot_option,          22},
+	{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_screenshot_folder, 27},
+	{IT_STRING|IT_CVAR, NULL, "Memory Level",      &cv_zlib_memory,                42},
+	{IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_level,                 47},
+	{IT_STRING|IT_CVAR, NULL, "Strategy",          &cv_zlib_strategy,              52},
+	{IT_STRING|IT_CVAR, NULL, "Window Size",       &cv_zlib_window_bits,           57},
+
+	{IT_HEADER, NULL, "Movie Mode (F9)", NULL, 64},
+	{IT_STRING|IT_CVAR, NULL, "Storage Location",  &cv_movie_option,              70},
+	{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_movie_folder, 	  75},
+	{IT_STRING|IT_CVAR, NULL, "Capture Mode",      &cv_moviemode,                 90},
+
+	{IT_STRING|IT_CVAR, NULL, "Region Optimizing", &cv_gif_optimize,              95},
+	{IT_STRING|IT_CVAR, NULL, "Downscaling",       &cv_gif_downscale,             100},
+
+	{IT_STRING|IT_CVAR, NULL, "Memory Level",      &cv_zlib_memorya,              95},
+	{IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_levela,               100},
+	{IT_STRING|IT_CVAR, NULL, "Strategy",          &cv_zlib_strategya,            105},
+	{IT_STRING|IT_CVAR, NULL, "Window Size",       &cv_zlib_window_bitsa,         110},
 };
 
 enum
 {
 	op_screenshot_colorprofile = 1,
-	op_screenshot_folder = 3,
-	op_screenshot_capture = 10,
-	op_screenshot_gif_start = 11,
-	op_screenshot_gif_end = 12,
-	op_screenshot_apng_start = 13,
-	op_screenshot_apng_end = 16,
+	op_screenshot_folder = 4,
+	op_movie_folder = 11,
+	op_screenshot_capture = 12,
+	op_screenshot_gif_start = 13,
+	op_screenshot_gif_end = 14,
+	op_screenshot_apng_start = 15,
+	op_screenshot_apng_end = 18,
 };
 
 static menuitem_t OP_EraseDataMenu[] =
@@ -2210,6 +2213,12 @@ void Addons_option_Onchange(void)
 		(cv_addons_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED);
 }
 
+void Moviemode_option_Onchange(void)
+{
+	OP_ScreenshotOptionsMenu[op_movie_folder].status =
+		(cv_movie_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED);
+}
+
 // ==========================================================================
 // END ORGANIZATION STUFF.
 // ==========================================================================
diff --git a/src/m_menu.h b/src/m_menu.h
index 347725e10ba3262ef64eb76073031fb15849fb69..4292ea58104714e9488502714c6aa7a55ba35fbd 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -397,6 +397,9 @@ void Screenshot_option_Onchange(void);
 // Addons menu updating
 void Addons_option_Onchange(void);
 
+// Moviemode menu updating
+void Moviemode_option_Onchange(void);
+
 // These defines make it a little easier to make menus
 #define DEFAULTMENUSTYLE(id, header, source, prev, x, y)\
 {\
diff --git a/src/m_misc.c b/src/m_misc.c
index 22e19df73d567000facee5d666af31c579ec86c5..aaaf30d67ff4263967f693f8126ff6b72b1b6f8c 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -108,6 +108,9 @@ consvar_t cv_screenshot_colorprofile = {"screenshot_colorprofile", "Yes", CV_SAV
 static CV_PossibleValue_t moviemode_cons_t[] = {{MM_GIF, "GIF"}, {MM_APNG, "aPNG"}, {MM_SCREENSHOT, "Screenshots"}, {0, NULL}};
 consvar_t cv_moviemode = {"moviemode_mode", "GIF", CV_SAVE|CV_CALL, moviemode_cons_t, Moviemode_mode_Onchange, 0, NULL, NULL, 0, 0, NULL};
 
+consvar_t cv_movie_option = {"movie_option", "Default", CV_SAVE|CV_CALL, screenshot_cons_t, Moviemode_option_Onchange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_movie_folder = {"movie_folder", "", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
+
 static CV_PossibleValue_t zlib_mem_level_t[] = {
 	{1, "(Min Memory) 1"},
 	{2, "2"}, {3, "3"}, {4, "4"}, {5, "5"}, {6, "6"}, {7, "7"},
@@ -1124,19 +1127,25 @@ static inline moviemode_t M_StartMovieGIF(const char *pathname)
 void M_StartMovie(void)
 {
 #if NUMSCREENS > 2
-	const char *pathname = ".";
+	char pathname[MAX_WADPATH];
 
 	if (moviemode)
 		return;
 
-	if (cv_screenshot_option.value == 0)
-		pathname = usehome ? srb2home : srb2path;
-	else if (cv_screenshot_option.value == 1)
-		pathname = srb2home;
-	else if (cv_screenshot_option.value == 2)
-		pathname = srb2path;
-	else if (cv_screenshot_option.value == 3 && *cv_screenshot_folder.string != '\0')
-		pathname = cv_screenshot_folder.string;
+	if (cv_movie_option.value == 0)
+		strcpy(pathname, usehome ? srb2home : srb2path);
+	else if (cv_movie_option.value == 1)
+		strcpy(pathname, srb2home);
+	else if (cv_movie_option.value == 2)
+		strcpy(pathname, srb2path);
+	else if (cv_movie_option.value == 3 && *cv_movie_folder.string != '\0')
+		strcpy(pathname, cv_movie_folder.string);
+
+	if (cv_movie_option.value != 3)
+	{
+		strcat(pathname, PATHSEP"movies"PATHSEP);
+		I_mkdir(pathname, 0755);
+	}
 
 	if (rendermode == render_none)
 		I_Error("Can't make a movie without a render system\n");
@@ -1474,7 +1483,8 @@ void M_ScreenShot(void)
 void M_DoScreenShot(void)
 {
 #if NUMSCREENS > 2
-	const char *freename = NULL, *pathname = ".";
+	const char *freename = NULL;
+	char pathname[MAX_WADPATH];
 	boolean ret = false;
 	UINT8 *linear = NULL;
 
@@ -1486,13 +1496,19 @@ void M_DoScreenShot(void)
 		return;
 
 	if (cv_screenshot_option.value == 0)
-		pathname = usehome ? srb2home : srb2path;
+		strcpy(pathname, usehome ? srb2home : srb2path);
 	else if (cv_screenshot_option.value == 1)
-		pathname = srb2home;
+		strcpy(pathname, srb2home);
 	else if (cv_screenshot_option.value == 2)
-		pathname = srb2path;
+		strcpy(pathname, srb2path);
 	else if (cv_screenshot_option.value == 3 && *cv_screenshot_folder.string != '\0')
-		pathname = cv_screenshot_folder.string;
+		strcpy(pathname, cv_screenshot_folder.string);
+
+	if (cv_screenshot_option.value != 3)
+	{
+		strcat(pathname, PATHSEP"screenshots"PATHSEP);
+		I_mkdir(pathname, 0755);
+	}
 
 #ifdef USE_PNG
 	freename = Newsnapshotfile(pathname,"png");
diff --git a/src/m_misc.h b/src/m_misc.h
index 6ac92dbcc085324c5c5bcba48c8f22fd2824c85f..7038e3e48d94cbed3c1bbb9ee71be9f35e33a942 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -30,7 +30,7 @@ typedef enum {
 extern moviemode_t moviemode;
 
 extern consvar_t cv_screenshot_option, cv_screenshot_folder, cv_screenshot_colorprofile;
-extern consvar_t cv_moviemode;
+extern consvar_t cv_moviemode, cv_movie_folder, cv_movie_option;
 extern consvar_t cv_zlib_memory, cv_zlib_level, cv_zlib_strategy, cv_zlib_window_bits;
 extern consvar_t cv_zlib_memorya, cv_zlib_levela, cv_zlib_strategya, cv_zlib_window_bitsa;
 extern consvar_t cv_apng_delay;
diff --git a/src/p_map.c b/src/p_map.c
index 28bfd280665b7c45b203d2afd9d6d80502adbb09..01c41131b83df9a8ee268c91f2cd89faae6fd843 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -1019,7 +1019,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			return true; // underneath
 		if (tmthing->flags & MF_SHOOTABLE && thing->health > 0)
 		{
-			UINT8 damagetype = (thing->info->mass & 0xFF);
+			UINT32 damagetype = (thing->info->mass & 0xFF);
 			if (!damagetype && thing->flags & MF_FIRE) // BURN!
 				damagetype = DMG_FIRE;
 			if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8)))
@@ -1036,7 +1036,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			return true; // underneath
 		if (thing->flags & MF_SHOOTABLE && tmthing->health > 0)
 		{
-			UINT8 damagetype = (tmthing->info->mass & 0xFF);
+			UINT32 damagetype = (tmthing->info->mass & 0xFF);
 			if (!damagetype && tmthing->flags & MF_FIRE) // BURN!
 				damagetype = DMG_FIRE;
 			if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8)))
@@ -2881,11 +2881,8 @@ static boolean P_ThingHeightClip(mobj_t *thing)
 			thing->z = thing->ceilingz - thing->height;
 	}
 
-	if (thing->z != oldz)
-	{
-		if (thing->player)
-			P_PlayerHitFloor(thing->player, !onfloor);
-	}
+	if (P_MobjFlip(thing)*(thing->z - oldz) > 0 && thing->player)
+		P_PlayerHitFloor(thing->player, !onfloor);
 
 	// debug: be sure it falls to the floor
 	thing->eflags &= ~MFE_ONGROUND;
@@ -3199,7 +3196,7 @@ static boolean P_IsClimbingValid(player_t *player, angle_t angle)
 				&& glidesector->sector->ceilingpic == skyflatnum)
 				return false;
 
-			if ((player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale) < ceilingz)
+			if ((player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale) < floorz)
 				|| (player->mo->z >= ceilingz))
 				floorclimb = true;
 		}
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 44c6b9f6ebb388f518943902744a1327318022c6..4cede843c6ad4ac827b154c9883cf4340ced7d88 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -2381,22 +2381,42 @@ boolean P_CheckDeathPitCollide(mobj_t *mo)
 
 boolean P_CheckSolidLava(mobj_t *mo, ffloor_t *rover)
 {
+	fixed_t topheight;
+
 	I_Assert(mo != NULL);
 	I_Assert(!P_MobjWasRemoved(mo));
 
+	// not a lava block with solid planes
+	if (!(rover->flags & FF_SWIMMABLE && GETSECSPECIAL(rover->master->frontsector->special, 1) == 3
+		&& !(rover->master->flags & ML_BLOCKMONSTERS)))
+		return false;
+
+	// is solid from the sides
+	if (rover->master->flags & ML_EFFECT3)
+		return true;
+
+	if (mo->eflags & MFE_VERTICALFLIP)
 	{
-		fixed_t topheight =
+		topheight =
 	#ifdef ESLOPE
-			*rover->t_slope ? P_GetZAt(*rover->t_slope, mo->x, mo->y) :
+			*rover->b_slope ? P_GetZAt(*rover->b_slope, mo->x, mo->y) :
 	#endif
-			*rover->topheight;
+			*rover->bottomheight;
 
-		if (rover->flags & FF_SWIMMABLE && GETSECSPECIAL(rover->master->frontsector->special, 1) == 3
-			&& !(rover->master->flags & ML_BLOCKMONSTERS)
-			&& ((rover->master->flags & ML_EFFECT3) || mo->z-mo->momz > topheight - FixedMul(16*FRACUNIT, mo->scale)))
+		if (mo->z+mo->height-mo->momz < topheight + FixedMul(16*FRACUNIT, mo->scale))
 				return true;
+		return false;
 	}
 
+	topheight =
+	#ifdef ESLOPE
+		*rover->t_slope ? P_GetZAt(*rover->t_slope, mo->x, mo->y) :
+	#endif
+		*rover->topheight;
+
+	if (mo->z-mo->momz > topheight - FixedMul(16*FRACUNIT, mo->scale))
+		return true;
+
 	return false;
 }
 
@@ -3981,6 +4001,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
 	}
 	else
 	{
+#if 0 // i don't know why this is here, it's causing a few undesired state glitches, and disabling it doesn't appear to negatively affect the game, but i don't want it gone permanently just in case some obscure bug crops up
 		if (!(mobj->player->powers[pw_carry] == CR_NIGHTSMODE)) // used for drilling
 			mobj->player->pflags &= ~PF_STARTJUMP;
 		mobj->player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
@@ -3990,6 +4011,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
 			mobj->player->powers[pw_tailsfly] = 0;
 			P_SetPlayerMobjState(mobj, S_PLAY_WALK);
 		}
+#endif
 		mobj->eflags &= ~MFE_JUSTHITFLOOR;
 	}
 
@@ -10412,7 +10434,7 @@ void P_SpawnPlayer(INT32 playernum)
 	mobj_t *mobj;
 
 	if (p->playerstate == PST_REBORN)
-		G_PlayerReborn(playernum);
+		G_PlayerReborn(playernum, false);
 
 	// spawn as spectator determination
 	if (!G_GametypeHasSpectators())
diff --git a/src/p_setup.c b/src/p_setup.c
index 0753f01d3840c965974115b31534b2643cb9237e..c83c8cd5cefaefecbb763c15f7a5742d64ea8cf5 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2246,6 +2246,8 @@ static void P_LevelInitStuff(void)
 
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
+		G_PlayerReborn(i, true);
+
 		if (canresetlives && (netgame || multiplayer) && playeringame[i] && (gametype == GT_COMPETITION || players[i].lives <= 0))
 		{
 			// In Co-Op, replenish a user's lives if they are depleted.
@@ -2253,42 +2255,18 @@ static void P_LevelInitStuff(void)
 		}
 
 		// obliteration station...
-		players[i].spheres =\
-		 players[i].xtralife = players[i].deadtimer =\
-		 players[i].numboxes = players[i].totalring =\
-		 players[i].laps = players[i].aiming =\
-		 players[i].losstime = players[i].timeshit =\
-		 players[i].marescore = players[i].lastmarescore =\
-		 players[i].maxlink = players[i].startedtime =\
-		 players[i].finishedtime = players[i].finishedspheres =\
-		 players[i].finishedrings = players[i].lastmare =\
-		 players[i].lastmarelap = players[i].lastmarebonuslap =\
-		 players[i].totalmarelap = players[i].totalmarebonuslap =\
-		 players[i].marebegunat = players[i].textvar =\
-		 players[i].texttimer = players[i].linkcount =\
-		 players[i].linktimer = players[i].flyangle =\
-		 players[i].anotherflyangle = players[i].nightstime =\
-		 players[i].oldscale = players[i].mare = players[i].marelap =\
-		 players[i].marebonuslap = players[i].lapbegunat =\
-		 players[i].lapstartedtime = players[i].totalmarescore =\
-		 players[i].realtime = players[i].exiting = 0;
-
-		// i guess this could be part of the above but i feel mildly uncomfortable implicitly casting
-		players[i].gotcontinue = false;
-
-		// aha, the first evidence this shouldn't be a memset!
+		players[i].numboxes = players[i].totalring =\
+		 players[i].laps = players[i].marescore = players[i].lastmarescore =\
+		 players[i].mare = players[i].exiting = 0;
+
 		players[i].drillmeter = 40*20;
-		players[i].rings = (ultimatemode ? 0 : mapheaderinfo[gamemap-1]->startrings);
 
-		P_ResetPlayer(&players[i]);
 		// hit these too
-		players[i].pflags &= ~(PF_GAMETYPEOVER|PF_TRANSFERTOCLOSEST);
-
-		// unset ALL the pointers. P_SetTarget isn't needed here because if this
-		// function is being called we're just going to clobber the data anyways
-		players[i].mo = players[i].followmobj = players[i].awayviewmobj =\
-		players[i].capsule = players[i].axis1 = players[i].axis2 = players[i].drone = NULL;
+		players[i].pflags &= ~(PF_GAMETYPEOVER);
 	}
+
+	if (botingame)
+		CV_SetValue(&cv_analog2, true);
 }
 
 //
diff --git a/src/p_user.c b/src/p_user.c
index 43138d2e959c466967938eebe0039bff14ef5016..0438075f2da09fa8944aa5547fc21f9ff8e481ad 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -1109,7 +1109,7 @@ boolean P_PlayerCanDamage(player_t *player, mobj_t *thing)
 	}
 	else if (P_MobjFlip(player->mo)*(topheight - (thing->z + thing->height/2)) < 0)
 	{
-		if (player->charability == CA_FLY && player->panim == PA_ABILITY && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) > 0))
+		if (player->charability == CA_FLY && player->panim == PA_ABILITY && !(player->mo->eflags & MFE_UNDERWATER) && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) > 0))
 			return true;
 	}
 
@@ -2217,147 +2217,152 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
 				player->pflags &= ~PF_SPINNING;
 		}
 
-		if (player->pflags & PF_SPINNING)
+		if (player->pflags & PF_BOUNCING)
 		{
-			if (player->mo->state-states != S_PLAY_ROLL && !(player->pflags & PF_STARTDASH))
+			if (dorollstuff && player->mo->state-states != S_PLAY_BOUNCE_LANDING)
 			{
-				P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
-				S_StartSound(player->mo, sfx_spin);
+				P_MobjCheckWater(player->mo);
+				player->mo->momz *= -1;
+				P_DoAbilityBounce(player, true);
+				if (player->scoreadd)
+					player->scoreadd--;
 			}
+			clipmomz = false;
 		}
-		else if (player->pflags & PF_GLIDING) // ground gliding
+		else
 		{
-			if (dorollstuff)
+			if (player->pflags & PF_SPINNING)
 			{
-				player->skidtime = TICRATE;
-				player->mo->tics = -1;
+				if (player->mo->state-states != S_PLAY_ROLL && !(player->pflags & PF_STARTDASH))
+				{
+					P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
+					S_StartSound(player->mo, sfx_spin);
+				}
 			}
-			else if (!player->skidtime)
-				player->pflags &= ~PF_GLIDING;
-		}
-		else if (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
-		{
-			if (player->mo->state-states != S_PLAY_MELEE_LANDING)
+			else if (player->pflags & PF_GLIDING) // ground gliding
 			{
-				mobjtype_t type = player->revitem;
-				P_SetPlayerMobjState(player->mo, S_PLAY_MELEE_LANDING);
-				player->mo->tics = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
-				S_StartSound(player->mo, sfx_s3k8b);
-				player->pflags |= PF_FULLSTASIS;
-
-				// hearticles
-				if (type)
+				if (dorollstuff)
 				{
-					UINT8 i = 0;
-					angle_t throwang = -(2*ANG30);
-					fixed_t xo = P_ReturnThrustX(player->mo, player->drawangle, 16*player->mo->scale);
-					fixed_t yo = P_ReturnThrustY(player->mo, player->drawangle, 16*player->mo->scale);
-					fixed_t zo = 6*player->mo->scale;
-					fixed_t mu = FixedMul(player->maxdash, player->mo->scale);
-					fixed_t mu2 = FixedHypot(player->mo->momx, player->mo->momy);
-					fixed_t ev;
-					mobj_t *missile = NULL;
-					if (mu2 < mu)
-						mu2 = mu;
-					ev = (50*FRACUNIT - (mu/25))/50;
-					while (i < 5)
-					{
-						missile = P_SpawnMobjFromMobj(player->mo, xo, yo, zo, type);
-						P_SetTarget(&missile->target, player->mo);
-						missile->angle = throwang + player->drawangle;
-						P_Thrust(missile, player->drawangle + ANGLE_90,
-							P_ReturnThrustY(missile, throwang, mu)); // side to side component
-						P_Thrust(missile, player->drawangle, mu2); // forward component
-						P_SetObjectMomZ(missile, (4 + ((i&1)<<1))*FRACUNIT, true);
-						missile->momz += player->mo->pmomz;
-						missile->fuse = TICRATE/2;
-						missile->extravalue2 = ev;
-
-						i++;
-						throwang += ANG30;
-					}
-					if (mobjinfo[type].seesound && missile)
-						S_StartSound(missile, missile->info->seesound);
+					player->skidtime = TICRATE;
+					player->mo->tics = -1;
 				}
+				else if (!player->skidtime)
+					player->pflags &= ~PF_GLIDING;
 			}
-		}
-		else if (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2)
-			;
-		else if (player->panim != PA_IDLE && player->panim != PA_WALK && player->panim != PA_RUN && player->panim != PA_DASH)
-		{
-			if (player->cmomx || player->cmomy)
+			else if (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
 			{
-				if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH)
-					P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
-				else if (player->speed >= FixedMul(player->runspeed, player->mo->scale)
-				&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
-					P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
-				else if ((player->rmomx || player->rmomy)
-				&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
-					P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
-				else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE)
-					P_SetPlayerMobjState(player->mo, S_PLAY_STND);
+				if (player->mo->state-states != S_PLAY_MELEE_LANDING)
+				{
+					mobjtype_t type = player->revitem;
+					P_SetPlayerMobjState(player->mo, S_PLAY_MELEE_LANDING);
+					player->mo->tics = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
+					S_StartSound(player->mo, sfx_s3k8b);
+					player->pflags |= PF_FULLSTASIS;
+
+					// hearticles
+					if (type)
+					{
+						UINT8 i = 0;
+						angle_t throwang = -(2*ANG30);
+						fixed_t xo = P_ReturnThrustX(player->mo, player->drawangle, 16*player->mo->scale);
+						fixed_t yo = P_ReturnThrustY(player->mo, player->drawangle, 16*player->mo->scale);
+						fixed_t zo = 6*player->mo->scale;
+						fixed_t mu = FixedMul(player->maxdash, player->mo->scale);
+						fixed_t mu2 = FixedHypot(player->mo->momx, player->mo->momy);
+						fixed_t ev;
+						mobj_t *missile = NULL;
+						if (mu2 < mu)
+							mu2 = mu;
+						ev = (50*FRACUNIT - (mu/25))/50;
+						while (i < 5)
+						{
+							missile = P_SpawnMobjFromMobj(player->mo, xo, yo, zo, type);
+							P_SetTarget(&missile->target, player->mo);
+							missile->angle = throwang + player->drawangle;
+							P_Thrust(missile, player->drawangle + ANGLE_90,
+								P_ReturnThrustY(missile, throwang, mu)); // side to side component
+							P_Thrust(missile, player->drawangle, mu2); // forward component
+							P_SetObjectMomZ(missile, (4 + ((i&1)<<1))*FRACUNIT, true);
+							missile->momz += player->mo->pmomz;
+							missile->fuse = TICRATE/2;
+							missile->extravalue2 = ev;
+
+							i++;
+							throwang += ANG30;
+						}
+						if (mobjinfo[type].seesound && missile)
+							S_StartSound(missile, missile->info->seesound);
+					}
+				}
 			}
-			else
+			else if (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2)
+				;
+			else if (player->panim != PA_IDLE && player->panim != PA_WALK && player->panim != PA_RUN && player->panim != PA_DASH)
 			{
-				if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH)
-					P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
-				else if (player->speed >= FixedMul(player->runspeed, player->mo->scale)
-				&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
-					P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
-				else if ((player->mo->momx || player->mo->momy)
-				&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
-					P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
-				else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE)
-					P_SetPlayerMobjState(player->mo, S_PLAY_STND);
+				if (player->cmomx || player->cmomy)
+				{
+					if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH)
+						P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
+					else if (player->speed >= FixedMul(player->runspeed, player->mo->scale)
+					&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
+						P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
+					else if ((player->rmomx || player->rmomy)
+					&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
+						P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
+					else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE)
+						P_SetPlayerMobjState(player->mo, S_PLAY_STND);
+				}
+				else
+				{
+					if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH)
+						P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
+					else if (player->speed >= FixedMul(player->runspeed, player->mo->scale)
+					&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
+						P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
+					else if ((player->mo->momx || player->mo->momy)
+					&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
+						P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
+					else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE)
+						P_SetPlayerMobjState(player->mo, S_PLAY_STND);
+				}
 			}
-		}
 
-		if (!(player->pflags & PF_GLIDING))
-			player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
-		player->pflags &= ~(PF_STARTJUMP|PF_THOKKED|PF_CANCARRY/*|PF_GLIDING*/);
-		player->secondjump = 0;
-		player->glidetime = 0;
-		player->climbing = 0;
-		player->powers[pw_tailsfly] = 0;
-
-		if (player->pflags & PF_SHIELDABILITY)
-		{
-			player->pflags &= ~PF_SHIELDABILITY;
+			if (!(player->pflags & PF_GLIDING))
+				player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
+			player->pflags &= ~(PF_STARTJUMP|PF_THOKKED|PF_CANCARRY/*|PF_GLIDING*/);
+			player->secondjump = 0;
+			player->glidetime = 0;
+			player->climbing = 0;
+			player->powers[pw_tailsfly] = 0;
 
-			if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack.
+			if (player->pflags & PF_SHIELDABILITY)
 			{
-				if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound
-					S_StartSound(player->mo, sfx_s3k4c);
-				else // create a fire pattern on the ground
+				player->pflags &= ~PF_SHIELDABILITY;
+
+				if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack.
+				{
+					if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound
+						S_StartSound(player->mo, sfx_s3k4c);
+					else // create a fire pattern on the ground
+					{
+						S_StartSound(player->mo, sfx_s3k47);
+						P_ElementalFire(player, true);
+					}
+					P_SetObjectMomZ(player->mo,
+					(player->mo->eflags & MFE_UNDERWATER)
+					? 6*FRACUNIT/5
+					: 5*FRACUNIT/2,
+					false);
+					P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
+					player->mo->momx = player->mo->momy = 0;
+					clipmomz = false;
+				}
+				else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) // Bubble shield's bounce attack.
 				{
-					S_StartSound(player->mo, sfx_s3k47);
-					P_ElementalFire(player, true);
+					P_DoBubbleBounce(player);
+					clipmomz = false;
 				}
-				P_SetObjectMomZ(player->mo,
-				(player->mo->eflags & MFE_UNDERWATER)
-				? 6*FRACUNIT/5
-				: 5*FRACUNIT/2,
-				false);
-				P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
-				player->mo->momx = player->mo->momy = 0;
-				clipmomz = false;
 			}
-			else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) // Bubble shield's bounce attack.
-			{
-				P_DoBubbleBounce(player);
-				clipmomz = false;
-			}
-		}
-
-		if (player->pflags & PF_BOUNCING)
-		{
-			P_MobjCheckWater(player->mo);
-			player->mo->momz *= -1;
-			P_DoAbilityBounce(player, true);
-			if (player->scoreadd)
-				player->scoreadd--;
-			clipmomz = false;
 		}
 	}
 
@@ -2964,22 +2969,19 @@ static void P_DoBubbleBreath(player_t *player)
 		P_SetScale(bubble, bubble->destscale);
 	}
 
-	if (player->powers[pw_carry] == CR_NIGHTSMODE) // NiGHTS Super doesn't spawn flight bubbles
-		return;
-
 	// Tails stirs up the water while flying in it
 	if (player->powers[pw_tailsfly] && (leveltime & 1) && player->charability != CA_SWIM)
 	{
-		fixed_t radius = (3*player->mo->radius)>>1;
+		fixed_t radius = player->mo->radius;
 		angle_t fa = ((leveltime%45)*FINEANGLES/8) & FINEMASK;
 		fixed_t stirwaterx = FixedMul(FINECOSINE(fa),radius);
 		fixed_t stirwatery = FixedMul(FINESINE(fa),radius);
 		fixed_t stirwaterz;
 
 		if (player->mo->eflags & MFE_VERTICALFLIP)
-			stirwaterz = player->mo->z + player->mo->height - FixedDiv(player->mo->height,3*FRACUNIT/2);
+			stirwaterz = player->mo->z + player->mo->height - (4<<FRACBITS);
 		else
-			stirwaterz = player->mo->z + FixedDiv(player->mo->height,3*FRACUNIT/2);
+			stirwaterz = player->mo->z + (4<<FRACBITS);
 
 		bubble = P_SpawnMobj(
 			player->mo->x + stirwaterx,
@@ -8083,7 +8085,7 @@ static void P_MovePlayer(player_t *player)
 	}
 	else if (player->pflags & PF_BOUNCING)
 	{
-		if (!(player->pflags & PF_JUMPDOWN) || (onground && P_MobjFlip(player->mo)*player->mo->momz <= 0)) // If not holding the jump button OR on flat ground
+		if (!(player->pflags & PF_JUMPDOWN)) // If not holding the jump button
 		{
 			P_ResetPlayer(player); // down, stop bouncing.
 			player->pflags |= PF_THOKKED;
@@ -8183,12 +8185,18 @@ static void P_MovePlayer(player_t *player)
 					if (P_MobjFlip(player->mo)*player->mo->momz < FixedMul(5*actionspd, player->mo->scale))
 						P_SetObjectMomZ(player->mo, actionspd/2, true);
 
+					P_SetPlayerMobjState(player->mo, player->mo->state->nextstate);
+
 					player->fly1--;
 				}
 			}
 
 			// Tails Put-Put noise
-			if (player->charability == CA_FLY && player->bot != 1 && leveltime % 10 == 0 && !player->spectator)
+			if (player->charability == CA_FLY
+				&& player->bot != 1
+				&& !(player->mo->eflags & MFE_UNDERWATER)
+				&& leveltime % 10 == 0
+				&& !player->spectator)
 				S_StartSound(player->mo, sfx_putput);
 
 			// Descend
@@ -8200,11 +8208,12 @@ static void P_MovePlayer(player_t *player)
 		else
 		{
 			// Tails-gets-tired Stuff
-			if (player->panim == PA_ABILITY)
+			if (player->panim == PA_ABILITY && player->mo->state-states != S_PLAY_FLY_TIRED)
 				P_SetPlayerMobjState(player->mo, S_PLAY_FLY_TIRED);
 
 			if (player->charability == CA_FLY && (leveltime % 10 == 0)
 				&& player->mo->state-states == S_PLAY_FLY_TIRED
+				&& !(player->mo->eflags & MFE_UNDERWATER)
 				&& !player->spectator)
 				S_StartSound(player->mo, sfx_pudpud);
 		}
@@ -10604,7 +10613,8 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails)
 	angle_t horizangle = player->drawangle;
 	fixed_t zoffs = 0;
 	fixed_t backwards = -1*FRACUNIT;
-	boolean doroll = (player->panim == PA_ROLL || player->panim == PA_JUMP);
+	boolean doswim = (player->panim == PA_ABILITY && (player->mo->eflags & MFE_UNDERWATER));
+	boolean doroll = (player->panim == PA_ROLL || (player->panim == PA_JUMP && !(player->charflags & SF_NOJUMPSPIN)) || doswim);
 	angle_t rollangle;
 	boolean panimchange;
 	INT32 ticnum = 0;
@@ -10631,17 +10641,25 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails)
 			if (testval < FRACUNIT)
 				testval = FRACUNIT;
 		}
-		if (smilesonground && !player->mo->reactiontime)
+
+		if (doswim)
+			zdist = player->mo->momz<<1;
+		else if (smilesonground && !player->mo->reactiontime)
 			zdist = (player->mo->z - tails->threshold);
 		else
 			zdist = player->mo->momz;
+
 		rollangle = R_PointToAngle2(0, 0, testval, -P_MobjFlip(player->mo)*zdist);
-		zoffs = 3*FRACUNIT + 12*FINESINE(rollangle >> ANGLETOFINESHIFT);
-		backwards = -12*FINECOSINE(rollangle >> ANGLETOFINESHIFT);
+
+		if (!doswim)
+		{
+			zoffs = 3*FRACUNIT + 12*FINESINE(rollangle >> ANGLETOFINESHIFT);
+			backwards = -12*FINECOSINE(rollangle >> ANGLETOFINESHIFT);
+		}
 	}
 	else if (player->panim == PA_RUN)
 		backwards = -5*FRACUNIT;
-	else if (player->panim == PA_SPRING)
+	else if (player->panim == PA_SPRING || player->panim == PA_JUMP)
 	{
 		zoffs += 4*FRACUNIT;
 		backwards /= 2;
@@ -10663,7 +10681,7 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails)
 		zoffs = -7*FRACUNIT;
 		backwards = -9*FRACUNIT;
 	}
-	else if (player->mo->sprite2 == SPR2_FLY || player->mo->sprite2 == SPR2_TIRE)
+	else if (player->panim == PA_ABILITY)
 		backwards = -5*FRACUNIT;
 
 	// sprite...
@@ -10680,7 +10698,7 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails)
 		else
 			chosenstate = S_TAILSOVERLAY_0DEGREES;
 	}
-	else if (player->panim == PA_SPRING)
+	else if (player->panim == PA_SPRING || player->panim == PA_JUMP)
 		chosenstate = S_TAILSOVERLAY_MINUS60DEGREES;
 	else if (player->panim == PA_FALL || player->mo->state-states == S_PLAY_RIDE)
 		chosenstate = S_TAILSOVERLAY_PLUS60DEGREES;
@@ -10703,6 +10721,8 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails)
 	}
 	else if (player->mo->sprite2 == SPR2_FLY)
 		chosenstate = S_TAILSOVERLAY_FLY;
+	else if (player->mo->sprite2 == SPR2_SWIM)
+		chosenstate = S_TAILSOVERLAY_FLY;
 	else if (player->mo->sprite2 == SPR2_TIRE)
 		chosenstate = S_TAILSOVERLAY_TIRE;
 	else if (player->panim == PA_ABILITY2)
@@ -10728,8 +10748,10 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails)
 		}
 	}
 
+#if 0
 	if (player->fly1 != 0 && player->powers[pw_tailsfly] != 0 && !smilesonground)
 		P_SetMobjState(tails, chosenstate);
+#endif
 
 	// animation...
 	if (player->panim == PA_SPRING || player->panim == PA_FALL || player->mo->state-states == S_PLAY_RIDE)
@@ -10744,7 +10766,7 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails)
 	else if (player->mo->state-states == S_PLAY_GASP)
 		tails->tics = -1;
 	else if (player->mo->sprite2 == SPR2_TIRE)
-		ticnum = 4;
+		ticnum = (doswim ? 2 : 4);
 	else if (player->panim != PA_IDLE)
 		ticnum = player->mo->tics;
 
@@ -10967,7 +10989,8 @@ void P_PlayerThink(player_t *player)
 
 	if (player->exiting == 2 || countdown2 == 2)
 	{
-		if (cv_playersforexit.value) // Count to be sure everyone's exited
+		UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value);
+		if (numneeded) // Count to be sure everyone's exited
 		{
 			INT32 i, total = 0, exiting = 0;
 
@@ -10983,7 +11006,7 @@ void P_PlayerThink(player_t *player)
 					exiting++;
 			}
 
-			if (!total || ((4*exiting)/total) >= cv_playersforexit.value)
+			if (!total || ((4*exiting)/total) >= numneeded)
 			{
 				if (server)
 					SendNetXCmd(XD_EXITLEVEL, NULL, 0);
@@ -11269,8 +11292,8 @@ void P_PlayerThink(player_t *player)
 		{
 			boolean currentlyonground = P_IsObjectOnGround(player->mo);
 
-			if (!player->powers[pw_carry]
-			&& ((player->pflags & (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE)) == (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE))
+			if (!player->powers[pw_carry] && !player->powers[pw_nocontrol]
+			&& ((player->pflags & (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE|PF_STASIS)) == (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE))
 			&& !(cmd->forwardmove || cmd->sidemove)
 			&& (player->rmomx || player->rmomy)
 			&& (!player->capsule || (player->capsule->reactiontime != (player-players)+1)))
@@ -11281,7 +11304,7 @@ void P_PlayerThink(player_t *player)
 				if (!currentlyonground)
 					acceleration /= 2;
 				// fake skidding! see P_SkidStuff for reference on conditionals
-				else if (!player->skidtime && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID) && P_AproxDistance(player->mo->momx, player->mo->momy) >= FixedMul(player->runspeed/2, player->mo->scale))
+				else if (!player->skidtime && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID) && P_AproxDistance(player->mo->momx, player->mo->momy) >= FixedMul(player->runspeed, player->mo->scale)) // modified from player->runspeed/2 'cuz the skid was just TOO frequent ngl
 				{
 					if (player->mo->state-states != S_PLAY_SKID)
 						P_SetPlayerMobjState(player->mo, S_PLAY_SKID);
@@ -11812,6 +11835,8 @@ void P_PlayerAfterThink(player_t *player)
 				{
 					if (player->mo->state-states != S_PLAY_RIDE)
 						P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
+					if ((tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER))
+						tails->player->powers[pw_tailsfly] = 0;
 				}
 				else
 					P_SetTarget(&player->mo->tracer, NULL);
diff --git a/src/r_data.c b/src/r_data.c
index 496f6bdd7eb0f22a8f3bb761a1b7a96ca5e4885f..8594c85954d8133145a371643e7cca62a16bbc5f 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -23,6 +23,7 @@
 #include "z_zone.h"
 #include "p_setup.h" // levelflats
 #include "v_video.h" // pMasterPalette
+#include "byteptr.h"
 #include "dehacked.h"
 
 #ifdef _WIN32
@@ -2819,18 +2820,14 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t
 	UINT8 *imgptr = imgbuf;
 	UINT8 *colpointers, *startofspan;
 
-	#define WRITE8(buf, a) ({*buf = (a); buf++;})
-	#define WRITE16(buf, a) ({*buf = (a)&255; buf++; *buf = (a)>>8; buf++;})
-	#define WRITE32(buf, a) ({WRITE16(buf, (a)&65535); WRITE16(buf, (a)>>16);})
-
 	if (!raw)
 		I_Error("R_PNGToPatch: conversion failed");
 
 	// Write image size and offset
-	WRITE16(imgptr, width);
-	WRITE16(imgptr, height);
-	WRITE16(imgptr, leftoffset);
-	WRITE16(imgptr, topoffset);
+	WRITEINT16(imgptr, width);
+	WRITEINT16(imgptr, height);
+	WRITEINT16(imgptr, leftoffset);
+	WRITEINT16(imgptr, topoffset);
 
 	// Leave placeholder to column pointers
 	colpointers = imgptr;
@@ -2845,7 +2842,7 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t
 
 		//printf("%d ", x);
 		// Write column pointer (@TODO may be wrong)
-		WRITE32(colpointers, imgptr - imgbuf);
+		WRITEINT32(colpointers, imgptr - imgbuf);
 
 		// Write pixels
 		for (y = 0; y < height; y++)
@@ -2857,7 +2854,7 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t
 			if (!opaque)
 			{
 				if (startofspan)
-					WRITE8(imgptr, 0);
+					WRITEUINT8(imgptr, 0);
 				startofspan = NULL;
 				continue;
 			}
@@ -2869,15 +2866,15 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t
 
 				// If we reached the span size limit, finish the previous span
 				if (startofspan)
-					WRITE8(imgptr, 0);
+					WRITEUINT8(imgptr, 0);
 
 				if (y > 254)
 				{
 					// Make sure we're aligned to 254
 					if (lastStartY < 254)
 					{
-						WRITE8(imgptr, 254);
-						WRITE8(imgptr, 0);
+						WRITEUINT8(imgptr, 254);
+						WRITEUINT8(imgptr, 0);
 						imgptr += 2;
 						lastStartY = 254;
 					}
@@ -2887,15 +2884,15 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t
 
 					while (writeY > 254)
 					{
-						WRITE8(imgptr, 254);
-						WRITE8(imgptr, 0);
+						WRITEUINT8(imgptr, 254);
+						WRITEUINT8(imgptr, 0);
 						imgptr += 2;
 						writeY -= 254;
 					}
 				}
 
 				startofspan = imgptr;
-				WRITE8(imgptr, writeY);///@TODO calculate starting y pos
+				WRITEUINT8(imgptr, writeY);///@TODO calculate starting y pos
 				imgptr += 2;
 				spanSize = 0;
 
@@ -2903,21 +2900,17 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t
 			}
 
 			// Write the pixel
-			WRITE8(imgptr, paletteIndex);
+			WRITEUINT8(imgptr, paletteIndex);
 			spanSize++;
 			startofspan[1] = spanSize;
 		}
 
 		if (startofspan)
-			WRITE8(imgptr, 0);
+			WRITEUINT8(imgptr, 0);
 
-		WRITE8(imgptr, 0xFF);
+		WRITEUINT8(imgptr, 0xFF);
 	}
 
-	#undef WRITE8
-	#undef WRITE16
-	#undef WRITE32
-
 	size = imgptr-imgbuf;
 	img = Z_Malloc(size, PU_STATIC, NULL);
 	memcpy(img, imgbuf, size);
diff --git a/src/r_things.c b/src/r_things.c
index 392821869fd18c3b0975de7069bd40a5f47aac7a..43e006a30ac6bcaaee6529cbf03690b7404da799 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -2524,7 +2524,7 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player)
 	if (!skin)
 		return 0;
 
-	if ((unsigned)(spr2 & ~FF_SPR2SUPER) >= free_spr2)
+	if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2)
 		return 0;
 
 	while (!(skin->sprites[spr2].numframes)
diff --git a/src/sdl/i_main.c b/src/sdl/i_main.c
index f54f0d7c5496226d84c84a9636bdb899dbdd4414..2a67e88fe227c781b50e0b83487357e587a9485b 100644
--- a/src/sdl/i_main.c
+++ b/src/sdl/i_main.c
@@ -26,6 +26,8 @@
 #include <unistd.h>
 #endif
 
+#include "time.h" // For log timestamps
+
 #ifdef HAVE_SDL
 
 #ifdef HAVE_TTF
@@ -114,6 +116,7 @@ int main(int argc, char **argv)
 #endif
 {
 	const char *logdir = NULL;
+	char logfile[MAX_WADPATH];
 	myargc = argc;
 	myargv = argv; /// \todo pull out path to exe from this string
 
@@ -125,15 +128,35 @@ int main(int argc, char **argv)
 #endif
 #endif
 
-	logdir = D_Home();
-
 #ifdef LOGMESSAGES
+	if (!M_CheckParm("-nolog"))
+	{
+		logdir = D_Home();
+
+		time_t my_time;
+		struct tm * timeinfo;
+		char buf[26];
+		my_time = time(NULL);
+		timeinfo = localtime(&my_time);
+
+		strftime(buf, 26, "%Y-%m-%d %H-%M-%S", timeinfo);
+		strcpy(logfile, va("log-%s.txt", buf));
+
 #ifdef DEFAULTDIR
-	if (logdir)
-		logstream = fopen(va("%s/"DEFAULTDIR"/log.txt",logdir), "wt");
-	else
+		if (logdir)
+		{
+			// Create dirs here because D_SRB2Main() is too late.
+			I_mkdir(va("%s%s"DEFAULTDIR, logdir, PATHSEP), 0755);
+			I_mkdir(va("%s%s"DEFAULTDIR"%slogs",logdir, PATHSEP, PATHSEP), 0755);
+			logstream = fopen(va("%s%s"DEFAULTDIR"%slogs%s%s",logdir, PATHSEP, PATHSEP, PATHSEP, logfile), "wt");
+		}
+		else
 #endif
-		logstream = fopen("./log.txt", "wt");
+		{
+			I_mkdir("."PATHSEP"logs"PATHSEP, 0755);
+			logstream = fopen(va("."PATHSEP"logs"PATHSEP"%s", logfile), "wt");
+		}
+	}
 #endif
 
 	//I_OutputMsg("I_StartupSystem() ...\n");
@@ -161,6 +184,7 @@ int main(int argc, char **argv)
 	CONS_Printf("Setting up SRB2...\n");
 	D_SRB2Main();
 	CONS_Printf("Entering main game loop...\n");
+	CONS_Printf("%s\n", logfile);
 	// never return
 	D_SRB2Loop();
 
diff --git a/src/st_stuff.c b/src/st_stuff.c
index c4f1327c0058a465e86170d5cf80319499acd6fc..20a132b3a19aecb158ba428e7a66460480e1de71 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2100,39 +2100,43 @@ static void ST_drawTextHUD(void)
 			textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game"))
 	}
 
-	if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && stplyr->exiting && cv_playersforexit.value)
+	if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && stplyr->exiting)
 	{
-		INT32 i, total = 0, exiting = 0;
-
-		for (i = 0; i < MAXPLAYERS; i++)
+		UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value);
+		if (numneeded)
 		{
-			if (!playeringame[i] || players[i].spectator)
-				continue;
-			if (players[i].lives <= 0)
-				continue;
+			INT32 i, total = 0, exiting = 0;
 
-			total++;
-			if (players[i].exiting)
-				exiting++;
-		}
+			for (i = 0; i < MAXPLAYERS; i++)
+			{
+				if (!playeringame[i] || players[i].spectator)
+					continue;
+				if (players[i].lives <= 0)
+					continue;
 
-		if (cv_playersforexit.value != 4)
-		{
-			total *= cv_playersforexit.value;
-			if (total & 3)
-				total += 4; // round up
-			total /= 4;
-		}
+				total++;
+				if (players[i].exiting)
+					exiting++;
+			}
 
-		if (exiting < total)
-		{
-			if (!splitscreen && !donef12)
+			if (numneeded != 4)
 			{
-				textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
-				donef12 = true;
+				total *= cv_playersforexit.value;
+				if (total & 3)
+					total += 4; // round up
+				total /= 4;
+			}
+
+			if (exiting < total)
+			{
+				if (!splitscreen && !donef12)
+				{
+					textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
+					donef12 = true;
+				}
+				total -= exiting;
+				textHUDdraw(va(M_GetText("%d player%s remaining"), total, ((total == 1) ? "" : "s")))
 			}
-			total -= exiting;
-			textHUDdraw(va(M_GetText("%d player%s remaining"), total, ((total == 1) ? "" : "s")))
 		}
 	}
 	else if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (!stplyr->spectator))
diff --git a/src/w_wad.c b/src/w_wad.c
index 9688de3284674a2d3c504c9fa4dd7adeeec65104..4d08b26dc03b03b0be41e4a70a23e736db5ba1ea 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1686,81 +1686,165 @@ void W_VerifyFileMD5(UINT16 wadfilenum, const char *matchmd5)
 #endif
 }
 
-// Note: This never opens lumps themselves and therefore doesn't have to
-// deal with compressed lumps.
-static int W_VerifyFile(const char *filename, lumpchecklist_t *checklist,
-	boolean status)
+// Verify versions for different archive
+// formats. checklist assumed to be valid.
+
+static int
+W_VerifyName (const char *name, lumpchecklist_t *checklist, boolean status)
 {
-	FILE *handle;
-	size_t i, j;
-	int goodfile = false;
+	size_t j;
+	for (j = 0; checklist[j].len && checklist[j].name; ++j)
+	{
+		if (( strncmp(name, checklist[j].name,
+						checklist[j].len) != false ) == status)
+		{
+			return true;
+		}
+	}
+	return false;
+}
 
-	if (!checklist)
-		I_Error("No checklist for %s\n", filename);
-	// open wad file
-	if ((handle = W_OpenWadFile(&filename, false)) == NULL)
-		return -1;
+static int
+W_VerifyWAD (FILE *fp, lumpchecklist_t *checklist, boolean status)
+{
+	size_t i;
 
-	// detect wad file by the absence of the other supported extensions
-	if (stricmp(&filename[strlen(filename) - 4], ".soc")
-#ifdef HAVE_BLUA
-	&& stricmp(&filename[strlen(filename) - 4], ".lua")
-#endif
-	&& stricmp(&filename[strlen(filename) - 4], ".pk3"))
-	{
-		// assume wad file
-		wadinfo_t header;
-		filelump_t lumpinfo;
+	// assume wad file
+	wadinfo_t header;
+	filelump_t lumpinfo;
 
-		// read the header
-		if (fread(&header, 1, sizeof header, handle) == sizeof header
+	// read the header
+	if (fread(&header, 1, sizeof header, fp) == sizeof header
 			&& header.numlumps < INT16_MAX
 			&& strncmp(header.identification, "ZWAD", 4)
 			&& strncmp(header.identification, "IWAD", 4)
 			&& strncmp(header.identification, "PWAD", 4)
 			&& strncmp(header.identification, "SDLL", 4))
-		{
-			fclose(handle);
+	{
+		return true;
+	}
+
+	header.numlumps = LONG(header.numlumps);
+	header.infotableofs = LONG(header.infotableofs);
+
+	// let seek to the lumpinfo list
+	if (fseek(fp, header.infotableofs, SEEK_SET) == -1)
+		return true;
+
+	for (i = 0; i < header.numlumps; i++)
+	{
+		// fill in lumpinfo for this wad file directory
+		if (fread(&lumpinfo, sizeof (lumpinfo), 1 , fp) != 1)
 			return true;
-		}
 
-		header.numlumps = LONG(header.numlumps);
-		header.infotableofs = LONG(header.infotableofs);
+		lumpinfo.filepos = LONG(lumpinfo.filepos);
+		lumpinfo.size = LONG(lumpinfo.size);
 
-		// let seek to the lumpinfo list
-		if (fseek(handle, header.infotableofs, SEEK_SET) == -1)
-		{
-			fclose(handle);
+		if (lumpinfo.size == 0)
+			continue;
+
+		if (! W_VerifyName(lumpinfo.name, checklist, status))
 			return false;
-		}
+	}
+
+	return true;
+}
+
+static int
+W_VerifyPK3 (FILE *fp, lumpchecklist_t *checklist, boolean status)
+{
+    zend_t zend;
+    zentry_t zentry;
+
+	UINT16 numlumps;
+	size_t i;
+
+	char pat_central[] = {0x50, 0x4b, 0x01, 0x02, 0x00};
+	char pat_end[] = {0x50, 0x4b, 0x05, 0x06, 0x00};
 
-		goodfile = true;
-		for (i = 0; i < header.numlumps; i++)
+	char lumpname[9];
+
+	// Haha the ResGetLumpsZip function doesn't
+	// check for file errors, so neither will I.
+
+	// Central directory bullshit
+
+	fseek(fp, 0, SEEK_END);
+	if (!ResFindSignature(fp, pat_end, max(0, ftell(fp) - (22 + 65536))))
+		return true;
+
+	fseek(fp, -4, SEEK_CUR);
+	if (fread(&zend, 1, sizeof zend, fp) < sizeof zend)
+		return true;
+
+	numlumps = zend.entries;
+
+	fseek(fp, zend.cdiroffset, SEEK_SET);
+	for (i = 0; i < numlumps; i++)
+	{
+		char* fullname;
+		char* trimname;
+		char* dotpos;
+
+		if (fread(&zentry, 1, sizeof(zentry_t), fp) < sizeof(zentry_t))
+			return true;
+		if (memcmp(zentry.signature, pat_central, 4))
+			return true;
+
+		fullname = malloc(zentry.namelen + 1);
+		if (fgets(fullname, zentry.namelen + 1, fp) != fullname)
+			return true;
+
+		// Strip away file address and extension for the 8char name.
+		if ((trimname = strrchr(fullname, '/')) != 0)
+			trimname++;
+		else
+			trimname = fullname; // Care taken for root files.
+
+		if (*trimname) // Ignore directories
 		{
-			// fill in lumpinfo for this wad file directory
-			if (fread(&lumpinfo, sizeof (lumpinfo), 1 , handle) != 1)
-			{
-				fclose(handle);
-				return -1;
-			}
+			if ((dotpos = strrchr(trimname, '.')) == 0)
+				dotpos = fullname + strlen(fullname); // Watch for files without extension.
 
-			lumpinfo.filepos = LONG(lumpinfo.filepos);
-			lumpinfo.size = LONG(lumpinfo.size);
+			memset(lumpname, '\0', 9); // Making sure they're initialized to 0. Is it necessary?
+			strncpy(lumpname, trimname, min(8, dotpos - trimname));
 
-			if (lumpinfo.size == 0)
-				continue;
+			if (! W_VerifyName(lumpname, checklist, status))
+				return false;
+		}
 
-			for (j = 0; j < NUMSPRITES; j++)
-				if (sprnames[j] && !strncmp(lumpinfo.name, sprnames[j], 4)) // Sprites
-					continue;
+		free(fullname);
+	}
+
+	return true;
+}
+
+// Note: This never opens lumps themselves and therefore doesn't have to
+// deal with compressed lumps.
+static int W_VerifyFile(const char *filename, lumpchecklist_t *checklist,
+	boolean status)
+{
+	FILE *handle;
+	int goodfile = false;
 
-			goodfile = false;
-			for (j = 0; checklist[j].len && checklist[j].name && !goodfile; j++)
-				if ((strncmp(lumpinfo.name, checklist[j].name, checklist[j].len) != false) == status)
-					goodfile = true;
+	if (!checklist)
+		I_Error("No checklist for %s\n", filename);
+	// open wad file
+	if ((handle = W_OpenWadFile(&filename, false)) == NULL)
+		return -1;
 
-			if (!goodfile)
-				break;
+	if (stricmp(&filename[strlen(filename) - 4], ".pk3") == 0)
+		goodfile = W_VerifyPK3(handle, checklist, status);
+	else
+	{
+		// detect wad file by the absence of the other supported extensions
+		if (stricmp(&filename[strlen(filename) - 4], ".soc")
+#ifdef HAVE_BLUA
+		&& stricmp(&filename[strlen(filename) - 4], ".lua")
+#endif
+		)
+		{
+			goodfile = W_VerifyWAD(handle, checklist, status);
 		}
 	}
 	fclose(handle);
diff --git a/src/y_inter.c b/src/y_inter.c
index 1cb1b9cd80a0bf7263aa9b98e89f13e47f542d1e..0d6a3d03cff6b9e7676838d7a7a9d93e18cbdbcc 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -261,7 +261,7 @@ void Y_IntermissionDrawer(void)
 
 			// draw time
 			ST_DrawPatchFromHud(HUD_TIME, sbotime);
-			if (cv_timetic.value == 1)
+			if (cv_timetic.value == 3)
 				ST_DrawNumFromHud(HUD_SECONDS, data.coop.tics);
 			else
 			{
@@ -275,8 +275,7 @@ void Y_IntermissionDrawer(void)
 				ST_DrawPatchFromHud(HUD_TIMECOLON, sbocolon); // Colon
 				ST_DrawPadNumFromHud(HUD_SECONDS, seconds, 2); // Seconds
 
-				// we should show centiseconds on the intermission screen too, if the conditions are right.
-				if (modeattacking || cv_timetic.value == 2)
+				if (cv_timetic.value == 1 || cv_timetic.value == 2 || modeattacking) // there's not enough room for tics in splitscreen, don't even bother trying!
 				{
 					ST_DrawPatchFromHud(HUD_TIMETICCOLON, sboperiod); // Period
 					ST_DrawPadNumFromHud(HUD_TICS, tictrn, 2); // Tics