diff --git a/src/g_game.c b/src/g_game.c
index 5466b483aca235f523684b0d36f234456093fc7c..aaaf558f451e477d2acc5056ba15a8345b07a174 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2622,6 +2622,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 	boolean spectator;
 	boolean outofcoop;
 	boolean removing;
+	boolean muted;
 	INT16 bot;
 	SINT8 pity;
 	INT16 rings;
@@ -2639,6 +2640,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 	spectator = players[player].spectator;
 	outofcoop = players[player].outofcoop;
 	removing = players[player].removing;
+	muted = players[player].muted;
 	pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER));
 	playerangleturn = players[player].angleturn;
 	oldrelangleturn = players[player].oldrelangleturn;
@@ -2716,6 +2718,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 	p->spectator = spectator;
 	p->outofcoop = outofcoop;
 	p->removing = removing;
+	p->muted = muted;
 	p->angleturn = playerangleturn;
 	p->oldrelangleturn = oldrelangleturn;
 
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index caf13a445716167adaa20424af74f268565787ea..a7d0aea742d569644ebc7ee35edd0a22fcb8aacd 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -587,8 +587,8 @@ static void Command_CSay_f(void)
 	DoSayCommand(0, 1, HU_CSAY);
 }
 
-static tic_t spam_tokens[MAXPLAYERS] = { 1 }; // fill the buffer with 1 so the motd can be sent.
-static tic_t spam_tics[MAXPLAYERS];
+UINT8 spam_tokens[MAXPLAYERS] = { 1 }; // fill the buffer with 1 so the motd can be sent.
+tic_t spam_tics[MAXPLAYERS];
 
 /** Receives a message, processing an ::XD_SAY command.
   * \sa DoSayCommand
@@ -649,14 +649,12 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 	else
 		spam_tokens[playernum] -= 1;
 
-	// run the lua hook even if we were supposed to eat the msg, netgame consistency goes first.
+	if (spam_eatmsg)
+		return; // don't proceed if we were supposed to eat the message.
 
 	if (LUA_HookPlayerMsg(playernum, target, flags, msg))
 		return;
 
-	if (spam_eatmsg)
-		return; // don't proceed if we were supposed to eat the message.
-
 	// If it's a CSAY, just CECHO and be done with it.
 	if (flags & HU_CSAY)
 	{
diff --git a/src/hu_stuff.h b/src/hu_stuff.h
index ca77ed93002750d6cefa28584d8e4de7be3bfc65..07881ce1a83a0d19c478cec6ea86cb3b596a2a6e 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -79,6 +79,9 @@ void HU_AddChatText(const char *text, boolean playsound);
 // set true when entering a chat message
 extern boolean chat_on;
 
+extern UINT8 spam_tokens[MAXPLAYERS];
+extern tic_t spam_tics[MAXPLAYERS];
+
 extern patch_t *emeraldpics[3][8];
 extern patch_t *rflagico;
 extern patch_t *bflagico;
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 5fee506af320578b7228cb6e5616ebac238120f7..d34dbc4e085e4d4f4a0c658ed376b37532ab214e 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1414,6 +1414,83 @@ static void IdleUpdate(void)
 	}
 }
 
+static void DedicatedIdleUpdate(INT32 *realtics)
+{
+	const tic_t dedicatedidletime = cv_dedicatedidletime.value * TICRATE;
+	static tic_t dedicatedidletimeprev = 0;
+	static tic_t dedicatedidle = 0;
+
+	if (!server || !dedicated || gamestate != GS_LEVEL)
+		return;
+
+	if (dedicatedidletime > 0)
+	{
+		INT32 i;
+
+		boolean empty = true;
+		for (i = 0; i < MAXPLAYERS; i++)
+			if (playeringame[i])
+			{
+				empty = false;
+				break;
+			}
+
+		if (empty)
+		{
+			if (leveltime == 2)
+			{
+				// On next tick...
+				dedicatedidle = dedicatedidletime - 1;
+			}
+			else if (dedicatedidle >= dedicatedidletime)
+			{
+				if (D_GetExistingTextcmd(gametic, 0) || D_GetExistingTextcmd(gametic + 1, 0))
+				{
+					CONS_Printf("DEDICATED: Awakening from idle (Netxcmd detected...)\n");
+					dedicatedidle = 0;
+				}
+				else
+				{
+					(*realtics) = 0;
+				}
+			}
+			else
+			{
+				dedicatedidle += (*realtics);
+
+				if (dedicatedidle >= dedicatedidletime)
+				{
+					const char *idlereason = "at round start";
+					if (leveltime > 3)
+						idlereason = va("for %d seconds", dedicatedidle / TICRATE);
+
+					CONS_Printf("DEDICATED: No players %s, idling...\n", idlereason);
+					(*realtics) = 0;
+					dedicatedidle = dedicatedidletime;
+				}
+			}
+		}
+		else
+		{
+			if (dedicatedidle >= dedicatedidletime)
+			{
+				CONS_Printf("DEDICATED: Awakening from idle (Player detected...)\n");
+			}
+			dedicatedidle = 0;
+		}
+	}
+	else
+	{
+		if (dedicatedidletimeprev > 0 && dedicatedidle >= dedicatedidletimeprev)
+		{
+			CONS_Printf("DEDICATED: Awakening from idle (Idle disabled...)\n");
+		}
+		dedicatedidle = 0;
+	}
+
+	dedicatedidletimeprev = dedicatedidletime;
+}
+
 // Handle timeouts to prevent definitive freezes from happenning
 static void HandleNodeTimeouts(void)
 {
@@ -1490,69 +1567,7 @@ void NetUpdate(void)
 			realtics = 5;
 	}
 
-	if (server && dedicated && gamestate == GS_LEVEL)
- 	{
-		const tic_t dedicatedidletime = cv_dedicatedidletime.value * TICRATE;
-		static tic_t dedicatedidletimeprev = 0;
-		static tic_t dedicatedidle = 0;
-
-		if (dedicatedidletime > 0)
-		{
-			INT32 i;
-
-			for (i = 1; i < MAXNETNODES; ++i)
-				if (netnodes[i].ingame)
-				{
-					if (dedicatedidle >= dedicatedidletime)
-					{
-						CONS_Printf("DEDICATED: Awakening from idle (Node %d detected...)\n", i);
-						dedicatedidle = 0;
-					}
-					break;
-				}
-
-			if (i == MAXNETNODES)
-			{
-				if (leveltime == 2)
-				{
-					// On next tick...
-					dedicatedidle = dedicatedidletime-1;
-				}
-				else if (dedicatedidle >= dedicatedidletime)
-				{
-					if (D_GetExistingTextcmd(gametic, 0) || D_GetExistingTextcmd(gametic+1, 0))
-					{
-						CONS_Printf("DEDICATED: Awakening from idle (Netxcmd detected...)\n");
-						dedicatedidle = 0;
-					}
-					else
-					{
-						realtics = 0;
-					}
-				}
-				else if ((dedicatedidle += realtics) >= dedicatedidletime)
-				{
-					const char *idlereason = "at round start";
-					if (leveltime > 3)
-						idlereason = va("for %d seconds", dedicatedidle/TICRATE);
-
-					CONS_Printf("DEDICATED: No nodes %s, idling...\n", idlereason);
-					realtics = 0;
-					dedicatedidle = dedicatedidletime;
-				}
-			}
-		}
-		else
-		{
-			if (dedicatedidletimeprev > 0 && dedicatedidle >= dedicatedidletimeprev)
-			{
-				CONS_Printf("DEDICATED: Awakening from idle (Idle disabled...)\n");
-			}
-			dedicatedidle = 0;
-		}
-
-		dedicatedidletimeprev = dedicatedidletime;
- 	}
+	DedicatedIdleUpdate(&realtics);
 
 	gametime = nowtime;
 
diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c
index b24409db158909970a73fb5bd7630500a4f3d63f..4f21ca984be027c82a80af0f625264efd798afa8 100644
--- a/src/netcode/d_net.c
+++ b/src/netcode/d_net.c
@@ -62,9 +62,6 @@ static doomdata_t reboundstore[MAXREBOUND];
 static INT16 reboundsize[MAXREBOUND];
 static INT32 rebound_head, rebound_tail;
 
-/// \brief bandwith of netgame
-INT32 net_bandwidth;
-
 /// \brief max length per packet
 INT16 hardware_MAXPACKETLENGTH;
 
@@ -1189,10 +1186,7 @@ void D_SetDoomcom(void)
 {
 	if (doomcom) return;
 	doomcom = Z_Calloc(sizeof (doomcom_t), PU_STATIC, NULL);
-	doomcom->id = DOOMCOM_ID;
 	doomcom->numslots = doomcom->numnodes = 1;
-	doomcom->gametype = 0;
-	doomcom->consoleplayer = 0;
 	doomcom->extratics = 0;
 }
 
@@ -1217,7 +1211,6 @@ boolean D_CheckNetGame(void)
 	I_NetMakeNodewPort = NULL;
 
 	hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
-	net_bandwidth = 30000;
 	// I_InitNetwork sets doomcom and netgame
 	// check and initialize the network driver
 	multiplayer = false;
@@ -1237,7 +1230,6 @@ boolean D_CheckNetGame(void)
 	server = true; // WTF? server always true???
 		// no! The deault mode is server. Client is set elsewhere
 		// when the client executes connect command.
-	doomcom->ticdup = 1;
 
 	if (M_CheckParm("-extratic"))
 	{
@@ -1248,21 +1240,6 @@ boolean D_CheckNetGame(void)
 		CONS_Printf(M_GetText("Set extratics to %d\n"), doomcom->extratics);
 	}
 
-	if (M_CheckParm("-bandwidth"))
-	{
-		if (M_IsNextParm())
-		{
-			net_bandwidth = atoi(M_GetNextParm());
-			if (net_bandwidth < 1000)
-				net_bandwidth = 1000;
-			if (net_bandwidth > 100000)
-				hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
-			CONS_Printf(M_GetText("Network bandwidth set to %d\n"), net_bandwidth);
-		}
-		else
-			I_Error("usage: -bandwidth <byte_per_sec>");
-	}
-
 	software_MAXPACKETLENGTH = hardware_MAXPACKETLENGTH;
 	if (M_CheckParm("-packetsize"))
 	{
@@ -1282,8 +1259,6 @@ boolean D_CheckNetGame(void)
 	if (netgame)
 		multiplayer = true;
 
-	if (doomcom->id != DOOMCOM_ID)
-		I_Error("Doomcom buffer invalid!");
 	if (doomcom->numnodes > MAXNETNODES)
 		I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES);
 
@@ -1293,7 +1268,7 @@ boolean D_CheckNetGame(void)
 	if (M_CheckParm("-debugfile"))
 	{
 		char filename[21];
-		INT32 k = doomcom->consoleplayer - 1;
+		INT32 k = consoleplayer - 1;
 		if (M_IsNextParm())
 			k = atoi(M_GetNextParm()) - 1;
 		while (!debugfile && k < MAXPLAYERS)
diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c
index a8a10d475d4afcc9c965573c1deaf59969c00b0f..adec1a0e47ecd4a2996a7edcf0016eb9c4529936 100644
--- a/src/netcode/d_netfil.c
+++ b/src/netcode/d_netfil.c
@@ -1033,7 +1033,6 @@ void FileSendTicker(void)
 
 	netbuffer->packettype = PT_FILEFRAGMENT;
 
-	// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
 	while (packetsent-- && filestosend != 0)
 	{
 		for (i = currentnode, j = 0; j < MAXNETNODES;
diff --git a/src/netcode/i_net.h b/src/netcode/i_net.h
index 09b842296c313cdd43623cfecd1d8da64534397b..6ac4bfc8756c86927066ac7fe8bf67f23080a234 100644
--- a/src/netcode/i_net.h
+++ b/src/netcode/i_net.h
@@ -32,7 +32,6 @@
 #define INETPACKETLENGTH 1024
 
 extern INT16 hardware_MAXPACKETLENGTH;
-extern INT32 net_bandwidth; // in byte/s
 
 #if defined(_MSC_VER)
 #pragma pack(1)
@@ -40,36 +39,17 @@ extern INT32 net_bandwidth; // in byte/s
 
 typedef struct
 {
-	/// Supposed to be DOOMCOM_ID
-	INT32 id;
-
-	/// SRB2 executes an INT32 to execute commands.
-	INT16 intnum;
-	/// Communication between SRB2 and the driver.
-	/// Is CMD_SEND or CMD_GET.
-	INT16 command;
 	/// Is dest for send, set by get (-1 = no packet).
 	INT16 remotenode;
-
 	/// Number of bytes in doomdata to be sent
 	INT16 datalength;
 
 	/// Info common to all nodes.
 	/// Console is always node 0.
 	INT16 numnodes;
-	/// Flag: 1 = no duplication, 2-5 = dup for slow nets.
-	INT16 ticdup;
 	/// Flag: 1 = send a backup tic in every packet.
 	INT16 extratics;
-	/// kind of game
-	INT16 gametype;
-	/// Flag: -1 = new game, 0-5 = load savegame
-	INT16 savegame;
-	/// currect map
-	INT16 map;
-
-	/// Info specific to this node.
-	INT16 consoleplayer;
+
 	/// Number of "slots": the highest player number in use plus one.
 	INT16 numslots;
 
diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c
index 24dfd7ec2797c190b0c0fdbf0343f19e33b001a0..d9af91c0465ed839854264d56705f86e4da5d9b6 100644
--- a/src/netcode/i_tcp.c
+++ b/src/netcode/i_tcp.c
@@ -37,6 +37,7 @@
 #endif
 
 #include "../doomdef.h"
+#include "../z_zone.h"
 
 #ifdef USE_WINSOCK1
 	#include <winsock.h>
@@ -87,6 +88,10 @@
 	#undef EHOSTUNREACH
 	#endif
 	#define EHOSTUNREACH WSAEHOSTUNREACH
+	#ifdef ENETUNREACH
+	#undef ENETUNREACH
+	#endif
+	#define ENETUNREACH WSAENETUNREACH
 	#ifndef IOC_VENDOR
 	#define IOC_VENDOR 0x18000000
 	#endif
@@ -120,8 +125,6 @@ typedef union
 	static boolean UPNP_support = true;
 	#endif // HAVE_MINIUPNC
 
-#define MAXBANS 100
-
 #include "../i_system.h"
 #include "i_net.h"
 #include "d_net.h"
@@ -169,8 +172,8 @@ static mysockaddr_t clientaddress[MAXNETNODES+1];
 static mysockaddr_t broadcastaddress[MAXNETNODES+1];
 static size_t broadcastaddresses = 0;
 static boolean nodeconnected[MAXNETNODES+1];
-static mysockaddr_t banned[MAXBANS];
-static UINT8 bannedmask[MAXBANS];
+static mysockaddr_t *banned;
+static UINT8 *bannedmask;
 
 static size_t numbans = 0;
 static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
@@ -685,7 +688,6 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr
 	socklen_t d6 = (socklen_t)sizeof(struct sockaddr_in6);
 #endif
 	socklen_t d, da = (socklen_t)sizeof(mysockaddr_t);
-	ssize_t status;
 
 	switch (sockaddr->any.sa_family)
 	{
@@ -696,14 +698,11 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr
 		default:       d = da; break;
 	}
 
-	status = sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d);
-	if (status == -1)
-	{
-		CONS_Alert(CONS_WARNING, "Unable to send packet to %s: %s\n", SOCK_AddrToStr(sockaddr), strerror(errno));
-	}
-	return status;
+	return sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d);
 }
 
+#define ALLOWEDERROR(x) ((x) == ECONNREFUSED || (x) == EWOULDBLOCK || (x) == EHOSTUNREACH || (x) == ENETUNREACH)
+
 static void SOCK_Send(void)
 {
 	ssize_t c = ERRSOCKET;
@@ -718,19 +717,25 @@ static void SOCK_Send(void)
 			for (size_t j = 0; j < broadcastaddresses; j++)
 			{
 				if (myfamily[i] == broadcastaddress[j].any.sa_family)
-					SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]);
+				{
+					c = SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]);
+					if (c == ERRSOCKET && !ALLOWEDERROR(errno))
+						break;
+				}
 			}
 		}
-		return;
 	}
 	else if (nodesocket[doomcom->remotenode] == (SOCKET_TYPE)ERRSOCKET)
 	{
 		for (size_t i = 0; i < mysocketses; i++)
 		{
 			if (myfamily[i] == clientaddress[doomcom->remotenode].any.sa_family)
-				SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]);
+			{
+				c = SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]);
+				if (c == ERRSOCKET && !ALLOWEDERROR(errno))
+					break;
+			}
 		}
-		return;
 	}
 	else
 	{
@@ -740,12 +745,14 @@ static void SOCK_Send(void)
 	if (c == ERRSOCKET)
 	{
 		int e = errno; // save error code so it can't be modified later
-		if (e != ECONNREFUSED && e != EWOULDBLOCK && e != EHOSTUNREACH)
+		if (!ALLOWEDERROR(e))
 			I_Error("SOCK_Send, error sending to node %d (%s) #%u: %s", doomcom->remotenode,
 				SOCK_GetNodeAddress(doomcom->remotenode), e, strerror(e));
 	}
 }
 
+#undef ALLOWEDERROR
+
 static void SOCK_FreeNodenum(INT32 numnode)
 {
 	// can't disconnect from self :)
@@ -1253,9 +1260,9 @@ static boolean SOCK_Ban(INT32 node)
 {
 	if (node > MAXNETNODES)
 		return false;
-	if (numbans == MAXBANS)
-		return false;
 
+	banned = Z_Realloc(banned, sizeof(*banned) * (numbans+1), PU_STATIC, NULL);
+	bannedmask = Z_Realloc(bannedmask, sizeof(*bannedmask) * (numbans+1), PU_STATIC, NULL);
 	M_Memcpy(&banned[numbans], &clientaddress[node], sizeof (mysockaddr_t));
 	if (banned[numbans].any.sa_family == AF_INET)
 	{
@@ -1278,7 +1285,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
 	struct my_addrinfo *ai, *runp, hints;
 	int gaie;
 
-	if (numbans == MAXBANS || !address)
+	if (!address)
 		return false;
 
 	memset(&hints, 0x00, sizeof(hints));
@@ -1293,8 +1300,10 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
 
 	runp = ai;
 
-	while(runp != NULL && numbans != MAXBANS)
+	while(runp != NULL)
 	{
+		banned = Z_Realloc(banned, sizeof(*banned) * (numbans+1), PU_STATIC, NULL);
+		bannedmask = Z_Realloc(bannedmask, sizeof(*bannedmask) * (numbans+1), PU_STATIC, NULL);
 		memcpy(&banned[numbans], runp->ai_addr, runp->ai_addrlen);
 
 		if (mask)
@@ -1324,6 +1333,10 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
 static void SOCK_ClearBans(void)
 {
 	numbans = 0;
+	Z_Free(banned);
+	banned = NULL;
+	Z_Free(bannedmask);
+	bannedmask = NULL;
 }
 
 boolean I_InitTcpNetwork(void)
@@ -1378,7 +1391,6 @@ boolean I_InitTcpNetwork(void)
 		// FIXME:
 		// ??? and now ?
 		// server on a big modem ??? 4*isdn
-		net_bandwidth = 16000;
 		hardware_MAXPACKETLENGTH = INETPACKETLENGTH;
 
 		ret = true;
@@ -1407,7 +1419,6 @@ boolean I_InitTcpNetwork(void)
 			// so we're on a LAN
 			COM_BufAddText("connect any\n");
 
-			net_bandwidth = 800000;
 			hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
 		}
 	}
diff --git a/src/p_map.c b/src/p_map.c
index b79f9d45c77ea069f079e1d2a50d6b45bfa1e646..c742e2e85e5513dcf1d239a612c6b75747688e1a 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -1465,13 +1465,13 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
 	}
 
 	// check for special pickup
-	if (thing->flags & MF_SPECIAL)
+	if (thing->flags & MF_SPECIAL && (tmthing->player || (tmthing->flags & MF_PUSHABLE))) // MF_PUSHABLE added for steam jets
 	{
 		P_TouchSpecialThing(thing, tmthing, true); // can remove thing
 		return CHECKTHING_COLLIDE;
 	}
 	// check again for special pickup
-	if (tmthing->flags & MF_SPECIAL)
+	if (tmthing->flags & MF_SPECIAL && (thing->player || (thing->flags & MF_PUSHABLE))) // MF_PUSHABLE added for steam jets
 	{
 		P_TouchSpecialThing(tmthing, thing, true); // can remove thing
 		return CHECKTHING_COLLIDE;
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 95e729c6658aaae350296f09f0fcc341297b971e..5c22285938b5e6ac1282a0692c225c50c2c841da 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -35,6 +35,7 @@
 #include "p_polyobj.h"
 #include "lua_script.h"
 #include "p_slopes.h"
+#include "hu_stuff.h"
 
 savedata_t savedata;
 
@@ -4854,6 +4855,12 @@ static void P_NetArchiveMisc(save_t *save_p, boolean resending)
 		P_WriteUINT8(save_p, 0x2f);
 	else
 		P_WriteUINT8(save_p, 0x2e);
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		P_WriteUINT8(save_p, spam_tokens[i]);
+		P_WriteUINT32(save_p, spam_tics[i]);
+	}
 }
 
 static inline boolean P_NetUnArchiveMisc(save_t *save_p, boolean reloading)
@@ -4951,6 +4958,12 @@ static inline boolean P_NetUnArchiveMisc(save_t *save_p, boolean reloading)
 	if (P_ReadUINT8(save_p) == 0x2f)
 		paused = true;
 
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		spam_tokens[i] = P_ReadUINT8(save_p);
+		spam_tics[i] = P_ReadUINT32(save_p);
+	}
+
 	return true;
 }
 
diff --git a/src/r_things.c b/src/r_things.c
index b32181670aa0ad9e3c5c59bcdc9f9fb2a810108d..36c35fde8ce99679d7f2b95762f132f8e29b7bb1 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -379,7 +379,7 @@ static void MirrorMissingRotations(void)
 
 			UINT8 baserotation = GetOppositeRotation(rotation, frame->rotate);
 			UINT32 lumpnum = frame->lumppat[baserotation - 1];
-			R_InstallSpriteLump(WADFILENUM(lumpnum), LUMPNUM(lumpnum), frame->lumpid[baserotation], framenum, rotation, 1);
+			R_InstallSpriteLump(WADFILENUM(lumpnum), LUMPNUM(lumpnum), frame->lumpid[baserotation - 1], framenum, rotation, 1);
 		}
 	}
 }
diff --git a/src/st_stuff.c b/src/st_stuff.c
index e088a448c129ed6e2392e8bcadf3cb62e9932525..9f1fb6d88c8e12b833fa618784ac9e4b90b8dab9 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -1042,6 +1042,9 @@ static void ST_drawInput(void)
 
 	INT32 x = hudinfo[HUD_INPUT].x, y = hudinfo[HUD_INPUT].y;
 
+	if (hu_showscores)
+		return;
+
 	if (stplyr->powers[pw_carry] == CR_NIGHTSMODE)
 		y += 8;
 	else if (modeattacking || !LUA_HudEnabled(hud_lives))
diff --git a/src/w_wad.c b/src/w_wad.c
index cc7cdc20167d616083d86db652b05aa8eb182e5e..97208296aadf193467c1c439c9def26d9d106ef0 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1329,10 +1329,10 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum
 			/* SLADE is special and puts a single directory entry. Skip that. */
 			if (strlen(lump_p->fullname) == name_length)
 				i++;
-			break;
+			return i;
 		}
 	}
-	return i;
+	return INT16_MAX;
 }
 
 // In a PK3 type of resource file, it looks for the next lumpinfo entry that doesn't share the specified pathfile.
@@ -1350,6 +1350,17 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump)
 	return i;
 }
 
+// Returns 0 if the folder is not empty, 1 if it is empty, -1 if it doesn't exist
+INT32 W_IsFolderEmpty(const char *name, UINT16 wad)
+{
+	UINT16 start = W_CheckNumForFolderStartPK3(name, wad, 0);
+	if (start == INT16_MAX)
+		return -1;
+
+	// Unlike W_CheckNumForFolderStartPK3, W_CheckNumForFolderEndPK3 doesn't return INT16_MAX.
+	return W_CheckNumForFolderEndPK3(name, wad, start) <= start;
+}
+
 char *W_GetLumpFolderPathPK3(UINT16 wad, UINT16 lump)
 {
 	const char *fullname = wadfiles[wad]->lumpinfo[lump].fullname;
@@ -1692,7 +1703,7 @@ lumpnum_t W_GetNumForLongName(const char *name)
 //
 static UINT16 W_CheckNumForPatchNamePwad(const char *name, UINT16 wad, boolean longname)
 {
-	UINT16 i, start, end;
+	UINT16 i, start = INT16_MAX, end = INT16_MAX;
 	static char uname[8 + 1] = { 0 };
 	UINT32 hash = 0;
 	lumpinfo_t *lump_p;
@@ -1714,8 +1725,11 @@ static UINT16 W_CheckNumForPatchNamePwad(const char *name, UINT16 wad, boolean l
 	// TODO: cache namespace lump IDs
 	if (W_FileHasFolders(wadfiles[wad]))
 	{
-		start = W_CheckNumForFolderStartPK3("Flats/", wad, 0);
-		end = W_CheckNumForFolderEndPK3("Flats/", wad, start);
+		if (!W_IsFolderEmpty("Flats/", wad))
+		{
+			start = W_CheckNumForFolderStartPK3("Flats/", wad, 0);
+			end = W_CheckNumForFolderEndPK3("Flats/", wad, start);
+		}
 	}
 	else
 	{
diff --git a/src/w_wad.h b/src/w_wad.h
index 3dcb9b6e8bbc6ee11669f473e9395a0006f84db0..4fd7b2c00896e4a57059ede8313d0e61340015c5 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -180,6 +180,7 @@ UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlu
 UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump);
 UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump);
 UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump);
+INT32 W_IsFolderEmpty(const char *name, UINT16 wad);
 char *W_GetLumpFolderPathPK3(UINT16 wad, UINT16 lump);
 char *W_GetLumpFolderNamePK3(UINT16 wad, UINT16 lump);