diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 9513d23bf722c36a141fc0f0cc8952542ca9686f..f4b262bacdad5b80bfb19d515b78424b40c06320 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3044,8 +3044,6 @@ FILESTAMP
 				case PT_SERVERREFUSE: // negative response of client join request
 					if (server && serverrunning)
 					{ // but wait I thought I'm the server?
-						if (I_Shun)
-							I_Shun(node); // No more garbage from you!
 						Net_CloseConnection(node);
 						break;
 					}
@@ -3070,8 +3068,6 @@ FILESTAMP
 
 					if (server && serverrunning && node != servernode)
 					{ // but wait I thought I'm the server?
-						if (I_Shun)
-							I_Shun(node); // No more garbage from you!
 						Net_CloseConnection(node);
 						break;
 					}
@@ -3122,8 +3118,6 @@ FILESTAMP
 				case PT_FILEFRAGMENT:
 					if (server)
 					{ // but wait I thought I'm the server?
-						if (I_Shun)
-							I_Shun(node); // No more garbage from you!
 						Net_CloseConnection(node);
 						break;
 					}
@@ -3147,8 +3141,6 @@ FILESTAMP
 						break;
 				default:
 					DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
-					if (I_Shun)
-						I_Shun(node); // No more garbage from you!
 					Net_CloseConnection(node);
 					break; // ignore it
 			} // switch
@@ -3225,11 +3217,13 @@ FILESTAMP
 				// Careful: When a consistency packet is sent, it overwrites the incoming packet containing the ticcmd.
 				//          Keep this in mind when changing the code that responds to these packets.
 				if (realstart <= gametic && realstart > gametic - BACKUPTICS+1
-					&& players[netconsole].pflags & PF_CONSISTANCY
-					&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
-					&& gamestate == GS_LEVEL)
+					&& gamestate == GS_LEVEL && playeringame[netconsole]
+					&& players[netconsole].playerstate == PST_LIVE
+					&& !players[netconsole].spectator
+					&& players[netconsole].jointime > 10
+					&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy))
 				{
-					if (cv_consfailprotect.value && playeringame[netconsole] && consfailcount[netconsole] < cv_consfailprotect.value)
+					if (cv_consfailprotect.value && consfailcount[netconsole] < cv_consfailprotect.value)
 					{
 						if (!consfailstatus[netconsole])
 						{
@@ -3266,7 +3260,6 @@ FILESTAMP
 						DEBFILE(va("player %d kicked (consistency failure) [%u] %d!=%d\n",
 							netconsole, realstart, consistancy[realstart%BACKUPTICS],
 							SHORT(netbuffer->u.clientpak.consistancy)));
-						players[netconsole].pflags &= ~PF_CONSISTANCY;
 						consfailstatus[netconsole] = 0;
 						consfailcount[netconsole] = 0;
 						break;
@@ -3477,7 +3470,6 @@ FILESTAMP
 // Builds ticcmds for console player,
 // sends out a packet
 //
-// no more use random generator, because at very first tic isn't yet synchronized
 // Note: It is called consistAncy on purpose.
 //
 static INT16 Consistancy(void)
@@ -3487,14 +3479,14 @@ static INT16 Consistancy(void)
 
 	DEBFILE(va("TIC %u ", gametic));
 	for (i = 0; i < MAXPLAYERS; i++)
-		if (playeringame[i] && players[i].mo && !players[i].spectator && players[i].pflags & PF_CONSISTANCY)
+		if (playeringame[i] && players[i].mo && players[i].playerstate == PST_LIVE && !players[i].spectator)
 		{
-			DEBFILE(va("p[%d].x = %f ", i, (double)FIXED_TO_FLOAT(players[i].mo->x)));
-			ret = (INT16)((ret + players[i].mo->x) & 0xFFFF);
+			//DEBFILE(va("p[%d].x = %f ", i, (double)FIXED_TO_FLOAT(players[i].mo->x)));
+			ret = (INT16)((ret + (players[i].mo->x>>8)) & 0xFFFF);
 			ret = (INT16)((ret + players[i].powers[pw_shield]) & 0xFFFF);
 		}
-	//DEBFILE(va("pos = %d, rnd %d\n", ret, P_GetRandSeed()));
-	//ret = (INT16)(ret + P_GetRandSeed());
+	DEBFILE(va("players = %d, rnd %d\n", ret, P_GetRandSeed()));
+	ret = (INT16)(ret + P_GetRandSeed());
 
 	return ret;
 }
diff --git a/src/d_main.c b/src/d_main.c
index 61d5d409399cc9575ef6f1b1c989c4122613c337..2492f622cea2bfbb637818bc4b0402b7d859d28b 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1087,16 +1087,14 @@ void D_SRB2Main(void)
 #endif
 	D_CleanFile();
 
-#if 1 // md5s last updated 3/15/14
-	// Yes, you read that right, that's the day of release.
-	// Aren't we batshit insane?
+#if 1 // md5s last updated 3/18/14
 
 	// Check MD5s of autoloaded files
 	W_VerifyFileMD5(0, "ac309fb3c7d4b5b685e2cd26beccf0e8"); // srb2.srb/srb2.wad
 	W_VerifyFileMD5(1, "a894044b555dfcc71865cee16a996e88"); // zones.dta
 	W_VerifyFileMD5(2, "4c410c1de6e0440cc5b2858dcca80c3e"); // player.dta
 	W_VerifyFileMD5(3, "85901ad4bf94637e5753d2ac2c03ea26"); // rings.dta
-	W_VerifyFileMD5(4, "17461512387ba6c5d7f2daa10346e1b5"); // patch.dta
+	W_VerifyFileMD5(4, "12c58561edf3be16a15505f1d5eacee0"); // patch.dta
 
 	// don't check music.dta because people like to modify it, and it doesn't matter if they do
 	// ...except it does if they slip maps in there, and that's what W_VerifyNMUSlumps is for.
@@ -1191,12 +1189,11 @@ void D_SRB2Main(void)
 	{
 		if (!M_IsNextParm())
 			I_Error("usage: -room <room_id>\nCheck the Master Server's webpage for room ID numbers.\n");
+		ms_RoomId = atoi(M_GetNextParm());
 
 #ifdef UPDATE_ALERT
 		GetMODVersion_Console();
 #endif
-
-		ms_RoomId = atoi(M_GetNextParm());
 	}
 
 	// init all NETWORK
diff --git a/src/d_net.c b/src/d_net.c
index f2f8f958db36c7756ca894e0d18c12312b4bc8ef..3c2213fca2f404e098fafdc70b8cb9753add96bc 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -71,7 +71,6 @@ void (*I_NetFreeNodenum)(INT32 nodenum) = NULL;
 SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL;
 boolean (*I_NetOpenSocket)(void) = NULL;
 boolean (*I_Ban) (INT32 node) = NULL;
-boolean (*I_Shun) (INT32 node) = NULL;
 void (*I_ClearBans)(void) = NULL;
 const char *(*I_GetNodeAddress) (INT32 node) = NULL;
 const char *(*I_GetBanAddress) (size_t ban) = NULL;
@@ -990,8 +989,6 @@ boolean HGetPacket(void)
 		if (netbuffer->checksum != NetbufferChecksum())
 		{
 			DEBFILE("Bad packet checksum\n");
-			if (I_Shun)
-				I_Shun(doomcom->remotenode); // No more garbage from you!
 			Net_CloseConnection(doomcom->remotenode);
 			continue;
 		}
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 0eddb5922ff16b5c2859c2cff7ba3d9592874821..57445244d66e42a01d0f52d2edf5629ec8a9feb0 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1079,6 +1079,10 @@ static void SendNameAndColor(void)
 	&& !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name))
 		return;
 
+	// We'll handle it later if we're not playing.
+	if (!Playing())
+		return;
+
 	// If you're not in a netgame, merely update the skin, color, and name.
 	if (!netgame)
 	{
@@ -1212,6 +1216,10 @@ static void SendNameAndColor2(void)
 			CV_StealthSet(&cv_playercolor2, cv_playercolor2.defaultvalue);
 	}
 
+	// We'll handle it later if we're not playing.
+	if (!Playing())
+		return;
+
 	// If you're not in a netgame, merely update the skin, color, and name.
 	if (botingame)
 	{
@@ -1365,8 +1373,6 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
 	}
 	else
 		SetPlayerSkinByNum(playernum, skin);
-
-	players[playernum].pflags |= PF_CONSISTANCY;
 }
 
 void SendWeaponPref(void)
@@ -1983,7 +1989,7 @@ static void Command_Pause(void)
 
 	if (cv_pause.value || server || (adminplayer == consoleplayer))
 	{
-		if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
+		if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
 		{
 			CONS_Printf(M_GetText("You can't pause here.\n"));
 			return;
@@ -2013,6 +2019,9 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
 		return;
 	}
 
+	if (modeattacking)
+		return;
+
 	paused = READUINT8(*cp);
 	dedicatedpause = READUINT8(*cp);
 
@@ -3127,15 +3136,12 @@ static void Command_Addfile(void)
 	}
 
 	p = fn+strlen(fn);
-	while(p > fn)
-	{
-		--p;
+	while(--p >= fn)
 		if (*p == '\\' || *p == '/' || *p == ':')
 		{
 			++p;
 			break;
 		}
-	}
 	WRITESTRINGN(buf_p,p,240);
 
 	{
@@ -4240,24 +4246,11 @@ static void Color_OnChange(void)
 {
 	if (!P_PlayerMoving(consoleplayer))
 	{
-		// Color change menu scrolling fix
-		// Determine what direction you are scrolling
-		// and skip the proper colors.
-		if (menuactive)
-		{
-			UINT8 prevcolor = players[consoleplayer].skincolor;
-			if (cv_playercolor.value == 0) // no color
-			{
-				if (prevcolor == 1)
-					CV_StealthSetValue(&cv_playercolor, MAXSKINCOLORS-1);
-				else
-					CV_StealthSetValue(&cv_playercolor, 1);
-			}
-		}
-
+		// Color change menu scrolling fix is no longer necessary
 		SendNameAndColor();
 	}
-	else {
+	else
+	{
 		CV_StealthSetValue(&cv_playercolor,
 			players[consoleplayer].skincolor);
 	}
@@ -4272,24 +4265,11 @@ static void Color2_OnChange(void)
 {
 	if (!P_PlayerMoving(secondarydisplayplayer))
 	{
-		// Color change menu scrolling fix
-		// Determine what direction you are scrolling
-		// and skip the proper colors.
-		if (menuactive)
-		{
-			UINT8 prevcolor = players[secondarydisplayplayer].skincolor;
-			if (cv_playercolor2.value == 0) // no color
-			{
-				if (prevcolor == 1)
-					CV_StealthSetValue(&cv_playercolor2, MAXSKINCOLORS-1);
-				else
-					CV_StealthSetValue(&cv_playercolor2, 1);
-			}
-		}
-
+		// Color change menu scrolling fix is no longer necessary
 		SendNameAndColor2();
 	}
-	else {
+	else
+	{
 		CV_StealthSetValue(&cv_playercolor2,
 			players[secondarydisplayplayer].skincolor);
 	}
diff --git a/src/d_player.h b/src/d_player.h
index e98f23ed06ad497cd636c1f6ceed3e4017be054d..3abc6ddf3dc60c7fccf24aafb2bd8e6526b7022e 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -147,9 +147,7 @@ typedef enum
 	PF_TAGGED            = 1<<27, // Player has been tagged and awaits the next round in hide and seek.
 	PF_TAGIT             = 1<<28, // The player is it! For Tag Mode
 
-	// free: 1<<29, 1<<30
-
-	PF_CONSISTANCY       = 1<<31 // DON'T mess with this flag or horrible things will happen.
+	// free: 1<<29, 1<<31
 } pflags_t;
 
 typedef enum
diff --git a/src/dehacked.c b/src/dehacked.c
index 4b1cbf76c81ac557caf8b8017a8097ed7e4d826d..e25f66e72e37ffded86087aeac4179cde2998d88 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -628,7 +628,7 @@ static void readfreeslots(MYFILE *f)
 			// TODO: Out-of-slots warnings/errors.
 			// TODO: Name too long (truncated) warnings.
 			if (fastcmp(type, "SFX"))
-				S_AddSoundFx(word, false, -1, false);
+				S_AddSoundFx(word, false, 0, false);
 			else if (fastcmp(type, "SPR"))
 			{
 				for (i = SPR_FIRSTFREESLOT; i <= SPR_LASTFREESLOT; i++)
@@ -8112,7 +8112,7 @@ static inline int lib_freeslot(lua_State *L)
 			sfxenum_t sfx;
 			strlwr(word);
 			CONS_Printf("Sound sfx_%s allocated.\n",word);
-			sfx = S_AddSoundFx(word, false, -1, false);
+			sfx = S_AddSoundFx(word, false, 0, false);
 			if (sfx != sfx_None) {
 				lua_pushinteger(L, sfx);
 				r++;
@@ -8189,12 +8189,12 @@ static inline int lib_freeslot(lua_State *L)
 static inline int lib_action(lua_State *L)
 {
 	actionf_t *action = lua_touserdata(L,lua_upvalueindex(1));
-	mobj_t **actor = luaL_checkudata(L,1,META_MOBJ);
+	mobj_t *actor = *((mobj_t **)luaL_checkudata(L,1,META_MOBJ));
 	var1 = (INT32)luaL_optinteger(L,2,0);
 	var2 = (INT32)luaL_optinteger(L,3,0);
-	if (!*actor)
+	if (!actor)
 		return LUA_ErrInvalid(L, "mobj_t");
-	action->acp1(*actor);
+	action->acp1(actor);
 	return 0;
 }
 
diff --git a/src/doomdef.h b/src/doomdef.h
index a659b23203f5bf749279538eb3180d0689737ced..3649db08ea7b170086e6235bf4ac7d0d95c9567d 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -144,8 +144,8 @@ extern FILE *logstream;
 #define VERSIONSTRING "Trunk"
 #else
 #define VERSION    201 // Game version
-#define SUBVERSION 2   // more precise version number
-#define VERSIONSTRING "v2.1.2"
+#define SUBVERSION 3   // more precise version number
+#define VERSIONSTRING "v2.1.3"
 #endif
 
 // Modification options
@@ -201,7 +201,7 @@ extern FILE *logstream;
 // it's only for detection of the version the player is using so the MS can alert them of an update.
 // Only set it higher, not lower, obviously.
 // Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
-#define MODVERSION 6
+#define MODVERSION 7
 
 
 
diff --git a/src/g_game.c b/src/g_game.c
index f6bc1c6c83d7a1e27e272b9595996b69fe9a1881..e3f2a2813e27d3a547242ec7926a2e96bf3293b3 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1711,18 +1711,8 @@ boolean G_Responder(event_t *ev)
 					// don't let busy scripts prevent pausing
 					pausedelay = NEWTICRATE/7;
 
-					if (cv_pause.value == 1 || server || (adminplayer == consoleplayer))
-					{
-						if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
-						{
-							CONS_Printf(M_GetText("You can't pause here.\n"));
-							return true;
-						}
-
-						COM_ImmedExecute("pause");
-					}
-					else
-						CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
+					// command will handle all the checks for us
+					COM_ImmedExecute("pause");
 					return true;
 				}
 				else
@@ -1794,15 +1784,8 @@ void G_Ticker(boolean run)
 		}
 
 		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i])
-			{
-				if (players[i].playerstate == PST_REBORN)
-				{
-					G_DoReborn(i);
-				}
-			}
-		}
+			if (playeringame[i] && players[i].playerstate == PST_REBORN)
+				G_DoReborn(i);
 	}
 	P_MapEnd();
 
@@ -1988,7 +1971,7 @@ void G_PlayerReborn(INT32 player)
 	exiting = players[player].exiting;
 	jointime = players[player].jointime;
 	spectator = players[player].spectator;
-	pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED|PF_CONSISTANCY));
+	pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED));
 
 	// As long as we're not in multiplayer, carry over cheatcodes from map to map
 	if (!(netgame || multiplayer))
@@ -2501,6 +2484,9 @@ void G_ExitLevel(void)
 
 		if (gametype != GT_COOP)
 			CONS_Printf(M_GetText("The round has ended.\n"));
+
+		// Remove CEcho text on round end.
+		HU_DoCEcho("");
 	}
 }
 
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 558083bdb6cc2dc0dcfafead9beb534fa5a27d52..c8f452a69e180ad63bbee580eb3e72d7f579020c 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -1180,7 +1180,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 		if (players[tab[i].num].spectator)
 			continue; //ignore them.
 
-		V_DrawString(x + 24, y,
+		V_DrawString(x + 20, y,
 		             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
 		             | ((players[tab[i].num].health > 0) ? 0 : V_60TRANS)
 		             | V_ALLOWLOWERCASE, tab[i].name);
@@ -1293,7 +1293,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 			continue;
 
 		strlcpy(name, tab[i].name, 9);
-		V_DrawString(x + 24, y,
+		V_DrawString(x + 20, y,
 		             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
 		             | ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT)
 		             | V_ALLOWLOWERCASE, name);
@@ -1326,7 +1326,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 			else
 				V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
 		}
-		V_DrawRightAlignedString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+		V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
 	}
 }
 
@@ -1343,16 +1343,13 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 	V_DrawFill(1, 26, 318, 1, 0); //And a horizontal line to make a T.
 	V_DrawFill(1, 180, 318, 1, 0); //And a horizontal line near the bottom.
 
-	if (gametype == GT_RACE || gametype == GT_COOP)
-		x -= 32; //we need more room!
-
 	for (i = 0; i < scorelines; i++)
 	{
 		if (players[tab[i].num].spectator)
 			continue; //ignore them.
 
 		strlcpy(name, tab[i].name, 9);
-		V_DrawString(x + 24, y,
+		V_DrawString(x + 20, y,
 		             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
 		             | ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT)
 		             | V_ALLOWLOWERCASE, name);
@@ -1400,20 +1397,21 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 			}
 		}
 
+		// All data drawn with thin string for space.
 		if (gametype == GT_RACE)
 		{
 			if (circuitmap)
 			{
 				if (players[tab[i].num].exiting)
-					V_DrawRightAlignedString(x+156, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
+					V_DrawRightAlignedThinString(x+156, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
 				else
-					V_DrawRightAlignedString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+					V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
 			}
 			else
-				V_DrawRightAlignedString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
+				V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
 		}
 		else
-			V_DrawRightAlignedString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+			V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
 
 		y += 16;
 		if (y > 160)
diff --git a/src/i_net.h b/src/i_net.h
index ecf6202de94ab13b534cf1880322b213db02577b..7a18ad186489bfe1d84874a19a88bbbb5fb34321 100644
--- a/src/i_net.h
+++ b/src/i_net.h
@@ -141,7 +141,6 @@ extern void (*I_NetCloseSocket)(void);
 
 
 extern boolean (*I_Ban) (INT32 node);
-extern boolean (*I_Shun) (INT32 node);
 extern void (*I_ClearBans)(void);
 extern const char *(*I_GetNodeAddress) (INT32 node);
 extern const char *(*I_GetBanAddress) (size_t ban);
diff --git a/src/i_tcp.c b/src/i_tcp.c
index 2127f1fc0c359f0c4de21955ec00a0fdabaea343..df7b3cf2875dd373e1e121c342b012e9125759b1 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -239,9 +239,6 @@ static size_t broadcastaddresses = 0;
 static boolean nodeconnected[MAXNETNODES+1];
 static mysockaddr_t banned[MAXBANS];
 static UINT8 bannedmask[MAXBANS];
-static mysockaddr_t shunned[MAXBANS];
-static UINT8 shunnedmask[MAXBANS];
-static size_t numshun = 0;
 #endif
 
 static size_t numbans = 0;
@@ -528,12 +525,6 @@ static void SOCK_Get(void)
 			(void *)&fromaddress, &fromlen);
 		if (c != ERRSOCKET)
 		{
-			// check if it's a DoS attacker and don't respond.
-			for (i = 0; i < numshun; i++)
-				if (SOCK_cmpaddr(&fromaddress, &shunned[i], shunnedmask[i]))
-					break;
-			if (i < numshun)
-				continue;
 			// find remote node number
 			for (j = 0; j <= MAXNETNODES; j++) //include LAN
 			{
@@ -1344,34 +1335,6 @@ static boolean SOCK_Ban(INT32 node)
 #endif
 }
 
-static boolean SOCK_Shun(INT32 node)
-{
-	if (node > MAXNETNODES)
-		return false;
-#ifdef NONET
-	return false;
-#else
-	if (numshun == MAXBANS)
-		return false;
-
-	M_Memcpy(&shunned[numshun], &clientaddress[node], sizeof (mysockaddr_t));
-	if (shunned[numshun].any.sa_family == AF_INET)
-	{
-		shunned[numshun].ip4.sin_port = 0;
-		shunnedmask[numshun] = 32;
-	}
-#ifdef HAVE_IPV6
-	else if (banned[numshun].any.sa_family == AF_INET6)
-	{
-		shunned[numshun].ip6.sin6_port = 0;
-		shunnedmask[numshun] = 128;
-	}
-#endif
-	numshun++;
-	return true;
-#endif
-}
-
 static boolean SOCK_SetBanAddress(const char *address, const char *mask)
 {
 #ifdef NONET
@@ -1511,7 +1474,6 @@ boolean I_InitTcpNetwork(void)
 
 	I_NetOpenSocket = SOCK_OpenSocket;
 	I_Ban = SOCK_Ban;
-	I_Shun = SOCK_Shun;
 	I_ClearBans = SOCK_ClearBans;
 	I_GetNodeAddress = SOCK_GetNodeAddress;
 	I_GetBanAddress = SOCK_GetBanAddress;
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 95f1926239a554c84fea851051660357d846e0e1..ece3d42988a96952c6fd36a4e4b14b3dcd4639d5 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -218,7 +218,7 @@ static int lib_pNewChaseDir(lua_State *L)
 static int lib_pLookForPlayers(lua_State *L)
 {
 	mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
-	fixed_t dist = (fixed_t)luaL_checkinteger(L, 2);
+	fixed_t dist = (fixed_t)luaL_optinteger(L, 2, 0);
 	boolean allaround = lua_optboolean(L, 3);
 	boolean tracer = lua_optboolean(L, 4);
 	NOHUD
@@ -341,8 +341,8 @@ static int lib_pSPMAngle(lua_State *L)
 	mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
 	mobjtype_t type = luaL_checkinteger(L, 2);
 	angle_t angle = (angle_t)luaL_checkinteger(L, 3);
-	UINT8 allowaim = (UINT8)luaL_checkinteger(L, 4);
-	UINT32 flags2 = (UINT32)luaL_checkinteger(L, 5);
+	UINT8 allowaim = (UINT8)luaL_optinteger(L, 4, 0);
+	UINT32 flags2 = (UINT32)luaL_optinteger(L, 5, 0);
 	NOHUD
 	if (!source)
 		return LUA_ErrInvalid(L, "mobj_t");
@@ -356,7 +356,7 @@ static int lib_pSpawnPlayerMissile(lua_State *L)
 {
 	mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
 	mobjtype_t type = luaL_checkinteger(L, 2);
-	UINT32 flags2 = (UINT32)luaL_checkinteger(L, 3);
+	UINT32 flags2 = (UINT32)luaL_optinteger(L, 3, 0);
 	NOHUD
 	if (!source)
 		return LUA_ErrInvalid(L, "mobj_t");
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index b0e6ed27b0c0847deda8b1df0386147889a77112..6aa503a6d945c9afd4da5b6da925ebcad7f7da2f 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -26,23 +26,32 @@
 boolean LUA_CallAction(const char *action, mobj_t *actor);
 state_t *astate;
 
-enum sfxinfo_e {
-	sfxinfo_name = 0,
-	sfxinfo_singularity,
-	sfxinfo_priority,
-	sfxinfo_flags, // "pitch"
-	sfxinfo_volume,
-	sfxinfo_skinsound
+enum sfxinfo_read {
+	sfxinfor_name = 0,
+	sfxinfor_singular,
+	sfxinfor_priority,
+	sfxinfor_flags, // "pitch"
+	sfxinfor_skinsound
 };
-const char *const sfxinfo_opt[] = {
+const char *const sfxinfo_ropt[] = {
 	"name",
-	"singularity",
+	"singular",
 	"priority",
 	"flags",
-	"volume",
 	"skinsound",
 	NULL};
 
+enum sfxinfo_write {
+	sfxinfow_singular = 0,
+	sfxinfow_priority,
+	sfxinfow_flags // "pitch"
+};
+const char *const sfxinfo_wopt[] = {
+	"singular",
+	"priority",
+	"flags",
+	NULL};
+
 //
 // Sprite Names
 //
@@ -718,6 +727,7 @@ static int lib_getSfxInfo(lua_State *L)
 static int lib_setSfxInfo(lua_State *L)
 {
 	sfxinfo_t *info;
+
 	lua_remove(L, 1);
 	info = &S_sfx[luaL_checkinteger(L, 1)]; // get the mobjinfo to assign to.
 	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
@@ -729,22 +739,27 @@ static int lib_setSfxInfo(lua_State *L)
 
 	lua_pushnil(L);
 	while (lua_next(L, 1)) {
-		lua_Integer i = 0;
-		const char *str = NULL;
+		enum sfxinfo_write i;
 
 		if (lua_isnumber(L, 2))
 			i = lua_tointeger(L, 2);
 		else
-			str = luaL_checkstring(L, 2);
+			i = luaL_checkoption(L, 2, NULL, sfxinfo_wopt);
 
-		if (i == 1 || (str && fastcmp(str,"singularity")))
+		switch(i)
+		{
+		case sfxinfow_singular:
 			info->singularity = luaL_checkboolean(L, 3);
-		else if (i == 2 || (str && fastcmp(str,"priority")))
+			break;
+		case sfxinfow_priority:
 			info->priority = (INT32)luaL_checkinteger(L, 3);
-		else if (i == 3 || (str && fastcmp(str,"pitch")) || (str && fastcmp(str,"flags")))
+			break;
+		case sfxinfow_flags:
 			info->pitch = (INT32)luaL_checkinteger(L, 3);
-		else if (i == 4 || (str && fastcmp(str,"volume")))
-			info->volume = (INT32)luaL_checkinteger(L, 3);
+			break;
+		default:
+			break;
+		}
 		lua_pop(L, 1);
 	}
 
@@ -761,39 +776,38 @@ static int lib_sfxlen(lua_State *L)
 static int sfxinfo_get(lua_State *L)
 {
 	sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
-	enum sfxinfo_e field = luaL_checkoption(L, 2, NULL, sfxinfo_opt);
+	enum sfxinfo_read field = luaL_checkoption(L, 2, NULL, sfxinfo_ropt);
 
 	I_Assert(sfx != NULL);
 
 	switch (field)
 	{
-	case sfxinfo_name:
+	case sfxinfor_name:
 		lua_pushstring(L, sfx->name);
 		return 1;
-	case sfxinfo_singularity:
+	case sfxinfor_singular:
 		lua_pushboolean(L, sfx->singularity);
 		return 1;
-	case sfxinfo_priority:
+	case sfxinfor_priority:
 		lua_pushinteger(L, sfx->priority);
 		return 1;
-	case sfxinfo_flags:
+	case sfxinfor_flags:
 		lua_pushinteger(L, sfx->pitch);
 		return 1;
-	case sfxinfo_volume:
-		lua_pushinteger(L, sfx->volume);
-		return 1;
-	case sfxinfo_skinsound:
+	case sfxinfor_skinsound:
 		lua_pushinteger(L, sfx->skinsound);
 		return 1;
+	default:
+		return luaL_error(L, "impossible error");
 	}
-	return luaL_error(L, "impossible error");
+	return 0;
 }
 
 // sfxinfo_t *, field, value
 static int sfxinfo_set(lua_State *L)
 {
 	sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
-	enum sfxinfo_e field = luaL_checkoption(L, 2, NULL, sfxinfo_opt);
+	enum sfxinfo_write field = luaL_checkoption(L, 2, NULL, sfxinfo_wopt);
 
 	if (hud_running)
 		return luaL_error(L, "Do not alter S_sfx in HUD rendering code!");
@@ -806,22 +820,18 @@ static int sfxinfo_set(lua_State *L)
 
 	switch (field)
 	{
-	case sfxinfo_singularity:
+	case sfxinfow_singular:
 		sfx->singularity = luaL_checkboolean(L, 1);
 		break;
-	case sfxinfo_priority:
+	case sfxinfow_priority:
 		sfx->priority = luaL_checkinteger(L, 1);
 		break;
-	case sfxinfo_flags:
+	case sfxinfow_flags:
 		sfx->pitch = luaL_checkinteger(L, 1);
 		break;
-	case sfxinfo_volume:
-		sfx->volume = luaL_checkinteger(L, 1);
-		break;
 	default:
-		return luaL_error(L, "Can't set 'S_sfx[%u].%s' here.", (UINT32)(sfx-S_sfx), sfxinfo_opt[field]);
+		return luaL_error(L, "impossible error");
 	}
-
 	return 0;
 }
 
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index fd6baec421324fa1cadf70ea99a520a4a720c9a4..34f79a42b2106877e1a55311532698b367f77945 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -131,7 +131,7 @@ static int player_get(lua_State *L)
 	else if (fastcmp(field,"powers"))
 		LUA_PushUserdata(L, plr->powers, META_POWERS);
 	else if (fastcmp(field,"pflags"))
-		lua_pushinteger(L, (plr->pflags & ~PF_CONSISTANCY));
+		lua_pushinteger(L, plr->pflags);
 	else if (fastcmp(field,"panim"))
 		lua_pushinteger(L, plr->panim);
 	else if (fastcmp(field,"flashcount"))
@@ -378,7 +378,7 @@ static int player_set(lua_State *L)
 	else if (fastcmp(field,"powers"))
 		return NOSET;
 	else if (fastcmp(field,"pflags"))
-		plr->pflags = (luaL_checkinteger(L, 3) & ~PF_CONSISTANCY)|(plr->pflags & PF_CONSISTANCY);
+		plr->pflags = luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"panim"))
 		plr->panim = luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"flashcount"))
diff --git a/src/m_misc.c b/src/m_misc.c
index 5e6f5508aabb644fc82f2ab91698416d58e5c5dc..acf6a5982dcff487a00a6b74b0b037da96a5278f 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -1778,10 +1778,20 @@ UINT8 M_CountBits(UINT32 num, UINT8 size)
 	for (i = 0; i < size; ++i)
 		if (num & (1 << i))
 			++sum;
-
 	return sum;
 }
 
+
+/** Get the most significant bit in a number.
+  * (integer log2)
+  */
+UINT8 M_HighestBit(UINT32 num)
+{
+	UINT8 i = 0;
+	while (num >>= 1) ++i;
+	return i;
+}
+
 const char *GetRevisionString(void)
 {
 	INT32 vinfo;
diff --git a/src/m_misc.h b/src/m_misc.h
index 8dd5f02791d1e2bcb9e480c0b57de91651c714e0..f681bfcb3ce7ec43581725876c1029b81f489d67 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -94,7 +94,8 @@ void strcatbf(char *s1, const char *s2, const char *s3);
 void M_SetupMemcpy(void);
 
 // counting bits, for weapon ammo code, usually
-UINT8 M_CountBits(UINT32 num, UINT8 size);
+FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);
+FUNCMATH UINT8 M_HighestBit(UINT32 num);
 
 // Flags for AA trees.
 #define AATREE_ZUSER	1		// Treat values as z_zone-allocated blocks and set their user fields
diff --git a/src/p_enemy.c b/src/p_enemy.c
index c93e47c8d808aef4677da6a267a2b5f3896dc04e..560b7d34a8cf56da66ea2cf21fbfc59b2159c138 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -1728,7 +1728,7 @@ void A_LobShot(mobj_t *actor)
 	if (LUA_CallAction("A_LobShot", actor))
 		return;
 #endif
-	if (!actor->target || P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 		return;
 
 	A_FaceTarget(actor);
@@ -1829,7 +1829,7 @@ void A_FireShot(mobj_t *actor)
 	if (LUA_CallAction("A_FireShot", actor))
 		return;
 #endif
-	if (!actor->target || P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 		return;
 
 	A_FaceTarget(actor);
@@ -1868,7 +1868,7 @@ void A_SuperFireShot(mobj_t *actor)
 	if (LUA_CallAction("A_SuperFireShot", actor))
 		return;
 #endif
-	if (!actor->target || P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 		return;
 
 	A_FaceTarget(actor);
@@ -1915,7 +1915,7 @@ void A_BossFireShot(mobj_t *actor)
 	if (LUA_CallAction("A_BossFireShot", actor))
 		return;
 #endif
-	if (!actor->target || P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 		return;
 
 	A_FaceTarget(actor);
@@ -1998,7 +1998,7 @@ void A_Boss7FireMissiles(mobj_t *actor)
 		return;
 #endif
 
-	if (!actor->target || P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 	{
 		P_SetMobjState(actor, actor->info->spawnstate);
 		return;
@@ -2056,7 +2056,7 @@ void A_Boss1Laser(mobj_t *actor)
 	if (LUA_CallAction("A_Boss1Laser", actor))
 		return;
 #endif
-	if (!actor->target || P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 		return;
 
 	switch (locvar2)
@@ -2279,7 +2279,7 @@ void A_SkullAttack(mobj_t *actor)
 	if (LUA_CallAction("A_SkullAttack", actor))
 		return;
 #endif
-	if (!actor->target || P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 		return;
 
 	speed = FixedMul(SKULLSPEED, actor->scale);
@@ -2335,7 +2335,7 @@ void A_BossZoom(mobj_t *actor)
 	if (LUA_CallAction("A_BossZoom", actor))
 		return;
 #endif
-	if (!actor->target || P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 		return;
 
 	dest = actor->target;
@@ -2501,6 +2501,9 @@ void A_1upThinker(mobj_t *actor)
 		if (!players[i].mo)
 			continue;
 
+		if ((netgame || multiplayer) && players[i].playerstate != PST_LIVE)
+			continue;
+
 		temp = P_AproxDistance(players[i].mo->x-actor->x, players[i].mo->y-actor->y);
 
 		if (temp < dist)
@@ -4815,12 +4818,6 @@ void A_UnidusBall(mobj_t *actor)
 
 	actor->angle += ANGLE_11hh;
 
-	if (!actor->target)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "A_UnidusBall: Spikeball has no target\n");
-		return;
-	}
-
 	if (actor->movecount)
 	{
 		if (P_AproxDistance(actor->momx, actor->momy) < FixedMul(actor->info->damage/2, actor->scale))
@@ -4828,15 +4825,16 @@ void A_UnidusBall(mobj_t *actor)
 		return;
 	}
 
-	if (!actor->target->health)
+	if (!actor->target || !actor->target->health)
 	{
+		CONS_Debug(DBG_GAMELOGIC, "A_UnidusBall: Removing unthrown spikeball from nonexistant Unidus\n");
 		P_RemoveMobj(actor);
 		return;
 	}
 
 	P_UnsetThingPosition(actor);
 	{
-		const angle_t angle = actor->movedir + FixedAngle(actor->info->speed*leveltime);
+		const angle_t angle = actor->movedir + FixedAngle(actor->info->speed*(leveltime%360));
 		const UINT16 fa = angle>>ANGLETOFINESHIFT;
 
 		actor->x = actor->target->x + FixedMul(FINECOSINE(fa),actor->threshold);
@@ -5030,27 +5028,9 @@ void A_MaceRotate(mobj_t *actor)
 		return;
 #endif
 
-	if (!actor->target) // This should NEVER happen.
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Mace object (type %d) has no target!\n", actor->type);
-		P_RemoveMobj(actor);
-		return;
-	}
-
 	// Target was removed.
-	if (P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 	{
-		UINT8 i;
-		if (actor->flags & MF_AMBUSH) // outermost chain only (no point checking the others for players)
-		{
-			for (i = 0; i < MAXPLAYERS; i++)
-				if (playeringame[i] && players[i].pflags & (PF_MACESPIN|PF_ITEMHANG) // is player in-game and attached to something?
-					&& players[i].mo && players[i].mo->tracer && players[i].mo->tracer == actor) // is player attached to YOU even?
-				{
-					players[i].pflags &= ~(PF_MACESPIN|PF_ITEMHANG);
-					P_SetTarget(&players[i].mo->tracer, NULL); // you're not going to exist in a sec anyway
-				} // don't stop yet, since apparently more than one player is able to grab onto these chains at a time
-		}
 		P_RemoveMobj(actor);
 		return;
 	}
@@ -6478,7 +6458,7 @@ void A_Boss2PogoTarget(mobj_t *actor)
 		return;
 #endif
 
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || actor->target->player->powers[pw_flashing]
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || (actor->target->player && actor->target->player->powers[pw_flashing])
 	|| P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) >= FixedMul(512*FRACUNIT, actor->scale))
 	{
 		// look for a new target
@@ -10064,7 +10044,7 @@ void A_BrakFireShot(mobj_t *actor)
 	if (LUA_CallAction("A_BrakFireShot", actor))
 		return;
 #endif
-	if (!actor->target || P_MobjWasRemoved(actor->target))
+	if (!actor->target)
 		return;
 
 	A_FaceTarget(actor);
diff --git a/src/p_inter.c b/src/p_inter.c
index 9954e8b61b7d2c1f766be88fe40ab3e2add26aea..03ad7c46ecb9048c2f77d87e5d42ee04df1329b2 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1518,6 +1518,8 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
 						str = M_GetText("%s%s's armageddon blast %s %s.\n");
 					else if (inflictor->player->powers[pw_invulnerability])
 						str = M_GetText("%s%s's invincibility aura %s %s.\n");
+					else if (inflictor->player->powers[pw_super])
+						str = M_GetText("%s%s's super aura %s %s.\n");
 					else
 						str = M_GetText("%s%s's tagging hand %s %s.\n");
 					break;
@@ -1579,6 +1581,10 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
 					break;
 				}
 				break;
+			case MT_EGGMANICO:
+			case MT_EGGMANBOX:
+				str = M_GetText("%s was %s by Eggman's nefarious TV magic.\n");
+				break;
 			case MT_SPIKE:
 				str = M_GetText("%s was %s by spikes.\n");
 				break;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index c640a8b2909be0967798a668bbfb85a3ec7071df..b973c6cc39ae8c5323e0694bb8e6d6aad968f9ec 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -58,7 +58,7 @@ void P_RunCachedActions(void)
 #ifdef HAVE_BLUA
 		astate = &states[ac->statenum];
 #endif
-		if (ac->mobj) // just in case...
+		if (ac->mobj && !P_MobjWasRemoved(ac->mobj)) // just in case...
 			states[ac->statenum].action.acp1(ac->mobj);
 		next = ac->next;
 		Z_Free(ac);
@@ -288,6 +288,11 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 			astate = st;
 #endif
 			st->action.acp1(mobj);
+
+			// woah. a player was removed by an action.
+			// this sounds like a VERY BAD THING, but there's nothing we can do now...
+			if (P_MobjWasRemoved(mobj))
+				return false;
 		}
 
 		seenstate[state] = 1 + st->nextstate;
@@ -5237,19 +5242,27 @@ INT32 numshields = 0;
 void P_RunShields(void)
 {
 	INT32 i;
-	mobj_t *mo;
+	mobj_t *mo, *next;
 	fixed_t destx,desty,zoffs;
 
+	// run shields
 	for (i = 0; i < numshields; i++)
 	{
 		P_ShieldLook(shields[i], shields[i]->info->speed);
 		P_SetTarget(&shields[i], NULL);
 	}
-
 	numshields = 0;
 
-	for (mo = overlaycap; mo; mo = mo->hnext)
+	// run overlays
+	next = NULL;
+	for (mo = overlaycap; mo; mo = next)
 	{
+		I_Assert(!P_MobjWasRemoved(mo));
+
+		// grab next in chain, then unset the chain target
+		next = mo->hnext;
+		P_SetTarget(&mo->hnext, NULL);
+
 		if (!mo->target)
 			continue;
 		if (!splitscreen /*&& rendermode != render_soft*/)
@@ -5292,8 +5305,7 @@ void P_RunShields(void)
 			P_SetThingPosition(mo);
 		P_CheckPosition(mo, mo->x, mo->y);
 	}
-
-	overlaycap = NULL;
+	P_SetTarget(&overlaycap, NULL);
 }
 
 static boolean P_AddShield(mobj_t *thing)
@@ -5330,17 +5342,38 @@ static boolean P_AddShield(mobj_t *thing)
 	return true;
 }
 
+// Called only when MT_OVERLAY thinks.
 static void P_AddOverlay(mobj_t *thing)
 {
-	mobj_t *mo;
-	if (!overlaycap)
-		overlaycap = thing;
+	I_Assert(thing != NULL);
+
+	if (overlaycap == NULL)
+		P_SetTarget(&overlaycap, thing);
 	else {
+		mobj_t *mo;
 		for (mo = overlaycap; mo && mo->hnext; mo = mo->hnext)
 			;
-		mo->hnext = thing;
+
+		I_Assert(mo != NULL);
+		I_Assert(mo->hnext == NULL);
+
+		P_SetTarget(&mo->hnext, thing);
 	}
-	thing->hnext = NULL;
+	P_SetTarget(&thing->hnext, NULL);
+}
+
+// Called only when MT_OVERLAY (or anything else in the overlaycap list) is removed.
+// Keeps the hnext list from corrupting.
+static void P_RemoveOverlay(mobj_t *thing)
+{
+	mobj_t *mo;
+	for (mo = overlaycap; mo; mo = mo->hnext)
+		if (mo->hnext == thing)
+		{
+			P_SetTarget(&mo->hnext, thing->hnext);
+			P_SetTarget(&thing->hnext, NULL);
+			return;
+		}
 }
 
 void A_BossDeath(mobj_t *mo);
@@ -5528,7 +5561,7 @@ void P_MobjThinker(mobj_t *mobj)
 				// Don't touch my fuse!
 				return;
 			case MT_OVERLAY:
-				if (!mobj->target || P_MobjWasRemoved(mobj->target))
+				if (!mobj->target)
 				{
 					P_RemoveMobj(mobj);
 					return;
@@ -5577,7 +5610,7 @@ void P_MobjThinker(mobj_t *mobj)
 				}
 				break;
 			case MT_DROWNNUMBERS:
-				if (!mobj->target || P_MobjWasRemoved(mobj->target))
+				if (!mobj->target)
 				{
 					P_RemoveMobj(mobj);
 					return;
@@ -5972,7 +6005,6 @@ void P_MobjThinker(mobj_t *mobj)
 			break;
 		case MT_EGGMOBILE2_POGO:
 			if (!mobj->target
-			|| P_MobjWasRemoved(mobj->target)
 			|| !mobj->target->health
 			|| mobj->target->state == &states[mobj->target->info->spawnstate]
 			|| mobj->target->state == &states[mobj->target->info->raisestate])
@@ -6010,7 +6042,6 @@ void P_MobjThinker(mobj_t *mobj)
 				fixed_t jetx, jety;
 
 				if (!mobj->target // if you have no target
-				|| P_MobjWasRemoved(mobj->target) // or your target has been removed
 				|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
 				{ // then remove yourself as well!
 					P_RemoveMobj(mobj);
@@ -6090,7 +6121,6 @@ void P_MobjThinker(mobj_t *mobj)
 				fixed_t jetx, jety;
 
 				if (!mobj->target // if you have no target
-				|| P_MobjWasRemoved(mobj->target) // or your target has been removed
 				|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
 				{ // then remove yourself as well!
 					P_RemoveMobj(mobj);
@@ -6121,7 +6151,6 @@ void P_MobjThinker(mobj_t *mobj)
 		case MT_JETFLAME:
 			{
 				if (!mobj->target // if you have no target
-				|| P_MobjWasRemoved(mobj->target) // or your target has been removed
 				|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
 				{ // then remove yourself as well!
 					P_RemoveMobj(mobj);
@@ -6151,7 +6180,7 @@ void P_MobjThinker(mobj_t *mobj)
 				mobj->z = mobj->floorz + mobj->height + (mobj->spawnpoint->options >> ZSHIFT) * FRACUNIT;
 				mobj->angle = 0;
 
-				if (!mobj->target || P_MobjWasRemoved(mobj->target))
+				if (!mobj->target)
 				{
 					mobj_t *goalpost = P_SpawnMobj(mobj->x, mobj->y, mobj->z + FRACUNIT, MT_NIGHTSGOAL);
 					CONS_Debug(DBG_NIGHTSBASIC, "Adding goal post\n");
@@ -7325,6 +7354,10 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 			astate = st;
 #endif
 			st->action.acp1(mobj);
+			// DANGER! This is the ONLY way for P_SpawnMobj to return NULL!
+			// Avoid using MF_RUNSPAWNFUNC on mobjs whose spawn state expects target or tracer to already be set!
+			if (P_MobjWasRemoved(mobj))
+				return NULL;
 		}
 	}
 
@@ -7434,6 +7467,9 @@ void P_RemoveMobj(mobj_t *mobj)
 			iquetail = (iquetail+1)&(ITEMQUESIZE-1);
 	}
 
+	if (mobj->type == MT_OVERLAY)
+		P_RemoveOverlay(mobj);
+
 	mobj->health = 0; // Just because
 
 	// unlink from sector and block lists
@@ -7862,9 +7898,19 @@ void P_SpawnPlayer(INT32 playernum)
 			p->spectator = false;
 	}
 
-	// Fix stupid non spectator spectators.
-	if (G_GametypeHasTeams() && !p->spectator && !p->ctfteam)
-		p->spectator = true;
+	if (G_GametypeHasTeams())
+	{
+		// Fix stupid non spectator spectators.
+		if (!p->spectator && !p->ctfteam)
+			p->spectator = true;
+
+		// Fix team colors.
+		// This code isn't being done right somewhere else. Oh well.
+		if (p->ctfteam == 1)
+			p->skincolor = skincolor_redteam;
+		else if (p->ctfteam == 2)
+			p->skincolor = skincolor_blueteam;
+	}
 
 	mobj = P_SpawnMobj(0, 0, 0, MT_PLAYER);
 	(mobj->player = p)->mo = mobj;
diff --git a/src/p_spec.c b/src/p_spec.c
index b3951222a9706ac87cbebcec9780aebaeee11652..20d86176f53b1d4d746baa5984ca0825e3294f93 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1598,7 +1598,7 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller)
 					if (!playeringame[i] || players[i].spectator)
 						continue;
 
-					if (!players[i].mo)
+					if (!players[i].mo || players[i].mo->health < 1)
 						continue;
 
 					rings += players[i].mo->health-1;
@@ -2907,15 +2907,28 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo)
 
 			while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
 			{
+				boolean tryagain;
 				sec = sectors + secnum;
-				for (thing = sec->thinglist; thing; thing = thing->snext)
-					if (thing->type == type)
-					{
-						if (state != NUMSTATES)
-							P_SetMobjState(thing, state);
-						else
-							P_SetMobjState(thing, thing->state->nextstate);
-					}
+				do {
+					tryagain = false;
+					for (thing = sec->thinglist; thing; thing = thing->snext)
+						if (thing->type == type)
+						{
+							if (state != NUMSTATES)
+							{
+								if (!P_SetMobjState(thing, state)) // set state to specific state
+								{ // mobj was removed
+									tryagain = true; // snext is corrupt, we'll have to start over.
+									break;
+								}
+							}
+							else if (!P_SetMobjState(thing, thing->state->nextstate)) // set state to nextstate
+							{ // mobj was removed
+								tryagain = true; // snext is corrupt, we'll have to start over.
+								break;
+							}
+						}
+				} while (tryagain);
 			}
 			break;
 		}
diff --git a/src/p_user.c b/src/p_user.c
index c755cb5edf87dedf3a175e60c2df674900c32087..5aa0d50a1be07c7553138ccdba32fb6463c4f447 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -261,6 +261,9 @@ boolean P_PlayerMoving(INT32 pnum)
 {
 	player_t *p = &players[pnum];
 
+	if (!Playing())
+		return false;
+
 	if (p->jointime < 5*TICRATE || p->playerstate == PST_DEAD || p->playerstate == PST_REBORN || p->spectator)
 		return false;
 
@@ -9134,7 +9137,7 @@ void P_PlayerAfterThink(player_t *player)
 				player->pflags &= ~PF_CARRIED;
 		}
 
-		if (player->mo->tracer->health <= 0 || (player->mo->tracer->player && player->mo->tracer->player->powers[pw_flashing]))
+		if (player->mo->tracer->health <= 0)
 			player->pflags &= ~PF_CARRIED;
 		else
 		{
diff --git a/src/r_things.c b/src/r_things.c
index ed776c5d0d128eb368b00f532c3b2f3cfff131c1..afbda8da5c53c12ceb3260f1c705e186b1d425dd 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1231,6 +1231,15 @@ static void R_ProjectSprite(mobj_t *thing)
 			return;
 	}
 
+	// quick check for possible overflows
+	// if either of these triggers then there's a possibility that drawing is unsafe
+	if (M_HighestBit(abs(gzt - viewz)) + M_HighestBit(abs(yscale)) > 47 // 31 bits + 16 from the division by FRACUNIT
+	 || M_HighestBit(abs(gz  - viewz)) + M_HighestBit(abs(yscale)) > 47)
+	{
+		CONS_Debug(DBG_RENDER, "Suspected overflow in ProjectSprite (sprite %s), ignoring\n", sprnames[thing->sprite]);
+		return;
+	}
+
 	// store information in a vissprite
 	vis = R_NewVisSprite();
 	vis->heightsec = heightsec; //SoM: 3/17/2000
@@ -1437,6 +1446,15 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
 		}
 	}
 
+	// quick check for possible overflows
+	// if either of these triggers then there's a possibility that drawing is unsafe
+	if (M_HighestBit(abs(gzt - viewz)) + M_HighestBit(abs(yscale)) > 47) // 31 bits + 16 from the division by FRACUNIT
+	{
+		CONS_Debug(DBG_RENDER, "Suspected overflow in ProjectPrecipitationSprite (sprite %s), ignoring\n", sprnames[thing->sprite]);
+		return;
+	}
+
+
 	// store information in a vissprite
 	vis = R_NewVisSprite();
 	vis->scale = yscale; //<<detailshift;
@@ -2690,7 +2708,7 @@ void R_AddSkins(UINT16 wadnum)
 							stoken + 2))
 					{
 						skin->soundsid[S_sfx[i].skinsound] =
-							S_AddSoundFx(value+2,S_sfx[i].singularity,S_sfx[i].pitch, true);
+							S_AddSoundFx(value+2, S_sfx[i].singularity, S_sfx[i].pitch, true);
 						found = true;
 					}
 				}
diff --git a/src/s_sound.c b/src/s_sound.c
index f191006369e850c0d4c3365d23661009131fe400..6e6c492a38d7453ecb3e9569a5215cde54ddbc42 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -1447,7 +1447,7 @@ void S_StartSoundName(void *mo, const char *soundname)
 			return;
 		}
 
-		soundnum = S_AddSoundFx(soundname, false, -1, false);
+		soundnum = S_AddSoundFx(soundname, false, 0, false);
 		newsounds[i] = soundnum;
 	}
 
diff --git a/src/sounds.c b/src/sounds.c
index a1e3bd791f0198ea8b8ccbe76f3f77ae3919dbd1..c03d6cea25fc66558237903ea4bc1e52cefb0d7e 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -1359,7 +1359,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3k51",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"s3k52",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"s3k53",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
-  {"s3k54",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
+  {"s3k54",  false,  64, 64, -1, NULL, 0,        -1,  -1, LUMPERROR}, // MetalSonic shot fire
   {"s3k55",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"s3k56",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"s3k57",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
@@ -1570,8 +1570,7 @@ void S_InitRuntimeSounds (void)
 
 // Add a new sound fx into a free sfx slot.
 //
-sfxenum_t S_AddSoundFx(const char *name, INT32 singularity, INT32 pitch,
-	boolean skinsound)
+sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags, boolean skinsound)
 {
 	sfxenum_t i, slot;
 
@@ -1585,9 +1584,9 @@ sfxenum_t S_AddSoundFx(const char *name, INT32 singularity, INT32 pitch,
 		if (!S_sfx[i].priority)
 		{
 			strncpy(freeslotnames[i-sfx_freeslot0], name, 6);
-			S_sfx[i].singularity = singularity;
+			S_sfx[i].singularity = singular;
 			S_sfx[i].priority = 60;
-			S_sfx[i].pitch = pitch;
+			S_sfx[i].pitch = flags;
 			S_sfx[i].volume = -1;
 			S_sfx[i].lumpnum = LUMPERROR;
 			S_sfx[i].skinsound = -1;
diff --git a/src/sounds.h b/src/sounds.h
index a42b986c2710121abeab242fdba478d337180653..ee1e60ba13ed373b35ae90b9bdc6abfe43dd2cf1 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -57,7 +57,7 @@ struct sfxinfo_struct
 	const char *name;
 
 	// Sfx singularity (only one at a time)
-	INT32 singularity;
+	boolean singularity;
 
 	// Sfx priority
 	INT32 priority;
@@ -1631,7 +1631,7 @@ typedef enum
 
 
 void S_InitRuntimeSounds(void);
-sfxenum_t S_AddSoundFx(const char *name, INT32 singularity, INT32 pitch, boolean skinsound);
+sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags, boolean skinsound);
 void S_RemoveSoundFx(sfxenum_t id);
 
 #endif
diff --git a/src/tables.c b/src/tables.c
index f85fc9fc501baac18903f46e5181bc7e474a7dfa..fa71effef44bd50761014a2db52580f672421f56 100644
--- a/src/tables.c
+++ b/src/tables.c
@@ -98,6 +98,11 @@ angle_t FixedAngleC(fixed_t fa, fixed_t factor)
 	if (fa == 0)
 		return 0;
 
+	// -2,147,483,648 has no absolute value in a 32 bit signed integer
+	// so this code _would_ infinite loop if passed it
+	if (fa == INT32_MIN)
+		return 0;
+
 	if (factor == 0)
 		return FixedAngle(fa);
 	else if (factor > 0)
@@ -132,6 +137,11 @@ angle_t FixedAngle(fixed_t fa)
 	if (fa == 0)
 		return 0;
 
+	// -2,147,483,648 has no absolute value in a 32 bit signed integer
+	// so this code _would_ infinite loop if passed it
+	if (fa == INT32_MIN)
+		return 0;
+
 	fa = abs(fa);
 
 	while (fa)